@itpay/cli 0.2.1 → 0.2.2
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 +13 -0
- package/lib/account-status.js +157 -0
- package/lib/render-human.js +54 -30
- package/lib/runtime.js +8 -84
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -158,6 +158,19 @@ Before starting a new purchase, agents should inspect recoverable local state:
|
|
|
158
158
|
itp status --refresh --json
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
+
Humans can use the default account overview:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
itp status
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
```text
|
|
168
|
+
Account: buyer_7xK2mP9vQ4
|
|
169
|
+
Linked: alipay, wechat
|
|
170
|
+
Orders: 12
|
|
171
|
+
Device: Codex on MacBook-Pro (active)
|
|
172
|
+
```
|
|
173
|
+
|
|
161
174
|
If an unfinished run exists, continue it:
|
|
162
175
|
|
|
163
176
|
```bash
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { coreApi } from "./http.js";
|
|
2
|
+
import { noRecoverableContext } from "./buyer.js";
|
|
3
|
+
import { cliCommand, output, readConfig, readCredentials, readSessionToken, safeErrorMessage } from "./env.js";
|
|
4
|
+
|
|
5
|
+
async function accountOverviewStatus(flags = {}) {
|
|
6
|
+
const config = readConfig();
|
|
7
|
+
const credentials = readCredentials();
|
|
8
|
+
const hasLocalSession = Boolean(readSessionToken(credentials) || flags.access_token);
|
|
9
|
+
const base = {
|
|
10
|
+
schema_version: "itp.agent.v1",
|
|
11
|
+
status: hasLocalSession ? "account_status_unavailable" : "unauthenticated",
|
|
12
|
+
phase: hasLocalSession ? "account_status_unavailable" : "unauthenticated",
|
|
13
|
+
authenticated: false,
|
|
14
|
+
session_verified: !hasLocalSession,
|
|
15
|
+
auth_source: hasLocalSession ? "local_files_unverified" : "local_files",
|
|
16
|
+
account_id: config.account_id || null,
|
|
17
|
+
buyer_account_id: config.account_id || null,
|
|
18
|
+
device_id: config.device_id || null,
|
|
19
|
+
agent_device_id: config.device_id || null,
|
|
20
|
+
linked_providers: [],
|
|
21
|
+
order_count: 0,
|
|
22
|
+
created_at: null,
|
|
23
|
+
device: config.device_id ? { agent_device_id: config.device_id } : null,
|
|
24
|
+
next: hasLocalSession
|
|
25
|
+
? { type: "retry_status", command: cliCommand("status", "--json"), safe_for_agent: true }
|
|
26
|
+
: { type: "start_auth", command: cliCommand("setup", "--plan", "credit-300", "--method", "alipay", "--json"), safe_for_agent: true },
|
|
27
|
+
recoverable_context: noRecoverableContext(),
|
|
28
|
+
agent_next_actions: hasLocalSession
|
|
29
|
+
? ["run_status_refresh", "view_orders", "create_refund_if_needed", "list_agent_read_grants"]
|
|
30
|
+
: ["start_human_auth_if_unauthenticated"],
|
|
31
|
+
secrets: { raw_key_included: false, session_token_included: false }
|
|
32
|
+
};
|
|
33
|
+
if (!hasLocalSession) return base;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const me = await coreApi("/v1/me", { method: "GET" }, flags);
|
|
37
|
+
const authenticated = Boolean(me.authenticated);
|
|
38
|
+
const accountID = me.buyer_account_id || config.account_id || null;
|
|
39
|
+
const deviceID = me.agent_device_id || me.device?.agent_device_id || config.device_id || null;
|
|
40
|
+
return {
|
|
41
|
+
...base,
|
|
42
|
+
status: authenticated ? "idle" : "unauthenticated",
|
|
43
|
+
phase: authenticated ? "idle" : "unauthenticated",
|
|
44
|
+
authenticated,
|
|
45
|
+
session_verified: true,
|
|
46
|
+
auth_source: "core",
|
|
47
|
+
account_id: accountID,
|
|
48
|
+
buyer_account_id: accountID,
|
|
49
|
+
device_id: deviceID,
|
|
50
|
+
agent_device_id: deviceID,
|
|
51
|
+
linked_providers: Array.isArray(me.linked_providers) ? me.linked_providers : [],
|
|
52
|
+
order_count: Number.isFinite(Number(me.order_count)) ? Number(me.order_count) : 0,
|
|
53
|
+
created_at: me.created_at || null,
|
|
54
|
+
device: me.device || (deviceID ? { agent_device_id: deviceID } : null),
|
|
55
|
+
next: authenticated
|
|
56
|
+
? { type: "buyer_ready", command: cliCommand("buyer", "auth", "status", "--json"), safe_for_agent: true }
|
|
57
|
+
: base.next,
|
|
58
|
+
agent_next_actions: authenticated
|
|
59
|
+
? ["search_catalog", "view_orders", "create_refund_if_needed", "list_agent_read_grants"]
|
|
60
|
+
: ["start_human_auth_if_unauthenticated"],
|
|
61
|
+
backend: {
|
|
62
|
+
environment: me.environment || null,
|
|
63
|
+
backend_version: me.backend_version || null
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return {
|
|
68
|
+
...base,
|
|
69
|
+
error: safeErrorMessage(error)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function outputAccountStatus(status, flags = {}) {
|
|
75
|
+
if (flags.json) {
|
|
76
|
+
output(status);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
console.log(formatStatusText(status));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function formatStatusText(status = {}) {
|
|
83
|
+
if (!status.authenticated) {
|
|
84
|
+
const lines = [
|
|
85
|
+
`Account: ${status.account_id ? `${status.account_id} (unverified)` : "not signed in"}`,
|
|
86
|
+
"Linked: -",
|
|
87
|
+
"Orders: -",
|
|
88
|
+
`Device: ${formatDeviceLine(status.device, status.device_id) || "-"}`
|
|
89
|
+
];
|
|
90
|
+
if (status.error) lines.push(`Error: ${status.error}`);
|
|
91
|
+
return lines.join("\n");
|
|
92
|
+
}
|
|
93
|
+
const linked = Array.isArray(status.linked_providers) && status.linked_providers.length
|
|
94
|
+
? status.linked_providers.join(", ")
|
|
95
|
+
: "-";
|
|
96
|
+
return [
|
|
97
|
+
`Account: ${status.buyer_account_id || status.account_id || "-"}`,
|
|
98
|
+
`Linked: ${linked}`,
|
|
99
|
+
`Orders: ${Number.isFinite(Number(status.order_count)) ? Number(status.order_count) : 0}`,
|
|
100
|
+
`Device: ${formatDeviceLine(status.device, status.device_id) || "-"}`
|
|
101
|
+
].join("\n");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function formatDeviceLine(device, fallbackID = "") {
|
|
105
|
+
const displayName = String(device?.display_name || "").trim();
|
|
106
|
+
const deviceID = String(device?.agent_device_id || fallbackID || "").trim();
|
|
107
|
+
const label = displayName || deviceID;
|
|
108
|
+
if (!label) return "";
|
|
109
|
+
const status = String(device?.status || "").trim();
|
|
110
|
+
return status ? `${label} (${status})` : label;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function refreshBuyerSessionForStatus(flags = {}) {
|
|
114
|
+
const config = readConfig();
|
|
115
|
+
const credentials = readCredentials();
|
|
116
|
+
if (!readSessionToken(credentials)) {
|
|
117
|
+
return {
|
|
118
|
+
authenticated: false,
|
|
119
|
+
session_verified: true,
|
|
120
|
+
account_id: config.account_id || null,
|
|
121
|
+
device_id: config.device_id || null,
|
|
122
|
+
auth_source: "core_no_session"
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const status = await coreApi("/v1/me/auth/status", { method: "GET" }, flags);
|
|
127
|
+
return {
|
|
128
|
+
authenticated: true,
|
|
129
|
+
session_verified: true,
|
|
130
|
+
account_id: status.buyer_account_id || config.account_id || null,
|
|
131
|
+
device_id: config.device_id || null,
|
|
132
|
+
account_status: status.account_status || null,
|
|
133
|
+
auth_source: "core"
|
|
134
|
+
};
|
|
135
|
+
} catch (error) {
|
|
136
|
+
return {
|
|
137
|
+
authenticated: false,
|
|
138
|
+
session_verified: true,
|
|
139
|
+
account_id: config.account_id || null,
|
|
140
|
+
device_id: config.device_id || null,
|
|
141
|
+
auth_source: "core",
|
|
142
|
+
error: safeErrorMessage(error)
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function nextActionForAuthStatus(auth = {}, flags = {}) {
|
|
148
|
+
if (auth.authenticated && auth.session_verified === false) {
|
|
149
|
+
return { type: "verify_buyer_session", command: cliCommand("status", "--refresh", "--json"), safe_for_agent: true };
|
|
150
|
+
}
|
|
151
|
+
if (auth.authenticated) {
|
|
152
|
+
return { type: "buyer_ready", command: cliCommand("buyer", "auth", "status", "--json"), safe_for_agent: true };
|
|
153
|
+
}
|
|
154
|
+
return { type: "start_auth", command: cliCommand("setup", "--plan", "credit-300", "--method", "alipay", "--json"), safe_for_agent: true };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export { accountOverviewStatus, outputAccountStatus, refreshBuyerSessionForStatus, nextActionForAuthStatus };
|
package/lib/render-human.js
CHANGED
|
@@ -8,7 +8,7 @@ import { apiTimeoutMs, cliCommand, commandExists, mergeRun, readRun, readState,
|
|
|
8
8
|
async function renderItPayPaymentAction(intent, flags = {}) {
|
|
9
9
|
const action = intent?.human_action ? { ...intent.human_action } : (intent?.payment_url ? {
|
|
10
10
|
id: intent.payment_intent_id,
|
|
11
|
-
title: "Scan
|
|
11
|
+
title: "Scan payment QR",
|
|
12
12
|
url: intent.payment_url,
|
|
13
13
|
expires_at: intent.qr?.expires_at
|
|
14
14
|
} : null);
|
|
@@ -23,7 +23,7 @@ async function renderItPayPaymentAction(intent, flags = {}) {
|
|
|
23
23
|
if (action && (intent?.qr_image_url || intent?.qr?.image_url)) {
|
|
24
24
|
action.qr_image_url = intent.qr_image_url || intent.qr.image_url;
|
|
25
25
|
action.display_mode = intent.qr?.scan_mode || "itpay_entry_qr";
|
|
26
|
-
action.description = action.description || "Scan the ItPay payment entry QR; ItPay will safely hand off to
|
|
26
|
+
action.description = action.description || "Scan the ItPay payment entry QR; ItPay will safely hand off to the payment provider.";
|
|
27
27
|
}
|
|
28
28
|
const result = await renderHumanAction(action, flags);
|
|
29
29
|
if (intent && action) {
|
|
@@ -39,7 +39,7 @@ function humanActionSummaryLines(action) {
|
|
|
39
39
|
if (!action?.url) return [];
|
|
40
40
|
const lines = [
|
|
41
41
|
"ITP HUMAN ACTION REQUIRED",
|
|
42
|
-
`Title: ${action
|
|
42
|
+
`Title: ${humanActionTitle(action)}`,
|
|
43
43
|
`URL: ${action.url}`
|
|
44
44
|
];
|
|
45
45
|
if (action.local_qr_path) {
|
|
@@ -55,7 +55,7 @@ function humanActionSummaryLines(action) {
|
|
|
55
55
|
lines.push(`Mobile wallet link: ${action.mobile_wallet_url}`);
|
|
56
56
|
}
|
|
57
57
|
if (action.oauth_start_url) {
|
|
58
|
-
lines.push(
|
|
58
|
+
lines.push(`${humanActionProviderLabel(action)} auth fallback: ${action.oauth_start_url}`);
|
|
59
59
|
}
|
|
60
60
|
if (action.fallback_text && !action.fallback_text.includes(action.url)) {
|
|
61
61
|
lines.push(`Fallback: ${action.fallback_text}`);
|
|
@@ -89,7 +89,7 @@ function writeWaitHeartbeat({ kind, idName, idValue, status, action, lastHeartbe
|
|
|
89
89
|
action?.qr_png_url ? `QR PNG: ${action.qr_png_url}` : null,
|
|
90
90
|
action?.qr_image_url ? `QR image: ${action.qr_image_url}` : null,
|
|
91
91
|
action?.mobile_wallet_url ? `Mobile wallet link: ${action.mobile_wallet_url}` : null,
|
|
92
|
-
action?.oauth_start_url ?
|
|
92
|
+
action?.oauth_start_url ? `${humanActionProviderLabel(action)} auth fallback: ${action.oauth_start_url}` : null,
|
|
93
93
|
command ? `Resume command: ${command}` : null
|
|
94
94
|
].filter(Boolean);
|
|
95
95
|
process.stderr.write(`\n${lines.join("\n")}\n\n`);
|
|
@@ -147,9 +147,29 @@ async function renderHumanAction(action, flags = {}) {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
const renderResult = { rendered: false, mode, outputs: [] };
|
|
150
|
+
const terminalQRAllowed = shouldRenderTerminalQR(host, mode);
|
|
151
|
+
const providerLabel = humanActionProviderLabel(action);
|
|
152
|
+
const actionTitle = humanActionTitle(action);
|
|
150
153
|
writeHumanActionSummary(action);
|
|
154
|
+
|
|
155
|
+
if ((mode === "auto" || mode === "terminal") && terminalQRAllowed) {
|
|
156
|
+
const qr = await QRCode.toString(action.url, {
|
|
157
|
+
type: terminalQRType(flags, host),
|
|
158
|
+
small: true,
|
|
159
|
+
errorCorrectionLevel: "M"
|
|
160
|
+
});
|
|
161
|
+
process.stderr.write(`\n${actionTitle}\n`);
|
|
162
|
+
if (action.description) process.stderr.write(`${action.description}\n`);
|
|
163
|
+
process.stderr.write(`${qr}\n`);
|
|
164
|
+
process.stderr.write(`${providerLabel} action URL: ${action.url}\n`);
|
|
165
|
+
if (action.expires_at) process.stderr.write(`Expires at: ${formatActionTime(action.expires_at)}\n`);
|
|
166
|
+
renderResult.rendered = true;
|
|
167
|
+
renderResult.outputs.push("terminal");
|
|
168
|
+
return renderResult;
|
|
169
|
+
}
|
|
170
|
+
|
|
151
171
|
if ((mode === "auto" || mode === "browser") && shouldOpenBrowser(flags)) {
|
|
152
|
-
if (openBrowser(action.mobile_wallet_url ||
|
|
172
|
+
if (openBrowser(qrImageURL || action.mobile_wallet_url || action.url)) {
|
|
153
173
|
renderResult.rendered = true;
|
|
154
174
|
renderResult.outputs.push("browser");
|
|
155
175
|
}
|
|
@@ -162,7 +182,7 @@ async function renderHumanAction(action, flags = {}) {
|
|
|
162
182
|
await downloadQRImage(qrImageURL, file, flags);
|
|
163
183
|
action.local_qr_path = file;
|
|
164
184
|
action.local_qr_mime = qrMimeType(qrImageURL, action);
|
|
165
|
-
process.stderr.write(
|
|
185
|
+
process.stderr.write(`${providerLabel} QR image: ${file}\n`);
|
|
166
186
|
renderResult.rendered = true;
|
|
167
187
|
renderResult.outputs.push(file);
|
|
168
188
|
if (mode === "file") return renderResult;
|
|
@@ -173,44 +193,42 @@ async function renderHumanAction(action, flags = {}) {
|
|
|
173
193
|
if (localPath) {
|
|
174
194
|
attachAgentLocalQR(action, localPath);
|
|
175
195
|
annotateHumanActionPresentation(action, "");
|
|
176
|
-
process.stderr.write(
|
|
196
|
+
process.stderr.write(`${providerLabel} action QR image: ${localPath}\n`);
|
|
177
197
|
renderResult.rendered = true;
|
|
178
198
|
renderResult.outputs.push(localPath);
|
|
179
199
|
if (mode === "file") return renderResult;
|
|
180
200
|
} else {
|
|
181
|
-
process.stderr.write(`No QR image available. Open action URL: ${action.url}\n`);
|
|
201
|
+
process.stderr.write(`No branded QR image available. Open action URL: ${action.url}\n`);
|
|
182
202
|
}
|
|
183
203
|
}
|
|
184
204
|
}
|
|
185
205
|
|
|
186
206
|
if (qrImageURL) {
|
|
187
|
-
process.stderr.write(
|
|
207
|
+
process.stderr.write(`${providerLabel} QR image URL: ${qrImageURL}\n`);
|
|
188
208
|
if (action.mobile_wallet_url) process.stderr.write(`Mobile wallet link: ${action.mobile_wallet_url}\n`);
|
|
189
209
|
renderResult.outputs.push("preferred_qr_url");
|
|
190
210
|
return renderResult;
|
|
191
211
|
}
|
|
192
212
|
|
|
193
|
-
|
|
194
|
-
const qr = await QRCode.toString(action.url, {
|
|
195
|
-
type: terminalQRType(flags, host),
|
|
196
|
-
small: true,
|
|
197
|
-
errorCorrectionLevel: "M"
|
|
198
|
-
});
|
|
199
|
-
process.stderr.write(`\n${action.title || "Scan with Alipay"}\n`);
|
|
200
|
-
if (action.description) process.stderr.write(`${action.description}\n`);
|
|
201
|
-
process.stderr.write(`${qr}\n`);
|
|
202
|
-
process.stderr.write(`Alipay action URL: ${action.url}\n`);
|
|
203
|
-
if (action.expires_at) process.stderr.write(`Expires at: ${formatActionTime(action.expires_at)}\n`);
|
|
204
|
-
renderResult.rendered = true;
|
|
205
|
-
renderResult.outputs.push("terminal");
|
|
206
|
-
return renderResult;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
process.stderr.write(`${action.fallback_text || `Open Alipay URL: ${action.url}`}\n`);
|
|
213
|
+
process.stderr.write(`${action.fallback_text || `Open ${providerLabel} URL: ${action.url}`}\n`);
|
|
210
214
|
renderResult.outputs.push("url");
|
|
211
215
|
return renderResult;
|
|
212
216
|
}
|
|
213
217
|
|
|
218
|
+
function humanActionTitle(action = {}) {
|
|
219
|
+
if (action.title) return action.title;
|
|
220
|
+
if (action.kind === "auth_qr") return `${humanActionProviderLabel(action)} authentication`;
|
|
221
|
+
return "Scan payment QR";
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function humanActionProviderLabel(action = {}) {
|
|
225
|
+
const raw = String(action.provider || action.channel || action.display_mode || "").toLowerCase();
|
|
226
|
+
if (raw.includes("alipay")) return "Alipay";
|
|
227
|
+
if (raw.includes("wechat")) return "WeChat Pay";
|
|
228
|
+
if (raw.includes("fake") || raw.includes("local")) return "ItPay local";
|
|
229
|
+
return "Alipay or WeChat Pay";
|
|
230
|
+
}
|
|
231
|
+
|
|
214
232
|
function preferredHumanActionQRURL(action) {
|
|
215
233
|
return action?.qr_png_url ||
|
|
216
234
|
humanActionPresentationURL(action, "qr_png_url") ||
|
|
@@ -416,16 +434,22 @@ function persistHumanAction(action, flags = {}) {
|
|
|
416
434
|
writeRun(mergeRun(run, { human_action: action }));
|
|
417
435
|
}
|
|
418
436
|
|
|
419
|
-
function shouldRenderTerminalQR(host) {
|
|
437
|
+
function shouldRenderTerminalQR(host, mode = "auto") {
|
|
438
|
+
if (["terminal", "tty"].includes(mode)) return true;
|
|
439
|
+
if (mode && !["auto", ""].includes(mode)) return false;
|
|
420
440
|
if (process.stderr.isTTY) return true;
|
|
421
|
-
return
|
|
441
|
+
return terminalQRHostAliases().has(String(host || "").toLowerCase());
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function terminalQRHostAliases() {
|
|
445
|
+
return new Set(["terminal", "tty", "bash", "zsh", "sh", "shell", "iterm", "iterm2"]);
|
|
422
446
|
}
|
|
423
447
|
|
|
424
448
|
function terminalQRType(flags = {}, host = "") {
|
|
425
449
|
const requested = String(flags.qr_format || process.env.ITP_QR_FORMAT || "").toLowerCase();
|
|
426
450
|
if (requested === "unicode" || requested === "utf8") return "utf8";
|
|
427
451
|
if (requested === "ansi" || requested === "terminal") return "terminal";
|
|
428
|
-
if (
|
|
452
|
+
if (terminalQRHostAliases().has(String(host || "").toLowerCase())) return "utf8";
|
|
429
453
|
return "terminal";
|
|
430
454
|
}
|
|
431
455
|
|
package/lib/runtime.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
recoverableBuyerContextForStatus, recoverableIntentCheckGuidance
|
|
8
8
|
} from "./buyer.js";
|
|
9
9
|
import { renderHumanAction, shouldReturnAfterAgentTextQR, writeWaitHeartbeat } from "./render-human.js";
|
|
10
|
+
import { accountOverviewStatus, nextActionForAuthStatus, outputAccountStatus, refreshBuyerSessionForStatus } from "./account-status.js";
|
|
10
11
|
import {
|
|
11
12
|
CREDENTIALS_PATH, CONFIG_DIR, RUNS_DIR, VERSION, apiBase, apiTimeoutMs, appendURLQuery, booleanFlag, cliCommand, commandExists,
|
|
12
13
|
cryptoRandom, csvValues, currentExecutable, deleteGrantCredential, deleteSessionCredential, detectNativeCredentialStore,
|
|
@@ -131,14 +132,14 @@ async function setup(flags) {
|
|
|
131
132
|
grant_id: checkout.grant_id || run.grant?.grant_id || null
|
|
132
133
|
},
|
|
133
134
|
human_action: checkout.human_action || null,
|
|
134
|
-
safe_summary: checkout.grant_id ? "Payment verified and grant is ready." : "Waiting for Alipay payment scan."
|
|
135
|
+
safe_summary: checkout.grant_id ? "Payment verified and grant is ready." : "Waiting for Alipay or WeChat Pay payment scan."
|
|
135
136
|
});
|
|
136
137
|
writeRun(run);
|
|
137
138
|
|
|
138
139
|
if (checkout.human_action) {
|
|
139
140
|
await renderHumanAction(checkout.human_action, setupFlags);
|
|
140
141
|
} else if (checkout.payment?.cashier_url) {
|
|
141
|
-
process.stderr.write(`Open Alipay payment URL: ${checkout.payment.cashier_url}\n`);
|
|
142
|
+
process.stderr.write(`Open Alipay or WeChat Pay payment URL: ${checkout.payment.cashier_url}\n`);
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
if (!checkout.grant_id && (setupFlags.no_wait_payment || setupFlags.no_wait)) {
|
|
@@ -263,40 +264,7 @@ async function agentStatus(flags) {
|
|
|
263
264
|
const runId = flags.run_id || readState().current_run_id;
|
|
264
265
|
const run = readRun(runId);
|
|
265
266
|
if (!run) {
|
|
266
|
-
|
|
267
|
-
const credentials = readCredentials();
|
|
268
|
-
const hasLocalSession = Boolean(readSessionToken(credentials));
|
|
269
|
-
const auth = flags.refresh
|
|
270
|
-
? await refreshBuyerSessionForStatus(flags)
|
|
271
|
-
: {
|
|
272
|
-
authenticated: hasLocalSession,
|
|
273
|
-
session_verified: false,
|
|
274
|
-
account_id: config.account_id || null,
|
|
275
|
-
device_id: config.device_id || null,
|
|
276
|
-
auth_source: hasLocalSession ? "local_files_unverified" : "local_files"
|
|
277
|
-
};
|
|
278
|
-
output({
|
|
279
|
-
schema_version: "itp.agent.v1",
|
|
280
|
-
status: auth.authenticated
|
|
281
|
-
? (auth.session_verified === false ? "local_session_unverified" : "idle")
|
|
282
|
-
: "unauthenticated",
|
|
283
|
-
phase: auth.authenticated ? "idle" : "unauthenticated",
|
|
284
|
-
authenticated: Boolean(auth.authenticated),
|
|
285
|
-
session_verified: auth.session_verified !== false,
|
|
286
|
-
auth_source: auth.auth_source,
|
|
287
|
-
account_id: auth.account_id || null,
|
|
288
|
-
device_id: auth.device_id || null,
|
|
289
|
-
next: nextActionForAuthStatus(auth, flags),
|
|
290
|
-
recoverable_context: noRecoverableContext(),
|
|
291
|
-
agent_next_actions: auth.authenticated && auth.session_verified !== false
|
|
292
|
-
? ["search_catalog", "view_orders", "create_refund_if_needed", "list_agent_read_grants"]
|
|
293
|
-
: ["run_status_refresh", "start_human_auth_if_unauthenticated"],
|
|
294
|
-
note: auth.authenticated && auth.session_verified === false
|
|
295
|
-
? "Local buyer session exists but the server has not verified it. Run the next.command before order/refund/account operations."
|
|
296
|
-
: undefined,
|
|
297
|
-
error: auth.error || undefined,
|
|
298
|
-
secrets: { raw_key_included: false, session_token_included: false }
|
|
299
|
-
});
|
|
267
|
+
outputAccountStatus(await accountOverviewStatus(flags), flags);
|
|
300
268
|
return;
|
|
301
269
|
}
|
|
302
270
|
const refreshed = flags.refresh ? await refreshRun(run, flags) : run;
|
|
@@ -306,50 +274,6 @@ async function agentStatus(flags) {
|
|
|
306
274
|
}));
|
|
307
275
|
}
|
|
308
276
|
|
|
309
|
-
async function refreshBuyerSessionForStatus(flags = {}) {
|
|
310
|
-
const config = readConfig();
|
|
311
|
-
const credentials = readCredentials();
|
|
312
|
-
if (!readSessionToken(credentials)) {
|
|
313
|
-
return {
|
|
314
|
-
authenticated: false,
|
|
315
|
-
session_verified: true,
|
|
316
|
-
account_id: config.account_id || null,
|
|
317
|
-
device_id: config.device_id || null,
|
|
318
|
-
auth_source: "core_no_session"
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
try {
|
|
322
|
-
const status = await coreApi("/v1/me/auth/status", { method: "GET" }, flags);
|
|
323
|
-
return {
|
|
324
|
-
authenticated: true,
|
|
325
|
-
session_verified: true,
|
|
326
|
-
account_id: status.buyer_account_id || config.account_id || null,
|
|
327
|
-
device_id: config.device_id || null,
|
|
328
|
-
account_status: status.account_status || null,
|
|
329
|
-
auth_source: "core"
|
|
330
|
-
};
|
|
331
|
-
} catch (error) {
|
|
332
|
-
return {
|
|
333
|
-
authenticated: false,
|
|
334
|
-
session_verified: true,
|
|
335
|
-
account_id: config.account_id || null,
|
|
336
|
-
device_id: config.device_id || null,
|
|
337
|
-
auth_source: "core",
|
|
338
|
-
error: safeErrorMessage(error)
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
function nextActionForAuthStatus(auth = {}, flags = {}) {
|
|
344
|
-
if (auth.authenticated && auth.session_verified === false) {
|
|
345
|
-
return { type: "verify_buyer_session", command: cliCommand("status", "--refresh", "--json"), safe_for_agent: true };
|
|
346
|
-
}
|
|
347
|
-
if (auth.authenticated) {
|
|
348
|
-
return { type: "buyer_ready", command: cliCommand("buyer", "auth", "status", "--json"), safe_for_agent: true };
|
|
349
|
-
}
|
|
350
|
-
return { type: "start_auth", command: cliCommand("setup", "--plan", "credit-300", "--method", "alipay", "--json"), safe_for_agent: true };
|
|
351
|
-
}
|
|
352
|
-
|
|
353
277
|
async function resume(flags) {
|
|
354
278
|
const runId = flags.run_id || readState().current_run_id;
|
|
355
279
|
const run = readRun(runId);
|
|
@@ -761,7 +685,7 @@ async function createCheckoutResult(flags) {
|
|
|
761
685
|
grant_id: response.grant_id || currentRun?.grant?.grant_id || null
|
|
762
686
|
},
|
|
763
687
|
human_action: response.human_action || null,
|
|
764
|
-
safe_summary: response.grant_id ? "Payment verified and grant is ready." : "Waiting for Alipay payment scan."
|
|
688
|
+
safe_summary: response.grant_id ? "Payment verified and grant is ready." : "Waiting for Alipay or WeChat Pay payment scan."
|
|
765
689
|
}, flags);
|
|
766
690
|
return response;
|
|
767
691
|
}
|
|
@@ -827,7 +751,7 @@ async function paymentWaitResult(checkoutId, flags) {
|
|
|
827
751
|
}
|
|
828
752
|
}
|
|
829
753
|
lastHeartbeatAt = writeWaitHeartbeat({
|
|
830
|
-
kind: "Alipay payment",
|
|
754
|
+
kind: "Alipay or WeChat Pay payment",
|
|
831
755
|
idName: "checkout_id",
|
|
832
756
|
idValue: checkoutId,
|
|
833
757
|
status: response.status || "waiting",
|
|
@@ -1376,7 +1300,7 @@ async function refreshRun(run, flags = {}) {
|
|
|
1376
1300
|
payment: { provider: next.payment_method, status: checkout.status },
|
|
1377
1301
|
grant: { ...(next.grant || {}), grant_id: checkout.grant_id || next.grant?.grant_id || null },
|
|
1378
1302
|
human_action: checkout.human_action || null,
|
|
1379
|
-
safe_summary: checkout.grant_id ? "Payment verified and grant is ready." : checkout.status === "waiting_user_payment" ? "Waiting for Alipay payment scan." : `Checkout status: ${checkout.status}`
|
|
1303
|
+
safe_summary: checkout.grant_id ? "Payment verified and grant is ready." : checkout.status === "waiting_user_payment" ? "Waiting for Alipay or WeChat Pay payment scan." : `Checkout status: ${checkout.status}`
|
|
1380
1304
|
});
|
|
1381
1305
|
} catch (error) {
|
|
1382
1306
|
next = mergeRun(next, { last_error: safeErrorMessage(error), safe_summary: "Could not refresh checkout status." });
|
|
@@ -1498,7 +1422,7 @@ function appendPassthroughFlag(args, flags, key, flagName = key) {
|
|
|
1498
1422
|
|
|
1499
1423
|
function safeUserMessageForRun(run) {
|
|
1500
1424
|
if (run.phase === "waiting_human_auth") return "Please scan the Alipay authentication QR. I will continue automatically after approval.";
|
|
1501
|
-
if (run.phase === "waiting_human_payment") return "Please scan the Alipay payment QR. I will continue automatically after payment is verified.";
|
|
1425
|
+
if (run.phase === "waiting_human_payment") return "Please scan the Alipay or WeChat Pay payment QR. I will continue automatically after payment is verified.";
|
|
1502
1426
|
if (run.grant?.installed) return "Payment verified. The API credential is stored locally.";
|
|
1503
1427
|
return run.safe_summary || "ITPay setup is in progress.";
|
|
1504
1428
|
}
|