@itpay/cli 2.0.12 → 2.0.14
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/README.md +14 -7
- package/dist/src/commands/compatibility.js +5 -0
- package/dist/src/commands/docs.js +1 -0
- package/dist/src/commands/guidance.js +10 -22
- package/dist/src/commands/install.js +3 -3
- package/dist/src/commands/readyz.js +2 -2
- package/dist/src/commands/services.js +17 -7
- package/dist/src/commands/skill.js +11 -11
- package/dist/src/main.js +50 -18
- package/dist/src/state/config.js +5 -4
- package/docs/agent/buyer/cart-checkout.json +16 -3
- package/docs/agent/buyer/catalog-list.json +1 -0
- package/docs/agent/buyer/identity-and-sessions.json +35 -14
- package/docs/agent/buyer/install-and-setup.json +28 -8
- package/docs/agent/buyer/orders-refunds.json +13 -2
- package/docs/agent/buyer/payment-flow.json +17 -3
- package/docs/agent/buyer/quickstart.json +48 -13
- package/docs/agent/buyer/render-hosts.json +14 -2
- package/docs/cli-reference/agent-types.md +2 -0
- package/docs/cli-reference/commands/buy.md +2 -0
- package/docs/cli-reference/commands/cart/add.md +2 -0
- package/docs/cli-reference/commands/cart/clear.md +2 -0
- package/docs/cli-reference/commands/cart/index.md +2 -0
- package/docs/cli-reference/commands/cart/next.md +2 -0
- package/docs/cli-reference/commands/cart/remove.md +3 -1
- package/docs/cli-reference/commands/cart/show.md +2 -0
- package/docs/cli-reference/commands/catalog/index.md +2 -0
- package/docs/cli-reference/commands/catalog/list.md +2 -0
- package/docs/cli-reference/commands/checkout.md +2 -0
- package/docs/cli-reference/commands/device.md +3 -1
- package/docs/cli-reference/commands/docs/index.md +2 -0
- package/docs/cli-reference/commands/docs/list.md +2 -0
- package/docs/cli-reference/commands/docs/search.md +2 -0
- package/docs/cli-reference/commands/docs/show.md +2 -0
- package/docs/cli-reference/commands/install.md +5 -3
- package/docs/cli-reference/commands/next.md +2 -0
- package/docs/cli-reference/commands/order.md +2 -0
- package/docs/cli-reference/commands/orders.md +2 -0
- package/docs/cli-reference/commands/pay.md +2 -0
- package/docs/cli-reference/commands/readyz.md +34 -6
- package/docs/cli-reference/commands/refund/cancel.md +2 -0
- package/docs/cli-reference/commands/refund/create.md +2 -0
- package/docs/cli-reference/commands/refund/get.md +2 -0
- package/docs/cli-reference/commands/refund/index.md +2 -0
- package/docs/cli-reference/commands/refund/list.md +2 -0
- package/docs/cli-reference/commands/refund/watch.md +2 -0
- package/docs/cli-reference/commands/services/action.md +2 -0
- package/docs/cli-reference/commands/services/checkout.md +2 -0
- package/docs/cli-reference/commands/services/events.md +2 -0
- package/docs/cli-reference/commands/services/get.md +2 -0
- package/docs/cli-reference/commands/services/index.md +2 -0
- package/docs/cli-reference/commands/services/invoke.md +73 -4
- package/docs/cli-reference/commands/services/list.md +2 -0
- package/docs/cli-reference/commands/services/next.md +2 -0
- package/docs/cli-reference/commands/services/quote.md +2 -0
- package/docs/cli-reference/commands/services/read-result.md +2 -0
- package/docs/cli-reference/commands/services/start.md +2 -0
- package/docs/cli-reference/commands/skill.md +6 -4
- package/docs/cli-reference/conventions.md +5 -0
- package/docs/cli-reference/index.md +3 -1
- package/package.json +6 -3
- package/skills/{itpay-buyer → itpay}/SKILL.md +43 -16
package/README.md
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
# ItPay CLI
|
|
2
2
|
|
|
3
|
-
The official V3 CLI
|
|
3
|
+
The official V3 CLI and the single ItPay entry point for Agent-driven commerce.
|
|
4
|
+
|
|
5
|
+
## One Entry Point, Two Actions
|
|
6
|
+
|
|
7
|
+
`itpay` is the only public CLI entry point, and `$itpay` is its user-facing Skill invocation. Under that one product entry point, the two top-level commerce actions are `buy` and `sell`: Buyer workflows are available now, while Seller workflows will use the same entry point and are not implemented yet. Do not create separate Buyer or Seller product entry points.
|
|
4
8
|
|
|
5
9
|
```bash
|
|
6
10
|
npm install -g @itpay/cli
|
|
7
11
|
itpay readyz --json
|
|
8
|
-
itpay skill show itpay
|
|
12
|
+
itpay skill show itpay --json
|
|
9
13
|
itpay install --json
|
|
10
14
|
itpay --agent-type codex-desktop readyz --json
|
|
11
15
|
# follow next.command: typed skill show, then catalog list
|
|
12
16
|
```
|
|
13
17
|
|
|
14
|
-
The
|
|
18
|
+
The production Backend is permanently pinned to `https://app.itpay.ai`. Runtime environment variables cannot redirect the CLI to another Backend.
|
|
15
19
|
|
|
16
20
|
## Output Contract
|
|
17
21
|
|
|
@@ -42,11 +46,11 @@ Normative per-command contracts: [CLI Command Reference](docs/cli-reference/inde
|
|
|
42
46
|
|
|
43
47
|
`--agent-type` identifies the stable runtime and registered Agent instance. Every returned ItPay command preserves it. Different windows or chats of the same type reuse one Agent Instance; they are not separate identities. `--host` only selects the human presentation surface, and `--target` only routes output to a Host destination. Use `itpay install <agent_type> --json` for the exact responsibility.
|
|
44
48
|
|
|
45
|
-
The local installation keeps one Ed25519 private key
|
|
49
|
+
The local installation keeps one Ed25519 private key and one production Device registration for `https://app.itpay.ai`, with one Agent Instance per Agent Type. A rejected session is renewed and the same request is retried once; revoked v2 registrations are never silently replaced.
|
|
46
50
|
|
|
47
51
|
## Command Families
|
|
48
52
|
|
|
49
|
-
- `readyz
|
|
53
|
+
- `readyz`: fixed production Backend liveness; `catalog list`: compatibility-gated discovery.
|
|
50
54
|
- `services start/invoke/action/checkout/next`: generic Service Execution flow.
|
|
51
55
|
- `cart add/show/remove/clear/next`, `buy`: canonical Cart and ordinary Checkout flow.
|
|
52
56
|
- `checkout`: authoritative payment and fulfillment recovery.
|
|
@@ -71,11 +75,10 @@ itpay --agent-type <agent_type> services checkout <service_execution_id> --resum
|
|
|
71
75
|
itpay checkout --id <checkout_id> --token <display_token> --json
|
|
72
76
|
```
|
|
73
77
|
|
|
74
|
-
The local `~/.itpay-v3` directory stores one owner-only signing key,
|
|
78
|
+
The local `~/.itpay-v3` directory stores one owner-only signing key, the `https://app.itpay.ai` Device registration and Agent instances, idempotency operations, and recovery handles. Backend state remains authoritative. Do not delete or rotate this identity to recover quota.
|
|
75
79
|
|
|
76
80
|
## Environment
|
|
77
81
|
|
|
78
|
-
- `ITPAY_BACKEND_URL`: deliberate API override.
|
|
79
82
|
- `ITPAY_AGENT_TYPE`: stable alternative to global `--agent-type`.
|
|
80
83
|
- `ITPAY_BEARER_TOKEN`: account-scoped Buyer session for account-only commands such as `orders`.
|
|
81
84
|
- `ITPAY_CART_SESSION_PATH`: local recovery-state path override.
|
|
@@ -91,10 +94,14 @@ Provider credentials, Buyer identity, payment provider choice, amount, refund po
|
|
|
91
94
|
```bash
|
|
92
95
|
npm run lint
|
|
93
96
|
npm test
|
|
97
|
+
npm run test:coverage
|
|
94
98
|
npm run test:package
|
|
95
99
|
npm run pack:dry-run
|
|
96
100
|
```
|
|
97
101
|
|
|
102
|
+
`npm install` configures the repository's pre-commit hook. Every commit must pass
|
|
103
|
+
`npm run lint` and `npm test`; do not bypass the hook for pull-request changes.
|
|
104
|
+
|
|
98
105
|
Source boundaries:
|
|
99
106
|
|
|
100
107
|
- `src/main.ts`: parser and error-envelope wiring.
|
|
@@ -10,6 +10,11 @@ export async function requirePlatformCompatibility(backend) {
|
|
|
10
10
|
throw new HttpError(426, {
|
|
11
11
|
code: "client_upgrade_required",
|
|
12
12
|
message: `CLI ${CLI_VERSION} contract ${API_CONTRACT_REVISION} is incompatible with platform ${platform.platform_revision} contract ${platform.api_contract_revision} (minimum CLI ${platform.minimum_cli_version}, maximum major ${platform.maximum_cli_major})`,
|
|
13
|
+
minimum_cli_version: platform.minimum_cli_version,
|
|
14
|
+
maximum_cli_major: platform.maximum_cli_major,
|
|
15
|
+
platform_revision: platform.platform_revision,
|
|
16
|
+
api_contract_revision: platform.api_contract_revision,
|
|
17
|
+
upgrade_command: `npm install -g @itpay/cli@${platform.minimum_cli_version}`,
|
|
13
18
|
}, "CLI is incompatible with the active ItPay platform release");
|
|
14
19
|
}
|
|
15
20
|
function compareVersions(left, right) {
|
|
@@ -92,6 +92,7 @@ function findDocsDir() {
|
|
|
92
92
|
function parseDoc(raw, file) {
|
|
93
93
|
const value = JSON.parse(raw);
|
|
94
94
|
if (typeof value.schema_version !== "string" ||
|
|
95
|
+
typeof value.product_scope !== "string" ||
|
|
95
96
|
typeof value.topic !== "string" ||
|
|
96
97
|
typeof value.title !== "string" ||
|
|
97
98
|
typeof value.purpose !== "string") {
|
|
@@ -25,7 +25,7 @@ export function writeCommandEnvelope(value, options = {}) {
|
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
27
|
out(`${qualified.status}\n`);
|
|
28
|
-
const facts = "
|
|
28
|
+
const facts = "error" in qualified ? qualified.error : qualified.result;
|
|
29
29
|
if (options.plainResult) {
|
|
30
30
|
for (const line of options.plainResult)
|
|
31
31
|
out(`${line}\n`);
|
|
@@ -34,6 +34,11 @@ export function writeCommandEnvelope(value, options = {}) {
|
|
|
34
34
|
for (const [key, fact] of Object.entries(facts)) {
|
|
35
35
|
out(`${key}: ${typeof fact === "string" ? fact : JSON.stringify(fact)}\n`);
|
|
36
36
|
}
|
|
37
|
+
if ("error" in qualified && qualified.result) {
|
|
38
|
+
for (const [key, fact] of Object.entries(qualified.result)) {
|
|
39
|
+
out(`${key}: ${typeof fact === "string" ? fact : JSON.stringify(fact)}\n`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
37
42
|
}
|
|
38
43
|
if ("handoff" in qualified && qualified.handoff) {
|
|
39
44
|
for (const [key, fact] of Object.entries(qualified.handoff)) {
|
|
@@ -252,8 +257,8 @@ export function errorRecoveryActions(error) {
|
|
|
252
257
|
return [
|
|
253
258
|
{
|
|
254
259
|
id: "read_agent_session_rules",
|
|
255
|
-
label: "Read identity and session recovery rules",
|
|
256
|
-
command: "itpay skill show itpay
|
|
260
|
+
label: "Read ItPay identity and session recovery rules",
|
|
261
|
+
command: "itpay skill show itpay --json",
|
|
257
262
|
reason: "The CLI already attempted one automatic session renewal; do not rotate identity or loop retries.",
|
|
258
263
|
},
|
|
259
264
|
];
|
|
@@ -299,14 +304,9 @@ export function errorRecoveryActions(error) {
|
|
|
299
304
|
return [
|
|
300
305
|
{
|
|
301
306
|
id: "retry_after_backend_recovers",
|
|
302
|
-
label: "Retry after
|
|
307
|
+
label: "Retry after app.itpay.ai is reachable",
|
|
303
308
|
command: "itpay readyz",
|
|
304
309
|
},
|
|
305
|
-
{
|
|
306
|
-
id: "check_backend_url",
|
|
307
|
-
label: "Check the configured backend URL",
|
|
308
|
-
command: "echo $ITPAY_BACKEND_URL",
|
|
309
|
-
},
|
|
310
310
|
];
|
|
311
311
|
}
|
|
312
312
|
return [];
|
|
@@ -417,13 +417,7 @@ function buildServiceGuidance(input) {
|
|
|
417
417
|
}
|
|
418
418
|
}
|
|
419
419
|
else if ((input.providerCalled || execution.next_action === "select_candidate") && (input.resultItems?.length ?? 0) === 0) {
|
|
420
|
-
|
|
421
|
-
id: "start_refined_search",
|
|
422
|
-
label: "Start a new execution with a more specific company name",
|
|
423
|
-
command: `itpay services start ${execution.service_id}`,
|
|
424
|
-
requires_human: true,
|
|
425
|
-
reason: "No candidates were found. This execution is finished; use one new execution per new keyword.",
|
|
426
|
-
});
|
|
420
|
+
// Terminal empty result: a later explicit human request starts a new flow.
|
|
427
421
|
}
|
|
428
422
|
else if (needsHumanSelection(execution, resultItem)) {
|
|
429
423
|
nextActions.push({
|
|
@@ -433,12 +427,6 @@ function buildServiceGuidance(input) {
|
|
|
433
427
|
requires_human: true,
|
|
434
428
|
reason: "Do not choose a candidate without explicit human confirmation.",
|
|
435
429
|
});
|
|
436
|
-
nextActions.push({
|
|
437
|
-
id: "start_another_search",
|
|
438
|
-
label: "Search another company in a new execution",
|
|
439
|
-
command: `itpay services start ${execution.service_id}`,
|
|
440
|
-
reason: "This execution has completed its one keyword lookup; do not reuse it for another keyword.",
|
|
441
|
-
});
|
|
442
430
|
}
|
|
443
431
|
else if (prePurchase) {
|
|
444
432
|
const action = {
|
|
@@ -13,7 +13,7 @@ const INSTRUCTIONS = {
|
|
|
13
13
|
"codex-cli": "在 Codex CLI 中始终传这个 Agent Type;付款交接只应显示在用户可见终端。",
|
|
14
14
|
"claude-code-desktop": "在 Claude Code Desktop 中始终传这个 Agent Type;付款时把返回的二维码和链接实际展示到当前对话。",
|
|
15
15
|
"claude-code-cli": "在 Claude Code CLI 中始终传这个 Agent Type;付款交接只应显示在用户可见终端。",
|
|
16
|
-
workbuddy: "在 WorkBuddy 中始终传这个 Agent Type
|
|
16
|
+
workbuddy: "在 WorkBuddy 中始终传这个 Agent Type。运行会持久化 ~/.itpay-v3 的 ItPay Bash 命令前,把 Shell 工具参数 dangerouslyDisableSandbox 设为 true;用户拒绝权限时停止,不要切换 Node、删除 identity 或处理 lock。Checkout 返回 qr_image_url 时,按当次 instruction 调用 present_files 在右侧打开二维码,不要检查本地二维码文件。",
|
|
17
17
|
};
|
|
18
18
|
export function runInstall(target, options = {}) {
|
|
19
19
|
if (!target || target === "list") {
|
|
@@ -48,9 +48,9 @@ export function runInstall(target, options = {}) {
|
|
|
48
48
|
instruction: INSTRUCTIONS[normalized],
|
|
49
49
|
next: {
|
|
50
50
|
command: `itpay --agent-type ${normalized} readyz --json`,
|
|
51
|
-
reason: "
|
|
51
|
+
reason: "验证固定生产 ItPay API 的可用性",
|
|
52
52
|
},
|
|
53
|
-
recovery: [{ command: "itpay docs show install-and-setup", reason: "
|
|
53
|
+
recovery: [{ command: "itpay docs show install-and-setup", reason: "查看固定生产后端和首次使用说明" }],
|
|
54
54
|
}, options);
|
|
55
55
|
}
|
|
56
56
|
function isInstallAgentType(value) {
|
|
@@ -5,8 +5,8 @@ export async function runReadyz(backend, options = {}) {
|
|
|
5
5
|
writeCommandEnvelope({
|
|
6
6
|
status: response.status,
|
|
7
7
|
result: { backend: "available", ...(options.agentType ? { agent_type: options.agentType } : {}) },
|
|
8
|
-
instruction: "ItPay 可用;先完整读取内置
|
|
9
|
-
next: { command: "itpay skill show itpay
|
|
8
|
+
instruction: "ItPay 可用;先完整读取内置 ItPay Skill,再进入当前已支持的 buy 流程。sell 将来也使用同一入口,但当前尚未实现。",
|
|
9
|
+
next: { command: "itpay skill show itpay --json", reason: "加载完整操作与安全规则" },
|
|
10
10
|
recovery: [],
|
|
11
11
|
}, options);
|
|
12
12
|
}
|
|
@@ -106,13 +106,14 @@ function invokedEnvelope(response, requestedCapability, capabilities, input) {
|
|
|
106
106
|
const baseResult = {
|
|
107
107
|
service_execution_id: response.execution.service_execution_id,
|
|
108
108
|
capability_id: requestedCapability.capability_id,
|
|
109
|
+
query: input,
|
|
109
110
|
items,
|
|
110
111
|
...(quota ? { quota } : {}),
|
|
111
112
|
};
|
|
112
113
|
let status = items.length > 0 ? "result_ready" : "no_result";
|
|
113
114
|
let instruction = items.length > 0
|
|
114
115
|
? "向用户展示编号和 safe_payload;若候选列表已满足用户目标,在此停止。仅在用户明确选择并希望继续时,才在当前 Execution 提交对应 rank。"
|
|
115
|
-
:
|
|
116
|
+
: `没有找到与“${queryText(input)}”匹配的结果。向用户展示本次为 0 个结果并停止。不要修改、缩短或猜测其他输入;只有用户明确提供新输入后,才能启动新的查询。`;
|
|
116
117
|
let next = null;
|
|
117
118
|
if (response.effective_quota?.exhausted) {
|
|
118
119
|
status = "quota_exhausted";
|
|
@@ -148,9 +149,7 @@ function invokedEnvelope(response, requestedCapability, capabilities, input) {
|
|
|
148
149
|
};
|
|
149
150
|
}
|
|
150
151
|
else if (items.length === 0) {
|
|
151
|
-
next =
|
|
152
|
-
? { command: `itpay services start ${response.execution.service_id}`, reason: "为新的服务输入启动新 execution" }
|
|
153
|
-
: { command: `itpay services next ${response.execution.service_execution_id} --json`, reason: "读取服务端恢复动作" };
|
|
152
|
+
next = null;
|
|
154
153
|
}
|
|
155
154
|
return {
|
|
156
155
|
value: { status, result: baseResult, instruction, next, recovery: [] },
|
|
@@ -162,11 +161,18 @@ function serviceResultPlainLines(result) {
|
|
|
162
161
|
`service_execution_id: ${String(result.service_execution_id)}`,
|
|
163
162
|
`capability_id: ${String(result.capability_id)}`,
|
|
164
163
|
];
|
|
164
|
+
const items = result.items;
|
|
165
|
+
const query = result.query;
|
|
166
|
+
if (query) {
|
|
167
|
+
for (const [key, value] of Object.entries(query))
|
|
168
|
+
lines.push(`${key}: ${String(value)}`);
|
|
169
|
+
}
|
|
170
|
+
if (items.length === 0)
|
|
171
|
+
lines.push("results: 0");
|
|
165
172
|
if (result.quota)
|
|
166
173
|
lines.push(`quota: ${JSON.stringify(result.quota)}`);
|
|
167
174
|
if (result.checkout)
|
|
168
175
|
lines.push(`checkout: ${JSON.stringify(result.checkout)}`);
|
|
169
|
-
const items = result.items;
|
|
170
176
|
if (items.length > 0) {
|
|
171
177
|
lines.push("items:");
|
|
172
178
|
for (const item of items) {
|
|
@@ -178,6 +184,10 @@ function serviceResultPlainLines(result) {
|
|
|
178
184
|
}
|
|
179
185
|
return lines;
|
|
180
186
|
}
|
|
187
|
+
function queryText(input) {
|
|
188
|
+
const value = Object.values(input).find((item) => typeof item === "string" && item.trim() !== "");
|
|
189
|
+
return typeof value === "string" ? value : JSON.stringify(input);
|
|
190
|
+
}
|
|
181
191
|
function missingRequiredInput(schema, input) {
|
|
182
192
|
return requiredInputFields(schema).filter((field) => {
|
|
183
193
|
if (!(field in input) || input[field] === null || input[field] === undefined)
|
|
@@ -695,12 +705,12 @@ function servicesNextEnvelope(model) {
|
|
|
695
705
|
? selection
|
|
696
706
|
? "Agent-visible 搜索已完成。向用户展示 items 中的编号、title 和 safe_payload,然后停止;不要调用 read-result。只有用户明确选择候选并要求继续时,才执行 next.command。"
|
|
697
707
|
: "这是当前 Graph 步骤对应的交付;结果已可供 Agent 使用,只使用 safe_payload。"
|
|
698
|
-
: "Agent-visible
|
|
708
|
+
: "Agent-visible 交付已完成但有 0 个结果。向用户展示空结果并停止;不要调用 read-result、重放当前 Execution、修改输入或创建新 Execution。",
|
|
699
709
|
next: selection ? {
|
|
700
710
|
command: `itpay services action ${execution.service_execution_id} --action select_candidate --actor-type human --status approved --candidate <rank> --json`,
|
|
701
711
|
reason: "仅在用户明确选择后锁定来源候选",
|
|
702
712
|
} : null,
|
|
703
|
-
recovery:
|
|
713
|
+
recovery: [],
|
|
704
714
|
};
|
|
705
715
|
}
|
|
706
716
|
if (deliveryMode === "vault_artifact") {
|
|
@@ -4,27 +4,27 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import { declaredAgentType } from "../state/agent_type.js";
|
|
5
5
|
import { CommandContractError, writeCommandEnvelope } from "./guidance.js";
|
|
6
6
|
const commandDir = dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
const
|
|
7
|
+
const ITPAY_SKILL = "itpay";
|
|
8
8
|
export function runSkillShow(name, options = {}) {
|
|
9
9
|
const normalized = name.trim().toLowerCase();
|
|
10
|
-
if (normalized !==
|
|
11
|
-
throw new CommandContractError("skill_not_found", `skill not found: ${name}`, `当前 CLI 只内置 ${
|
|
10
|
+
if (normalized !== ITPAY_SKILL) {
|
|
11
|
+
throw new CommandContractError("skill_not_found", `skill not found: ${name}`, `当前 CLI 只内置 ${ITPAY_SKILL};不要猜测 Skill 名称。`, [{ command: `itpay skill show ${ITPAY_SKILL} --json`, reason: "读取完整 ItPay Skill" }]);
|
|
12
12
|
}
|
|
13
13
|
let content;
|
|
14
14
|
try {
|
|
15
15
|
content = readFileSync(findSkillPath(), "utf8");
|
|
16
16
|
}
|
|
17
17
|
catch {
|
|
18
|
-
throw new Error("packaged skill is unavailable: itpay
|
|
18
|
+
throw new Error("packaged skill is unavailable: itpay");
|
|
19
19
|
}
|
|
20
20
|
validateSkill(content);
|
|
21
21
|
const agentType = options.agentType ?? declaredAgentType();
|
|
22
22
|
const envelope = {
|
|
23
23
|
status: "shown",
|
|
24
|
-
result: { skill:
|
|
24
|
+
result: { skill: ITPAY_SKILL, content },
|
|
25
25
|
instruction: agentType
|
|
26
26
|
? agentType === "workbuddy"
|
|
27
|
-
? "完整读取并遵守 Skill;保持 workbuddy
|
|
27
|
+
? "完整读取并遵守 Skill;保持 workbuddy 和同一 Node/CLI launcher。运行会持久化 ~/.itpay-v3 的 ItPay Bash 命令前,把 Shell 工具参数 dangerouslyDisableSandbox 设为 true;用户拒绝权限时停止,不要切换 Node、删除 identity 或处理 lock。"
|
|
28
28
|
: `完整读取并遵守 Skill;当前 Agent Type 是 ${agentType},后续命令保持不变。`
|
|
29
29
|
: "完整读取并遵守 Skill;先如实选择当前运行环境对应的 Agent Type。",
|
|
30
30
|
next: agentType
|
|
@@ -40,16 +40,16 @@ export function runSkillShow(name, options = {}) {
|
|
|
40
40
|
}
|
|
41
41
|
function findSkillPath() {
|
|
42
42
|
if (process.env.ITPAY_CLI_SKILLS_DIR) {
|
|
43
|
-
return resolve(process.env.ITPAY_CLI_SKILLS_DIR,
|
|
43
|
+
return resolve(process.env.ITPAY_CLI_SKILLS_DIR, ITPAY_SKILL, "SKILL.md");
|
|
44
44
|
}
|
|
45
|
-
const packagePath = resolve(commandDir, "..", "..", "..", "skills",
|
|
45
|
+
const packagePath = resolve(commandDir, "..", "..", "..", "skills", ITPAY_SKILL, "SKILL.md");
|
|
46
46
|
if (existsSync(packagePath))
|
|
47
47
|
return packagePath;
|
|
48
|
-
return resolve(commandDir, "..", "..", "skills",
|
|
48
|
+
return resolve(commandDir, "..", "..", "skills", ITPAY_SKILL, "SKILL.md");
|
|
49
49
|
}
|
|
50
50
|
function validateSkill(content) {
|
|
51
51
|
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1];
|
|
52
|
-
if (!frontmatter || !/^name:\s*itpay
|
|
53
|
-
throw new Error("invalid packaged skill: itpay
|
|
52
|
+
if (!frontmatter || !/^name:\s*itpay\s*$/m.test(frontmatter) || !/^description:\s*(?:>|\S)/m.test(frontmatter)) {
|
|
53
|
+
throw new Error("invalid packaged skill: itpay");
|
|
54
54
|
}
|
|
55
55
|
}
|
package/dist/src/main.js
CHANGED
|
@@ -26,7 +26,7 @@ import { collectOption, parseKeyValueList, runServicesAction, runServicesCheckou
|
|
|
26
26
|
const program = new Command();
|
|
27
27
|
program
|
|
28
28
|
.name("itpay")
|
|
29
|
-
.description("V3 ItPay CLI —
|
|
29
|
+
.description("V3 ItPay CLI — one entry point for buy workflows and future sell workflows")
|
|
30
30
|
.option("--agent-type <type>", "agent runtime type used for device enrollment and client-specific guidance")
|
|
31
31
|
.version(CLI_VERSION);
|
|
32
32
|
function withHost(value) {
|
|
@@ -95,17 +95,22 @@ function reportCLIError(error, contract) {
|
|
|
95
95
|
error.code === "client_compatibility_headers_required" ||
|
|
96
96
|
error.code === "platform_release_unavailable" ||
|
|
97
97
|
(error.status === 404 && error.code === "unknown_error"));
|
|
98
|
+
const requiredCLIVersion = incompatible && error instanceof HttpError && /^\d+\.\d+\.\d+$/.test(error.payload?.minimum_cli_version ?? "")
|
|
99
|
+
? error.payload.minimum_cli_version
|
|
100
|
+
: undefined;
|
|
98
101
|
const backendInternal = error instanceof HttpError && error.status === 500 && error.code === "internal_error";
|
|
99
102
|
const providerConnectionUnavailable = error instanceof HttpError && error.code === "provider_connection_unavailable";
|
|
100
103
|
const providerTemporary = error instanceof HttpError && error.code === "provider_temporarily_unavailable";
|
|
101
104
|
const providerRejected = error instanceof HttpError && error.code === "provider_rejected";
|
|
105
|
+
const providerInputRejected = error instanceof HttpError && error.code === "provider_input_rejected";
|
|
106
|
+
const providerContractMismatch = error instanceof HttpError && error.code === "provider_contract_mismatch";
|
|
102
107
|
const capabilityInputInvalid = error instanceof HttpError && error.code === "capability_input_invalid";
|
|
103
108
|
const deviceRecovery = deviceError ? [{
|
|
104
|
-
command: "itpay skill show itpay
|
|
105
|
-
reason: "
|
|
109
|
+
command: "itpay skill show itpay --json",
|
|
110
|
+
reason: "读取 ItPay 身份边界;该错误需要用户或运营恢复 Backend 登记,不能通过换类型或删除本地身份绕过",
|
|
106
111
|
}] : [];
|
|
107
112
|
const stateRecovery = stateError ? [{
|
|
108
|
-
command: "itpay skill show itpay
|
|
113
|
+
command: "itpay skill show itpay --json",
|
|
109
114
|
reason: "读取 Device 状态边界;修复当前 Host 的持久写权限后重试原命令",
|
|
110
115
|
}] : [];
|
|
111
116
|
const authorizationInstruction = stateError
|
|
@@ -124,21 +129,50 @@ function reportCLIError(error, contract) {
|
|
|
124
129
|
code: incompatible ? "backend_contract_incompatible" : commandError?.code ?? (error instanceof HttpError ? error.code : stateError?.code ?? deviceError?.code ?? contract?.code ?? "command_failed"),
|
|
125
130
|
message: error instanceof Error ? error.message : String(error),
|
|
126
131
|
},
|
|
132
|
+
...(requiredCLIVersion ? {
|
|
133
|
+
result: {
|
|
134
|
+
current_cli_version: CLI_VERSION,
|
|
135
|
+
required_cli_version: requiredCLIVersion,
|
|
136
|
+
},
|
|
137
|
+
} : error instanceof HttpError && error.payload?.service_execution_id ? {
|
|
138
|
+
result: {
|
|
139
|
+
service_execution_id: error.payload.service_execution_id,
|
|
140
|
+
provider_called: error.payload.provider_called === true,
|
|
141
|
+
...(error.payload.effective_quota ? {
|
|
142
|
+
quota: {
|
|
143
|
+
remaining: error.payload.effective_quota.remaining,
|
|
144
|
+
limit: error.payload.effective_quota.limit,
|
|
145
|
+
},
|
|
146
|
+
} : {}),
|
|
147
|
+
},
|
|
148
|
+
} : {}),
|
|
127
149
|
instruction: incompatible
|
|
128
|
-
?
|
|
150
|
+
? requiredCLIVersion
|
|
151
|
+
? "当前 CLI 与 Backend 合约不兼容。停止所有 ItPay 业务命令;只执行 recovery.command,将 @itpay/cli 更新到 Backend 指定的精确版本。安装完成后确认 itpay --version 与 result.required_cli_version 完全一致,再重新运行 readyz。不要安装 latest、猜测版本、切换 Agent Type 或删除 Device 身份。"
|
|
152
|
+
: "立即向用户报告 error.message 并结束本次任务。Backend 未提供可验证的兼容 CLI 版本;不要运行其他 ItPay 或 npm 命令,不要猜测版本、切换 Agent Type 或删除 Device 身份。"
|
|
129
153
|
: backendInternal
|
|
130
154
|
? "Backend 内部故障;立即停止并向用户报告。不要重试、检查或删除 Device 身份、创建替代 Execution、切换 Backend,或尝试 quote、checkout、cart、buy、pay 等付费路径。"
|
|
131
155
|
: providerConnectionUnavailable
|
|
132
156
|
? "Provider 请求未发出,预留免费额度已释放;当前 Execution 已失败。立即向用户报告 error.message 并停止,不要自动重试、不要继续同一 Execution,也不要进入任何付费路径。只有运营确认连接恢复且用户明确要求重新查询后,才启动新的 Service Execution。"
|
|
133
157
|
: providerTemporary
|
|
134
|
-
? "上游服务暂时不可用;向用户逐字报告 error.message
|
|
135
|
-
:
|
|
136
|
-
?
|
|
137
|
-
:
|
|
138
|
-
? "
|
|
139
|
-
:
|
|
158
|
+
? "上游服务暂时不可用;向用户逐字报告 error.message 和 result.quota 并停止,不要自动重试、不要创建新 Execution。只有用户之后明确提出新请求,才可重新开始。"
|
|
159
|
+
: providerInputRejected
|
|
160
|
+
? `Provider 明确拒绝了该输入:${error instanceof Error ? error.message : String(error)}。请向用户报告 error.message 和 result.quota 并停止。不要自行修改输入、不要重试、不要创建新 Execution;只有用户明确提供新输入后才能重新查询。`
|
|
161
|
+
: providerContractMismatch
|
|
162
|
+
? "Provider 响应与已发布契约不一致。这不是用户输入问题。立即停止,不要修改输入、不要重试、不要创建新 Execution,也不要进入付费路径;向用户报告平台故障和 result.quota。"
|
|
163
|
+
: providerRejected
|
|
164
|
+
? "Provider 拒绝了本次请求,但未声明这是输入错误;向用户逐字报告 error.message 和 result.quota 并停止。不要修改输入、不要重试、不要创建新 Execution。"
|
|
165
|
+
: capabilityInputInvalid
|
|
166
|
+
? "输入未通过本地校验,上游尚未被调用且用户额度未变化。向用户逐字报告 error.message 并停止,不要原样重试或运行其他恢复命令。用户提供修正后的输入后,继续使用当前未结束的 Execution。"
|
|
167
|
+
: commandError?.instruction ?? authorizationInstruction ?? contract?.instruction ?? "检查命令参数后重试。",
|
|
140
168
|
next: null,
|
|
141
|
-
recovery: incompatible
|
|
169
|
+
recovery: incompatible
|
|
170
|
+
? requiredCLIVersion
|
|
171
|
+
? [{ command: `npm install -g @itpay/cli@${requiredCLIVersion}`, reason: "安装 Backend 指定的兼容 CLI 版本" }]
|
|
172
|
+
: []
|
|
173
|
+
: backendInternal || providerConnectionUnavailable || providerTemporary || providerInputRejected || providerContractMismatch || providerRejected || capabilityInputInvalid
|
|
174
|
+
? []
|
|
175
|
+
: commandError?.recovery ?? (stateError ? stateRecovery : deviceError ? deviceRecovery : identityRecovery ? httpRecovery : contract?.recovery ?? []),
|
|
142
176
|
}, {
|
|
143
177
|
...(contract?.jsonOutput !== undefined ? { jsonOutput: contract.jsonOutput } : {}),
|
|
144
178
|
output: (text) => { process.stderr.write(text); },
|
|
@@ -175,17 +209,15 @@ program
|
|
|
175
209
|
const config = loadConfig();
|
|
176
210
|
const backend = newBackendClient(config);
|
|
177
211
|
try {
|
|
178
|
-
await requirePlatformCompatibility(backend);
|
|
179
212
|
await runReadyz(backend, { jsonOutput: Boolean(options.json), ...(config.agentType ? { agentType: config.agentType } : {}) });
|
|
180
213
|
}
|
|
181
214
|
catch (error) {
|
|
182
215
|
reportCLIError(error, {
|
|
183
216
|
jsonOutput: Boolean(options.json),
|
|
184
217
|
code: "backend_unavailable",
|
|
185
|
-
instruction: "
|
|
218
|
+
instruction: "固定生产后端 https://app.itpay.ai 当前不可用;后端恢复前不要继续下单,也不要切换到其他地址。",
|
|
186
219
|
recovery: [
|
|
187
|
-
{ command: "
|
|
188
|
-
{ command: "itpay readyz", reason: "重试可用性检查" },
|
|
220
|
+
{ command: "itpay readyz", reason: "重试固定生产后端的可用性检查" },
|
|
189
221
|
],
|
|
190
222
|
});
|
|
191
223
|
}
|
|
@@ -194,14 +226,14 @@ program
|
|
|
194
226
|
const deviceCmd = program.command("device").description("Recover local Device registration state after an operator-confirmed Backend reset");
|
|
195
227
|
deviceCmd
|
|
196
228
|
.command("recover")
|
|
197
|
-
.description("Forget only the
|
|
229
|
+
.description("Forget only the app.itpay.ai registration while preserving the local private key")
|
|
198
230
|
.option("--confirm-backend-reset", "confirm that an operator reset the selected Backend registration database")
|
|
199
231
|
.option("--json", "output JSON instead of terminal text")
|
|
200
232
|
.action(async (options) => {
|
|
201
233
|
const config = loadConfig();
|
|
202
234
|
try {
|
|
203
235
|
if (!config.agentType) {
|
|
204
|
-
throw new CommandContractError("agent_type_required", "agent type is required for
|
|
236
|
+
throw new CommandContractError("agent_type_required", "agent type is required for app.itpay.ai Device recovery", "如实声明当前 Agent Type;恢复后必须用同一类型重新登记。", [{ command: "itpay install --json", reason: "选择当前真实 Agent Type" }]);
|
|
205
237
|
}
|
|
206
238
|
if (!options.confirmBackendReset) {
|
|
207
239
|
throw new CommandContractError("backend_reset_confirmation_required", "--confirm-backend-reset is required", "仅在运营已确认当前 Backend 的 Device 登记数据库被重建或清空后执行;普通 session 失效或 revoked 不得使用。", [{ command: "itpay docs show identity-and-sessions --json", reason: "检查适用边界" }]);
|
package/dist/src/state/config.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
// CLI configuration loader.
|
|
1
|
+
// CLI configuration loader. The production Backend is pinned to app.itpay.ai;
|
|
2
|
+
// environment variables configure only non-Backend runtime details. Checkout
|
|
2
3
|
// display-token persistence belongs to the cart session file, protected with
|
|
3
4
|
// owner-only permissions. Provider secrets are explicitly out of scope here.
|
|
4
5
|
import { homedir } from "node:os";
|
|
@@ -10,8 +11,8 @@ import { declaredAgentType } from "./agent_type.js";
|
|
|
10
11
|
import { DeviceAuthority } from "./device_authority.js";
|
|
11
12
|
import { OperationJournal } from "./operation_journal.js";
|
|
12
13
|
export const DEFAULT_BASE_URL = "https://app.itpay.ai";
|
|
13
|
-
export const CLI_VERSION = "2.0.
|
|
14
|
-
export const API_CONTRACT_REVISION = "sha256:
|
|
14
|
+
export const CLI_VERSION = "2.0.14";
|
|
15
|
+
export const API_CONTRACT_REVISION = "sha256:7f4c40b082292bf823631bcd37d452f4a8537153e30636d5eb3a2b24a77ce602";
|
|
15
16
|
const CART_SESSION_DEFAULT_DIR = ".itpay-v3";
|
|
16
17
|
const CART_SESSION_FILENAME = "cart.json";
|
|
17
18
|
const OPERATION_JOURNAL_FILENAME = "operations.json";
|
|
@@ -24,7 +25,7 @@ export function cartSessionPath(env = process.env) {
|
|
|
24
25
|
return resolve(dir, CART_SESSION_FILENAME);
|
|
25
26
|
}
|
|
26
27
|
export function loadConfig(env = process.env) {
|
|
27
|
-
const baseURL =
|
|
28
|
+
const baseURL = DEFAULT_BASE_URL;
|
|
28
29
|
const bearerToken = env.ITPAY_BEARER_TOKEN || undefined;
|
|
29
30
|
const agentType = declaredAgentType(env);
|
|
30
31
|
const checkoutCurrency = env.ITPAY_CURRENCY || "CNY";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": "itp.agent_doc.v1",
|
|
3
3
|
"role": "buyer",
|
|
4
|
+
"product_scope": "itpay is the single public CLI entry point, and $itpay is its user-facing Skill invocation. Under that one product entry point, the two top-level commerce actions are buy and sell: Buyer workflows are available now; Seller workflows will use the same entry point and are not implemented yet.",
|
|
4
5
|
"topic": "cart-checkout",
|
|
5
6
|
"title": "Canonical Cart And Checkout Routing",
|
|
6
7
|
"purpose": "Use the canonical server Cart and route service-backed lines through Service Execution without hardcoded service logic.",
|
|
@@ -43,8 +44,20 @@
|
|
|
43
44
|
"Do not treat QR display as payment verification."
|
|
44
45
|
],
|
|
45
46
|
"next_docs": [
|
|
46
|
-
{
|
|
47
|
-
|
|
47
|
+
{
|
|
48
|
+
"condition": "Checkout handoff is ready",
|
|
49
|
+
"topic": "payment-flow"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"condition": "Host rendering needs clarification",
|
|
53
|
+
"topic": "render-hosts"
|
|
54
|
+
}
|
|
48
55
|
],
|
|
49
|
-
"search_terms": [
|
|
56
|
+
"search_terms": [
|
|
57
|
+
"cart",
|
|
58
|
+
"checkout",
|
|
59
|
+
"service execution",
|
|
60
|
+
"resume",
|
|
61
|
+
"contact"
|
|
62
|
+
]
|
|
50
63
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": "itp.agent_doc.v1",
|
|
3
3
|
"role": "buyer",
|
|
4
|
+
"product_scope": "itpay is the single public CLI entry point, and $itpay is its user-facing Skill invocation. Under that one product entry point, the two top-level commerce actions are buy and sell: Buyer workflows are available now; Seller workflows will use the same entry point and are not implemented yet.",
|
|
4
5
|
"topic": "catalog-list",
|
|
5
6
|
"title": "Browse ItPay V3 Service Catalog",
|
|
6
7
|
"purpose": "Teach the agent how to browse and display available catalog services to the buyer.",
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": "itp.agent_doc.v1",
|
|
3
3
|
"role": "buyer",
|
|
4
|
+
"product_scope": "itpay is the single public CLI entry point, and $itpay is its user-facing Skill invocation. Under that one product entry point, the two top-level commerce actions are buy and sell: Buyer workflows are available now; Seller workflows will use the same entry point and are not implemented yet.",
|
|
4
5
|
"topic": "identity-and-sessions",
|
|
5
6
|
"title": "Device Identity And Session Recovery",
|
|
6
|
-
"purpose": "Explain stable local identity,
|
|
7
|
+
"purpose": "Explain stable local identity, the fixed app.itpay.ai production registration, Agent Type instances, and bounded automatic session recovery.",
|
|
7
8
|
"when_to_use": [
|
|
8
9
|
"A different Agent window or runtime starts using an existing CLI installation.",
|
|
9
10
|
"The CLI reports device_state_unwritable, or the Backend reports agent_identity_required, agent_device_session_required, or agent_device_revoked.",
|
|
10
|
-
"
|
|
11
|
+
"An operator confirms that the app.itpay.ai Device registration database was reset."
|
|
11
12
|
],
|
|
12
13
|
"identity_model": {
|
|
13
14
|
"local_key": "One owner-only Ed25519 private key for the local installation.",
|
|
14
|
-
"backend_registration": "One
|
|
15
|
-
"agent_instance": "One instance per agent_type under
|
|
15
|
+
"backend_registration": "One production Device registration for https://app.itpay.ai; runtime environment variables cannot redirect the CLI.",
|
|
16
|
+
"agent_instance": "One instance per agent_type under the production registration; same-type windows and chats reuse it.",
|
|
16
17
|
"presentation": "Host and target route output only and never identify the Agent or provide business input."
|
|
17
18
|
},
|
|
18
19
|
"automatic_recovery": [
|
|
@@ -29,36 +30,56 @@
|
|
|
29
30
|
"commands": [
|
|
30
31
|
{
|
|
31
32
|
"intent": "reload the complete rules",
|
|
32
|
-
"command": "itpay --agent-type <agent_type> skill show itpay
|
|
33
|
+
"command": "itpay --agent-type <agent_type> skill show itpay --json",
|
|
33
34
|
"success_signal": "the full Skill is returned and next keeps the same Agent Type"
|
|
34
35
|
},
|
|
35
36
|
{
|
|
36
|
-
"intent": "confirm
|
|
37
|
+
"intent": "confirm app.itpay.ai is available",
|
|
37
38
|
"command": "itpay --agent-type <agent_type> readyz --json",
|
|
38
39
|
"success_signal": "Backend is available and onboarding returns to the Skill"
|
|
39
40
|
},
|
|
40
41
|
{
|
|
41
|
-
"intent": "recover after an operator-confirmed reset of the
|
|
42
|
+
"intent": "recover after an operator-confirmed reset of the app.itpay.ai Device registration database",
|
|
42
43
|
"command": "itpay --agent-type <agent_type> device recover --confirm-backend-reset --json",
|
|
43
|
-
"success_signal": "only the
|
|
44
|
+
"success_signal": "only the app.itpay.ai registration is removed and the private key remains"
|
|
44
45
|
}
|
|
45
46
|
],
|
|
46
47
|
"agent_rules": [
|
|
47
48
|
"Use the real runtime type and keep it stable across every command in the flow.",
|
|
48
49
|
"Keep the same CLI/Node launcher and Host-approved permission context for the whole flow.",
|
|
49
50
|
"Treat a new window, chat, task, or process of the same runtime as the same Agent Type, not a new identity.",
|
|
50
|
-
"Expect
|
|
51
|
-
"After automatic recovery fails, stop and report the exact code,
|
|
51
|
+
"Expect all production CLI traffic to use https://app.itpay.ai; do not set or suggest a Backend URL override.",
|
|
52
|
+
"After automatic recovery fails, stop and report the exact code, app.itpay.ai, Agent Type, and command family without exposing private keys or tokens."
|
|
52
53
|
],
|
|
53
54
|
"forbidden": [
|
|
54
55
|
"Do not delete ~/.itpay-v3, rotate the key, switch Agent Type, or repeatedly retry to obtain fresh quota.",
|
|
55
56
|
"Do not use device recover for session expiry, revocation, quota recovery, or an unconfirmed Backend failure.",
|
|
56
|
-
"Do not
|
|
57
|
+
"Do not redirect the CLI to dev, test, local, or any non-app.itpay.ai Backend.",
|
|
57
58
|
"Do not use --target as identity or service input."
|
|
58
59
|
],
|
|
59
60
|
"next_docs": [
|
|
60
|
-
{
|
|
61
|
-
|
|
61
|
+
{
|
|
62
|
+
"condition": "Identity is healthy",
|
|
63
|
+
"topic": "quickstart"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"condition": "Need setup details",
|
|
67
|
+
"topic": "install-and-setup"
|
|
68
|
+
}
|
|
62
69
|
],
|
|
63
|
-
"search_terms": [
|
|
70
|
+
"search_terms": [
|
|
71
|
+
"identity",
|
|
72
|
+
"device",
|
|
73
|
+
"session",
|
|
74
|
+
"login",
|
|
75
|
+
"authentication",
|
|
76
|
+
"agent type",
|
|
77
|
+
"backend",
|
|
78
|
+
"revoked",
|
|
79
|
+
"recover",
|
|
80
|
+
"unwritable",
|
|
81
|
+
"lock",
|
|
82
|
+
"window",
|
|
83
|
+
"chat"
|
|
84
|
+
]
|
|
64
85
|
}
|