@lawrenceliang-btc/atel-sdk 1.1.27 → 1.1.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## 1.1.29 - 2026-04-06
4
+
5
+ ### Improved
6
+ - Clarified TokenHub CLI help so API key creation and account commands are easier to discover.
7
+ - Tightened English terminology across README, quick-start guides, and SKILL references.
8
+ - Standardized the distinction between DID-signed platform requests and TokenHub API-key requests.
9
+ - Updated swap guidance to describe `pending_verification` precisely.
10
+
11
+ ### Fixed
12
+ - `atel key create` now falls back to DID-signed bootstrap when a saved hub API key is invalid or revoked.
13
+ - `atel hub swap` now reports `pending_verification` as a submitted settlement state instead of implying final success.
package/README.md CHANGED
@@ -90,6 +90,37 @@ For custom runtimes, point `ATEL_EXECUTOR_URL` at your own service.
90
90
 
91
91
  For paid orders, do not hardcode Base as the only chain. Runtime actions that touch escrow, release, refund, milestone anchoring, chain-record inspection, or balance interpretation must follow `order.chain`.
92
92
 
93
+
94
+ ## TokenHub and AI Gateway
95
+
96
+ ATEL currently exposes two related but distinct integration surfaces:
97
+
98
+ - **DID-signed platform requests** under `/account/v1/...`
99
+ These are used by the CLI for account actions such as balance lookup, swaps, transfers, and account history.
100
+ - **TokenHub API-key requests** under `/tokenhub/v1/...`
101
+ These are used for external model access, OpenAI-compatible chat calls, and direct gateway integrations.
102
+
103
+ Canonical first-run flow:
104
+
105
+ ```bash
106
+ atel key create --name my-agent-key
107
+ atel hub balance
108
+ atel hub models --search gpt
109
+ atel hub chat openai/gpt-4o-mini "Hello"
110
+ atel swap usdc 0.01 --chain bsc
111
+ atel swap token 100 --chain bsc
112
+ atel transfer did:atel:ed25519:TARGET_DID 250 --memo "settlement"
113
+ ```
114
+
115
+ Important terminology:
116
+
117
+ - **TokenHub API key**: a gateway credential stored in `~/.atel/hub.json`
118
+ - **Platform DID**: the DID-backed account identity used for signed `/account/v1/...` requests
119
+ - **OpenAI-compatible gateway**: the `/tokenhub/v1/chat/completions` surface
120
+ - **`pending_verification`**: the on-chain settlement transaction was sent, but accounting is waiting for verification before balances are finalized
121
+
122
+ Use `atel hub swap-history` and `atel hub ledger` to inspect post-settlement account state.
123
+
93
124
  ## Architecture
94
125
 
95
126
  ATEL is organized into protocol and runtime layers:
package/bin/atel.mjs CHANGED
@@ -53,7 +53,7 @@
53
53
 
54
54
  import { readFileSync, writeFileSync, existsSync, mkdirSync, appendFileSync, copyFileSync } from 'node:fs';
55
55
  import { fileURLToPath } from 'node:url';
56
- import { cmdHub } from './hub-helpers.mjs';
56
+ import { cmdHub, cmdSwap, cmdTransfer } from './hub-helpers.mjs';
57
57
  import { resolve, join, dirname } from 'node:path';
58
58
  import crypto from 'node:crypto';
59
59
  import {
@@ -74,8 +74,8 @@ import { parseAttachmentFlags, processAttachments } from './atel-attachment-help
74
74
 
75
75
  const ATEL_DIR = resolve(process.env.ATEL_DIR || '.atel');
76
76
  const IDENTITY_FILE = resolve(ATEL_DIR, 'identity.json');
77
- const REGISTRY_URL = process.env.ATEL_REGISTRY || 'https://api.atelai.org';
78
- const ATEL_PLATFORM = process.env.ATEL_PLATFORM || 'https://api.atelai.org';
77
+ const ATEL_PLATFORM = process.env.ATEL_PLATFORM || process.env.ATEL_API || process.env.ATEL_REGISTRY || 'https://api.atelai.org';
78
+ const REGISTRY_URL = process.env.ATEL_REGISTRY || ATEL_PLATFORM || 'https://api.atelai.org';
79
79
  const ATEL_RELAY = process.env.ATEL_RELAY || 'https://api.atelai.org';
80
80
  const ATEL_NOTIFY_GATEWAY = process.env.ATEL_NOTIFY_GATEWAY || process.env.OPENCLAW_GATEWAY_URL || '';
81
81
  const ATEL_NOTIFY_TARGET = process.env.ATEL_NOTIFY_TARGET || '';
@@ -158,7 +158,7 @@ function shouldAllowRepoAccess(context = {}) {
158
158
  const resultSummary = String(context?.resultSummary || '').toLowerCase();
159
159
  const previousApprovedOutputs = String(context?.previousApprovedOutputs || '').toLowerCase();
160
160
  const combined = `${description}\n${objective}\n${resultSummary}\n${previousApprovedOutputs}`;
161
- return /(repo|repository|codebase|仓库|代码库|项目代码|source code|read files|analyze code|修改代码|修复代码|实现功能)/i.test(combined);
161
+ return /(repo|repository|codebase|\u4ed3\u5e93|\u4ee3\u7801\u5e93|\u9879\u76ee\u4ee3\u7801|source code|read files|analyze code|\u4fee\u6539\u4ee3\u7801|\u4fee\u590d\u4ee3\u7801|\u5b9e\u73b0\u529f\u80fd)/i.test(combined);
162
162
  }
163
163
 
164
164
  function summarizeApprovedMilestones(milestones = [], beforeIndex = Number.MAX_SAFE_INTEGER) {
@@ -312,26 +312,26 @@ async function pushTradeNotification(eventType, payload, body) {
312
312
  if (enabled.length === 0) return;
313
313
 
314
314
  const templates = {
315
- 'order_created': (p) => `📥 收到新订单\n订单: ${p.orderId || body?.orderId || '?'}\n金额: $${p.priceAmount ?? '?'} USDC\n来自: ${p.requesterDid || '未知请求方'}\n请审核后决定是否接单`,
316
- 'order_accepted': (p) => `📋 订单已被接单\n订单: ${p.orderId || body?.orderId || '?'}\n执行方已开始处理,进入里程碑阶段`,
315
+ 'order_created': (p) => `📥 New order received\nOrder: ${p.orderId || body?.orderId || '?'}\nAmount: $${p.priceAmount ?? '?'} USDC\nFrom: ${p.requesterDid || 'unknown requester'}\nReview and decide whether to accept`,
316
+ 'order_accepted': (p) => `📋 Order accepted\nOrder: ${p.orderId || body?.orderId || '?'}\nThe executor has started work. The order is now in the milestone phase`,
317
317
  'milestone_submitted': (p) => {
318
- const desc = p.milestoneDescription ? `\n目标: ${p.milestoneDescription}` : '';
319
- const content = p.resultSummary ? `\n提交内容: ${String(p.resultSummary).substring(0, 200)}` : '';
320
- return `📝 里程碑 M${p.milestoneIndex ?? '?'} 已提交\n订单: ${p.orderId || body?.orderId || '?'}${desc}${content}\n等待审核`;
318
+ const desc = p.milestoneDescription ? `\nGoal: ${p.milestoneDescription}` : '';
319
+ const content = p.resultSummary ? `\nSubmission: ${String(p.resultSummary).substring(0, 200)}` : '';
320
+ return `📝 Milestone M${p.milestoneIndex ?? '?'} submitted\nOrder: ${p.orderId || body?.orderId || '?'}${desc}${content}\nAwaiting review`;
321
321
  },
322
322
  'milestone_verified': (p) => {
323
- const desc = p.milestoneDescription ? `\n目标: ${p.milestoneDescription}` : '';
324
- const content = p.resultSummary ? `\n提交内容: ${String(p.resultSummary).substring(0, 200)}` : '';
325
- const progress = p.totalMilestones ? `\n进度: ${(p.milestoneIndex ?? 0) + 1}/${p.totalMilestones}` : '';
326
- return `✅ 里程碑 M${p.milestoneIndex ?? '?'} 审核通过\n订单: ${p.orderId || body?.orderId || '?'}${desc}${content}${progress}`;
323
+ const desc = p.milestoneDescription ? `\nGoal: ${p.milestoneDescription}` : '';
324
+ const content = p.resultSummary ? `\nSubmission: ${String(p.resultSummary).substring(0, 200)}` : '';
325
+ const progress = p.totalMilestones ? `\nProgress: ${(p.milestoneIndex ?? 0) + 1}/${p.totalMilestones}` : '';
326
+ return `✅ Milestone M${p.milestoneIndex ?? '?'} approved\nOrder: ${p.orderId || body?.orderId || '?'}${desc}${content}${progress}`;
327
327
  },
328
328
  'milestone_rejected': (p) => {
329
- const desc = p.milestoneDescription ? `\n目标: ${p.milestoneDescription}` : '';
330
- return `❌ 里程碑 M${p.milestoneIndex ?? '?'} 被拒绝\n订单: ${p.orderId || body?.orderId || '?'}${desc}\n原因: ${p.rejectReason || '未说明'}`;
329
+ const desc = p.milestoneDescription ? `\nGoal: ${p.milestoneDescription}` : '';
330
+ return `❌ Milestone M${p.milestoneIndex ?? '?'} rejected\nOrder: ${p.orderId || body?.orderId || '?'}${desc}\nReason: ${p.rejectReason || 'not provided'}`;
331
331
  },
332
332
  'order_settled': (p) => {
333
- const amount = p.priceAmount ? `\n金额: $${p.priceAmount} USDC` : '';
334
- return `💰 订单已结算完成\n订单: ${p.orderId || body?.orderId || '?'}${amount}\nUSDC 已支付`;
333
+ const amount = p.priceAmount ? `\nAmount: $${p.priceAmount} USDC` : '';
334
+ return `💰 Order settled\nOrder: ${p.orderId || body?.orderId || '?'}${amount}\nUSDC has been paid`;
335
335
  },
336
336
  };
337
337
  const tmpl = templates[eventType];
@@ -382,12 +382,12 @@ async function pushP2PNotification(eventType, payload = {}) {
382
382
  if (enabled.length === 0) return;
383
383
 
384
384
  const templates = {
385
- 'p2p_task_sent': (p) => `📤 P2P任务已发送\n任务: ${p.taskId || '?'}\n目标: ${p.peerDid || '?'}`,
386
- 'p2p_task_received': (p) => `📩 收到新的P2P任务\n任务: ${p.taskId || '?'}\n来自: ${p.peerDid || '?'}`,
387
- 'p2p_task_started': (p) => `▶️ P2P任务开始处理\n任务: ${p.taskId || '?'}\n来自: ${p.peerDid || '?'}`,
388
- 'p2p_result_submitted': (p) => `📨 P2P结果已发回对方\n任务: ${p.taskId || '?'}\n目标: ${p.peerDid || '?'}`,
389
- 'p2p_result_received': (p) => `✅ P2P任务已完成\n任务: ${p.taskId || '?'}\n结果: ${String(p.result || '').slice(0, 80) || '已返回'}`,
390
- 'p2p_task_failed': (p) => `❌ P2P任务失败\n任务: ${p.taskId || '?'}\n原因: ${p.reason || '未知错误'}`,
385
+ 'p2p_task_sent': (p) => `📤 P2P task sent\nTask: ${p.taskId || '?'}\nTarget: ${p.peerDid || '?'}`,
386
+ 'p2p_task_received': (p) => `📩 New P2P task received\nTask: ${p.taskId || '?'}\nFrom: ${p.peerDid || '?'}`,
387
+ 'p2p_task_started': (p) => `▶️ P2P task started\nTask: ${p.taskId || '?'}\nFrom: ${p.peerDid || '?'}`,
388
+ 'p2p_result_submitted': (p) => `📨 P2P result sent back\nTask: ${p.taskId || '?'}\nTarget: ${p.peerDid || '?'}`,
389
+ 'p2p_result_received': (p) => `✅ P2P task completed\nTask: ${p.taskId || '?'}\nResult: ${String(p.result || '').slice(0, 80) || 'returned'}`,
390
+ 'p2p_task_failed': (p) => `❌ P2P task failed\nTask: ${p.taskId || '?'}\nReason: ${p.reason || 'unknown error'}`,
391
391
  };
392
392
  const tmpl = templates[eventType];
393
393
  if (!tmpl) return;
@@ -2950,18 +2950,18 @@ async function cmdStart(port) {
2950
2950
  function buildGatewayCallbackPrompt(eventType, promptText, callbackUrl, dedupeKey, cwd, payload = {}) {
2951
2951
  const callbackExamples = {
2952
2952
  milestone_submitted: [
2953
- '通过时执行:',
2953
+ 'Run on approval:',
2954
2954
  "python3 - <<'PY'",
2955
2955
  'import json, urllib.request',
2956
- `body = {"dedupeKey": "${dedupeKey}", "status": "done", "eventType": "${eventType}", "decision": "pass", "summary": "审核通过的简短原因"}`,
2956
+ `body = {"dedupeKey": "${dedupeKey}", "status": "done", "eventType": "${eventType}", "decision": "pass", "summary": "brief reason for approval"}`,
2957
2957
  `req = urllib.request.Request("${callbackUrl}", data=json.dumps(body).encode(), headers={"Content-Type": "application/json"})`,
2958
2958
  'urllib.request.urlopen(req, timeout=10).read()',
2959
2959
  'PY',
2960
2960
  '',
2961
- '拒绝时执行:',
2961
+ 'Run on rejection:',
2962
2962
  "python3 - <<'PY'",
2963
2963
  'import json, urllib.request',
2964
- `body = {"dedupeKey": "${dedupeKey}", "status": "done", "eventType": "${eventType}", "decision": "reject", "reason": "具体拒绝原因", "summary": "简短审核结论"}`,
2964
+ `body = {"dedupeKey": "${dedupeKey}", "status": "done", "eventType": "${eventType}", "decision": "reject", "reason": "specific rejection reason", "summary": "brief review conclusion"}`,
2965
2965
  `req = urllib.request.Request("${callbackUrl}", data=json.dumps(body).encode(), headers={"Content-Type": "application/json"})`,
2966
2966
  'urllib.request.urlopen(req, timeout=10).read()',
2967
2967
  'PY',
@@ -2969,7 +2969,7 @@ async function cmdStart(port) {
2969
2969
  default: [
2970
2970
  "python3 - <<'PY'",
2971
2971
  'import json, urllib.request',
2972
- `result = """在这里写最终交付内容"""`,
2972
+ `result = """Write the final deliverable here"""`,
2973
2973
  `body = {"dedupeKey": "${dedupeKey}", "status": "done", "eventType": "${eventType}", "result": result, "summary": result[:120]}`,
2974
2974
  `req = urllib.request.Request("${callbackUrl}", data=json.dumps(body).encode(), headers={"Content-Type": "application/json"})`,
2975
2975
  'urllib.request.urlopen(req, timeout=10).read()',
@@ -2980,7 +2980,7 @@ async function cmdStart(port) {
2980
2980
  const callbackFailed = [
2981
2981
  "python3 - <<'PY'",
2982
2982
  'import json, urllib.request',
2983
- `body = {"dedupeKey": "${dedupeKey}", "status": "failed", "eventType": "${eventType}", "error": "在这里写失败原因"}`,
2983
+ `body = {"dedupeKey": "${dedupeKey}", "status": "failed", "eventType": "${eventType}", "error": "Write the failure reason here"}`,
2984
2984
  `req = urllib.request.Request("${callbackUrl}", data=json.dumps(body).encode(), headers={"Content-Type": "application/json"})`,
2985
2985
  'urllib.request.urlopen(req, timeout=10).read()',
2986
2986
  'PY',
@@ -2990,27 +2990,27 @@ async function cmdStart(port) {
2990
2990
  const contextFile = join(cwd, 'ORDER_CONTEXT.md');
2991
2991
  const allowRepoAccess = shouldAllowRepoAccess(payload);
2992
2992
  const fileAccessRule = allowRepoAccess
2993
- ? `3. 仅允许使用目录 ${cwd} 下与当前订单直接相关的内容;如果订单明确要求读仓库,也只能读取该订单工作区中明确提供的路径。`
2994
- : `3. 本单禁止读取任何本地文件、共享草稿、仓库或其他项目。不要使用文件搜索、目录浏览、读文件等方式扩展上下文;只允许依据本条消息中的订单描述、里程碑目标、提交内容来工作。`;
2993
+ ? `3. Only use content directly related to the current order under ${cwd}. If the order explicitly requires repository analysis, only read the paths explicitly provided inside this order workspace.`
2994
+ : `3. This order forbids reading any local files, shared drafts, repositories, or other projects. Do not expand context through file search, directory browsing, or file reads. Work only from the order description, milestone goal, and submission content in this message.`;
2995
2995
  const contextRule = allowRepoAccess
2996
- ? `4. 优先读取 ${contextFile},严格以其中的订单描述、里程碑目标、提交内容为准。`
2997
- : `4. 不要扫描本机其他目录,不要读取 /root/atel-workspace 下的共享文件,不要根据历史项目或 stray files 推断任务。`;
2996
+ ? `4. Read ${contextFile} first and treat its order description, milestone goal, and submission content as the authoritative source.`
2997
+ : `4. Do not scan other directories on the machine, do not read shared files under /root/atel-workspace, and do not infer the task from historical projects or stray files.`;
2998
2998
  const repoRule = allowRepoAccess
2999
- ? `5. 只有当订单明确要求分析仓库/代码时,才允许读取订单工作区里显式提供的代码路径;禁止顺带读取其他目录。`
3000
- : `5. 本单不是 repo/code 任务。禁止把任务扩展成代码分析、工程改造或共享草稿筛选。`;
2999
+ ? `5. Only when the order explicitly requires repository or code analysis may you read code paths explicitly provided in the order workspace. Do not read other directories opportunistically.`
3000
+ : `5. This order is not a repo/code task. Do not expand it into code analysis, engineering changes, or shared-draft review.`;
3001
3001
  if (eventType === 'p2p_task') {
3002
3002
  return `${promptText}
3003
3003
 
3004
- 重要要求:
3005
- 1. 这是一个 P2P 任务。不要调用 atel result;本地 SDK 会在你回调后自动提交结果。
3006
- 2. 你的任务是认真完成 AI 工作,并把最终结论通过回调发回本地 SDK
3004
+ Important requirements:
3005
+ 1. This is a P2P task. Do not call atel result; the local SDK will submit the result automatically after your callback.
3006
+ 2. Your job is to complete the AI work carefully and send the final conclusion back to the local SDK through the callback.
3007
3007
  ${fileAccessRule}
3008
3008
  ${contextRule}
3009
- 5. 完成后,必须立刻执行下面这个成功回调命令模板,并把其中内容替换成你的真实结果:
3009
+ 5. After finishing, you must immediately run the success callback command template below and replace its content with your real result:
3010
3010
 
3011
3011
  ${callbackDone}
3012
3012
 
3013
- 6. 如果重试后仍然失败,也必须执行下面这个失败回调命令:
3013
+ 6. If it still fails after retries, you must also run the failure callback command below:
3014
3014
 
3015
3015
  ${callbackFailed}
3016
3016
  `;
@@ -3018,17 +3018,17 @@ ${callbackFailed}
3018
3018
 
3019
3019
  return `${promptText}
3020
3020
 
3021
- 重要要求:
3022
- 1. 不要执行 atel milestone-submit / milestone-verify / milestone-feedback 命令;这些命令会由本地 SDK 在你回调后代为执行。
3023
- 2. 你的任务是认真完成 AI 工作,并把最终结论通过回调发回本地 SDK
3021
+ Important requirements:
3022
+ 1. Do not run atel milestone-submit / milestone-verify / milestone-feedback. The local SDK will execute those commands on your behalf after the callback.
3023
+ 2. Your job is to complete the AI work carefully and send the final conclusion back to the local SDK through the callback.
3024
3024
  ${fileAccessRule}
3025
3025
  ${contextRule}
3026
3026
  ${repoRule}
3027
- 6. 完成后,必须立刻执行下面这个成功回调命令模板,并把其中内容替换成你的真实结果:
3027
+ 6. After finishing, you must immediately run the success callback command template below and replace its content with your real result:
3028
3028
 
3029
3029
  ${callbackDone}
3030
3030
 
3031
- 7. 如果重试后仍然失败,也必须执行下面这个失败回调命令:
3031
+ 7. If it still fails after retries, you must also run the failure callback command below:
3032
3032
 
3033
3033
  ${callbackFailed}
3034
3034
  `;
@@ -3038,26 +3038,26 @@ ${callbackFailed}
3038
3038
  if (eventType === 'milestone_submitted') {
3039
3039
  return `${promptText}
3040
3040
 
3041
- 重要:你不是在和用户聊天。
3042
- 不要输出 markdown、代码块、解释、分析过程。
3043
- 你必须只输出一行 JSON
3041
+ Important: you are not chatting with the user.
3042
+ Do not output markdown, code blocks, explanations, or your reasoning.
3043
+ You must output exactly one line of JSON.
3044
3044
 
3045
- 通过时:
3046
- {"decision":"pass","summary":"简短通过原因"}
3045
+ For approval:
3046
+ {"decision":"pass","summary":"brief approval reason"}
3047
3047
 
3048
- 拒绝时:
3049
- {"decision":"reject","reason":"具体拒绝原因","summary":"简短审核结论"}`;
3048
+ For rejection:
3049
+ {"decision":"reject","reason":"specific rejection reason","summary":"brief review conclusion"}`;
3050
3050
  }
3051
3051
 
3052
3052
  if (['milestone_plan_confirmed', 'milestone_verified', 'milestone_rejected'].includes(eventType)) {
3053
3053
  return `${promptText}
3054
3054
 
3055
- 重要:你不是在和用户聊天。
3056
- 不要输出 markdown、标题、项目符号、解释、分析过程。
3057
- 你必须只输出一行 JSON
3055
+ Important: you are not chatting with the user.
3056
+ Do not output markdown, headings, bullet points, explanations, or your reasoning.
3057
+ You must output exactly one line of JSON.
3058
3058
 
3059
- 格式:
3060
- {"result":"当前里程碑的真实交付内容"}`;
3059
+ Format:
3060
+ {"result":"the actual deliverable for the current milestone"}`;
3061
3061
  }
3062
3062
 
3063
3063
  return promptText;
@@ -3168,10 +3168,10 @@ ${callbackFailed}
3168
3168
  return buildAgentCallbackAction(eventType, payload, parsed);
3169
3169
  } catch {
3170
3170
  const lowered = cleaned.toLowerCase();
3171
- if (lowered.startsWith('pass') || cleaned.includes('通过')) {
3171
+ if (lowered.startsWith('pass') || cleaned.includes('\u901a\u8fc7')) {
3172
3172
  return buildAgentCallbackAction(eventType, payload, { decision: 'pass', summary: cleaned });
3173
3173
  }
3174
- if (lowered.startsWith('reject') || cleaned.includes('拒绝')) {
3174
+ if (lowered.startsWith('reject') || cleaned.includes('\u62d2\u7edd')) {
3175
3175
  return buildAgentCallbackAction(eventType, payload, { decision: 'reject', reason: cleaned, summary: cleaned });
3176
3176
  }
3177
3177
  return { ok: false, error: 'invalid_local_review_stdout' };
@@ -3375,8 +3375,8 @@ ${callbackFailed}
3375
3375
  previousApprovedOutputs,
3376
3376
  };
3377
3377
  const promptText = currentIndex === 0
3378
- ? `你是ATEL接单方Agent。双方已确认方案,开始执行。\n订单原始要求:${orderDescription || '未提供'}\n当前里程碑 M0:${currentMilestone.title || ''}\n请只围绕这个订单要求完成当前里程碑,并通过回调返回最终交付内容。`
3379
- : `你是ATEL接单方Agent。M${currentIndex - 1} 已通过审核。\n订单原始要求:${orderDescription || '未提供'}\n下一个里程碑 M${currentIndex}:${currentMilestone.title || ''}\n前面已通过的阶段结果如下:\n${previousApprovedOutputs || ''}\n\n请严格基于这些已通过结果推进当前里程碑,不要自行假设缺失材料,也不要读取本地共享文件来补上下文。完成后通过回调返回最终交付内容。`;
3378
+ ? `You are the ATEL executor agent. The plan has been confirmed and execution begins.\nOriginal order requirements: ${orderDescription || 'not provided'}\nCurrent milestone M0: ${currentMilestone.title || ''}\nComplete only this milestone for the current order and return the final deliverable via the callback.`
3379
+ : `You are the ATEL executor agent. M${currentIndex - 1} has been approved.\nOriginal order requirements: ${orderDescription || 'not provided'}\nNext milestone M${currentIndex}: ${currentMilestone.title || ''}\nPreviously approved outputs:\n${previousApprovedOutputs || 'none'}\n\nAdvance the current milestone strictly based on these approved results. Do not invent missing materials or read local shared files to fill missing context. After completion, return the final deliverable via the callback.`;
3380
3380
  const recoveryKey = buildMilestoneHookRecoveryKey(eventType, payload);
3381
3381
  log({ event: 'trade_reconcile_executor', orderId, currentMilestone: currentIndex, recoveryKey });
3382
3382
  const queued = queueAgentHook(eventType, recoveryKey, promptText, workspace.dir, payload, { recoveryKey });
@@ -3409,7 +3409,7 @@ ${callbackFailed}
3409
3409
  orderDescription,
3410
3410
  previousApprovedOutputs,
3411
3411
  };
3412
- const promptText = `你是ATEL发单方Agent,需要审核执行方提交的工作。\n订单原始要求:${orderDescription || '未提供'}\n里程碑目标:${submittedMilestone.title || ''}\n前面已通过的阶段结果如下:\n${previousApprovedOutputs || ''}\n提交内容:${submittedMilestone.resultSummary || ''}\n请只按该订单要求和前序已通过结果审慎决定通过还是拒绝,并通过回调返回 decision=pass decision=reject。`;
3412
+ const promptText = `You are the ATEL requester agent. You need to review the work submitted by the executor.\nOriginal order requirements: ${orderDescription || 'not provided'}\nMilestone goal: ${submittedMilestone.title || ''}\nPreviously approved outputs:\n${previousApprovedOutputs || 'none'}\nSubmission: ${submittedMilestone.resultSummary || ''}\nDecide carefully whether to pass or reject based only on the order requirements and the previously approved outputs, then return decision=pass or decision=reject via the callback.`;
3413
3413
  const recoveryKey = buildMilestoneHookRecoveryKey('milestone_submitted', payload);
3414
3414
  const queued = queueAgentHook('milestone_submitted', recoveryKey, promptText, workspace.dir, payload, { recoveryKey });
3415
3415
  if (queued) log({ event: 'trade_reconcile_requester', orderId, milestoneIndex: submittedMilestone.index, recoveryKey });
@@ -3615,7 +3615,7 @@ ${callbackFailed}
3615
3615
  return;
3616
3616
  }
3617
3617
  // Add working directory context so agent runs atel commands in the right place
3618
- const cwdNote = `\n\n重要:OpenClaw 的分析工作目录是 ${hookCwd}。所有 atel 命令必须在目录 ${atelCwd} 下执行(cd ${atelCwd} && atel ...)。`;
3618
+ const cwdNote = `\n\nImportant: OpenClaw's analysis working directory is ${hookCwd}. All atel commands must be run in ${atelCwd} (cd ${atelCwd} && atel ...).`;
3619
3619
  const enrichedPrompt = sanitizeAgentPrompt(prompt + cwdNote, { eventType: event, dedupeKey });
3620
3620
  if (!enrichedPrompt) {
3621
3621
  res.json({ status: 'received', eventId, eventType: event, skipped: true });
@@ -4542,7 +4542,7 @@ ${callbackFailed}
4542
4542
  fetch(EXECUTOR_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ taskId, from: message.from, action, payload, encrypted: !!session?.encrypted, toolProxy: `http://127.0.0.1:${toolProxyPort}` }) }).catch(e => log({ event: 'executor_forward_failed', taskId, error: e.message }));
4543
4543
  return { status: 'accepted', taskId, message: 'Task accepted. Result will be pushed when ready.' };
4544
4544
  } else {
4545
- const p2pPrompt = `你是 ATEL 接单方 Agent,收到一个 P2P 任务。\n任务类型:${action}\n任务内容:${JSON.stringify(payload?.payload || payload || {}, null, 2)}\n请认真完成任务,并通过回调返回最终结果。`;
4545
+ const p2pPrompt = `You are the ATEL executor agent and received a P2P task.\nTask type: ${action}\nTask payload: ${JSON.stringify(payload?.payload || payload || {}, null, 2)}\nComplete the task carefully and return the final result via the callback.`;
4546
4546
  appendP2PTaskStatus({ taskId, role: 'executor', peerDid: message.from, status: 'task_started' });
4547
4547
  pushP2PNotification('p2p_task_received', { taskId, peerDid: message.from }).catch(() => {});
4548
4548
  pushP2PNotification('p2p_task_started', { taskId, peerDid: message.from }).catch(() => {});
@@ -4934,6 +4934,17 @@ async function cmdRegister(name, capabilities, endpointUrl) {
4934
4934
  if (preferredChain) registerPayload.metadata = { preferredChain };
4935
4935
 
4936
4936
  const entry = await client.register(registerPayload, id);
4937
+ let actualWallets = wallets || null;
4938
+ for (let i = 0; i < 8; i++) {
4939
+ try {
4940
+ const resp = await fetch(`${REGISTRY_URL}/registry/v1/agent/${encodeURIComponent(id.did)}`, { signal: AbortSignal.timeout(5000) });
4941
+ if (resp.ok) {
4942
+ const fresh = await resp.json();
4943
+ if (fresh?.wallets) { actualWallets = fresh.wallets; break; }
4944
+ }
4945
+ } catch {}
4946
+ await new Promise(r => setTimeout(r, 750));
4947
+ }
4937
4948
  if (!wallets || !preferredChain) {
4938
4949
  console.error('[register] INFO: registered without published chain readiness. Yellow page registration still works. Free tasks can run normally. Paid orders require an anchoring key at execution time; re-register or restart later if you want wallets/preferredChain shown in the registry.');
4939
4950
  }
@@ -4953,7 +4964,7 @@ async function cmdRegister(name, capabilities, endpointUrl) {
4953
4964
  capabilities: capDisplay,
4954
4965
  endpoint: ep,
4955
4966
  discoverable,
4956
- wallets: wallets || null,
4967
+ wallets: actualWallets || null,
4957
4968
  preferredChain: preferredChain || null,
4958
4969
  registry: REGISTRY_URL,
4959
4970
  }, null, 2));
@@ -6037,6 +6048,73 @@ async function cmdCompletionProof(orderId) {
6037
6048
  }
6038
6049
  }
6039
6050
 
6051
+ async function cmdVerifyTx(txHash) {
6052
+ if (!txHash) { console.error('Usage: atel verify-tx <txHash>'); process.exit(1); }
6053
+ try {
6054
+ if (!txHash.startsWith('0x')) txHash = '0x' + txHash;
6055
+ const res = await fetch(`${PLATFORM_URL}/verify/v1/tx/${txHash}`);
6056
+ const data = await res.json();
6057
+ if (!res.ok) {
6058
+ console.log(data.error || 'No record found for this transaction hash.');
6059
+ return;
6060
+ }
6061
+ const records = data.records || [];
6062
+ const order = data.order;
6063
+
6064
+ for (const r of records) {
6065
+ console.log('\n=== On-Chain Record ===');
6066
+ console.log(`Transaction: ${r.txHash || '-'}`);
6067
+ console.log(`Chain: ${r.chain || '-'}`);
6068
+ console.log(`Type: ${(r.operationType || '-').replace(/_/g, ' ')}`);
6069
+ console.log(`Order: ${r.orderId || '-'}`);
6070
+ console.log(`Anchor Key: ${r.anchorKey || '-'}`);
6071
+ console.log(`Status: ${r.status || '-'}`);
6072
+ if (r.createdAt) console.log(`Created: ${r.createdAt}`);
6073
+
6074
+ if (r.enrichedData) {
6075
+ console.log('\n--- Enriched Data ---');
6076
+ const ed = r.enrichedData;
6077
+ if (ed.executorDid) console.log(` Executor: ${ed.executorDid}`);
6078
+ if (ed.requesterDid) console.log(` Requester: ${ed.requesterDid}`);
6079
+ if (ed.capability) console.log(` Capability: ${ed.capability}`);
6080
+ if (ed.priceAmount != null) console.log(` Amount: $${ed.priceAmount}`);
6081
+ if (ed.title) console.log(` Title: ${ed.title}`);
6082
+ if (ed.milestoneIndex != null) console.log(` Milestone: #${ed.milestoneIndex}`);
6083
+ if (ed.resultSummary) console.log(` Result: ${ed.resultSummary.slice(0, 100)}${ed.resultSummary.length > 100 ? '...' : ''}`);
6084
+ if (ed.submittedAt) console.log(` Submitted: ${ed.submittedAt}`);
6085
+ if (ed.verifiedAt) console.log(` Verified: ${ed.verifiedAt}`);
6086
+ if (ed.eventType) console.log(` Event: ${ed.eventType}`);
6087
+ if (ed.scoreDelta != null) console.log(` Score Delta: ${ed.scoreDelta > 0 ? '+' : ''}${ed.scoreDelta}`);
6088
+ if (ed.scoreAfter != null) console.log(` Score After: ${ed.scoreAfter}`);
6089
+ }
6090
+
6091
+ if (r.verification) {
6092
+ const v = r.verification;
6093
+ console.log('\n--- Hash Verification ---');
6094
+ console.log(` Stored Hash: ${v.dataHashStored || '-'}`);
6095
+ if (v.dataHashRecomputed) console.log(` Recomputed: ${v.dataHashRecomputed}`);
6096
+ if (v.match != null) console.log(` Match: ${v.match ? '✅ Verified' : '❌ Mismatch'}`);
6097
+ console.log(` ${v.message}`);
6098
+ }
6099
+ }
6100
+
6101
+ if (order) {
6102
+ console.log('\n=== Associated Order ===');
6103
+ console.log(`Order: ${order.orderId}`);
6104
+ console.log(`Status: ${order.status}`);
6105
+ console.log(`Chain: ${order.chain}`);
6106
+ console.log(`Amount: $${order.priceAmount}`);
6107
+ console.log(`Capability: ${order.capabilityType}`);
6108
+ console.log(`Executor: ${order.executorDid}`);
6109
+ console.log(`Requester: ${order.requesterDid}`);
6110
+ if (order.verdict) console.log(`Verdict: ${order.verdict}`);
6111
+ console.log(`Chain Records: ${order.totalChainRecords}`);
6112
+ }
6113
+ } catch (e) {
6114
+ console.error('Failed to verify transaction:', e.message);
6115
+ }
6116
+ }
6117
+
6040
6118
  async function cmdOrderInfo(orderId) {
6041
6119
  if (!orderId) { console.error('Usage: atel order-info <orderId>'); process.exit(1); }
6042
6120
  const res = await fetch(`${PLATFORM_URL}/trade/v1/order/${orderId}`);
@@ -6515,7 +6593,7 @@ async function cmdMilestoneFeedback(orderId) {
6515
6593
 
6516
6594
  if (!approve && !feedback) {
6517
6595
  console.error('Usage: atel milestone-feedback <orderId> --approve');
6518
- console.error(' atel milestone-feedback <orderId> --feedback "修改意见"');
6596
+ console.error(' atel milestone-feedback <orderId> --feedback "feedback text"');
6519
6597
  process.exit(1);
6520
6598
  }
6521
6599
 
@@ -6526,7 +6604,7 @@ async function cmdMilestoneFeedback(orderId) {
6526
6604
 
6527
6605
  async function cmdMilestoneSubmit(orderId, indexStr) {
6528
6606
  if (!orderId || indexStr === undefined) {
6529
- console.error('Usage: atel milestone-submit <orderId> <index> --result "结果描述" [--file <path>]');
6607
+ console.error('Usage: atel milestone-submit <orderId> <index> --result "result summary" [--file <path>]');
6530
6608
  console.error(' atel milestone-submit <orderId> <index> --result ./file.pdf [--hash 0x...] [--file <path>]');
6531
6609
  process.exit(1);
6532
6610
  }
@@ -6598,7 +6676,7 @@ async function cmdMilestoneSubmit(orderId, indexStr) {
6598
6676
  async function cmdMilestoneVerify(orderId, indexStr) {
6599
6677
  if (!orderId || indexStr === undefined) {
6600
6678
  console.error('Usage: atel milestone-verify <orderId> <index> --pass');
6601
- console.error(' atel milestone-verify <orderId> <index> --reject "原因"');
6679
+ console.error(' atel milestone-verify <orderId> <index> --reject "reason"');
6602
6680
  process.exit(1);
6603
6681
  }
6604
6682
  const pass = rawArgs.includes('--pass');
@@ -8136,6 +8214,7 @@ const commands = {
8136
8214
  // AVIP
8137
8215
  'intent-info': () => cmdIntentInfo(args[0]),
8138
8216
  'completion-proof': () => cmdCompletionProof(args[0]),
8217
+ 'verify-tx': () => cmdVerifyTx(args[0]),
8139
8218
  // Dispute
8140
8219
  dispute: () => cmdDispute(args[0], args[1], args[2]),
8141
8220
  evidence: () => cmdEvidence(args[0], args[1]),
@@ -8230,6 +8309,10 @@ const commands = {
8230
8309
  auth: () => cmdAuth(args[0]),
8231
8310
  // Send (Rich Media P2P Message)
8232
8311
  hub: () => cmdHub(args[0], args.slice(1), rawArgs),
8312
+ key: () => cmdHub('key', args, rawArgs),
8313
+ // Platform account operations
8314
+ swap: () => cmdSwap(args[0], args[1], rawArgs),
8315
+ transfer: () => cmdTransfer(args[0], args[1], rawArgs),
8233
8316
  send: () => {
8234
8317
  if (rawArgs.includes('--help') || rawArgs.includes('-h') || args.length === 0) {
8235
8318
  showSendHelp();
@@ -8351,7 +8434,7 @@ const commands = {
8351
8434
  const resp = await fetch(`https://api.telegram.org/bot${target.botToken}/sendMessage`, {
8352
8435
  method: 'POST',
8353
8436
  headers: { 'Content-Type': 'application/json' },
8354
- body: JSON.stringify({ chat_id: target.target, text: '🔔 ATEL 通知测试\n如果你看到这条消息,说明通知已正确配置!' }),
8437
+ body: JSON.stringify({ chat_id: target.target, text: '🔔 ATEL notification test\nIf you can see this message, notifications are configured correctly!' }),
8355
8438
  signal: AbortSignal.timeout(10000),
8356
8439
  });
8357
8440
  const data = await resp.json();
@@ -8436,13 +8519,24 @@ Account Commands:
8436
8519
  transactions List payment history
8437
8520
 
8438
8521
  Hub Commands:
8439
- hub balance Show ATELToken balance
8440
- hub usage [--model <id>] [--days 7] Usage history
8441
- hub topup Top-up instructions
8442
- hub swap <usdc> [--chain bsc|base] Swap USDC → ATELToken
8443
- hub models [--search <kw>] List available models
8444
- hub chat <model> "<prompt>" [--stream] Quick chat
8445
- hub key <create|list|revoke|use> Manage TokenHub API keys
8522
+ swap usdc <amount> [--chain bsc|base] Swap USDC → ATELToken
8523
+ swap token <amount> [--chain bsc|base] Swap ATELToken → USDC
8524
+ transfer <to_did> <amount> [--memo] Transfer ATELToken to another DID
8525
+
8526
+ key create [--name <name>] Create and save a TokenHub API key
8527
+ key list List TokenHub API keys
8528
+ key revoke <id> Revoke a TokenHub API key
8529
+ key use Print OpenAI-compatible env exports
8530
+
8531
+ hub balance Show USDC and ATELToken balances
8532
+ hub dashboard Show a compact TokenHub account summary
8533
+ hub usage [--model <id>] [--days 7] Show model usage history
8534
+ hub ledger [--page N] [--limit N] Show account transaction records
8535
+ hub swap-history [--page N] [--limit N] Show swap records
8536
+ hub stats Show public TokenHub stats
8537
+ hub models [--search <kw>] List available models
8538
+ hub chat <model> "<prompt>" [--stream] Send a quick chat request
8539
+ hub key <create|list|revoke|use> Manage TokenHub API keys
8446
8540
 
8447
8541
  Trade Commands:
8448
8542
  trade-task <cap> <desc> [--budget N] One-shot: search → order → wait → confirm (requester)