@itpay/cli 2.1.19 → 2.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/README.md +2 -2
- package/dist/src/commands/buy.js +3 -3
- package/dist/src/commands/cart.js +15 -15
- package/dist/src/commands/catalog.js +10 -7
- package/dist/src/commands/checkout.js +8 -11
- package/dist/src/commands/docs.js +3 -14
- package/dist/src/commands/guidance.js +41 -25
- package/dist/src/commands/install.js +2 -2
- package/dist/src/commands/order.js +4 -3
- package/dist/src/commands/orders.js +4 -9
- package/dist/src/commands/pay.js +1 -1
- package/dist/src/commands/rail_catalog.js +160 -11
- package/dist/src/commands/refund.js +5 -5
- package/dist/src/commands/services.js +658 -361
- package/dist/src/commands/skill.js +1 -1
- package/dist/src/commands/vault.js +27 -20
- package/dist/src/main.js +39 -24
- package/dist/src/render/markdown.js +0 -3
- package/dist/src/state/account_auth.js +9 -5
- package/dist/src/state/cart_session.js +4 -1
- package/dist/src/state/config.js +1 -1
- package/dist/src/state/task_journal.js +2 -0
- package/docs/agent/buyer/cart-checkout.json +5 -4
- package/docs/agent/buyer/catalog-list.json +24 -6
- package/docs/agent/buyer/identity-and-sessions.json +16 -15
- package/docs/agent/buyer/install-and-setup.json +5 -4
- package/docs/agent/buyer/orders-refunds.json +11 -5
- package/docs/agent/buyer/payment-flow.json +3 -3
- package/docs/agent/buyer/purchased-content.json +18 -4
- package/docs/agent/buyer/quickstart.json +22 -14
- package/docs/agent/buyer/rail-booking.json +125 -171
- package/docs/agent/buyer/rail-fast-checkout.json +21 -44
- package/docs/agent/buyer/render-hosts.json +9 -6
- package/docs/agent/buyer/service-feedback.json +2 -1
- package/docs/cli-reference/agent-types.md +1 -1
- package/docs/cli-reference/commands/auth.md +2 -0
- package/docs/cli-reference/commands/buy.md +1 -1
- package/docs/cli-reference/commands/cart/add.md +1 -1
- package/docs/cli-reference/commands/cart/clear.md +1 -1
- package/docs/cli-reference/commands/cart/index.md +1 -1
- package/docs/cli-reference/commands/cart/next.md +1 -1
- package/docs/cli-reference/commands/cart/remove.md +1 -1
- package/docs/cli-reference/commands/cart/show.md +1 -1
- package/docs/cli-reference/commands/catalog/index.md +1 -1
- package/docs/cli-reference/commands/catalog/list.md +1 -1
- package/docs/cli-reference/commands/checkout.md +1 -1
- package/docs/cli-reference/commands/device.md +8 -7
- package/docs/cli-reference/commands/docs/index.md +1 -1
- package/docs/cli-reference/commands/docs/list.md +1 -1
- package/docs/cli-reference/commands/docs/search.md +1 -1
- package/docs/cli-reference/commands/docs/show.md +1 -1
- package/docs/cli-reference/commands/install.md +2 -2
- package/docs/cli-reference/commands/next.md +1 -1
- package/docs/cli-reference/commands/order.md +1 -1
- package/docs/cli-reference/commands/pay.md +1 -1
- package/docs/cli-reference/commands/readyz.md +1 -1
- package/docs/cli-reference/commands/refund/cancel.md +1 -1
- package/docs/cli-reference/commands/refund/create.md +1 -1
- package/docs/cli-reference/commands/refund/get.md +2 -2
- package/docs/cli-reference/commands/refund/index.md +1 -1
- package/docs/cli-reference/commands/refund/list.md +1 -1
- package/docs/cli-reference/commands/refund/watch.md +1 -1
- package/docs/cli-reference/commands/services/action.md +3 -3
- package/docs/cli-reference/commands/services/checkout.md +1 -1
- package/docs/cli-reference/commands/services/events.md +1 -1
- package/docs/cli-reference/commands/services/get.md +1 -1
- package/docs/cli-reference/commands/services/index.md +1 -1
- package/docs/cli-reference/commands/services/invoke.md +5 -13
- package/docs/cli-reference/commands/services/list.md +1 -1
- package/docs/cli-reference/commands/services/next.md +12 -10
- package/docs/cli-reference/commands/services/page.md +9 -7
- package/docs/cli-reference/commands/services/quote.md +1 -1
- package/docs/cli-reference/commands/services/read-result.md +16 -9
- package/docs/cli-reference/commands/services/run.md +4 -2
- package/docs/cli-reference/commands/services/start.md +1 -1
- package/docs/cli-reference/conventions.md +2 -2
- package/package.json +7 -5
- package/skills/itpay/SKILL.md +77 -238
- package/docs/reviews/agent-ux-handoff-2026-09-23/IMPLEMENTATION-EVIDENCE.md +0 -174
- package/docs/skill-bundle-rollout/01-mcp-authentication.md +0 -134
- package/docs/skill-bundle-rollout/02-platform-bundle-repositories.md +0 -210
- package/docs/skill-bundle-rollout/03-platform-publishing.md +0 -161
- package/docs/skill-bundle-rollout/04-first-wave-platforms.md +0 -70
- package/docs/skill-bundle-rollout/05-sync-operations.md +0 -62
- package/docs/skill-bundle-rollout/README.md +0 -151
- /package/bin/{itp → itp.mjs} +0 -0
|
@@ -24,7 +24,7 @@ export function runSkillShow(name, options = {}) {
|
|
|
24
24
|
result: { skill: ITPAY_SKILL, content },
|
|
25
25
|
instruction: agentType
|
|
26
26
|
? agentType === "workbuddy"
|
|
27
|
-
? "完整读取并遵守 Skill
|
|
27
|
+
? "完整读取并遵守 Skill;按用户意图选择第一条命令。保持 workbuddy 和同一 CLI launcher,并为 ~/.itpay-v3 申请宿主允许的持久化权限;权限被拒时报告受阻操作,不删除 identity。"
|
|
28
28
|
: `完整读取并遵守 Skill;把自己视为服务用户的代理,先理解用户意图和解释用户权益,再选择购买、已购内容、订单或退款对应的第一条命令。当前 Agent Type 是 ${agentType},后续命令保持不变。`
|
|
29
29
|
: "完整读取并遵守 Skill;先如实选择当前运行环境对应的 Agent Type。",
|
|
30
30
|
next: agentType
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HttpError } from "../client/http.js";
|
|
2
|
-
import { requiresTarget } from "../state/client_context.js";
|
|
2
|
+
import { requiresTarget, validateContext } from "../state/client_context.js";
|
|
3
3
|
import { formatMoney } from "../render/output.js";
|
|
4
|
-
import { CommandContractError, writeCommandEnvelope } from "./guidance.js";
|
|
4
|
+
import { CommandContractError, shellArgument, writeCommandEnvelope } from "./guidance.js";
|
|
5
5
|
import { buildVaultHandoff } from "./vault_handoff.js";
|
|
6
6
|
function outputOptions(options, plainResult) {
|
|
7
7
|
return {
|
|
@@ -19,21 +19,21 @@ export async function runVaultList(backend, input) {
|
|
|
19
19
|
const value = await backend.listBuyerVaultArtifacts(input);
|
|
20
20
|
writeCommandEnvelope({
|
|
21
21
|
status: value.items.length ? "vault_listed" : "no_vault_artifacts",
|
|
22
|
-
result: { items: value.items, next_cursor: value.next_cursor || null },
|
|
22
|
+
result: { items: value.items.map(item => ({ ...item, amount: formatMoney(item.amount_minor, item.currency), reader: { command: vaultReadCommand(item.artifact_ref, [], input), reason: "读取这一已购内容" } })), next_cursor: value.next_cursor || null },
|
|
23
23
|
instruction: value.items.length
|
|
24
|
-
? "
|
|
24
|
+
? "用编号、服务名称、内容主体、购买时间、金额和订单号说明匹配结果,不要向用户显示内部内容标识。一个精确匹配可按用户原始查看意图继续读取;已有任务可明确匹配时读取对应行;只有真实歧义才让用户选择。"
|
|
25
25
|
: "当前账号没有匹配的已购内容。向用户说明没有找到,不要猜测内容标识、自动购买或发起新的服务查询。",
|
|
26
|
-
next: null,
|
|
26
|
+
next: value.next_cursor ? { command: `itpay vault list --limit ${input.limit}${input.query ? ` --query ${shellArgument(input.query)}` : ""} --cursor ${shellArgument(value.next_cursor)}${contextFlags(input)} --json`, reason: "读取同一筛选的下一页" } : null,
|
|
27
27
|
recovery: [],
|
|
28
|
-
}, outputOptions(input
|
|
28
|
+
}, outputOptions(input));
|
|
29
29
|
}
|
|
30
30
|
catch (error) {
|
|
31
31
|
if (error instanceof HttpError && error.code === "vault_authorization_required") {
|
|
32
32
|
writeCommandEnvelope({
|
|
33
33
|
status: "human_authorization_required",
|
|
34
34
|
result: { intent: "list_purchased_content", query: input.query ?? "" },
|
|
35
|
-
instruction:
|
|
36
|
-
|
|
35
|
+
instruction: `需要用户确认一次身份和只读权限。按当前动作或待填模板生成官方入口,不要声称链接已经创建;用户完成后重新运行原始 vault list 命令。${accessContextInstruction(input)}`,
|
|
36
|
+
...vaultAccessGuidance(undefined, input), recovery: [],
|
|
37
37
|
}, outputOptions(input));
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
@@ -41,6 +41,9 @@ export async function runVaultList(backend, input) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
export async function runVaultAccess(backend, artifactRef, options) {
|
|
44
|
+
const issue = validateContext(options.host, options.target);
|
|
45
|
+
if (issue)
|
|
46
|
+
throw new CommandContractError(issue.code, issue.message, "先补齐当前可信会话的真实宿主和目标;尚未创建授权请求。", [], { schema_version: "itpay.interaction.v1", stage: "input_required", input_template: { command: vaultAccessCommand(artifactRef, options), required_input: issue.code === "target_required" ? ["target"] : ["host"], executable: false } });
|
|
44
47
|
const value = await backend.createVaultAccessRequest(artifactRef
|
|
45
48
|
? { purpose: "artifact_reveal", artifact_ref: artifactRef }
|
|
46
49
|
: { purpose: "account_window" });
|
|
@@ -90,23 +93,23 @@ export async function runVaultRead(backend, artifactRef, sections, options) {
|
|
|
90
93
|
: value.status === "result_preparing"
|
|
91
94
|
? "这份已购内容仍在准备。稍后只重试同一 read,不要重新授权、购买或调用 Provider。"
|
|
92
95
|
: "这份已购内容当前不可用。停止,不要重试、重新购买或绕过退款锁。",
|
|
93
|
-
next: null, recovery: [],
|
|
96
|
+
next: value.status === "result_preparing" ? { command: vaultReadCommand(artifactRef, normalized, options), reason: "稍后读取同一内容及原sections;不重新授权或购买" } : null, recovery: [],
|
|
94
97
|
}, outputOptions(options));
|
|
95
98
|
}
|
|
96
99
|
catch (error) {
|
|
97
100
|
if (error instanceof HttpError && error.code === "artifact_authorization_required") {
|
|
98
101
|
writeCommandEnvelope({
|
|
99
102
|
status: "human_authorization_required", result: { artifact_ref: artifactRef },
|
|
100
|
-
instruction:
|
|
101
|
-
|
|
103
|
+
instruction: `这份内容需要用户单独确认读取权限。按当前动作或待填模板生成一次官方入口;用户完成后重新运行原始 read,不要重复创建授权请求。${accessContextInstruction(options)}`,
|
|
104
|
+
...vaultAccessGuidance(artifactRef, options), recovery: [],
|
|
102
105
|
}, outputOptions(options));
|
|
103
106
|
return;
|
|
104
107
|
}
|
|
105
108
|
if (error instanceof HttpError && error.code === "vault_authorization_required") {
|
|
106
109
|
writeCommandEnvelope({
|
|
107
110
|
status: "human_authorization_required", result: { artifact_ref: artifactRef },
|
|
108
|
-
instruction:
|
|
109
|
-
|
|
111
|
+
instruction: `账号读取授权已缺失或过期。按当前动作或待填模板生成一次官方入口;用户完成后重新运行原始 read。${accessContextInstruction(options)}`,
|
|
112
|
+
...vaultAccessGuidance(undefined, options), recovery: [],
|
|
110
113
|
}, outputOptions(options));
|
|
111
114
|
return;
|
|
112
115
|
}
|
|
@@ -121,7 +124,7 @@ export function vaultAccessCommand(artifactRef, options) {
|
|
|
121
124
|
const host = options.host ?? (openClaw ? "<host>" : undefined);
|
|
122
125
|
if (host)
|
|
123
126
|
parts.push("--host", host);
|
|
124
|
-
const target = options.target ?? (openClaw &&
|
|
127
|
+
const target = options.target ?? (((openClaw && !options.host) || (options.host && requiresTarget(options.host))) ? "<target>" : undefined);
|
|
125
128
|
if (target)
|
|
126
129
|
parts.push("--target", shellArgument(target));
|
|
127
130
|
parts.push("--json");
|
|
@@ -132,10 +135,14 @@ export function accessContextInstruction(options) {
|
|
|
132
135
|
? " 将 <host> 和 <target> 替换为当前可信 OpenClaw 会话的真实值,不要照抄占位符或猜测目标。"
|
|
133
136
|
: "";
|
|
134
137
|
}
|
|
135
|
-
function
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
function contextFlags(options) {
|
|
139
|
+
return `${options.host ? ` --host ${options.host}` : ""}${options.target ? ` --target ${shellArgument(options.target)}` : ""}`;
|
|
140
|
+
}
|
|
141
|
+
function vaultReadCommand(artifact, sections, options) {
|
|
142
|
+
return `itpay vault read --artifact ${shellArgument(artifact)}${sections.map(section => ` --section ${shellArgument(section)}`).join("")}${contextFlags(options)} --json`;
|
|
143
|
+
}
|
|
144
|
+
export function vaultAccessGuidance(artifact, options) {
|
|
145
|
+
const command = vaultAccessCommand(artifact, options);
|
|
146
|
+
const missing = [...(options.agentType === "openclaw" && !options.host ? ["host"] : []), ...(options.host && requiresTarget(options.host) && !options.target || options.agentType === "openclaw" && !options.host && !options.target ? ["target"] : [])];
|
|
147
|
+
return missing.length ? { next: null, interaction: { schema_version: "itpay.interaction.v1", stage: "input_required", input_template: { command, required_input: missing, executable: false } } } : { next: { command, reason: "创建一次官方只读授权入口;用户完成后恢复原reader" } };
|
|
141
148
|
}
|
package/dist/src/main.js
CHANGED
|
@@ -25,7 +25,7 @@ import { runCancelRefund, runGetRefund, runListRefunds, runRefund, runWatchRefun
|
|
|
25
25
|
import { runCartAdd, runCartAddQuoteServer, runCartAddServer, runCartAbandonServer, runCartClear, runCartNext, runCartRemove, runCartRemoveServer, runCartShow, runCartShowServer, } from "./commands/cart.js";
|
|
26
26
|
import { CommandContractError, errorRecoveryActions, printErrorRecovery, writeCommandEnvelope } from "./commands/guidance.js";
|
|
27
27
|
import { runDocsList, runDocsShow, runDocsSearch } from "./commands/docs.js";
|
|
28
|
-
import { runInstall } from "./commands/install.js";
|
|
28
|
+
import { INSTALL_AGENT_TYPES, runInstall } from "./commands/install.js";
|
|
29
29
|
import { runSkillShow } from "./commands/skill.js";
|
|
30
30
|
import { runNext } from "./commands/next.js";
|
|
31
31
|
import { runVaultAccess, runVaultList, runVaultRead } from "./commands/vault.js";
|
|
@@ -139,17 +139,12 @@ function reportCLIError(error, contract) {
|
|
|
139
139
|
const providerContractMismatch = error instanceof HttpError && error.code === "provider_contract_mismatch";
|
|
140
140
|
const capabilityInputInvalid = error instanceof HttpError && error.code === "capability_input_invalid";
|
|
141
141
|
const deviceKeyResettable = Boolean(deviceError && (deviceError.code === "agent_device_key_rotated" ||
|
|
142
|
-
deviceError.code === "agent_device_key_conflict"
|
|
143
|
-
(deviceError.enrollmentFailed && deviceError.status === 500)));
|
|
142
|
+
deviceError.code === "agent_device_key_conflict"));
|
|
144
143
|
const deviceRecovery = deviceError ? [
|
|
145
144
|
{
|
|
146
145
|
command: "itpay skill show itpay --json",
|
|
147
146
|
reason: "读取 ItPay 身份边界;该错误需要用户或运营恢复 Backend 登记,不能通过换类型或删除本地身份绕过",
|
|
148
147
|
},
|
|
149
|
-
...(deviceKeyResettable ? [{
|
|
150
|
-
command: "itpay device reset-key --confirm-key-reset --json",
|
|
151
|
-
reason: "服务端拒绝以当前私钥完成设备登记;生成全新 Ed25519 密钥并重新登记(旧设备身份在服务端保留为孤儿,不影响新身份和额度谱系)",
|
|
152
|
-
}] : []),
|
|
153
148
|
] : [];
|
|
154
149
|
const stateRecovery = stateError ? [{
|
|
155
150
|
command: "itpay skill show itpay --json",
|
|
@@ -167,10 +162,10 @@ function reportCLIError(error, contract) {
|
|
|
167
162
|
? "CLI 已自动续期并重试同一请求一次,仍被拒绝;停止重试,不要切换 Agent Type 或旋转身份。"
|
|
168
163
|
: deviceError?.code === "agent_device_revoked"
|
|
169
164
|
? "Backend 已撤销当前 Device 登记;CLI 没有自动创建替代身份。停止重试并请用户或运营恢复登记。"
|
|
170
|
-
:
|
|
171
|
-
? "
|
|
165
|
+
: deviceError?.enrollmentFailed && deviceError.status === 500
|
|
166
|
+
? "服务端未能完成设备登记;保持原密钥和Agent Type,说明系统故障,待服务端恢复后继续原任务。不要重置身份。"
|
|
172
167
|
: deviceKeyResettable
|
|
173
|
-
? "
|
|
168
|
+
? "服务端拒绝当前设备私钥登记(轮换或冲突)。停止自动恢复;换钥会放弃旧身份,旧执行访问不会迁移。仅用户或运营明确决定放弃旧身份后,按 device reset-key 帮助执行。"
|
|
174
169
|
: deviceError
|
|
175
170
|
? "Device 身份验证失败;停止重试,不要切换 Agent Type、删除状态或旋转私钥。"
|
|
176
171
|
: undefined;
|
|
@@ -226,9 +221,10 @@ function reportCLIError(error, contract) {
|
|
|
226
221
|
? "临时网络故障;CLI 已仅对可安全重放的操作完成有限自动重试,但仍未获得完整响应。按 recovery 查询同一资源的权威状态;不要创建替代 Checkout、Execution、Payment 或 Refund。"
|
|
227
222
|
: "网络在完整响应前中断;当前写操作没有安全重放合同,因此 CLI 未自动重试。按 recovery 查询权威状态;不要原样重放或创建替代 Checkout、Execution、Payment 或 Refund。"
|
|
228
223
|
: backendOverrideError
|
|
229
|
-
? "移除 ITPAY_BACKEND_URL
|
|
224
|
+
? "移除 ITPAY_BACKEND_URL 使用正式环境,或按当前测试目标准确设置为 https://sandbox.itpay.ai 或 https://dev.itpay.ai。不要通过切换环境规避当前错误。"
|
|
230
225
|
: commandError?.instruction ?? authorizationInstruction ?? contract?.instruction ?? "检查命令参数后重试。",
|
|
231
226
|
next: null,
|
|
227
|
+
...(commandError?.interaction ? { interaction: commandError.interaction } : {}),
|
|
232
228
|
recovery: incompatible
|
|
233
229
|
? requiredCLIVersion
|
|
234
230
|
? [distributionUpgradeAction(requiredCLIVersion)]
|
|
@@ -238,7 +234,7 @@ function reportCLIError(error, contract) {
|
|
|
238
234
|
: backendOverrideError ? [] : commandError?.recovery ?? (stateError ? stateRecovery : lockError ? lockRecovery : deviceError ? deviceRecovery : identityRecovery ? httpRecovery : contract?.recovery ?? []),
|
|
239
235
|
}, {
|
|
240
236
|
...(contract?.jsonOutput !== undefined ? { jsonOutput: contract.jsonOutput } : backendOverrideError || lockError || stateError ? { jsonOutput: process.argv.includes("--json") } : {}),
|
|
241
|
-
output: (text) => { process.
|
|
237
|
+
output: (text) => { process.stdout.write(text); },
|
|
242
238
|
});
|
|
243
239
|
process.exitCode = 1;
|
|
244
240
|
return;
|
|
@@ -323,7 +319,7 @@ for (const action of ["login", "status"]) {
|
|
|
323
319
|
// and non-JSON output is human-readable instead of a raw JSON dump.
|
|
324
320
|
const envelope = {
|
|
325
321
|
status: String(result.status ?? "error"),
|
|
326
|
-
result: (result.result ?? result),
|
|
322
|
+
result: (result.result ?? Object.fromEntries(Object.entries(result).filter(([key]) => !["status", "instruction", "next", "recovery", "handoff"].includes(key)))),
|
|
327
323
|
...(result.handoff ? { handoff: result.handoff } : {}),
|
|
328
324
|
instruction: typeof result.instruction === "string" ? result.instruction : "按返回状态继续。",
|
|
329
325
|
next: (result.next ?? null),
|
|
@@ -422,8 +418,11 @@ deviceCmd
|
|
|
422
418
|
.action(async (options) => {
|
|
423
419
|
const config = loadConfig();
|
|
424
420
|
try {
|
|
421
|
+
if (!config.agentType || !INSTALL_AGENT_TYPES.some(type => type === config.agentType)) {
|
|
422
|
+
throw new CommandContractError("agent_type_required", "supported Agent Type required before key reset", "先按真实宿主配置正式支持的Agent Type;尚未修改本地密钥。", [{ command: "itpay install --json", reason: "读取当前宿主配置" }]);
|
|
423
|
+
}
|
|
425
424
|
if (!options.confirmKeyReset) {
|
|
426
|
-
throw new CommandContractError("key_reset_confirmation_required", "--confirm-key-reset is required", "
|
|
425
|
+
throw new CommandContractError("key_reset_confirmation_required", "--confirm-key-reset is required", "仅在用户或运营明确决定放弃旧身份后使用;会丢弃本地私钥,旧执行访问不会迁移。internal_error 需服务端恢复,不能换钥。", [{ command: "itpay docs show identity-and-sessions --json", reason: "检查适用边界" }]);
|
|
427
426
|
}
|
|
428
427
|
const reset = await new DeviceAuthority({
|
|
429
428
|
baseURL: config.baseURL,
|
|
@@ -437,9 +436,9 @@ deviceCmd
|
|
|
437
436
|
private_key_preserved: false,
|
|
438
437
|
server_side_device: "orphaned_under_previous_key",
|
|
439
438
|
},
|
|
440
|
-
instruction: "本地设备私钥已重置;下一次需要设备身份的命令会以全新 Ed25519
|
|
439
|
+
instruction: "本地设备私钥已重置;下一次需要设备身份的命令会以全新 Ed25519 密钥重新登记;这不能恢复旧身份的订单或用于刷新额度。服务端旧设备记录保留为孤儿;如需清理请走运营流程。",
|
|
441
440
|
next: {
|
|
442
|
-
command: `itpay --agent-type ${config.agentType
|
|
441
|
+
command: `itpay --agent-type ${config.agentType} services list --limit 1 --json`,
|
|
443
442
|
reason: "用无业务写入的签名请求完成新密钥的重新登记",
|
|
444
443
|
},
|
|
445
444
|
recovery: [],
|
|
@@ -1413,7 +1412,7 @@ services
|
|
|
1413
1412
|
.argument("<result_item_id>")
|
|
1414
1413
|
.option("--offset <offset>", "zero-based candidate offset", Number, 0)
|
|
1415
1414
|
.option("--cursor <cursor>", "opaque v2 page cursor (rcur_<offset>); takes precedence over --offset")
|
|
1416
|
-
.option("--limit <limit>", "page size (1-20)", Number,
|
|
1415
|
+
.option("--limit <limit>", "page size (1-20)", Number, 20)
|
|
1417
1416
|
.option("--json", "output JSON")
|
|
1418
1417
|
.action(async (serviceExecutionID, resultItemID, options) => {
|
|
1419
1418
|
const config = loadConfig();
|
|
@@ -1477,12 +1476,18 @@ services
|
|
|
1477
1476
|
.argument("<service_execution_id>")
|
|
1478
1477
|
.requiredOption("--capability <capability_id>")
|
|
1479
1478
|
.option("--input <key=value>", "redacted input summary", collectOption, [])
|
|
1479
|
+
.option("--input-json <file>", "JSON object containing the complete capability input")
|
|
1480
1480
|
.option("--json", "output JSON")
|
|
1481
1481
|
.action(async (serviceExecutionID, options) => {
|
|
1482
1482
|
const config = loadConfig();
|
|
1483
1483
|
const backend = newBackendClient(config);
|
|
1484
1484
|
try {
|
|
1485
|
-
|
|
1485
|
+
const recovery = [{ command: `itpay services next ${serviceExecutionID} --json`, reason: "读取当前动作要求" }];
|
|
1486
|
+
if (options.inputJson && options.input.length > 0) {
|
|
1487
|
+
throw new CommandContractError("capability_input_invalid", "--input and --input-json cannot be combined", "嵌套输入用 --input-json <file>,扁平输入用 --input key=value,二选一;本次未调用供应商。", recovery);
|
|
1488
|
+
}
|
|
1489
|
+
const input = options.inputJson ? readInputJsonObject(options.inputJson, "capability_input_invalid", recovery) : parseKeyValueList(options.input);
|
|
1490
|
+
await runServicesInvoke(backend, config, serviceExecutionID, options.capability, input, { jsonOutput: Boolean(options.json) });
|
|
1486
1491
|
}
|
|
1487
1492
|
catch (error) {
|
|
1488
1493
|
reportCLIError(error, {
|
|
@@ -1665,12 +1670,16 @@ services
|
|
|
1665
1670
|
.description("Show the next recommended agent action for a Service Execution")
|
|
1666
1671
|
.argument("<service_execution_id>")
|
|
1667
1672
|
.option("--since-snapshot <snapshot_id>", "delta read: suppress the journey payload when unchanged")
|
|
1673
|
+
.option("--timeout <seconds>", "wait up to 120 seconds for a useful result on this execution", (value) => Number(value))
|
|
1668
1674
|
.option("--json", "output JSON instead of terminal text")
|
|
1669
1675
|
.action(async (serviceExecutionID, options) => {
|
|
1670
1676
|
const config = loadConfig();
|
|
1671
1677
|
const backend = newBackendClient(config);
|
|
1672
1678
|
try {
|
|
1673
|
-
|
|
1679
|
+
if (options.timeout !== undefined && (!Number.isInteger(options.timeout) || options.timeout < 0 || options.timeout > 120)) {
|
|
1680
|
+
throw new Error("--timeout must be an integer from 0 to 120");
|
|
1681
|
+
}
|
|
1682
|
+
await runServicesNext(backend, serviceExecutionID, { jsonOutput: Boolean(options.json), sinceSnapshot: options.sinceSnapshot, timeoutSeconds: options.timeout });
|
|
1674
1683
|
}
|
|
1675
1684
|
catch (error) {
|
|
1676
1685
|
reportCLIError(error, {
|
|
@@ -1683,22 +1692,28 @@ services
|
|
|
1683
1692
|
});
|
|
1684
1693
|
services
|
|
1685
1694
|
.command("read-result")
|
|
1686
|
-
.description("Read
|
|
1695
|
+
.description("Read saved railway results or human-granted service content")
|
|
1687
1696
|
.argument("<service_execution_id>")
|
|
1688
|
-
.option("--snapshot <snapshot_id>", "
|
|
1697
|
+
.option("--snapshot <snapshot_id>", "Smart: saved snapshot catalog; add --journey for a journey detail. Exact: saved station-pair rows without --snapshot")
|
|
1689
1698
|
.option("--journey <journey_id>", "rail.progressive.v2: journey id to read")
|
|
1699
|
+
.option("--offset <offset>", "saved journey offset", Number, 0)
|
|
1700
|
+
.option("--limit <limit>", "rows per page (1-20): Exact 20, Smart 3", Number)
|
|
1701
|
+
.option("--all", "read every saved journey page")
|
|
1690
1702
|
.option("--json", "output JSON instead of terminal text")
|
|
1691
1703
|
.action(async (serviceExecutionID, options) => {
|
|
1692
1704
|
const config = loadConfig();
|
|
1693
1705
|
const backend = newBackendClient(config);
|
|
1694
1706
|
try {
|
|
1695
|
-
await runServicesReadResult(backend, serviceExecutionID, { jsonOutput: Boolean(options.json), snapshot: options.snapshot, journey: options.journey
|
|
1707
|
+
await runServicesReadResult(backend, serviceExecutionID, { jsonOutput: Boolean(options.json), snapshot: options.snapshot, journey: options.journey,
|
|
1708
|
+
offset: options.offset, limit: options.limit, all: Boolean(options.all) });
|
|
1696
1709
|
}
|
|
1697
1710
|
catch (error) {
|
|
1698
1711
|
reportCLIError(error, {
|
|
1699
1712
|
jsonOutput: Boolean(options.json),
|
|
1700
|
-
code: "agent_access_denied",
|
|
1701
|
-
instruction: "
|
|
1713
|
+
code: error instanceof HttpError && error.code === "agent_access_denied" ? "agent_access_denied" : "service_result_read_failed",
|
|
1714
|
+
instruction: error instanceof HttpError && error.code === "agent_access_denied"
|
|
1715
|
+
? "请用户在订单页面重新授权;不要使用开发者权限绕过授权或退款锁。"
|
|
1716
|
+
: "读取同一服务的已保存结果;若快照已不可读,先查看该服务当前状态和有效动作。",
|
|
1702
1717
|
recovery: [{ command: `itpay services next ${serviceExecutionID} --json`, reason: "检查交付模式和 grant 状态" }],
|
|
1703
1718
|
});
|
|
1704
1719
|
}
|
|
@@ -24,9 +24,6 @@ export function buildAgentChatHandoff(plan) {
|
|
|
24
24
|
if (plan.orderItems?.length) {
|
|
25
25
|
lines.push(`项目:${plan.orderItems.map((item) => `${item.title} x${item.quantity}`).join("、")}`);
|
|
26
26
|
}
|
|
27
|
-
if (plan.afterActionCommand) {
|
|
28
|
-
lines.push("", `付款后查询:\`${plan.afterActionCommand}\``);
|
|
29
|
-
}
|
|
30
27
|
return {
|
|
31
28
|
type: "send_payment_handoff",
|
|
32
29
|
must_send_to_user: true,
|
|
@@ -59,11 +59,11 @@ const TERMINAL_SESSION_STATES = {
|
|
|
59
59
|
},
|
|
60
60
|
cancelled: {
|
|
61
61
|
status: 'auth_cancelled',
|
|
62
|
-
instruction: '
|
|
62
|
+
instruction: '用户在官方页取消了授权;停止登录。只有用户明确重新要求授权时才发起新的登录。',
|
|
63
63
|
},
|
|
64
64
|
denied: {
|
|
65
65
|
status: 'auth_denied',
|
|
66
|
-
instruction: '
|
|
66
|
+
instruction: '官方授权被拒绝;停止登录,不自动创建新请求。只有用户明确重新要求授权时才重新登录。',
|
|
67
67
|
},
|
|
68
68
|
};
|
|
69
69
|
class AuthRequestError extends Error {
|
|
@@ -200,9 +200,13 @@ async function accountAuth(action, baseURL, env, fetcher, backend) {
|
|
|
200
200
|
}
|
|
201
201
|
catch (error) {
|
|
202
202
|
if (error instanceof AuthRequestError && (error.httpStatus === 404 || error.httpStatus === 410)) {
|
|
203
|
-
|
|
203
|
+
rmSync(sellerAuthPath(baseURL, env, purpose), { force: true });
|
|
204
|
+
// The explicit login call may open one fresh session now. Status
|
|
205
|
+
// remains read-only and still reports the missing old session.
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
return authStatusUnknown(open.sessionID);
|
|
204
209
|
}
|
|
205
|
-
return authStatusUnknown(open.sessionID);
|
|
206
210
|
}
|
|
207
211
|
}
|
|
208
212
|
}
|
|
@@ -285,7 +289,7 @@ async function accountAuth(action, baseURL, env, fetcher, backend) {
|
|
|
285
289
|
status: terminal.status,
|
|
286
290
|
result: { dashboard_auth_session_id: state.sessionID, stage: progress.status },
|
|
287
291
|
instruction: terminal.instruction,
|
|
288
|
-
next: { command: 'itpay auth login --json', reason: '
|
|
292
|
+
next: ['denied', 'cancelled'].includes(progress.status) ? null : { command: 'itpay auth login --json', reason: '需要登录时重新发起官方授权' },
|
|
289
293
|
recovery: [],
|
|
290
294
|
};
|
|
291
295
|
}
|
|
@@ -55,9 +55,12 @@ export class CartSession {
|
|
|
55
55
|
...(this.state.lastDisplayToken ? { lastDisplayToken: this.state.lastDisplayToken } : {}),
|
|
56
56
|
...(this.state.lastCheckoutURL ? { lastCheckoutURL: this.state.lastCheckoutURL } : {}),
|
|
57
57
|
};
|
|
58
|
+
const contents = JSON.stringify(toSave, null, 2);
|
|
59
|
+
if (existsSync(path) && readFileSync(path, "utf-8") === contents)
|
|
60
|
+
return;
|
|
58
61
|
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
59
62
|
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
60
|
-
writeFileSync(temporary,
|
|
63
|
+
writeFileSync(temporary, contents, { encoding: "utf-8", mode: 0o600 });
|
|
61
64
|
chmodSync(temporary, 0o600);
|
|
62
65
|
renameSync(temporary, path);
|
|
63
66
|
chmodSync(path, 0o600);
|
package/dist/src/state/config.js
CHANGED
|
@@ -15,7 +15,7 @@ import { OperationJournal } from "./operation_journal.js";
|
|
|
15
15
|
export const DEFAULT_BASE_URL = "https://app.itpay.ai";
|
|
16
16
|
export const DEV_BASE_URL = "https://dev.itpay.ai";
|
|
17
17
|
export const SANDBOX_BASE_URL = "https://sandbox.itpay.ai";
|
|
18
|
-
export const CLI_VERSION = "2.1.
|
|
18
|
+
export const CLI_VERSION = "2.1.29";
|
|
19
19
|
export const API_CONTRACT_REVISION = "sha256:2f9fbcdc5cd0630910abb52671304cc019e348e9a778017ec92ddc3513b3639f";
|
|
20
20
|
const CART_SESSION_DEFAULT_DIR = ".itpay-v3";
|
|
21
21
|
const CART_SESSION_FILENAME = "cart.json";
|
|
@@ -45,6 +45,8 @@ export class TaskJournal {
|
|
|
45
45
|
observe(serviceExecutionID, stage, resumeCommand) {
|
|
46
46
|
const file = this.readFile();
|
|
47
47
|
const existing = file.tasks[serviceExecutionID];
|
|
48
|
+
if (existing && existing.stage === stage && (resumeCommand === undefined || existing.resume_command === resumeCommand))
|
|
49
|
+
return;
|
|
48
50
|
file.tasks[serviceExecutionID] = {
|
|
49
51
|
service_execution_id: serviceExecutionID,
|
|
50
52
|
...(existing?.service_id ? { service_id: existing.service_id } : {}),
|
|
@@ -1,7 +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
|
|
4
|
+
"product_scope": "itpay is the single public CLI entry point for Buyer services and the existing itpay sell Seller workflow; $itpay is its user-facing Skill invocation.",
|
|
5
5
|
"topic": "cart-checkout",
|
|
6
6
|
"title": "Canonical Cart And Checkout Routing",
|
|
7
7
|
"purpose": "Use the canonical server Cart and route service-backed lines through Service Execution without hardcoded service logic.",
|
|
@@ -32,11 +32,12 @@
|
|
|
32
32
|
}
|
|
33
33
|
],
|
|
34
34
|
"agent_rules": [
|
|
35
|
-
"The Backend Cart is business truth;
|
|
35
|
+
"The Backend Cart is business truth; the current backend-isolated local cart state file only stores local recovery handles and explicit drafts.",
|
|
36
36
|
"Run the exact next.command returned by the CLI. Do not extract or invent a capability from a raw list.",
|
|
37
|
-
"
|
|
37
|
+
"Railway query and booking use distinct Service Executions for one purchase journey. Start another execution for an independent intent.",
|
|
38
38
|
"Ask for email only when the selected capability requires protected delivery; never invent contact data.",
|
|
39
|
-
"A lost Checkout response is retried with the same Cart and persisted idempotency operation."
|
|
39
|
+
"A lost Checkout response is retried with the same Cart and persisted idempotency operation.",
|
|
40
|
+
"A single railway service checkout normally follows its booking execution; Cart is used only when the returned flow calls for it."
|
|
40
41
|
],
|
|
41
42
|
"forbidden": [
|
|
42
43
|
"Do not call ordinary buy for a service-backed Cart line.",
|
|
@@ -1,7 +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
|
|
4
|
+
"product_scope": "itpay is the single public CLI entry point for Buyer services and the existing itpay sell Seller workflow; $itpay is its user-facing Skill invocation.",
|
|
5
5
|
"topic": "catalog-list",
|
|
6
6
|
"title": "Browse ItPay V3 Service Catalog",
|
|
7
7
|
"purpose": "Explain available services, outcomes, conditions, and prices in human language without exposing catalog identifiers.",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"item_format": "Explain the title, what the human receives, any auxiliary discovery step, and the published price.",
|
|
21
21
|
"service_flow": "Explain discovery as an auxiliary identity-confirmation step, including free quota and paid continuation. Present primary_service as the actual paid outcome. Read all values from the manifest.",
|
|
22
22
|
"variant_selection": "Internal IDs are for the Agent's next command only and are never shown or requested from the human.",
|
|
23
|
-
"price_note": "amount_minor is in minor units (e.g., 10000 = 100.00 CNY). The CLI formats prices as XX.YY currency automatically."
|
|
23
|
+
"price_note": "amount_minor is in minor units (e.g., 10000 = 100.00 CNY). The CLI formats prices as XX.YY currency automatically.",
|
|
24
|
+
"railway": "Railway entries point to itpay docs show rail-booking; use its query and booking contracts instead of a generic start→invoke→cart sequence."
|
|
24
25
|
},
|
|
25
26
|
"agent_rules": [
|
|
26
27
|
"Use catalog list only when the human asks for a new service, not when they want an earlier purchase.",
|
|
@@ -28,15 +29,32 @@
|
|
|
28
29
|
"Explain the customer journey from service_flow instead of presenting every variant as an unrelated service.",
|
|
29
30
|
"State free quota, paid continuation price, email requirement, and claim purpose exactly as published; do not exaggerate or omit conditions.",
|
|
30
31
|
"Ask the human to choose only when multiple services could satisfy the goal; never make them choose an internal variant or offer ID.",
|
|
31
|
-
"
|
|
32
|
+
"Use each service’s returned input contract; railway planning and booking follow rail-booking, not a universal Cart route."
|
|
32
33
|
],
|
|
33
34
|
"human_language": {
|
|
34
|
-
"prefer": [
|
|
35
|
-
|
|
35
|
+
"prefer": [
|
|
36
|
+
"服务",
|
|
37
|
+
"查询",
|
|
38
|
+
"报告",
|
|
39
|
+
"免费次数",
|
|
40
|
+
"价格",
|
|
41
|
+
"你会得到什么"
|
|
42
|
+
],
|
|
43
|
+
"avoid": [
|
|
44
|
+
"service_id",
|
|
45
|
+
"variant_id",
|
|
46
|
+
"offer_id",
|
|
47
|
+
"capability",
|
|
48
|
+
"manifest"
|
|
49
|
+
]
|
|
36
50
|
},
|
|
37
51
|
"next_docs": [
|
|
38
52
|
{
|
|
39
|
-
"condition": "
|
|
53
|
+
"condition": "Railway service selected",
|
|
54
|
+
"topic": "rail-booking"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"condition": "Non-rail Buyer selected a service and wants to purchase",
|
|
40
58
|
"topic": "cart-checkout",
|
|
41
59
|
"file": "docs/agent/buyer/cart-checkout.json"
|
|
42
60
|
}
|
|
@@ -1,7 +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
|
|
4
|
+
"product_scope": "itpay is the single public CLI entry point for Buyer services and the existing itpay sell Seller workflow; $itpay is its user-facing Skill invocation.",
|
|
5
5
|
"topic": "identity-and-sessions",
|
|
6
6
|
"title": "Device Identity And Session Recovery",
|
|
7
7
|
"purpose": "Explain stable local identity, official Backend-scoped registrations, Agent Type instances, and bounded automatic session recovery.",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
],
|
|
13
13
|
"identity_model": {
|
|
14
14
|
"local_key": "One owner-only Ed25519 private key for the local installation.",
|
|
15
|
-
"backend_registration": "One registration per official Backend
|
|
15
|
+
"backend_registration": "One registration per configured official Backend. Follow the current CLI backend allowlist; do not switch to evade failures.",
|
|
16
16
|
"agent_instance": "One instance per agent_type under each Backend registration; same-type windows and chats reuse it.",
|
|
17
17
|
"presentation": "Host and target route output only and never identify the Agent or provide business input."
|
|
18
18
|
},
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
"When a previously unseen Agent Type is registered under an existing Device, the CLI skips locally known Agent Instances that the Backend reports as revoked and uses the first remaining valid Instance. It never reactivates a revoked Instance or replaces the Device; if none is valid, registration stops.",
|
|
24
24
|
"When the Backend reports agent_device_not_found (HTTP 404) for a locally known registration — for example after the Backend registration database was rebuilt — the CLI drops only that Backend's registration and re-enrolls once with the same private key, re-attaching to the same device and quota lineage where the Backend still holds the key.",
|
|
25
25
|
"A revoked v2 registration requires explicit operator recovery; it is never silently replaced.",
|
|
26
|
-
"Re-enrolling a public key the Backend already holds re-attaches to the existing device and quota lineage after proof-of-possession; it never creates a duplicate device.
|
|
26
|
+
"Re-enrolling a public key the Backend already holds re-attaches to the existing device and quota lineage after proof-of-possession; it never creates a duplicate device. An internal_error is a server failure: preserve the key and wait for service recovery. If enrollment returns agent_device_key_rotated or agent_device_key_conflict, stop automatic recovery. Only an explicit human/operator decision to abandon the old identity permits reset-key; old execution access does not migrate."
|
|
27
27
|
],
|
|
28
28
|
"local_state_recovery": [
|
|
29
|
-
"Device state
|
|
29
|
+
"Device state uses an owner-only atomic file replacement and a lock that the CLI can inspect. On a stale lock, run the returned device repair-lock command; it only reclaims a proven stale owner.",
|
|
30
30
|
"Operation idempotency uses one immutable owner-only record per operation key. Different commands and Agent processes do not share a global operation-journal lock; legacy operations.json is read only.",
|
|
31
31
|
"On device_state_unwritable, use the reported operation and errno to diagnose the failing stage without exposing local paths; keep the same Agent Type and CLI/Node launcher, restore Host-approved persistent access to ~/.itpay-v3, then retry the original command.",
|
|
32
|
-
"Do not
|
|
32
|
+
"Do not delete a live lock, switch Node runtimes, delete identity, or rotate Agent Type as a retry strategy."
|
|
33
33
|
],
|
|
34
34
|
"commands": [
|
|
35
35
|
{
|
|
@@ -48,22 +48,22 @@
|
|
|
48
48
|
"success_signal": "only the selected Backend registration is removed and the private key and other Backend registration remain"
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
|
-
"intent": "
|
|
52
|
-
"command": "itpay device
|
|
53
|
-
"success_signal": "the
|
|
51
|
+
"intent": "repair only a proven stale local device lock",
|
|
52
|
+
"command": "itpay device repair-lock --json",
|
|
53
|
+
"success_signal": "the CLI reports whether the lock was stale and reclaimed; an active owner stays protected"
|
|
54
54
|
}
|
|
55
55
|
],
|
|
56
56
|
"agent_rules": [
|
|
57
57
|
"Use the real runtime type and keep it stable across every command in the flow.",
|
|
58
58
|
"Keep the same CLI/Node launcher and Host-approved permission context for the whole flow.",
|
|
59
59
|
"Treat a new window, chat, task, or process of the same runtime as the same Agent Type, not a new identity.",
|
|
60
|
-
"
|
|
60
|
+
"Production defaults to https://app.itpay.ai; explicit sandbox or dev testing uses https://sandbox.itpay.ai or https://dev.itpay.ai. Keep the complete returned Backend-prefixed command for that flow.",
|
|
61
61
|
"After automatic recovery fails, stop and report the exact code, selected official Backend, Agent Type, and command family without exposing private keys or tokens."
|
|
62
62
|
],
|
|
63
63
|
"forbidden": [
|
|
64
64
|
"Do not delete ~/.itpay-v3, rotate the key, switch Agent Type, or repeatedly retry to obtain fresh quota.",
|
|
65
65
|
"Do not use device recover for session expiry, revocation, quota recovery, or an unconfirmed Backend failure.",
|
|
66
|
-
"Do not
|
|
66
|
+
"Do not switch Backend to evade a failure. Allowed public URLs are app.itpay.ai, sandbox.itpay.ai and dev.itpay.ai; local loopback requires the explicit CLI development flag.",
|
|
67
67
|
"Do not use --target as identity or service input."
|
|
68
68
|
],
|
|
69
69
|
"next_docs": [
|
|
@@ -93,10 +93,11 @@
|
|
|
93
93
|
],
|
|
94
94
|
"auth_session_states": {
|
|
95
95
|
"stages": "The CLI now reports the auth session stage exactly as the server returns it: waiting_provider (show the handoff and wait), email_verification_required (the human must verify the emailed code on the auth page), merge_confirmation_required (an existing account matches — the human must confirm the merge on the auth page), provider_pending/pending (generic wait), completed (session done — bind it, do not restart).",
|
|
96
|
-
"terminal_states": "auth_expired/auth_denied/auth_cancelled/auth_failed mean this session is over; say so plainly and
|
|
97
|
-
"transport_unknown": "
|
|
98
|
-
"missing": "
|
|
99
|
-
"completed_resume": "
|
|
96
|
+
"terminal_states": "auth_expired/auth_denied/auth_cancelled/auth_failed mean this session is over; say so plainly and respect denial or cancellation. Start a new session only on an explicit login request; never keep polling a dead one.",
|
|
97
|
+
"transport_unknown": "auth_status_unknown means a network error while polling — the outcome is unknown. Keep waiting on the SAME session (it may still complete); do NOT restart auth, and do not assume denial.",
|
|
98
|
+
"missing": "auth_session_missing means the cached session is gone. An explicit auth login opens one fresh official session in that call; auth status stays read-only.",
|
|
99
|
+
"completed_resume": "After login completes, execute the returned command to resume the same paused service execution. Login does not silently run that command.",
|
|
100
100
|
"path_b": "A buyer who reaches checkout without a login completes identity inside the protected checkout page; never force a separate itpay auth login first."
|
|
101
|
-
}
|
|
101
|
+
},
|
|
102
|
+
"key_reset_decision": "Only after explicit user/operator acceptance of abandoning the old identity, consult device reset-key help; this is not automatic error recovery, and old execution access does not migrate."
|
|
102
103
|
}
|
|
@@ -35,20 +35,21 @@
|
|
|
35
35
|
"For first setup or a runtime change, identify the real Agent Type once, run itpay install <agent_type> --json yourself, and follow its setup continuation until the packaged Skill is loaded. Ordinary service requests start from the Skill's service command, without repeating install or readyz.",
|
|
36
36
|
"After setup, tell the human only that ItPay is ready; never ask them to run setup commands or explain technical setup steps.",
|
|
37
37
|
"Use the CLI distribution already provided by the current npm package, Skill, or plugin; update only through that distribution's own instructions.",
|
|
38
|
-
"Production defaults to https://app.itpay.ai. Testing may set ITPAY_BACKEND_URL to the exact official URL
|
|
38
|
+
"Production defaults to https://app.itpay.ai. Testing may set ITPAY_BACKEND_URL to the exact official URL the configured official test Backend only when explicitly requested; follow the CLI allowlist.",
|
|
39
39
|
"If backend_contract_incompatible includes result.required_cli_version, stop all ItPay business commands and use only the exact distribution-specific update recovery returned by the CLI; never replace its version with latest.",
|
|
40
40
|
"After upgrading, require itpay --version to equal result.required_cli_version before running readyz again; never change Agent Type or Device identity to recover compatibility.",
|
|
41
41
|
"Use one exact type: codex-desktop, codex-cli, claude-code-desktop, claude-code-cli, workbuddy, zcode, doubao-work, kimi-code, or openclaw.",
|
|
42
42
|
"For compatibility, a global --agent-type codex declaration is normalized immediately to codex-desktop; all registration, output, and returned commands use codex-desktop. Never generate the alias, and do not use codex as an install target.",
|
|
43
|
-
"One local private key is reused, while Device registrations and quota lineage remain separate for app.itpay.ai and
|
|
43
|
+
"One local private key is reused, while Device registrations and quota lineage remain separate for app.itpay.ai, sandbox.itpay.ai and dev.itpay.ai.",
|
|
44
44
|
"Within each official Backend registration, each Agent Type has one Agent Instance; all windows and chats of the same type reuse it.",
|
|
45
45
|
"Agent Type identifies the runtime. Host and target only select a presentation destination; load render-hosts only when a handoff cannot be shown.",
|
|
46
46
|
"Keep the exact Agent Type in every next or recovery command; do not fall back to a type previously used by another runtime.",
|
|
47
|
-
"Do not change Agent Type or rotate local identity to reset quota or recover a failed command."
|
|
47
|
+
"Do not change Agent Type or rotate local identity to reset quota or recover a failed command.",
|
|
48
|
+
"Skillhub and local host bundles are separate installed artifacts; verify their version and packaged Skill/docs instead of assuming an npm update changed them."
|
|
48
49
|
],
|
|
49
50
|
"forbidden": [
|
|
50
51
|
"Do not use terminal, claude-code, or plain-chat as Agent Types. Do not generate codex as an Agent Type; it is accepted only as a legacy global declaration and becomes codex-desktop before any request.",
|
|
51
|
-
"Do not set or suggest
|
|
52
|
+
"Do not set or suggest a Backend outside the CLI allowlist: https://app.itpay.ai, https://sandbox.itpay.ai, https://dev.itpay.ai; local loopback requires the CLI development flag. Do not switch Backend to evade a failure.",
|
|
52
53
|
"Do not claim that install writes host configuration or registers a device; it only prints instructions.",
|
|
53
54
|
"Do not create a new identity for a different window, task, chat, or process of the same Agent Type."
|
|
54
55
|
],
|