diff --git a/README.md b/README.md index 19ccdf5..0c4f681 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,12 @@ RUN_LIVE_TESTS=1 bun run test:e2e - `AELFSCAN_MCP_MAX_CHARS` (default: `60000`) - `AELFSCAN_MCP_INCLUDE_RAW` (default: `false`) +## Wallet Context Compatibility + +- This skill is read-only and does not consume signer/private-key context for on-chain writes. +- It is compatible with the shared wallet-context protocol (`~/.portkey/skill-wallet/context.v1.json`) used by write-capable skills. +- `bun run deps:check` validates wallet-context schema version when a local context file exists. + ## License MIT diff --git a/README.zh-CN.md b/README.zh-CN.md index 2ce4090..cadf618 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -120,6 +120,12 @@ RUN_LIVE_TESTS=1 bun run test:e2e - `AELFSCAN_MCP_MAX_CHARS`(默认 `60000`) - `AELFSCAN_MCP_INCLUDE_RAW`(默认 `false`) +## 钱包上下文兼容性 + +- 本 skill 为只读,不消费 signer/private-key 上下文,也不执行链上写操作。 +- 兼容写能力 skill 使用的共享 wallet-context 协议(`~/.portkey/skill-wallet/context.v1.json`)。 +- 当本地存在 context 文件时,`bun run deps:check` 会校验 wallet-context schema 版本。 + ## License MIT diff --git a/SKILL.md b/SKILL.md index d1cd6c1..b319439 100644 --- a/SKILL.md +++ b/SKILL.md @@ -16,8 +16,8 @@ description: "AelfScan explorer data retrieval and analytics skill for agents." ## Safe usage rules - Never print private keys, mnemonics, or tokens in channel outputs. -- For write operations, require explicit user confirmation and validate parameters before sending transactions. -- Prefer `simulate` or read-only queries first when available. +- This skill is read-only; do not attempt to execute chain writes via this package. +- If user intent requires writes, route to wallet + domain write skills and keep this skill for analytics. ## Command recipes - Start MCP server: `bun run mcp` @@ -28,5 +28,6 @@ description: "AelfScan explorer data retrieval and analytics skill for agents." ## Limits / Non-goals - This skill focuses on domain operations and adapters; it is not a full wallet custody system. +- It does not consume signer context for transaction signing. - Do not hardcode environment secrets in source code or docs. - Avoid bypassing validation for external service calls. diff --git a/bin/setup.ts b/bin/setup.ts index 053fd6d..25e45d9 100755 --- a/bin/setup.ts +++ b/bin/setup.ts @@ -1,6 +1,7 @@ #!/usr/bin/env bun import { Command } from 'commander'; import * as fs from 'node:fs'; +import packageJson from '../package.json'; import { LOG, SERVER_NAME, @@ -20,7 +21,7 @@ const program = new Command(); program .name('aelfscan-setup') .description('Configure @aelfscan/agent-skills for Claude/Cursor/OpenClaw') - .version('0.1.0'); + .version(packageJson.version); const withCommonMcpOptions = (command: Command) => command diff --git a/bun.lock b/bun.lock index e89db52..725cda7 100644 --- a/bun.lock +++ b/bun.lock @@ -11,6 +11,7 @@ }, "devDependencies": { "@types/bun": "latest", + "ajv": "^8.17.1", "typescript": "^5.7.0", }, }, diff --git a/package.json b/package.json index 97edf0b..ae1c7db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@aelfscan/agent-skills", - "version": "0.2.1", + "version": "0.2.2", "description": "AelfScan explorer skill toolkit for AI agents: MCP, CLI, and SDK interfaces.", "type": "module", "main": "index.ts", @@ -70,6 +70,7 @@ }, "devDependencies": { "@types/bun": "latest", + "ajv": "^8.17.1", "typescript": "^5.7.0" } } diff --git a/schemas/wallet-context.v1.schema.json b/schemas/wallet-context.v1.schema.json new file mode 100644 index 0000000..32e8d09 --- /dev/null +++ b/schemas/wallet-context.v1.schema.json @@ -0,0 +1,101 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/AElfProject/aelf-skills/docs/schemas/wallet-context.v1.schema.json", + "title": "WalletContextFileV1", + "type": "object", + "additionalProperties": false, + "required": [ + "version", + "activeProfileId", + "profiles", + "lastWriter" + ], + "properties": { + "version": { + "type": "integer", + "const": 1 + }, + "activeProfileId": { + "type": "string", + "minLength": 1 + }, + "profiles": { + "type": "object", + "minProperties": 0, + "additionalProperties": { + "$ref": "#/$defs/activeProfile" + } + }, + "lastWriter": { + "$ref": "#/$defs/lastWriter" + } + }, + "$defs": { + "activeProfile": { + "type": "object", + "additionalProperties": false, + "required": [ + "walletType", + "source", + "updatedAt" + ], + "properties": { + "walletType": { + "type": "string", + "enum": [ + "EOA", + "CA" + ] + }, + "source": { + "type": "string", + "enum": [ + "eoa-local", + "ca-keystore", + "env" + ] + }, + "network": { + "type": "string" + }, + "address": { + "type": "string" + }, + "caAddress": { + "type": "string" + }, + "caHash": { + "type": "string" + }, + "walletFile": { + "type": "string" + }, + "keystoreFile": { + "type": "string" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + } + } + }, + "lastWriter": { + "type": "object", + "additionalProperties": false, + "required": [ + "skill", + "version" + ], + "properties": { + "skill": { + "type": "string", + "minLength": 1 + }, + "version": { + "type": "string", + "minLength": 1 + } + } + } + } +} diff --git a/scripts/check-deps-baseline.ts b/scripts/check-deps-baseline.ts index 812c4e9..a62b2e4 100644 --- a/scripts/check-deps-baseline.ts +++ b/scripts/check-deps-baseline.ts @@ -1,6 +1,8 @@ #!/usr/bin/env bun import { existsSync, readFileSync } from 'node:fs'; +import { homedir } from 'node:os'; import { resolve } from 'node:path'; +import Ajv from 'ajv'; type Baseline = { dependencies: Record; @@ -14,6 +16,7 @@ function main() { const cwd = process.cwd(); const baselinePath = resolve(cwd, 'deps-baseline.json'); const packagePath = resolve(cwd, 'package.json'); + const contextSchemaPath = resolve(cwd, 'schemas', 'wallet-context.v1.schema.json'); if (!existsSync(baselinePath)) { console.error(`[deps:check] missing deps-baseline.json at ${baselinePath}`); @@ -44,8 +47,31 @@ function main() { } } + const contextPath = + process.env.PORTKEY_SKILL_WALLET_CONTEXT_PATH || + resolve(homedir(), '.portkey', 'skill-wallet', 'context.v1.json'); + if (!existsSync(contextSchemaPath)) { + failures.push(`missing wallet-context schema: ${contextSchemaPath}`); + } else if (existsSync(contextPath)) { + try { + const schema = readJson>(contextSchemaPath); + const contextRaw = readJson>(contextPath); + const ajv = new Ajv({ allErrors: true, strict: false }); + const validate = ajv.compile(schema); + if (!validate(contextRaw)) { + const details = (validate.errors || []) + .map((err) => `${err.instancePath || '/'} ${err.message || 'invalid'}`) + .join('; '); + failures.push(`wallet-context schema validation failed (${contextPath}): ${details}`); + } + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + failures.push(`wallet-context parse/validation failed: ${message}`); + } + } + if (failures.length > 0) { - console.error('[deps:check] dependency baseline mismatch:'); + console.error('[deps:check] check failed:'); for (const failure of failures) { console.error(`- ${failure}`); }