@xiashe/cli 0.1.31 → 0.1.33
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 +4 -4
- package/bin/xiashe.mjs +110 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ xiashe skills publish submit "<submission-id>"
|
|
|
26
26
|
# This checks only that the bridge CLI is present; it does not query a wallet.
|
|
27
27
|
xiashe payment bridge-info
|
|
28
28
|
# After the Agent shows the Skill and purpose and the user explicitly agrees:
|
|
29
|
-
xiashe payment pay --resource-url "https://<payment-resource-site>/payment/alipay-ai-collect/skills/<skillCatalogId>?orderId=<id>" --intent-summary "使用付费 Skill:<name>" --
|
|
29
|
+
xiashe payment pay --resource-url "https://<payment-resource-site>/payment/alipay-ai-collect/skills/<skillCatalogId>?orderId=<id>" --intent-summary "使用付费 Skill:<name>" --confirm
|
|
30
30
|
xiashe payment status --resource-url "https://<payment-resource-site>/payment/alipay-ai-collect/skills/<skillCatalogId>?orderId=<id>"
|
|
31
31
|
# Only for an Alipay support-assisted read-only query. This is not out_trade_no or Payment-Proof.
|
|
32
32
|
xiashe payment status --resource-url "https://<payment-resource-site>/payment/alipay-ai-collect/skills/<skillCatalogId>?orderId=<id>" --business-no "<32-digit outShakeNo with 8282 in positions 11–14>"
|
|
@@ -50,11 +50,11 @@ Production defaults:
|
|
|
50
50
|
|
|
51
51
|
`xiashe payment` is the shell-capable Agent path for Alipay AI Pay. It delegates only to the official local `alipay-bot` supplied by `@alipay/agent-payment`; it does not implement or expose `Payment-Needed` / `Payment-Proof` itself.
|
|
52
52
|
|
|
53
|
-
- `xiashe payment bridge-info` checks that the official buyer CLI can run and
|
|
54
|
-
- `xiashe payment pay` requests the resource once, saves its exact 402 request in a private local file, and passes that file plus the current Agent business-session ID to the official buyer CLI. It requires an HTTPS AI-Collect resource URL, a visible purchase-purpose summary, `--session-id`,
|
|
53
|
+
- `xiashe payment bridge-info` checks that the official buyer CLI can run and reports the non-sensitive availability of the `xiashe.payment-host.v1` host-session contract. It does not check wallet authorization, merchant activation, or whether a transaction can be created. `payment doctor` remains a manual wallet-status diagnostic only.
|
|
54
|
+
- `xiashe payment pay` requests the resource once, saves its exact 402 request in a private local file, and passes that file plus the current Agent business-session ID to the official buyer CLI. It requires an HTTPS AI-Collect resource URL, a visible purchase-purpose summary, and `--confirm`. The Agent host must inject its real current task or conversation ID only into that foreground child process as `XIASHE_HOST_BUSINESS_SESSION` under `xiashe.payment-host.v1`. The command does not accept `--session-id`, never generates, prints, persists, or sends a replacement session ID to XiaShe. If no valid host session is available, it returns `host_session_unavailable`; if an override is supplied, it returns `host_session_override_forbidden`; neither result invokes the wallet.
|
|
55
55
|
- `xiashe payment status` resumes the same local attempt with its official query credential and returns a normalized status only. If Alipay support provides the documented 32-digit `outShakeNo` (with `8282` in positions 11–14, sometimes called a business order number), use `--out-shake-no` or `--business-no` for one read-only status query; it is validated, never stored, and cannot create a payment or acknowledge fulfillment. No wallet protocol payload or proof is printed. A QR or confirmation image is exposed only as `officialMedia` when the official wallet emitted that image in the same invocation; the bridge never generates one itself.
|
|
56
56
|
- `xiashe payment preflight` requests the seller resource once and validates the public field shape of its 402 bill without opening a wallet or creating a trade. It does not prove that Alipay accepts the seller signature, service binding, or merchant activation. `xiashe payment diagnose` is read-only. It reports the last stored safe diagnosis for that local attempt, including allow-listed official error/sub-codes, message, and request/trace identifiers when present, without contacting Alipay, replaying the resource, or exposing its local query credential.
|
|
57
57
|
|
|
58
58
|
An Agent must never add `--confirm` before the user has explicitly agreed to the displayed purpose. If the wallet is unavailable, obtain permission before running the official `@alipay/agent-payment` setup, then run `payment doctor` again.
|
|
59
59
|
|
|
60
|
-
For a clean Codex Task, the XiaShe MCP response provides a version-pinned ephemeral command such as `npx -y @xiashe/cli@0.1.
|
|
60
|
+
For a clean Codex Task, the XiaShe MCP response provides a version-pinned ephemeral command such as `npx -y @xiashe/cli@0.1.33 payment bridge-info`. No global `xiashe` installation is required for that path. Do not run preflight before requesting payment confirmation: it is a read-only diagnostic only. After explicit confirmation, the host injects `XIASHE_HOST_BUSINESS_SESSION` only into the supplied payment command’s foreground child process and runs the command exactly as supplied. The Agent must inspect bridge JSON instead of suppressing output or guessing status from shell text. Failed attempts include a redacted `diagnosis` object and `userVisible` next step; do not replace them with a generic payment-failed message.
|
package/bin/xiashe.mjs
CHANGED
|
@@ -51,6 +51,20 @@ const EXIT_RUNTIME = 5;
|
|
|
51
51
|
const COMMAND_NAME = process.env.XIASHE_CLI_NAME || 'xiashe';
|
|
52
52
|
const PRODUCT_NAME = process.env.XIASHE_PRODUCT_NAME || (COMMAND_NAME === 'agentpie' ? 'AgentPie' : 'XiaShe');
|
|
53
53
|
const PRODUCT_ENV = COMMAND_NAME === 'agentpie' ? 'global' : 'cn';
|
|
54
|
+
// xiashe.payment-host.v1 is the portable contract between an Agent host and
|
|
55
|
+
// this CLI. A host injects its *current* business session only into the
|
|
56
|
+
// foreground payment subprocess. The value never enters MCP, XiaShe APIs,
|
|
57
|
+
// local state, stdout, or a model-visible command argument.
|
|
58
|
+
const PAYMENT_HOST_CONTRACT_VERSION = 'xiashe.payment-host.v1';
|
|
59
|
+
const PAYMENT_HOST_SESSION_ENV = 'XIASHE_HOST_BUSINESS_SESSION';
|
|
60
|
+
// These are compatibility adapters for known OpenAI/Codex hosts while their
|
|
61
|
+
// runtimes adopt the portable environment contract. Unknown host variables
|
|
62
|
+
// are deliberately not guessed: other Agents must inject the standard name.
|
|
63
|
+
const PAYMENT_HOST_COMPATIBILITY_PROVIDERS = Object.freeze([
|
|
64
|
+
{ environmentName: 'CODEX_THREAD_ID', provider: 'codex_current_task' },
|
|
65
|
+
{ environmentName: 'OPENAI_THREAD_ID', provider: 'openai_current_thread' }
|
|
66
|
+
]);
|
|
67
|
+
const HOST_BUSINESS_SESSION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,191}$/;
|
|
54
68
|
|
|
55
69
|
function usage() {
|
|
56
70
|
const c = COMMAND_NAME;
|
|
@@ -103,7 +117,7 @@ Usage:
|
|
|
103
117
|
|
|
104
118
|
${c} payment bridge-info|doctor
|
|
105
119
|
${c} payment preflight --resource-url <https-url>
|
|
106
|
-
${c} payment pay --resource-url <https-url> --intent-summary <text> --
|
|
120
|
+
${c} payment pay --resource-url <https-url> --intent-summary <text> --confirm [--wallet-timeout-ms <ms>]
|
|
107
121
|
${c} payment status --resource-url <https-url> [--out-shake-no <32-digit-8282>|--business-no <32-digit-8282>]
|
|
108
122
|
${c} payment diagnose --resource-url <https-url>
|
|
109
123
|
${c} payment support-report --resource-url <https-url>
|
|
@@ -251,6 +265,78 @@ function requireArg(value, label) {
|
|
|
251
265
|
return value;
|
|
252
266
|
}
|
|
253
267
|
|
|
268
|
+
function hostSessionContractStatus(resolution = null) {
|
|
269
|
+
return {
|
|
270
|
+
protocolVersion: PAYMENT_HOST_CONTRACT_VERSION,
|
|
271
|
+
requiredEnvironment: PAYMENT_HOST_SESSION_ENV,
|
|
272
|
+
resolution: resolution
|
|
273
|
+
? { available: true, provider: resolution.provider }
|
|
274
|
+
: { available: false, provider: null },
|
|
275
|
+
sessionValueExposed: false,
|
|
276
|
+
sessionValuePersisted: false,
|
|
277
|
+
sessionValueSentToXiaShe: false
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function resolvePaymentBusinessSession(flags) {
|
|
282
|
+
// A model-visible command-line session is not an acceptable trust boundary.
|
|
283
|
+
// Hosts that cannot inject the standard environment must implement the v1
|
|
284
|
+
// adapter rather than asking a user or model to provide a replacement.
|
|
285
|
+
if (flags['session-id'] !== undefined || flags.sessionId !== undefined) {
|
|
286
|
+
return { error: 'host_session_override_forbidden' };
|
|
287
|
+
}
|
|
288
|
+
const standardCandidate = String(process.env[PAYMENT_HOST_SESSION_ENV] || '').trim();
|
|
289
|
+
if (HOST_BUSINESS_SESSION_PATTERN.test(standardCandidate)) {
|
|
290
|
+
return {
|
|
291
|
+
sessionId: standardCandidate,
|
|
292
|
+
provider: 'standard_host_environment'
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
for (const compatibilityProvider of PAYMENT_HOST_COMPATIBILITY_PROVIDERS) {
|
|
296
|
+
const candidate = String(process.env[compatibilityProvider.environmentName] || '').trim();
|
|
297
|
+
if (HOST_BUSINESS_SESSION_PATTERN.test(candidate)) {
|
|
298
|
+
return {
|
|
299
|
+
sessionId: candidate,
|
|
300
|
+
provider: compatibilityProvider.provider
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function hostBusinessSessionUnavailableResult() {
|
|
308
|
+
return {
|
|
309
|
+
ok: false,
|
|
310
|
+
status: 'host_session_unavailable',
|
|
311
|
+
failureCode: 'HOST_BUSINESS_SESSION_UNAVAILABLE',
|
|
312
|
+
walletInvoked: false,
|
|
313
|
+
diagnosis: {
|
|
314
|
+
code: 'host_business_session_unavailable',
|
|
315
|
+
stage: 'host_session_resolution'
|
|
316
|
+
},
|
|
317
|
+
hostSessionContract: hostSessionContractStatus(),
|
|
318
|
+
userVisible: '当前 Agent 未提供可用的任务会话标识,尚未发起支付宝付款。',
|
|
319
|
+
nextStep: `请让当前 Agent 宿主仅为本次付款子进程注入 ${PAYMENT_HOST_SESSION_ENV}(${PAYMENT_HOST_CONTRACT_VERSION});不要手工创建、粘贴或让模型传入替代值。`
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function hostBusinessSessionOverrideForbiddenResult() {
|
|
324
|
+
const hostResolution = resolvePaymentBusinessSession({});
|
|
325
|
+
return {
|
|
326
|
+
ok: false,
|
|
327
|
+
status: 'host_session_override_forbidden',
|
|
328
|
+
failureCode: 'HOST_SESSION_OVERRIDE_FORBIDDEN',
|
|
329
|
+
walletInvoked: false,
|
|
330
|
+
diagnosis: {
|
|
331
|
+
code: 'host_session_override_forbidden',
|
|
332
|
+
stage: 'host_session_resolution'
|
|
333
|
+
},
|
|
334
|
+
hostSessionContract: hostSessionContractStatus(hostResolution?.error ? null : hostResolution),
|
|
335
|
+
userVisible: '付款命令不接受模型或用户提供的业务会话标识,尚未发起支付宝付款。',
|
|
336
|
+
nextStep: `请让当前 Agent 宿主按 ${PAYMENT_HOST_CONTRACT_VERSION} 私有注入 ${PAYMENT_HOST_SESSION_ENV},然后原样重试 payCommand。`
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
254
340
|
function publicError(error) {
|
|
255
341
|
const message = error instanceof Error ? error.message : String(error || '');
|
|
256
342
|
const code = error?.code || message.match(/\b[A-Z][A-Z0-9_]{4,}\b/)?.[0] || '';
|
|
@@ -1184,7 +1270,11 @@ async function cmdPayment(sub, args, global) {
|
|
|
1184
1270
|
const flags = parseFlags(args);
|
|
1185
1271
|
if (sub === 'bridge-info' || sub === 'bridge' || sub === 'prepare') {
|
|
1186
1272
|
const result = await getAlipayWalletBridgeInfo({ timeoutMs: global.timeoutMs });
|
|
1187
|
-
|
|
1273
|
+
const businessSession = resolvePaymentBusinessSession({});
|
|
1274
|
+
print({
|
|
1275
|
+
...result,
|
|
1276
|
+
hostSessionContract: hostSessionContractStatus(businessSession?.error ? null : businessSession)
|
|
1277
|
+
}, global);
|
|
1188
1278
|
return;
|
|
1189
1279
|
}
|
|
1190
1280
|
const walletTimeoutValue = flags['wallet-timeout-ms'] ?? flags.walletTimeoutMs ?? (global.timeoutMsExplicit ? global.timeoutMs : undefined);
|
|
@@ -1208,14 +1298,29 @@ async function cmdPayment(sub, args, global) {
|
|
|
1208
1298
|
if (sub === 'pay') {
|
|
1209
1299
|
const resourceUrl = requireArg(flags['resource-url'] || flags.resourceUrl || flags.r, '--resource-url');
|
|
1210
1300
|
const intentSummary = requireArg(flags['intent-summary'] || flags.intentSummary || flags.i, '--intent-summary');
|
|
1211
|
-
const
|
|
1301
|
+
const businessSession = resolvePaymentBusinessSession(flags);
|
|
1302
|
+
if (businessSession?.error === 'host_session_override_forbidden') {
|
|
1303
|
+
print(hostBusinessSessionOverrideForbiddenResult(), global);
|
|
1304
|
+
process.exitCode = EXIT_RUNTIME;
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
if (!businessSession) {
|
|
1308
|
+
print(hostBusinessSessionUnavailableResult(), global);
|
|
1309
|
+
process.exitCode = EXIT_RUNTIME;
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1212
1312
|
const result = await payAlipay402Resource({
|
|
1213
1313
|
resourceUrl,
|
|
1214
1314
|
intentSummary,
|
|
1215
|
-
sessionId,
|
|
1315
|
+
sessionId: businessSession.sessionId,
|
|
1216
1316
|
confirm: flags.confirm === true
|
|
1217
1317
|
}, walletTimeoutMs ? { timeoutMs: walletTimeoutMs } : {});
|
|
1218
|
-
print(
|
|
1318
|
+
print({
|
|
1319
|
+
...result,
|
|
1320
|
+
// This confirms resolution occurred without exposing the identifier or
|
|
1321
|
+
// the source environment variable that contained it.
|
|
1322
|
+
hostSessionContract: hostSessionContractStatus(businessSession)
|
|
1323
|
+
}, global);
|
|
1219
1324
|
return;
|
|
1220
1325
|
}
|
|
1221
1326
|
if (sub === 'status') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiashe/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.33",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"xiashe": "bin/xiashe.mjs"
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"README.md"
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@xiashe/sdk": "0.1.
|
|
13
|
+
"@xiashe/sdk": "0.1.33"
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
16
|
"node": ">=20"
|