@itpay/cli 2.0.5 → 2.0.7
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 +8 -4
- package/dist/src/client/http.js +29 -23
- package/dist/src/commands/catalog.js +3 -2
- package/dist/src/commands/checkout.js +23 -13
- package/dist/src/commands/guidance.js +37 -13
- package/dist/src/commands/readyz.js +3 -3
- package/dist/src/commands/services.js +122 -41
- package/dist/src/commands/skill.js +55 -0
- package/dist/src/main.js +116 -7
- package/dist/src/state/agent_type.js +19 -0
- package/dist/src/state/config.js +5 -13
- package/dist/src/state/device_authority.js +174 -56
- package/docs/agent/buyer/catalog-list.json +2 -1
- package/docs/agent/buyer/identity-and-sessions.json +64 -0
- package/docs/agent/buyer/install-and-setup.json +19 -5
- package/docs/agent/buyer/payment-flow.json +5 -1
- package/docs/agent/buyer/quickstart.json +12 -4
- package/docs/cli-reference/agent-types.md +9 -3
- package/docs/cli-reference/commands/catalog/list.md +1 -1
- package/docs/cli-reference/commands/checkout.md +4 -4
- package/docs/cli-reference/commands/device.md +13 -0
- package/docs/cli-reference/commands/install.md +3 -1
- package/docs/cli-reference/commands/readyz.md +4 -5
- package/docs/cli-reference/commands/services/action.md +10 -5
- package/docs/cli-reference/commands/services/checkout.md +3 -3
- package/docs/cli-reference/commands/services/invoke.md +6 -6
- package/docs/cli-reference/commands/services/next.md +23 -4
- package/docs/cli-reference/commands/services/quote.md +5 -1
- package/docs/cli-reference/commands/services/start.md +5 -3
- package/docs/cli-reference/commands/skill.md +17 -0
- package/docs/cli-reference/conventions.md +4 -1
- package/docs/cli-reference/index.md +1 -0
- package/package.json +1 -1
- package/skills/itpay-buyer/SKILL.md +33 -6
package/README.md
CHANGED
|
@@ -4,9 +4,11 @@ The official V3 CLI for Agent-driven ItPay service discovery, checkout, delivery
|
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm install -g @itpay/cli
|
|
7
|
+
itpay readyz --json
|
|
8
|
+
itpay skill show itpay-buyer --json
|
|
7
9
|
itpay install --json
|
|
8
10
|
itpay --agent-type codex-desktop readyz --json
|
|
9
|
-
|
|
11
|
+
# follow next.command: typed skill show, then catalog list
|
|
10
12
|
```
|
|
11
13
|
|
|
12
14
|
The default API is `https://app.itpay.ai`. Set `ITPAY_BACKEND_URL` only for an intentional test or local environment.
|
|
@@ -38,7 +40,9 @@ Normative per-command contracts: [CLI Command Reference](docs/cli-reference/inde
|
|
|
38
40
|
| `claude-code-cli` | `terminal` |
|
|
39
41
|
| `workbuddy` | `plain-chat` |
|
|
40
42
|
|
|
41
|
-
`--agent-type` identifies the stable runtime and registered Agent instance. `--host` only selects the human presentation surface. Use `itpay install <agent_type> --json` for the exact responsibility.
|
|
43
|
+
`--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
|
+
|
|
45
|
+
The local installation keeps one Ed25519 private key. Each normalized Backend API base URL (`dev`, `test`, `app`, or local) has an independent server device registration, quota lineage, Agent instances, and sessions. A rejected session is renewed and the same request is retried once; revoked v2 registrations are never silently replaced.
|
|
42
46
|
|
|
43
47
|
## Command Families
|
|
44
48
|
|
|
@@ -50,7 +54,7 @@ Normative per-command contracts: [CLI Command Reference](docs/cli-reference/inde
|
|
|
50
54
|
- `order`, `orders`: exact order and account order views.
|
|
51
55
|
- `refund create/list/get/watch/cancel`: Refund Owner flow.
|
|
52
56
|
- `services get/events`: redacted support diagnostics; normal flows should use `services next`.
|
|
53
|
-
- `install`, `docs list/show/search`: offline packaged guidance.
|
|
57
|
+
- `install`, `skill show`, `docs list/show/search`: offline packaged guidance.
|
|
54
58
|
- `pay`: operator escape hatch only; normal buyers use the ItPay Checkout page.
|
|
55
59
|
|
|
56
60
|
Run `itpay <command> --help` or browse [the command index](docs/cli-reference/index.md) for parameters.
|
|
@@ -67,7 +71,7 @@ itpay --agent-type <agent_type> services checkout <service_execution_id> --resum
|
|
|
67
71
|
itpay checkout --id <checkout_id> --token <display_token> --json
|
|
68
72
|
```
|
|
69
73
|
|
|
70
|
-
The local `~/.itpay-v3` directory stores
|
|
74
|
+
The local `~/.itpay-v3` directory stores one owner-only signing key, Backend-scoped Device registrations and Agent instances, idempotency operations, and recovery handles. Backend state remains authoritative. Do not delete or rotate this identity to recover quota.
|
|
71
75
|
|
|
72
76
|
## Environment
|
|
73
77
|
|
package/dist/src/client/http.js
CHANGED
|
@@ -17,6 +17,7 @@ export class HttpClient {
|
|
|
17
17
|
fetchImpl;
|
|
18
18
|
defaultHeaders;
|
|
19
19
|
requestAuthorizer;
|
|
20
|
+
recoverAuthorization;
|
|
20
21
|
constructor(config) {
|
|
21
22
|
this.baseURL = config.baseURL.replace(/\/$/, "");
|
|
22
23
|
this.fetchImpl = config.fetchImpl ?? globalThis.fetch;
|
|
@@ -26,35 +27,40 @@ export class HttpClient {
|
|
|
26
27
|
...(config.defaultHeaders ?? {}),
|
|
27
28
|
};
|
|
28
29
|
this.requestAuthorizer = config.requestAuthorizer;
|
|
30
|
+
this.recoverAuthorization = config.recoverAuthorization;
|
|
29
31
|
}
|
|
30
32
|
async request(path, options = {}) {
|
|
31
33
|
const url = path.startsWith("http") ? path : this.baseURL + path;
|
|
32
|
-
const headers = { ...this.defaultHeaders };
|
|
33
34
|
const method = options.method ?? "GET";
|
|
34
35
|
const body = options.body !== undefined ? JSON.stringify(options.body) : "";
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
const requestPath = new URL(url).pathname + new URL(url).search;
|
|
37
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
38
|
+
const headers = { ...this.defaultHeaders };
|
|
39
|
+
if (this.requestAuthorizer) {
|
|
40
|
+
Object.assign(headers, await this.requestAuthorizer({ method, path: requestPath, body }));
|
|
41
|
+
}
|
|
42
|
+
if (options.bearer)
|
|
43
|
+
headers.Authorization = `Bearer ${options.bearer}`;
|
|
44
|
+
if (options.idempotencyKey)
|
|
45
|
+
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
46
|
+
const response = await this.fetchImpl(url, {
|
|
47
|
+
method,
|
|
48
|
+
headers,
|
|
49
|
+
...(options.body !== undefined ? { body } : {}),
|
|
50
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
51
|
+
});
|
|
52
|
+
const text = await response.text();
|
|
53
|
+
const parsed = text.length > 0 ? safeParseJson(text) : undefined;
|
|
54
|
+
if (response.ok)
|
|
55
|
+
return parsed;
|
|
56
|
+
const error = new HttpError(response.status, parsed, `HTTP ${response.status}`);
|
|
57
|
+
if (attempt === 0 && error.status === 401 && error.code === "agent_device_session_required" && this.recoverAuthorization) {
|
|
58
|
+
await this.recoverAuthorization();
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
37
62
|
}
|
|
38
|
-
|
|
39
|
-
headers.Authorization = `Bearer ${options.bearer}`;
|
|
40
|
-
}
|
|
41
|
-
if (options.idempotencyKey) {
|
|
42
|
-
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
43
|
-
}
|
|
44
|
-
const requestInit = {
|
|
45
|
-
method,
|
|
46
|
-
headers,
|
|
47
|
-
...(options.body !== undefined ? { body } : {}),
|
|
48
|
-
...(options.signal ? { signal: options.signal } : {}),
|
|
49
|
-
};
|
|
50
|
-
const response = await this.fetchImpl(url, requestInit);
|
|
51
|
-
const text = await response.text();
|
|
52
|
-
const parsed = text.length > 0 ? safeParseJson(text) : undefined;
|
|
53
|
-
if (!response.ok) {
|
|
54
|
-
const errPayload = parsed;
|
|
55
|
-
throw new HttpError(response.status, errPayload, `HTTP ${response.status}`);
|
|
56
|
-
}
|
|
57
|
-
return parsed;
|
|
63
|
+
throw new Error("unreachable HTTP retry state");
|
|
58
64
|
}
|
|
59
65
|
get(path, options = {}) {
|
|
60
66
|
return this.request(path, { ...options, method: "GET" });
|
|
@@ -4,6 +4,7 @@ export async function runCatalogList(backend, options = {}) {
|
|
|
4
4
|
const services = manifest.manifest.items.map(summarizeService);
|
|
5
5
|
const firstServiceID = manifest.manifest.items.find((item) => item.service_id)?.service_id;
|
|
6
6
|
const empty = services.length === 0;
|
|
7
|
+
const jsonFlag = options.jsonOutput ? " --json" : "";
|
|
7
8
|
writeCommandEnvelope({
|
|
8
9
|
status: empty ? "catalog_empty" : "listed",
|
|
9
10
|
result: { catalog_version: manifest.version, services },
|
|
@@ -11,9 +12,9 @@ export async function runCatalogList(backend, options = {}) {
|
|
|
11
12
|
? "当前没有已发布服务;稍后重试,不要猜测 service_id。"
|
|
12
13
|
: "向用户解释主服务、辅助步骤和价格;得到用户意图后再启动对应 service_id。",
|
|
13
14
|
next: empty
|
|
14
|
-
? { command:
|
|
15
|
+
? { command: `itpay catalog list${jsonFlag}`, reason: "稍后重新读取已发布目录" }
|
|
15
16
|
: {
|
|
16
|
-
command: `itpay services start ${services.length === 1 && firstServiceID ? firstServiceID : "<service_id>"}`,
|
|
17
|
+
command: `itpay services start ${services.length === 1 && firstServiceID ? firstServiceID : "<service_id>"}${jsonFlag}`,
|
|
17
18
|
reason: "启动用户选择的服务",
|
|
18
19
|
},
|
|
19
20
|
recovery: [],
|
|
@@ -20,8 +20,8 @@ export async function runCheckoutPresentation(backend, options) {
|
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
22
|
const checkoutURL = checkoutPageURL(options.baseURL, options.checkoutID, options.displayToken);
|
|
23
|
-
const qrPNGURL = presentation.qr_png_url ?? checkoutQRPNGURL(options.baseURL, options.checkoutID, options.displayToken);
|
|
24
|
-
const nextCommand = `itpay checkout --id ${options.checkoutID} --token ${options.displayToken}`;
|
|
23
|
+
const qrPNGURL = absolutePublicURL(options.baseURL, presentation.qr_png_url ?? checkoutQRPNGURL(options.baseURL, options.checkoutID, options.displayToken));
|
|
24
|
+
const nextCommand = `itpay checkout --id ${options.checkoutID} --token ${options.displayToken} --json`;
|
|
25
25
|
const plan = buildCheckoutQRPlan({
|
|
26
26
|
host,
|
|
27
27
|
checkoutID: options.checkoutID,
|
|
@@ -61,19 +61,20 @@ function pendingCheckoutEnvelope(presentation, checkoutURL, plan, nextCommand) {
|
|
|
61
61
|
if (platform === "markdown") {
|
|
62
62
|
handoff.markdown = buildAgentChatHandoff(plan).markdown;
|
|
63
63
|
}
|
|
64
|
-
else if (platform === "plain_chat" &&
|
|
65
|
-
handoff.qr_image_url =
|
|
64
|
+
else if (platform === "plain_chat" && plan.preferredQRSources[0]) {
|
|
65
|
+
handoff.qr_image_url = plan.preferredQRSources[0];
|
|
66
66
|
}
|
|
67
|
+
const amount = formatMoney(presentation.checkout.amount_minor, presentation.checkout.currency);
|
|
67
68
|
return {
|
|
68
69
|
status: "human_checkout_required",
|
|
69
70
|
result: {
|
|
70
71
|
checkout_id: presentation.checkout.checkout_id,
|
|
71
72
|
payment: "pending",
|
|
72
|
-
amount
|
|
73
|
+
amount,
|
|
73
74
|
},
|
|
74
75
|
handoff,
|
|
75
|
-
instruction: pendingInstruction(platform),
|
|
76
|
-
next: { command: nextCommand, reason: "
|
|
76
|
+
instruction: pendingInstruction(platform, amount),
|
|
77
|
+
next: { command: nextCommand, reason: "稍后只查询同一 Checkout" },
|
|
77
78
|
recovery: [],
|
|
78
79
|
};
|
|
79
80
|
}
|
|
@@ -96,9 +97,9 @@ function terminalCheckoutEnvelope(presentation) {
|
|
|
96
97
|
const recovery = [];
|
|
97
98
|
if (payment === "verified") {
|
|
98
99
|
status = "completed";
|
|
99
|
-
instruction = "
|
|
100
|
+
instruction = "Backend 已确认这笔付款。不要再次展示付款入口,不要调用 pay,不要创建新 Checkout 或 Execution。现在只执行 next.command,读取同一 Execution 的履约结果。";
|
|
100
101
|
next = serviceExecutionIDs.length === 1
|
|
101
|
-
? { command: `itpay services next ${serviceExecutionIDs[0]} --json`, reason: "
|
|
102
|
+
? { command: `itpay services next ${serviceExecutionIDs[0]} --json`, reason: "读取同一笔已付款 Service Execution" }
|
|
102
103
|
: presentation.completed_order_id
|
|
103
104
|
? { command: `itpay order ${presentation.completed_order_id}`, reason: "读取已创建订单" }
|
|
104
105
|
: { command: "itpay orders", reason: "恢复已付款订单" };
|
|
@@ -122,12 +123,12 @@ function checkoutNeedsHumanHandoff(status) {
|
|
|
122
123
|
function checkoutPlainResult(result) {
|
|
123
124
|
return Object.entries(result).map(([key, value]) => `${key}: ${typeof value === "string" ? value : JSON.stringify(value)}`);
|
|
124
125
|
}
|
|
125
|
-
function pendingInstruction(platform) {
|
|
126
|
+
function pendingInstruction(platform, amount) {
|
|
126
127
|
if (platform === "markdown")
|
|
127
|
-
return
|
|
128
|
+
return `Backend 尚未确认付款。把 handoff.markdown 原样发送到当前桌面对话,确认二维码、链接和金额 ${amount} 已对用户可见,然后停止等待。不要创建新 Checkout、Execution 或 Payment Intent;稍后仍然只执行 next.command 查询这一笔 Checkout。`;
|
|
128
129
|
if (platform === "terminal")
|
|
129
|
-
return
|
|
130
|
-
return
|
|
130
|
+
return `Backend 尚未确认付款。在用户可见终端展示当前同一 Checkout 的二维码、链接和金额 ${amount},然后停止等待。不要创建新 Checkout、Execution 或 Payment Intent;稍后仍然只执行 next.command 查询这一笔 Checkout。`;
|
|
131
|
+
return `Backend 尚未确认付款。把 handoff.url 作为可点击链接发送给用户,优先把 handoff.qr_local_path 作为图片附件发送,不能发送本地附件时使用 handoff.qr_image_url,并说明金额 ${amount},然后停止等待。不要声称付款成功,不要创建新 Checkout、Execution 或 Payment Intent。稍后仍然只执行 next.command 查询这一笔 Checkout。`;
|
|
131
132
|
}
|
|
132
133
|
function formatMoney(amountMinor, currency) {
|
|
133
134
|
return `${(amountMinor / 100).toFixed(2)} ${currency}`;
|
|
@@ -143,3 +144,12 @@ function checkoutQRPNGURL(baseURL, checkoutID, displayToken) {
|
|
|
143
144
|
function publicRoot(baseURL) {
|
|
144
145
|
return (baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
145
146
|
}
|
|
147
|
+
function absolutePublicURL(baseURL, value) {
|
|
148
|
+
try {
|
|
149
|
+
const root = publicRoot(baseURL);
|
|
150
|
+
return new URL(value, `${root}/`).toString();
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return value;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HttpError } from "../client/http.js";
|
|
2
2
|
import { resolveOutput } from "../render/sink.js";
|
|
3
|
+
import { declaredAgentType, qualifyItPayCommand } from "../state/agent_type.js";
|
|
3
4
|
export function isTerminalServiceExecutionStatus(status) {
|
|
4
5
|
return status === "failed" || status === "refunded" || status === "cancelled";
|
|
5
6
|
}
|
|
@@ -17,12 +18,14 @@ export class CommandContractError extends Error {
|
|
|
17
18
|
}
|
|
18
19
|
export function writeCommandEnvelope(value, options = {}) {
|
|
19
20
|
const out = resolveOutput(options.output);
|
|
21
|
+
const agentType = options.agentType ?? declaredAgentType();
|
|
22
|
+
const qualified = qualifyEnvelope(value, agentType);
|
|
20
23
|
if (options.jsonOutput) {
|
|
21
|
-
out(JSON.stringify(
|
|
24
|
+
out(JSON.stringify(qualified, null, 2) + "\n");
|
|
22
25
|
return;
|
|
23
26
|
}
|
|
24
|
-
out(`${
|
|
25
|
-
const facts = "result" in
|
|
27
|
+
out(`${qualified.status}\n`);
|
|
28
|
+
const facts = "result" in qualified ? qualified.result : qualified.error;
|
|
26
29
|
if (options.plainResult) {
|
|
27
30
|
for (const line of options.plainResult)
|
|
28
31
|
out(`${line}\n`);
|
|
@@ -32,17 +35,17 @@ export function writeCommandEnvelope(value, options = {}) {
|
|
|
32
35
|
out(`${key}: ${typeof fact === "string" ? fact : JSON.stringify(fact)}\n`);
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
|
-
if ("handoff" in
|
|
36
|
-
for (const [key, fact] of Object.entries(
|
|
38
|
+
if ("handoff" in qualified && qualified.handoff) {
|
|
39
|
+
for (const [key, fact] of Object.entries(qualified.handoff)) {
|
|
37
40
|
out(`handoff.${key}: ${typeof fact === "string" ? fact : JSON.stringify(fact)}\n`);
|
|
38
41
|
}
|
|
39
42
|
}
|
|
40
|
-
out(`instruction: ${
|
|
41
|
-
if (
|
|
42
|
-
out(`next: ${
|
|
43
|
-
if (
|
|
43
|
+
out(`instruction: ${qualified.instruction}\n`);
|
|
44
|
+
if (qualified.next)
|
|
45
|
+
out(`next: ${qualified.next.command}\n`);
|
|
46
|
+
if (qualified.recovery.length > 0) {
|
|
44
47
|
out("recovery:\n");
|
|
45
|
-
for (const action of
|
|
48
|
+
for (const action of qualified.recovery) {
|
|
46
49
|
out(` - ${action.command}\n`);
|
|
47
50
|
out(` reason: ${action.reason}\n`);
|
|
48
51
|
}
|
|
@@ -56,6 +59,7 @@ export function attachAgentGuidance(payload, guidance) {
|
|
|
56
59
|
}
|
|
57
60
|
export function printAgentGuidance(guidance, output) {
|
|
58
61
|
const out = resolveOutput(output);
|
|
62
|
+
const agentType = declaredAgentType();
|
|
59
63
|
out(`${guidance.summary}\n`);
|
|
60
64
|
if (guidance.visible_results?.length) {
|
|
61
65
|
out("results:\n");
|
|
@@ -75,7 +79,7 @@ export function printAgentGuidance(guidance, output) {
|
|
|
75
79
|
out("next actions:\n");
|
|
76
80
|
for (const action of guidance.next_actions) {
|
|
77
81
|
out(` - ${action.label}\n`);
|
|
78
|
-
out(` ${action.command}\n`);
|
|
82
|
+
out(` ${qualifyItPayCommand(action.command, agentType)}\n`);
|
|
79
83
|
if (action.requires_human)
|
|
80
84
|
out(" requires human confirmation\n");
|
|
81
85
|
if (action.reason)
|
|
@@ -86,10 +90,20 @@ export function printAgentGuidance(guidance, output) {
|
|
|
86
90
|
out("recovery:\n");
|
|
87
91
|
for (const action of guidance.recovery) {
|
|
88
92
|
out(` - ${action.label}\n`);
|
|
89
|
-
out(` ${action.command}\n`);
|
|
93
|
+
out(` ${qualifyItPayCommand(action.command, agentType)}\n`);
|
|
90
94
|
}
|
|
91
95
|
}
|
|
92
96
|
}
|
|
97
|
+
function qualifyEnvelope(value, agentType) {
|
|
98
|
+
return {
|
|
99
|
+
...value,
|
|
100
|
+
next: value.next ? { ...value.next, command: qualifyItPayCommand(value.next.command, agentType) } : null,
|
|
101
|
+
recovery: value.recovery.map((action) => ({
|
|
102
|
+
...action,
|
|
103
|
+
command: qualifyItPayCommand(action.command, agentType),
|
|
104
|
+
})),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
93
107
|
export function buildCartGuidance(cart, serviceModel) {
|
|
94
108
|
const serviceLine = latestServiceLine(cart);
|
|
95
109
|
if (serviceLine?.service_execution_id) {
|
|
@@ -225,7 +239,7 @@ export function buildServiceHandleGuidance(serviceExecutionID, checkoutCapabilit
|
|
|
225
239
|
export function errorRecoveryActions(error) {
|
|
226
240
|
if (!(error instanceof HttpError))
|
|
227
241
|
return [];
|
|
228
|
-
if (error.code === "agent_identity_required"
|
|
242
|
+
if (error.code === "agent_identity_required") {
|
|
229
243
|
return [
|
|
230
244
|
{
|
|
231
245
|
id: "inspect_agent_setup",
|
|
@@ -234,6 +248,16 @@ export function errorRecoveryActions(error) {
|
|
|
234
248
|
},
|
|
235
249
|
];
|
|
236
250
|
}
|
|
251
|
+
if (error.code === "agent_device_session_required") {
|
|
252
|
+
return [
|
|
253
|
+
{
|
|
254
|
+
id: "read_agent_session_rules",
|
|
255
|
+
label: "Read identity and session recovery rules",
|
|
256
|
+
command: "itpay skill show itpay-buyer --json",
|
|
257
|
+
reason: "The CLI already attempted one automatic session renewal; do not rotate identity or loop retries.",
|
|
258
|
+
},
|
|
259
|
+
];
|
|
260
|
+
}
|
|
237
261
|
if (error.code === "quota_exhausted" || error.code === "checkout_required") {
|
|
238
262
|
return [
|
|
239
263
|
{
|
|
@@ -4,9 +4,9 @@ export async function runReadyz(backend, options = {}) {
|
|
|
4
4
|
const response = await backend.readyz();
|
|
5
5
|
writeCommandEnvelope({
|
|
6
6
|
status: response.status,
|
|
7
|
-
result: { backend: "available" },
|
|
8
|
-
instruction: "ItPay
|
|
9
|
-
next: { command: "itpay
|
|
7
|
+
result: { backend: "available", ...(options.agentType ? { agent_type: options.agentType } : {}) },
|
|
8
|
+
instruction: "ItPay 可用;先完整读取内置 Buyer Skill,再开始服务流程。",
|
|
9
|
+
next: { command: "itpay skill show itpay-buyer --json", reason: "加载完整操作与安全规则" },
|
|
10
10
|
recovery: [],
|
|
11
11
|
}, options);
|
|
12
12
|
}
|