@itpay/cli 0.2.16 → 2.0.0-rc.1
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 +148 -447
- package/bin/itp +1 -150
- package/dist/src/client/backend.js +154 -0
- package/dist/src/client/http.js +76 -0
- package/dist/src/client/types.js +4 -0
- package/dist/src/commands/buy.js +351 -0
- package/dist/src/commands/cart.js +264 -0
- package/dist/src/commands/catalog.js +26 -0
- package/dist/src/commands/checkout.js +106 -0
- package/dist/src/commands/docs.js +61 -0
- package/dist/src/commands/guidance.js +422 -0
- package/dist/src/commands/install.js +95 -0
- package/dist/src/commands/order.js +78 -0
- package/dist/src/commands/orders.js +22 -0
- package/dist/src/commands/pay.js +26 -0
- package/dist/src/commands/readyz.js +8 -0
- package/dist/src/commands/refund.js +20 -0
- package/dist/src/commands/services.js +317 -0
- package/dist/src/main.js +606 -0
- package/dist/src/render/feishu.js +201 -0
- package/dist/src/render/ide.js +321 -0
- package/dist/src/render/index.js +57 -0
- package/dist/src/render/interaction.js +49 -0
- package/dist/src/render/markdown.js +83 -0
- package/dist/src/render/output.js +42 -0
- package/dist/src/render/plain_chat.js +60 -0
- package/dist/src/render/plan.js +31 -0
- package/dist/src/render/qr.js +32 -0
- package/dist/src/render/sink.js +6 -0
- package/dist/src/render/status.js +37 -0
- package/dist/src/render/telegram.js +172 -0
- package/dist/src/render/terminal.js +148 -0
- package/dist/src/render/terminal_image.js +19 -0
- package/dist/src/state/cart_session.js +151 -0
- package/dist/src/state/client_context.js +73 -0
- package/dist/src/state/config.js +82 -0
- package/dist/src/state/device_authority.js +217 -0
- package/dist/src/state/operation_journal.js +80 -0
- package/docs/agent/buyer/cart-checkout.json +56 -94
- package/docs/agent/buyer/catalog-list.json +47 -0
- package/docs/agent/buyer/install-and-setup.json +82 -0
- package/docs/agent/buyer/orders-refunds.json +76 -0
- package/docs/agent/buyer/payment-flow.json +77 -0
- package/docs/agent/buyer/quickstart.json +143 -75
- package/docs/agent/buyer/render-hosts.json +79 -0
- package/package.json +32 -13
- package/skills/itpay-buyer/SKILL.md +107 -238
- package/docs/agent/buyer/account-portal.json +0 -81
- package/docs/agent/buyer/catalog-search.json +0 -106
- package/docs/agent/buyer/human-claim-ui.json +0 -77
- package/docs/agent/buyer/payment-qr.json +0 -97
- package/docs/agent/buyer/payment-wait.json +0 -84
- package/docs/agent/buyer/product-recommendation.json +0 -80
- package/docs/agent/buyer/qr-refresh.json +0 -67
- package/docs/agent/buyer/recovery.json +0 -85
- package/docs/agent/buyer/safety-policy.json +0 -70
- package/docs/agent/buyer/secure-delivery.json +0 -90
- package/docs/agent/buyer/vault-agent-read.json +0 -95
- package/install.ps1 +0 -65
- package/install.sh +0 -66
- package/lib/account-status.js +0 -157
- package/lib/buyer.js +0 -2332
- package/lib/client-context.js +0 -126
- package/lib/docs.js +0 -200
- package/lib/env.js +0 -723
- package/lib/http.js +0 -151
- package/lib/ops.js +0 -135
- package/lib/render-human.js +0 -718
- package/lib/runtime.js +0 -1456
package/lib/render-human.js
DELETED
|
@@ -1,718 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { execFileSync } from "node:child_process";
|
|
5
|
-
import QRCode from "qrcode";
|
|
6
|
-
import { clientCommandArgs, clientHost, clientTarget } from "./client-context.js";
|
|
7
|
-
import { apiTimeoutMs, cliCommand, commandExists, mergeRun, readRun, readState, safeErrorMessage, writeRun } from "./env.js";
|
|
8
|
-
|
|
9
|
-
async function renderItPayPaymentAction(intent, flags = {}) {
|
|
10
|
-
const action = intent?.human_action ? { ...intent.human_action } : (intent?.payment_url ? {
|
|
11
|
-
kind: "payment_qr",
|
|
12
|
-
id: intent.payment_intent_id,
|
|
13
|
-
payment_intent_id: intent.payment_intent_id,
|
|
14
|
-
title: "Scan payment QR",
|
|
15
|
-
url: intent.payment_url,
|
|
16
|
-
expires_at: intent.qr?.expires_at
|
|
17
|
-
} : null);
|
|
18
|
-
if (action) {
|
|
19
|
-
if (!action.kind && isPaymentIntentHandoff(intent)) action.kind = "payment_qr";
|
|
20
|
-
if (!action.id && intent?.payment_intent_id) action.id = intent.payment_intent_id;
|
|
21
|
-
if (!action.payment_intent_id && intent?.payment_intent_id) action.payment_intent_id = intent.payment_intent_id;
|
|
22
|
-
if (!action.url && (intent?.payment_url || intent?.payment_entry_url)) action.url = intent.payment_url || intent.payment_entry_url;
|
|
23
|
-
if (intent?.qr_png_url || intent?.qr?.png_url) {
|
|
24
|
-
action.qr_png_url = intent.qr_png_url || intent.qr.png_url;
|
|
25
|
-
}
|
|
26
|
-
if (intent?.mobile_wallet_url) {
|
|
27
|
-
action.mobile_wallet_url = intent.mobile_wallet_url;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
if (action && (intent?.qr_image_url || intent?.qr?.image_url)) {
|
|
31
|
-
action.qr_image_url = intent.qr_image_url || intent.qr.image_url;
|
|
32
|
-
action.display_mode = intent.qr?.scan_mode || "itpay_entry_qr";
|
|
33
|
-
action.description = action.description || "Scan the ItPay payment entry QR; ItPay will safely hand off to the payment provider.";
|
|
34
|
-
}
|
|
35
|
-
const result = await renderHumanAction(action, flags);
|
|
36
|
-
if (intent && action) {
|
|
37
|
-
intent.human_action = { ...(intent.human_action || {}), ...action };
|
|
38
|
-
if (action.local_qr_path) intent.local_qr_path = action.local_qr_path;
|
|
39
|
-
if (action.preferred_qr_url) intent.preferred_qr_url = action.preferred_qr_url;
|
|
40
|
-
if (action.mobile_wallet_url) intent.mobile_wallet_url = action.mobile_wallet_url;
|
|
41
|
-
}
|
|
42
|
-
return result;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function isPaymentIntentHandoff(intent = {}) {
|
|
46
|
-
return Boolean(
|
|
47
|
-
intent?.payment_intent_id ||
|
|
48
|
-
intent?.payment_url ||
|
|
49
|
-
intent?.payment_entry_url ||
|
|
50
|
-
intent?.qr_png_url ||
|
|
51
|
-
intent?.qr_image_url ||
|
|
52
|
-
intent?.qr?.png_url ||
|
|
53
|
-
intent?.qr?.image_url
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function humanActionSummaryLines(action) {
|
|
58
|
-
if (!action?.url) return [];
|
|
59
|
-
const lines = [
|
|
60
|
-
"ITP HUMAN ACTION REQUIRED",
|
|
61
|
-
`Title: ${humanActionTitle(action)}`,
|
|
62
|
-
`URL: ${action.url}`
|
|
63
|
-
];
|
|
64
|
-
if (action.local_qr_path) {
|
|
65
|
-
lines.push(`Local QR image: ${action.local_qr_path}`);
|
|
66
|
-
}
|
|
67
|
-
if (action.qr_png_url) {
|
|
68
|
-
lines.push(`QR PNG: ${action.qr_png_url}`);
|
|
69
|
-
}
|
|
70
|
-
if (action.qr_image_url) {
|
|
71
|
-
lines.push(`QR image: ${action.qr_image_url}`);
|
|
72
|
-
}
|
|
73
|
-
if (action.mobile_wallet_url) {
|
|
74
|
-
lines.push(`Mobile wallet link: ${action.mobile_wallet_url}`);
|
|
75
|
-
}
|
|
76
|
-
if (action.oauth_start_url) {
|
|
77
|
-
lines.push(`${humanActionProviderLabel(action)} auth fallback: ${action.oauth_start_url}`);
|
|
78
|
-
}
|
|
79
|
-
if (action.fallback_text && !action.fallback_text.includes(action.url)) {
|
|
80
|
-
lines.push(`Fallback: ${action.fallback_text}`);
|
|
81
|
-
}
|
|
82
|
-
if (action.expires_at) {
|
|
83
|
-
lines.push(`Expires at: ${formatActionTime(action.expires_at)}`);
|
|
84
|
-
}
|
|
85
|
-
return lines;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function writeHumanActionSummary(action, suffix = "") {
|
|
89
|
-
const lines = humanActionSummaryLines(action);
|
|
90
|
-
if (!lines.length) return;
|
|
91
|
-
process.stderr.write(`\n${lines.join("\n")}${suffix ? `\n${suffix}` : ""}\n\n`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function waitHeartbeatMs(flags = {}) {
|
|
95
|
-
if (flags.quiet || process.env.ITP_WAIT_HEARTBEAT_SECONDS === "0") return 0;
|
|
96
|
-
return Math.max(5000, Number(flags.heartbeat || process.env.ITP_WAIT_HEARTBEAT_SECONDS || 20) * 1000);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function writeWaitHeartbeat({ kind, idName, idValue, status, action, lastHeartbeatAt, flags, command }) {
|
|
100
|
-
const heartbeatMs = waitHeartbeatMs(flags);
|
|
101
|
-
if (!heartbeatMs) return lastHeartbeatAt;
|
|
102
|
-
const now = Date.now();
|
|
103
|
-
if (now - lastHeartbeatAt < heartbeatMs) return lastHeartbeatAt;
|
|
104
|
-
const lines = [
|
|
105
|
-
`ITP waiting for ${kind}: ${idName}=${idValue} status=${status}`,
|
|
106
|
-
action?.url ? `URL: ${action.url}` : null,
|
|
107
|
-
action?.local_qr_path ? `Local QR image: ${action.local_qr_path}` : null,
|
|
108
|
-
action?.qr_png_url ? `QR PNG: ${action.qr_png_url}` : null,
|
|
109
|
-
action?.qr_image_url ? `QR image: ${action.qr_image_url}` : null,
|
|
110
|
-
action?.mobile_wallet_url ? `Mobile wallet link: ${action.mobile_wallet_url}` : null,
|
|
111
|
-
action?.oauth_start_url ? `${humanActionProviderLabel(action)} auth fallback: ${action.oauth_start_url}` : null,
|
|
112
|
-
command ? `Resume command: ${command}` : null
|
|
113
|
-
].filter(Boolean);
|
|
114
|
-
process.stderr.write(`\n${lines.join("\n")}\n\n`);
|
|
115
|
-
return now;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
async function renderHumanAction(action, flags = {}) {
|
|
119
|
-
if (!action?.url) return null;
|
|
120
|
-
const qrImageURL = preferredHumanActionQRURL(action);
|
|
121
|
-
const mode = String(flags.display || process.env.ITP_DISPLAY || "").toLowerCase() || "auto";
|
|
122
|
-
annotateHumanActionPresentation(action, qrImageURL);
|
|
123
|
-
if (flags.json || mode === "none" || mode === "json") {
|
|
124
|
-
if (qrImageURL && shouldPrepareLocalQRForJSON(mode, flags, action)) {
|
|
125
|
-
const localPath = await prepareLocalQRFile(action, qrImageURL, flags, mode !== "file" && !flags.qr_file && !process.env.ITP_QR_FILE);
|
|
126
|
-
if (localPath) {
|
|
127
|
-
attachAgentQRImage(action, qrImageURL, localPath);
|
|
128
|
-
persistHumanAction(action, flags);
|
|
129
|
-
return { rendered: false, mode: "json-local-qr", outputs: [localPath] };
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
if (!qrImageURL && shouldGenerateLocalQRFromActionURL(action, mode, flags)) {
|
|
133
|
-
const localPath = await prepareLocalQRFromActionURL(action, flags, mode !== "file" && !flags.qr_file && !process.env.ITP_QR_FILE);
|
|
134
|
-
if (localPath) {
|
|
135
|
-
attachAgentLocalQR(action, localPath);
|
|
136
|
-
annotateHumanActionPresentation(action, "");
|
|
137
|
-
persistHumanAction(action, flags);
|
|
138
|
-
return { rendered: false, mode: "json-local-url-qr", outputs: [localPath] };
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return { rendered: false, mode: flags.json ? "json" : mode };
|
|
142
|
-
}
|
|
143
|
-
const host = agentHost(flags);
|
|
144
|
-
if (["discord", "telegram", "whatsapp"].includes(host)) {
|
|
145
|
-
return { rendered: false, mode: "chat-json", host };
|
|
146
|
-
}
|
|
147
|
-
if (shouldUseAgentTextQR(flags)) {
|
|
148
|
-
if (qrImageURL) {
|
|
149
|
-
const localPath = await prepareLocalQRFile(action, qrImageURL, flags, true);
|
|
150
|
-
attachAgentQRImage(action, qrImageURL, localPath);
|
|
151
|
-
persistHumanAction(action, flags);
|
|
152
|
-
return { rendered: false, mode: localPath ? "agent-local-image-qr" : "agent-image-qr", host, outputs: [localPath || "preferred_qr_url"] };
|
|
153
|
-
}
|
|
154
|
-
if (shouldGenerateLocalQRFromActionURL(action, mode, flags)) {
|
|
155
|
-
const localPath = await prepareLocalQRFromActionURL(action, flags, true);
|
|
156
|
-
if (localPath) {
|
|
157
|
-
attachAgentLocalQR(action, localPath);
|
|
158
|
-
annotateHumanActionPresentation(action, "");
|
|
159
|
-
persistHumanAction(action, flags);
|
|
160
|
-
return { rendered: false, mode: "agent-local-url-qr", host, outputs: [localPath] };
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
process.stderr.write(`No QR image available. Open action URL: ${action.url}\n`);
|
|
164
|
-
persistHumanAction(action, flags);
|
|
165
|
-
return { rendered: false, mode: "agent-url-fallback", host, outputs: ["action_url"] };
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const renderResult = { rendered: false, mode, outputs: [] };
|
|
169
|
-
const terminalQRAllowed = shouldRenderTerminalQR(host, mode);
|
|
170
|
-
const providerLabel = humanActionProviderLabel(action);
|
|
171
|
-
const actionTitle = humanActionTitle(action);
|
|
172
|
-
writeHumanActionSummary(action);
|
|
173
|
-
|
|
174
|
-
if ((mode === "auto" || mode === "terminal") && terminalQRAllowed) {
|
|
175
|
-
const qr = await QRCode.toString(action.url, {
|
|
176
|
-
type: terminalQRType(flags, host),
|
|
177
|
-
small: true,
|
|
178
|
-
errorCorrectionLevel: "M"
|
|
179
|
-
});
|
|
180
|
-
process.stderr.write(`\n${actionTitle}\n`);
|
|
181
|
-
if (action.description) process.stderr.write(`${action.description}\n`);
|
|
182
|
-
process.stderr.write(`${qr}\n`);
|
|
183
|
-
process.stderr.write(`${providerLabel} action URL: ${action.url}\n`);
|
|
184
|
-
if (action.expires_at) process.stderr.write(`Expires at: ${formatActionTime(action.expires_at)}\n`);
|
|
185
|
-
renderResult.rendered = true;
|
|
186
|
-
renderResult.outputs.push("terminal");
|
|
187
|
-
return renderResult;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if ((mode === "auto" || mode === "browser") && shouldOpenBrowser(flags)) {
|
|
191
|
-
if (openBrowser(qrImageURL || action.mobile_wallet_url || action.url)) {
|
|
192
|
-
renderResult.rendered = true;
|
|
193
|
-
renderResult.outputs.push("browser");
|
|
194
|
-
}
|
|
195
|
-
if (mode === "browser") return renderResult;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (mode === "file" || flags.qr_file || process.env.ITP_QR_FILE) {
|
|
199
|
-
const file = flags.qr_file || process.env.ITP_QR_FILE || defaultQRFilePath(action, qrImageURL);
|
|
200
|
-
if (qrImageURL) {
|
|
201
|
-
await downloadQRImage(qrImageURL, file, flags);
|
|
202
|
-
action.local_qr_path = file;
|
|
203
|
-
action.local_qr_mime = qrMimeType(qrImageURL, action);
|
|
204
|
-
process.stderr.write(`${providerLabel} QR image: ${file}\n`);
|
|
205
|
-
renderResult.rendered = true;
|
|
206
|
-
renderResult.outputs.push(file);
|
|
207
|
-
if (mode === "file") return renderResult;
|
|
208
|
-
} else {
|
|
209
|
-
const localPath = shouldGenerateLocalQRFromActionURL(action, mode, flags)
|
|
210
|
-
? await prepareLocalQRFromActionURL(action, flags, false)
|
|
211
|
-
: "";
|
|
212
|
-
if (localPath) {
|
|
213
|
-
attachAgentLocalQR(action, localPath);
|
|
214
|
-
annotateHumanActionPresentation(action, "");
|
|
215
|
-
process.stderr.write(`${providerLabel} action QR image: ${localPath}\n`);
|
|
216
|
-
renderResult.rendered = true;
|
|
217
|
-
renderResult.outputs.push(localPath);
|
|
218
|
-
if (mode === "file") return renderResult;
|
|
219
|
-
} else {
|
|
220
|
-
process.stderr.write(`No branded QR image available. Open action URL: ${action.url}\n`);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (qrImageURL) {
|
|
226
|
-
process.stderr.write(`${providerLabel} QR image URL: ${qrImageURL}\n`);
|
|
227
|
-
if (action.mobile_wallet_url) process.stderr.write(`Mobile wallet link: ${action.mobile_wallet_url}\n`);
|
|
228
|
-
renderResult.outputs.push("preferred_qr_url");
|
|
229
|
-
return renderResult;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
process.stderr.write(`${action.fallback_text || `Open ${providerLabel} URL: ${action.url}`}\n`);
|
|
233
|
-
renderResult.outputs.push("url");
|
|
234
|
-
return renderResult;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function humanActionTitle(action = {}) {
|
|
238
|
-
if (action.title) return action.title;
|
|
239
|
-
if (action.kind === "auth_qr") return `${humanActionProviderLabel(action)} authentication`;
|
|
240
|
-
return "Scan payment QR";
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function humanActionProviderLabel(action = {}) {
|
|
244
|
-
const raw = String(action.provider || action.channel || action.display_mode || "").toLowerCase();
|
|
245
|
-
if (raw.includes("alipay")) return "Alipay";
|
|
246
|
-
if (raw.includes("wechat")) return "WeChat Pay";
|
|
247
|
-
if (raw.includes("fake") || raw.includes("local")) return "ItPay local";
|
|
248
|
-
return "Alipay or WeChat Pay";
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
function preferredHumanActionQRURL(action) {
|
|
252
|
-
return action?.qr_png_url ||
|
|
253
|
-
humanActionPresentationURL(action, "qr_png_url") ||
|
|
254
|
-
action?.qr_image_url ||
|
|
255
|
-
action?.qr?.png_url ||
|
|
256
|
-
action?.qr?.image_url ||
|
|
257
|
-
humanActionPresentationURL(action, "qr_svg_url") ||
|
|
258
|
-
"";
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
function humanActionPresentationURL(action, type) {
|
|
262
|
-
const display = action?.presentation?.display;
|
|
263
|
-
if (!Array.isArray(display)) return "";
|
|
264
|
-
const found = display.find((item) => item?.type === type && item?.url);
|
|
265
|
-
return found?.url || "";
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function annotateHumanActionPresentation(action, qrImageURL) {
|
|
269
|
-
if (!action) return action;
|
|
270
|
-
if (qrImageURL) {
|
|
271
|
-
action.preferred_qr_url = qrImageURL;
|
|
272
|
-
action.preferred_qr_mime = qrMimeType(qrImageURL, action);
|
|
273
|
-
}
|
|
274
|
-
const mobileURL = action.mobile_wallet_url || humanActionPresentationURL(action, "mobile_wallet_url");
|
|
275
|
-
if (mobileURL) action.mobile_wallet_url = mobileURL;
|
|
276
|
-
action.agent_display_hint = {
|
|
277
|
-
primary: action.local_qr_path ? "local_qr_path" : (action.qr_png_url ? "qr_png_url" : "preferred_qr_url"),
|
|
278
|
-
desktop: action.kind === "auth_qr"
|
|
279
|
-
? "Show local_qr_path when present; otherwise show the ItPay first-purchase entry URL. This QR starts login/registration/profile authorization and should continue to payment for the same checkout after approval; it is not payment proof."
|
|
280
|
-
: "Show local_qr_path when present; otherwise show qr_png_url/preferred_qr_url directly. This is an ItPay-hosted human QR image and may render the native provider payment code; do not render your own QR from payment_entry_url.",
|
|
281
|
-
mobile: "Show mobile_wallet_url as a clickable human-only fallback when present.",
|
|
282
|
-
proof: "Only payment_intent.verified proves payment. QR display or page open is not payment proof."
|
|
283
|
-
};
|
|
284
|
-
return action;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function buildHumanActionRenderPlan(action = {}, intent = {}, flags = {}) {
|
|
288
|
-
if (!action || typeof action !== "object") return null;
|
|
289
|
-
const kind = action.kind || (isPaymentIntentHandoff(intent) ? "payment_qr" : "human_action");
|
|
290
|
-
const entryURL = intent.payment_entry_url || intent.payment_url || action.url || action.web_url || "";
|
|
291
|
-
const checkoutID = intent.checkout_id || action.checkout_id || checkoutIDFromURL(entryURL);
|
|
292
|
-
const planAction = compactObject({ ...(action.kind === kind ? action : { ...action, kind }), checkout_id: checkoutID || undefined });
|
|
293
|
-
const qrPNGURL = action.qr_png_url || intent.qr_png_url || intent.qr?.png_url || "";
|
|
294
|
-
const preferredQRURL = action.preferred_qr_url || qrPNGURL || action.qr_image_url || intent.qr_image_url || intent.qr?.image_url || "";
|
|
295
|
-
const localQRPath = action.local_qr_path || intent.local_qr_path || "";
|
|
296
|
-
const mobileWalletURL = action.mobile_wallet_url || intent.mobile_wallet_url || humanActionPresentationURL(action, "mobile_wallet_url") || "";
|
|
297
|
-
const imageSource = localQRPath || qrPNGURL || preferredQRURL;
|
|
298
|
-
const requiredOutputs = [];
|
|
299
|
-
if (imageSource) {
|
|
300
|
-
requiredOutputs.push(compactObject({
|
|
301
|
-
type: "image",
|
|
302
|
-
local_path: localQRPath || undefined,
|
|
303
|
-
fallback_url: preferredQRURL || qrPNGURL || undefined,
|
|
304
|
-
must_be_user_visible: true
|
|
305
|
-
}));
|
|
306
|
-
}
|
|
307
|
-
if (entryURL) requiredOutputs.push({ type: "link", label: kind === "auth_qr" ? "打开授权页面" : "打开付款页面", url: entryURL });
|
|
308
|
-
if (mobileWalletURL) requiredOutputs.push({ type: "link", label: "手机钱包打开", url: mobileWalletURL });
|
|
309
|
-
const markdown = humanActionMarkdown(planAction, { localQRPath, qrPNGURL, preferredQRURL, entryURL, mobileWalletURL });
|
|
310
|
-
const telegram = telegramRenderPlan(planAction, { localQRPath, preferredQRURL, entryURL, mobileWalletURL });
|
|
311
|
-
const host = agentHost(flags);
|
|
312
|
-
const platforms = compactObject({
|
|
313
|
-
codex_app: { format: "markdown_image_and_links", markdown },
|
|
314
|
-
claude_code: { format: "markdown_image_and_links", markdown },
|
|
315
|
-
telegram,
|
|
316
|
-
plain_chat: {
|
|
317
|
-
format: "image_or_link_then_human_reply",
|
|
318
|
-
text: kind === "payment_qr"
|
|
319
|
-
? "请扫码或点击链接完成支付。付完后回复“我已付款”,我再查询真实状态。"
|
|
320
|
-
: "请打开上面的授权入口。完成后告诉我,我再继续查询状态。",
|
|
321
|
-
links: requiredOutputs.filter((item) => item.type === "link")
|
|
322
|
-
},
|
|
323
|
-
terminal: {
|
|
324
|
-
format: "cli_prints_qr_then_wait",
|
|
325
|
-
print_terminal_qr: true,
|
|
326
|
-
print_links: true
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
const selected = selectedRenderPlan(host, platforms);
|
|
330
|
-
return compactObject({
|
|
331
|
-
kind,
|
|
332
|
-
proof_rule: kind === "payment_qr"
|
|
333
|
-
? "Only payment_intent.verified proves payment."
|
|
334
|
-
: "This human action is not payment proof.",
|
|
335
|
-
host: host || undefined,
|
|
336
|
-
platform_selection: {
|
|
337
|
-
rule: host
|
|
338
|
-
? "selected was chosen from the detected host. If your actual channel has a more specific branch in platforms, use that branch."
|
|
339
|
-
: "CLI could not detect the chat channel. If your current channel appears in platforms, use that branch; selected.fallback is only for unsupported plain chat.",
|
|
340
|
-
telegram: "For Telegram/OpenClaw, use platforms.telegram.openclaw_message instead of selected.fallback."
|
|
341
|
-
},
|
|
342
|
-
required_outputs: requiredOutputs,
|
|
343
|
-
selected,
|
|
344
|
-
platforms,
|
|
345
|
-
forbidden: [
|
|
346
|
-
"Do not say 'scan the QR above' unless an image or scannable URL is actually attached.",
|
|
347
|
-
"Do not read a local image file as model input and treat that as sent to the human.",
|
|
348
|
-
"Do not treat page open, QR display, button click, or human text as payment proof.",
|
|
349
|
-
"Do not create a new checkout because payment is still pending."
|
|
350
|
-
]
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function humanActionMarkdown(action = {}, { localQRPath = "", qrPNGURL = "", preferredQRURL = "", entryURL = "", mobileWalletURL = "" } = {}) {
|
|
355
|
-
const imageURL = localQRPath || qrPNGURL || preferredQRURL;
|
|
356
|
-
const lines = [action.kind === "auth_qr" ? "请打开 ItPay 授权入口:" : "请扫码付款:"];
|
|
357
|
-
if (imageURL) lines.push(``);
|
|
358
|
-
if (entryURL) lines.push(`[${action.kind === "auth_qr" ? "打开授权页面" : "打开付款页面"}](${entryURL})`);
|
|
359
|
-
if (mobileWalletURL) lines.push(`[手机钱包打开](${mobileWalletURL})`);
|
|
360
|
-
lines.push(action.kind === "payment_qr" ? "付款后回复“我已付款”,我会查询真实支付状态。" : "完成授权后回复,我会继续同一个 checkout。");
|
|
361
|
-
return lines.join("\n\n");
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
function telegramRenderPlan(action = {}, { localQRPath = "", preferredQRURL = "", entryURL = "", mobileWalletURL = "" } = {}) {
|
|
365
|
-
const media = localQRPath || preferredQRURL
|
|
366
|
-
? [compactObject({ type: "photo", local_path: localQRPath || undefined, fallback_url: preferredQRURL || undefined })]
|
|
367
|
-
: [];
|
|
368
|
-
const links = [];
|
|
369
|
-
if (entryURL) links.push({ label: action.kind === "auth_qr" ? "打开授权页面" : "打开付款页面", url: entryURL });
|
|
370
|
-
if (mobileWalletURL) links.push({ label: "手机钱包打开", url: mobileWalletURL });
|
|
371
|
-
const buttons = action.kind === "payment_qr"
|
|
372
|
-
? [
|
|
373
|
-
{ text: "支付遇到问题 / 刷新", intent: "refresh_payment_qr" },
|
|
374
|
-
{ text: "我已付款,查询状态", intent: "check_payment_status" }
|
|
375
|
-
]
|
|
376
|
-
: [
|
|
377
|
-
...(entryURL ? [{ text: "打开授权页面", url: entryURL }] : []),
|
|
378
|
-
...(action.checkout_id ? [{ text: "我已完成,查询状态", intent: "check_checkout_status" }] : [])
|
|
379
|
-
];
|
|
380
|
-
return {
|
|
381
|
-
format: action.kind === "payment_qr" ? "photo_text_inline_buttons" : "text_inline_buttons",
|
|
382
|
-
media,
|
|
383
|
-
text: action.kind === "payment_qr"
|
|
384
|
-
? "请扫码或点击链接完成支付。付款后点“我已付款,查询状态”。"
|
|
385
|
-
: "请打开 ItPay 授权入口,完成后回到当前对话。",
|
|
386
|
-
links,
|
|
387
|
-
buttons,
|
|
388
|
-
openclaw_message: openclawTelegramMessage({ action, media, links, buttons })
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
function openclawTelegramMessage({ action = {}, media = [], links = [], buttons = [] } = {}) {
|
|
393
|
-
const mediaURL = media[0]?.local_path || media[0]?.fallback_url || "";
|
|
394
|
-
const text = action.kind === "payment_qr"
|
|
395
|
-
? "请扫码或点击链接完成支付。付款完成后点“我已付款,查询状态”,或直接回复“我已付款”。"
|
|
396
|
-
: "请打开 ItPay 授权入口,完成后回到当前对话。";
|
|
397
|
-
const presentationButtons = links.map((link) => openclawURLButton(link.label, link.url));
|
|
398
|
-
if (action.kind === "payment_qr") {
|
|
399
|
-
presentationButtons.push(
|
|
400
|
-
openclawCallbackButton("支付遇到问题 / 刷新", "refresh_payment_qr", action),
|
|
401
|
-
openclawCallbackButton("我已付款,查询状态", "check_payment_status", action)
|
|
402
|
-
);
|
|
403
|
-
} else {
|
|
404
|
-
for (const button of buttons) {
|
|
405
|
-
if (button.url && !presentationButtons.some((item) => item.url === button.url)) {
|
|
406
|
-
presentationButtons.push(openclawURLButton(button.text || "打开链接", button.url));
|
|
407
|
-
}
|
|
408
|
-
if (button.intent === "check_checkout_status") {
|
|
409
|
-
presentationButtons.push(openclawCallbackButton("我已完成,查询状态", "check_checkout_status", action));
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
const presentation = {
|
|
414
|
-
blocks: [
|
|
415
|
-
{ type: "text", text },
|
|
416
|
-
...(presentationButtons.length ? [{ type: "buttons", buttons: presentationButtons }] : [])
|
|
417
|
-
]
|
|
418
|
-
};
|
|
419
|
-
return compactObject({
|
|
420
|
-
adapter: "openclaw",
|
|
421
|
-
channel: "telegram",
|
|
422
|
-
use: "message send --media/--presentation; do not use legacy inline media directives",
|
|
423
|
-
message: text,
|
|
424
|
-
media: mediaURL || undefined,
|
|
425
|
-
presentation,
|
|
426
|
-
command_args: compactObject({
|
|
427
|
-
channel: "telegram",
|
|
428
|
-
target: clientTarget() || undefined,
|
|
429
|
-
message: text,
|
|
430
|
-
media: mediaURL || undefined,
|
|
431
|
-
presentation
|
|
432
|
-
}),
|
|
433
|
-
callbacks: openclawCallbackCommands(action)
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
function openclawURLButton(text, url) {
|
|
438
|
-
return compactObject({ text, url });
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
function openclawCallbackButton(text, intent, action = {}) {
|
|
442
|
-
const id = action.payment_intent_id || action.checkout_id || action.id || "current";
|
|
443
|
-
return { text, callback_data: `itp:${intent}:${id}` };
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
function openclawCallbackCommands(action = {}) {
|
|
447
|
-
const contextArgs = clientCommandArgs();
|
|
448
|
-
if (action.kind === "payment_qr") {
|
|
449
|
-
const id = action.payment_intent_id || action.id || "";
|
|
450
|
-
return compactObject({
|
|
451
|
-
refresh_payment_qr: id ? cliCommand("buyer", "payment", "refresh-qr", id, ...contextArgs, "--json") : undefined,
|
|
452
|
-
check_payment_status: id ? cliCommand("buyer", "payment", "wait", id, "--timeout", "1", ...contextArgs, "--json") : undefined
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
if (action.checkout_id) {
|
|
456
|
-
return { check_checkout_status: cliCommand("buyer", "checkout", "resume", action.checkout_id, ...contextArgs, "--json") };
|
|
457
|
-
}
|
|
458
|
-
return undefined;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
function checkoutIDFromURL(value = "") {
|
|
462
|
-
try {
|
|
463
|
-
const url = new URL(value);
|
|
464
|
-
return url.pathname.match(/\/checkouts\/([^/]+)/)?.[1] || "";
|
|
465
|
-
} catch {
|
|
466
|
-
return String(value || "").match(/\/checkouts\/([^/?#]+)/)?.[1] || "";
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
function selectedRenderPlatform(host = "") {
|
|
471
|
-
const normalized = String(host || "").toLowerCase().replaceAll("-", "_");
|
|
472
|
-
if (["codex", "codex_app", "codex_cli"].includes(normalized)) return "codex_app";
|
|
473
|
-
if (["claude", "claude_code", "claudecode"].includes(normalized)) return "claude_code";
|
|
474
|
-
if (["telegram", "tg"].includes(normalized)) return "telegram";
|
|
475
|
-
if (terminalQRHostAliases().has(normalized)) return "terminal";
|
|
476
|
-
return "";
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
function selectedRenderPlan(host = "", platforms = {}) {
|
|
480
|
-
const selectedPlatform = selectedRenderPlatform(host);
|
|
481
|
-
if (selectedPlatform && platforms[selectedPlatform]) {
|
|
482
|
-
return { platform: selectedPlatform, ...platforms[selectedPlatform] };
|
|
483
|
-
}
|
|
484
|
-
return { platform: "plain_chat", ...platforms.plain_chat };
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
function compactObject(value = {}) {
|
|
488
|
-
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined && item !== null && item !== ""));
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
function shouldPrepareLocalQRForJSON(mode, flags = {}, action = {}) {
|
|
492
|
-
if (mode === "file" || flags.qr_file || process.env.ITP_QR_FILE) return true;
|
|
493
|
-
if (action?.qr_png_url || action?.preferred_qr_url || action?.qr_image_url) return true;
|
|
494
|
-
return false;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
function shouldGenerateLocalQRFromActionURL(action = {}, mode = "", flags = {}) {
|
|
498
|
-
if (!action?.url) return false;
|
|
499
|
-
if (action.kind === "auth_qr") return true;
|
|
500
|
-
return mode === "file" || Boolean(flags.qr_file || process.env.ITP_QR_FILE);
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
async function prepareLocalQRFile(action, qrImageURL, flags = {}, optional = false) {
|
|
504
|
-
if (!qrImageURL) return "";
|
|
505
|
-
const file = flags.qr_file || process.env.ITP_QR_FILE || defaultQRFilePath(action, qrImageURL);
|
|
506
|
-
try {
|
|
507
|
-
await downloadQRImage(qrImageURL, file, flags);
|
|
508
|
-
} catch (error) {
|
|
509
|
-
if (!optional) throw error;
|
|
510
|
-
action.local_qr_error = safeErrorMessage(error);
|
|
511
|
-
return "";
|
|
512
|
-
}
|
|
513
|
-
action.local_qr_path = file;
|
|
514
|
-
action.local_qr_mime = qrMimeType(qrImageURL, action);
|
|
515
|
-
return file;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
async function prepareLocalQRFromActionURL(action, flags = {}, optional = false) {
|
|
519
|
-
if (!action?.url) return "";
|
|
520
|
-
const file = flags.qr_file || process.env.ITP_QR_FILE || defaultGeneratedQRFilePath(action);
|
|
521
|
-
try {
|
|
522
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
523
|
-
await QRCode.toFile(file, action.url, {
|
|
524
|
-
type: "png",
|
|
525
|
-
errorCorrectionLevel: "M",
|
|
526
|
-
margin: 2,
|
|
527
|
-
width: 512
|
|
528
|
-
});
|
|
529
|
-
} catch (error) {
|
|
530
|
-
if (!optional) throw error;
|
|
531
|
-
action.local_qr_error = safeErrorMessage(error);
|
|
532
|
-
return "";
|
|
533
|
-
}
|
|
534
|
-
action.local_qr_path = file;
|
|
535
|
-
action.local_qr_mime = "image/png";
|
|
536
|
-
return file;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
function defaultQRFilePath(action, qrImageURL) {
|
|
540
|
-
const id = sanitizeFilename(action?.payment_intent_id || action?.id || "qr");
|
|
541
|
-
return path.join(os.tmpdir(), `itp-${id}.${qrFileExtension(qrImageURL, action)}`);
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
function defaultGeneratedQRFilePath(action) {
|
|
545
|
-
const id = sanitizeFilename(action?.payment_intent_id || action?.id || "qr");
|
|
546
|
-
return path.join(os.tmpdir(), `itp-${id}.png`);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function qrFileExtension(qrImageURL, action = {}) {
|
|
550
|
-
const mime = qrMimeType(qrImageURL, action);
|
|
551
|
-
if (mime === "image/png") return "png";
|
|
552
|
-
if (mime === "image/svg+xml") return "svg";
|
|
553
|
-
return "img";
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
function qrMimeType(qrImageURL, action = {}) {
|
|
557
|
-
if (qrImageURL && action?.qr_png_url && qrImageURL === action.qr_png_url) return "image/png";
|
|
558
|
-
if (String(qrImageURL || "").toLowerCase().includes(".png")) return "image/png";
|
|
559
|
-
if (String(qrImageURL || "").toLowerCase().includes(".svg")) return "image/svg+xml";
|
|
560
|
-
return action?.preferred_qr_mime || "image/png";
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
function sanitizeFilename(value) {
|
|
564
|
-
return String(value || "qr").replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 96) || "qr";
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
function formatActionTime(value) {
|
|
568
|
-
if (value === null || value === undefined || value === "") return "";
|
|
569
|
-
if (typeof value === "number" || /^[0-9]+$/.test(String(value))) {
|
|
570
|
-
const numeric = Number(value);
|
|
571
|
-
const millis = numeric > 100000000000 ? numeric : numeric * 1000;
|
|
572
|
-
const date = new Date(millis);
|
|
573
|
-
if (!Number.isNaN(date.getTime())) return date.toISOString();
|
|
574
|
-
}
|
|
575
|
-
const date = new Date(String(value));
|
|
576
|
-
if (!Number.isNaN(date.getTime())) return date.toISOString();
|
|
577
|
-
return String(value);
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
function shouldUseAgentTextQR(flags = {}) {
|
|
581
|
-
const mode = String(flags.display || process.env.ITP_DISPLAY || "").toLowerCase() || "auto";
|
|
582
|
-
const host = agentHost(flags);
|
|
583
|
-
return mode === "chat" || mode === "agent" || (agentTextQRHosts().has(host) && mode === "auto");
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
function shouldReturnAfterAgentTextQR(flags = {}) {
|
|
587
|
-
if (flags.wait || flags.wait_human) return false;
|
|
588
|
-
return shouldUseAgentTextQR(flags);
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
function agentHost(flags = {}) {
|
|
592
|
-
return clientHost(flags);
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
function agentTextQRHosts() {
|
|
596
|
-
return new Set(["codex", "codex-cli", "claude", "claude-code", "gemini", "gemini-cli"]);
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
function attachAgentQRImage(action, qrImageURL, localPath = "") {
|
|
600
|
-
if (!action || !qrImageURL) return action;
|
|
601
|
-
action.preferred_qr_url = qrImageURL;
|
|
602
|
-
if (qrMimeType(qrImageURL, action) === "image/png") {
|
|
603
|
-
action.qr_png_url = action.qr_png_url || qrImageURL;
|
|
604
|
-
} else {
|
|
605
|
-
action.qr_image_url = action.qr_image_url || qrImageURL;
|
|
606
|
-
}
|
|
607
|
-
if (!Array.isArray(action.display)) {
|
|
608
|
-
action.display = [];
|
|
609
|
-
}
|
|
610
|
-
if (!action.display.some((item) => item?.type === "image")) {
|
|
611
|
-
action.display.push({
|
|
612
|
-
type: "image",
|
|
613
|
-
format: qrFileExtension(qrImageURL, action),
|
|
614
|
-
url: qrImageURL,
|
|
615
|
-
local_path: localPath || undefined,
|
|
616
|
-
instructions: "Render local_path when present; otherwise render this ItPay-hosted QR image for the human to scan. ItPay may render a native provider payment code inside the image, but the agent must not request, decode, or expose provider payloads. Do not encode payment_entry_url or mobile_wallet_url into your own QR."
|
|
617
|
-
});
|
|
618
|
-
}
|
|
619
|
-
return action;
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
function attachAgentLocalQR(action, localPath = "") {
|
|
623
|
-
if (!action || !localPath) return action;
|
|
624
|
-
if (!Array.isArray(action.display)) {
|
|
625
|
-
action.display = [];
|
|
626
|
-
}
|
|
627
|
-
if (!action.display.some((item) => item?.type === "image" && item?.local_path === localPath)) {
|
|
628
|
-
action.display.push({
|
|
629
|
-
type: "image",
|
|
630
|
-
format: "png",
|
|
631
|
-
local_path: localPath,
|
|
632
|
-
instructions: "Render this local QR image for the human to scan. It encodes the ItPay human action URL, not a provider raw QR payload."
|
|
633
|
-
});
|
|
634
|
-
}
|
|
635
|
-
return action;
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
async function downloadQRImage(qrImageURL, file, flags = {}) {
|
|
639
|
-
const controller = new AbortController();
|
|
640
|
-
const timer = setTimeout(() => controller.abort(), apiTimeoutMs(flags));
|
|
641
|
-
let response;
|
|
642
|
-
try {
|
|
643
|
-
response = await fetch(qrImageURL, { method: "GET", signal: controller.signal });
|
|
644
|
-
} catch (error) {
|
|
645
|
-
if (error?.name === "AbortError") {
|
|
646
|
-
throw new Error(`QR image download timed out: ${qrImageURL}`);
|
|
647
|
-
}
|
|
648
|
-
throw error;
|
|
649
|
-
} finally {
|
|
650
|
-
clearTimeout(timer);
|
|
651
|
-
}
|
|
652
|
-
if (!response.ok) {
|
|
653
|
-
throw new Error(`QR image download failed: ${response.status}`);
|
|
654
|
-
}
|
|
655
|
-
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
656
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
657
|
-
fs.writeFileSync(file, bytes);
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
function persistHumanAction(action, flags = {}) {
|
|
661
|
-
const runId = flags.run_id || readState().current_run_id;
|
|
662
|
-
if (!runId || !action?.id) return;
|
|
663
|
-
const run = readRun(runId);
|
|
664
|
-
if (!run?.human_action?.id || run.human_action.id !== action.id) return;
|
|
665
|
-
writeRun(mergeRun(run, { human_action: action }));
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
function shouldRenderTerminalQR(host, mode = "auto") {
|
|
669
|
-
if (["terminal", "tty"].includes(mode)) return true;
|
|
670
|
-
if (mode && !["auto", ""].includes(mode)) return false;
|
|
671
|
-
if (process.stderr.isTTY) return true;
|
|
672
|
-
return terminalQRHostAliases().has(String(host || "").toLowerCase());
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
function terminalQRHostAliases() {
|
|
676
|
-
return new Set(["terminal", "tty", "bash", "zsh", "sh", "shell", "iterm", "iterm2"]);
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
function terminalQRType(flags = {}, host = "") {
|
|
680
|
-
const requested = String(flags.qr_format || process.env.ITP_QR_FORMAT || "").toLowerCase();
|
|
681
|
-
if (requested === "unicode" || requested === "utf8") return "utf8";
|
|
682
|
-
if (requested === "ansi" || requested === "terminal") return "terminal";
|
|
683
|
-
if (terminalQRHostAliases().has(String(host || "").toLowerCase())) return "utf8";
|
|
684
|
-
return "terminal";
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
function shouldOpenBrowser(flags = {}) {
|
|
688
|
-
if (flags.no_open_browser || process.env.ITP_OPEN_BROWSER === "false" || process.env.ITP_OPEN_BROWSER === "0") return false;
|
|
689
|
-
if (flags.open_browser || process.env.ITP_OPEN_BROWSER === "true" || process.env.ITP_OPEN_BROWSER === "1") return true;
|
|
690
|
-
if (process.env.SSH_CONNECTION || process.env.CI) return false;
|
|
691
|
-
return Boolean(process.stderr.isTTY && (process.platform === "darwin" || process.platform === "win32" || process.env.DISPLAY || process.env.WAYLAND_DISPLAY || process.env.WSL_DISTRO_NAME));
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
function openBrowser(targetURL) {
|
|
695
|
-
try {
|
|
696
|
-
if (process.platform === "darwin") {
|
|
697
|
-
execFileSync("open", [targetURL], { stdio: "ignore", timeout: 2000 });
|
|
698
|
-
return true;
|
|
699
|
-
}
|
|
700
|
-
if (process.platform === "win32") {
|
|
701
|
-
execFileSync("cmd", ["/c", "start", "", targetURL], { stdio: "ignore", timeout: 2000 });
|
|
702
|
-
return true;
|
|
703
|
-
}
|
|
704
|
-
if (process.env.WSL_DISTRO_NAME && commandExists("cmd.exe")) {
|
|
705
|
-
execFileSync("cmd.exe", ["/c", "start", "", targetURL], { stdio: "ignore", timeout: 2000 });
|
|
706
|
-
return true;
|
|
707
|
-
}
|
|
708
|
-
if (commandExists("xdg-open")) {
|
|
709
|
-
execFileSync("xdg-open", [targetURL], { stdio: "ignore", timeout: 2000 });
|
|
710
|
-
return true;
|
|
711
|
-
}
|
|
712
|
-
} catch {
|
|
713
|
-
return false;
|
|
714
|
-
}
|
|
715
|
-
return false;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
export { renderItPayPaymentAction, humanActionSummaryLines, writeHumanActionSummary, waitHeartbeatMs, writeWaitHeartbeat, renderHumanAction, buildHumanActionRenderPlan, preferredHumanActionQRURL, humanActionPresentationURL, annotateHumanActionPresentation, shouldPrepareLocalQRForJSON, shouldGenerateLocalQRFromActionURL, prepareLocalQRFile, prepareLocalQRFromActionURL, defaultQRFilePath, defaultGeneratedQRFilePath, qrFileExtension, qrMimeType, sanitizeFilename, formatActionTime, shouldUseAgentTextQR, shouldReturnAfterAgentTextQR, attachAgentQRImage, attachAgentLocalQR, downloadQRImage, persistHumanAction, shouldRenderTerminalQR, terminalQRType, shouldOpenBrowser, openBrowser };
|