@itpay/cli 0.1.0 → 0.1.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 +41 -246
- package/bin/itp +56 -57
- package/docs/agent/buyer/cart-checkout.json +1 -1
- package/docs/agent/buyer/human-claim-ui.json +2 -1
- package/docs/agent/buyer/vault-agent-read.json +5 -3
- package/package.json +1 -4
- package/skills/itpay-buyer/SKILL.md +8 -2
- package/e2e-local.sh +0 -134
- package/skills/voltagent/SKILL.md +0 -603
- package/smoke.sh +0 -426
package/bin/itp
CHANGED
|
@@ -9,7 +9,7 @@ import { fileURLToPath } from "node:url";
|
|
|
9
9
|
import QRCode from "qrcode";
|
|
10
10
|
|
|
11
11
|
const VERSION = "0.1.2";
|
|
12
|
-
const DEFAULT_API_BASE = process.env.ITPAY_API_BASE || process.env.ITPAY_CORE_BASE_URL ||
|
|
12
|
+
const DEFAULT_API_BASE = process.env.ITPAY_API_BASE || process.env.ITPAY_CORE_BASE_URL || "http://localhost:3000";
|
|
13
13
|
const CONFIG_DIR = path.join(os.homedir(), ".itp");
|
|
14
14
|
const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
|
|
15
15
|
const STATE_PATH = path.join(CONFIG_DIR, "state.json");
|
|
@@ -193,15 +193,6 @@ async function main() {
|
|
|
193
193
|
output({
|
|
194
194
|
version: VERSION,
|
|
195
195
|
commands: [
|
|
196
|
-
"auth register",
|
|
197
|
-
"auth register --host gemini --display chat --no-wait --json",
|
|
198
|
-
"auth login",
|
|
199
|
-
"auth device start",
|
|
200
|
-
"auth device poll <auth_id>",
|
|
201
|
-
"setup --credits 100 --method alipay",
|
|
202
|
-
"setup --plan credit-300 --method alipay",
|
|
203
|
-
"setup --plan credit-300 --method alipay --host gemini --display chat --json",
|
|
204
|
-
"setup --credits 100 --target codex --method alipay --install-runtime",
|
|
205
196
|
"buy var_pubg_couple_skin_cny20 --sandbox --email buyer@example.com --phone +8613800000000 --json",
|
|
206
197
|
"buy var_pubg_couple_skin_cny20 --sandbox --email buyer@example.com --phone +8613800000000 --no-wait --json",
|
|
207
198
|
"buyer catalog search --query 企业工商 --category business_data_api --provider itpay_enterprise_data --json",
|
|
@@ -236,30 +227,7 @@ async function main() {
|
|
|
236
227
|
"resume --json",
|
|
237
228
|
"resume --run-id <run_id> --host gemini --display none --json",
|
|
238
229
|
"runs list|current|show <run_id>|forget <run_id>",
|
|
239
|
-
"auth status",
|
|
240
|
-
"account show",
|
|
241
230
|
"account login-link",
|
|
242
|
-
"account set-password --password-stdin",
|
|
243
|
-
"plans list",
|
|
244
|
-
"plans show <plan>",
|
|
245
|
-
"checkout create --credits 100 --method alipay",
|
|
246
|
-
"checkout create --plan credit-300 --method alipay --idempotency-key <uuid>",
|
|
247
|
-
"checkout qr <checkout_id>",
|
|
248
|
-
"checkout open",
|
|
249
|
-
"checkout recover <checkout_id>",
|
|
250
|
-
"checkout list --limit 20",
|
|
251
|
-
"payment wait <checkout_id> --timeout 120",
|
|
252
|
-
"balance",
|
|
253
|
-
"usage --today --model <model>",
|
|
254
|
-
"grants list",
|
|
255
|
-
"grants show <grant_id>",
|
|
256
|
-
"grants install <grant_id> --target codex",
|
|
257
|
-
"grants revoke <grant_id>",
|
|
258
|
-
"keys list",
|
|
259
|
-
"keys rotate --grant <grant_id>",
|
|
260
|
-
"keys revoke --grant <grant_id>",
|
|
261
|
-
"token issue --grant <grant_id> --stdout",
|
|
262
|
-
"sync",
|
|
263
231
|
"skill show",
|
|
264
232
|
"skill show --role buyer --json",
|
|
265
233
|
"skill path --role buyer",
|
|
@@ -870,6 +838,7 @@ async function buyer(command, rest, flags) {
|
|
|
870
838
|
if (subcommand === "grants") {
|
|
871
839
|
const action = rest[1] && !String(rest[1]).startsWith("--") ? rest[1] : "list";
|
|
872
840
|
if (action === "list") {
|
|
841
|
+
await ensureBuyerSessionForVaultAccess(flags);
|
|
873
842
|
const grants = await listBuyerAgentReadGrants(flags);
|
|
874
843
|
output(buyerRunOutput({
|
|
875
844
|
status: "agent_read_grants",
|
|
@@ -881,6 +850,7 @@ async function buyer(command, rest, flags) {
|
|
|
881
850
|
if (action === "read" || action === "show") {
|
|
882
851
|
const grantID = flags.grant || flags.grant_id || flags.agent_read_grant_id || positional(rest, 2);
|
|
883
852
|
if (!grantID) throw new Error("agent_read_grant_id is required");
|
|
853
|
+
await ensureBuyerSessionForVaultAccess(flags);
|
|
884
854
|
const view = await readBuyerAgentReadGrant(grantID, flags);
|
|
885
855
|
output(buyerRunOutput({
|
|
886
856
|
status: "agent_read_grant_view",
|
|
@@ -891,6 +861,7 @@ async function buyer(command, rest, flags) {
|
|
|
891
861
|
}
|
|
892
862
|
}
|
|
893
863
|
if (subcommand === "read") {
|
|
864
|
+
await ensureBuyerSessionForVaultAccess(flags);
|
|
894
865
|
const view = await readBuyerVaultArtifactGrant(flags);
|
|
895
866
|
output(buyerRunOutput({
|
|
896
867
|
status: "agent_read_grant_view",
|
|
@@ -1357,6 +1328,35 @@ async function maybeClaimBuyerSessionForCheckout(checkout, flags = {}) {
|
|
|
1357
1328
|
return await maybeClaimBuyerSessionFromAuthAction(action, flags);
|
|
1358
1329
|
}
|
|
1359
1330
|
|
|
1331
|
+
async function ensureBuyerSessionForVaultAccess(flags = {}) {
|
|
1332
|
+
if (readSessionToken()) return null;
|
|
1333
|
+
const checkoutID =
|
|
1334
|
+
flags.checkout ||
|
|
1335
|
+
flags.checkout_id ||
|
|
1336
|
+
readState().last_core_checkout_id ||
|
|
1337
|
+
readState().last_core_auth_checkout_id;
|
|
1338
|
+
if (checkoutID) {
|
|
1339
|
+
try {
|
|
1340
|
+
const checkout = await getBuyerCheckout(checkoutID, flags);
|
|
1341
|
+
rememberCoreAuthAction(checkout.checkout_id || checkoutID, checkout.human_action);
|
|
1342
|
+
const claimed = await maybeClaimBuyerSessionForCheckout(checkout, { ...flags, quiet: true });
|
|
1343
|
+
if (claimed || readSessionToken()) return claimed;
|
|
1344
|
+
} catch {
|
|
1345
|
+
// Continue with the locally remembered auth action below.
|
|
1346
|
+
}
|
|
1347
|
+
const action = readCoreAuthAction(checkoutID);
|
|
1348
|
+
const claimed = await maybeClaimBuyerSessionFromAuthAction(action, { ...flags, quiet: true });
|
|
1349
|
+
if (claimed || readSessionToken()) return claimed;
|
|
1350
|
+
}
|
|
1351
|
+
const state = readState();
|
|
1352
|
+
const entries = Object.entries(state.core_auth_actions || {});
|
|
1353
|
+
for (const [, action] of entries.reverse()) {
|
|
1354
|
+
const claimed = await maybeClaimBuyerSessionFromAuthAction(action, { ...flags, quiet: true });
|
|
1355
|
+
if (claimed || readSessionToken()) return claimed;
|
|
1356
|
+
}
|
|
1357
|
+
return null;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
1360
|
function parseBuyerAuthActionURL(action) {
|
|
1361
1361
|
const rawURL = action?.url || action?.auth_url || "";
|
|
1362
1362
|
if (!rawURL) return {};
|
|
@@ -2456,20 +2456,20 @@ function installClaudeCode(grantId, credentials, dryRun) {
|
|
|
2456
2456
|
|
|
2457
2457
|
function installCodex(grantId, credentials, dryRun) {
|
|
2458
2458
|
const configPath = path.join(os.homedir(), ".codex", "config.toml");
|
|
2459
|
-
const envPath = path.join(CONFIG_DIR, "
|
|
2459
|
+
const envPath = path.join(CONFIG_DIR, "itpay.env");
|
|
2460
2460
|
const existing = readText(configPath, "");
|
|
2461
2461
|
const block = [
|
|
2462
|
-
'model_provider = "
|
|
2462
|
+
'model_provider = "itpay"',
|
|
2463
2463
|
'model = "openai-code-default"',
|
|
2464
2464
|
"",
|
|
2465
|
-
"[model_providers.
|
|
2466
|
-
'name = "
|
|
2465
|
+
"[model_providers.itpay]",
|
|
2466
|
+
'name = "ItPay"',
|
|
2467
2467
|
`base_url = "${escapeTomlString(credentials.openai_base_url)}"`,
|
|
2468
|
-
'env_key = "
|
|
2468
|
+
'env_key = "ITPAY_API_KEY"'
|
|
2469
2469
|
].join("\n");
|
|
2470
|
-
const nextConfig = replaceManagedBlock(existing, "
|
|
2470
|
+
const nextConfig = replaceManagedBlock(existing, "itpay", block);
|
|
2471
2471
|
const configWrite = writeTextWithBackup(configPath, nextConfig, 0o600, dryRun);
|
|
2472
|
-
const envWrite = writeTextWithBackup(envPath, `export
|
|
2472
|
+
const envWrite = writeTextWithBackup(envPath, `export ITPAY_API_KEY=${quoteShell(credentials.key)}\n`, 0o600, dryRun);
|
|
2473
2473
|
return {
|
|
2474
2474
|
target: "codex",
|
|
2475
2475
|
grant_id: grantId,
|
|
@@ -2479,7 +2479,7 @@ function installCodex(grantId, credentials, dryRun) {
|
|
|
2479
2479
|
{ path: envPath, action: envWrite.action, backup_path: envWrite.backup_path || null }
|
|
2480
2480
|
],
|
|
2481
2481
|
warnings: [
|
|
2482
|
-
"Codex reads
|
|
2482
|
+
"Codex reads ITPAY_API_KEY from its process environment; source ~/.itp/itpay.env before starting Codex if your launcher does not load it."
|
|
2483
2483
|
]
|
|
2484
2484
|
};
|
|
2485
2485
|
}
|
|
@@ -2489,7 +2489,7 @@ function installOpenClaw(grantId, credentials, dryRun) {
|
|
|
2489
2489
|
const current = readJSON(configPath, {});
|
|
2490
2490
|
current.models = current.models || {};
|
|
2491
2491
|
current.models.providers = current.models.providers || {};
|
|
2492
|
-
current.models.providers.
|
|
2492
|
+
current.models.providers.itpay = {
|
|
2493
2493
|
baseUrl: credentials.openai_base_url,
|
|
2494
2494
|
api: "openai-compatible",
|
|
2495
2495
|
apiKey: credentials.key,
|
|
@@ -2689,7 +2689,7 @@ async function skill(command, flags) {
|
|
|
2689
2689
|
if (!command || command === "show" || command === "read") {
|
|
2690
2690
|
const content = fs.readFileSync(skillPath, "utf8");
|
|
2691
2691
|
if (flags.json) {
|
|
2692
|
-
output({ skill:
|
|
2692
|
+
output({ skill: "itpay-buyer", role, path: skillPath, content });
|
|
2693
2693
|
} else {
|
|
2694
2694
|
process.stdout.write(content.endsWith("\n") ? content : `${content}\n`);
|
|
2695
2695
|
}
|
|
@@ -2697,7 +2697,7 @@ async function skill(command, flags) {
|
|
|
2697
2697
|
}
|
|
2698
2698
|
if (command === "path") {
|
|
2699
2699
|
if (flags.json) {
|
|
2700
|
-
output({ skill:
|
|
2700
|
+
output({ skill: "itpay-buyer", role, path: skillPath });
|
|
2701
2701
|
} else {
|
|
2702
2702
|
process.stdout.write(`${skillPath}\n`);
|
|
2703
2703
|
}
|
|
@@ -2709,7 +2709,6 @@ async function skill(command, flags) {
|
|
|
2709
2709
|
function normalizeSkillRole(role) {
|
|
2710
2710
|
const normalized = String(role || "buyer").trim().toLowerCase();
|
|
2711
2711
|
if (normalized === "buyer" || normalized === "itpay-buyer") return "buyer";
|
|
2712
|
-
if (normalized === "voltagent" || normalized === "legacy") return "voltagent";
|
|
2713
2712
|
if (normalized === "merchant" || normalized === "itpay-merchant") {
|
|
2714
2713
|
throw new Error("merchant skill is not packaged yet; use --role buyer for current external-agent tests");
|
|
2715
2714
|
}
|
|
@@ -2717,8 +2716,8 @@ function normalizeSkillRole(role) {
|
|
|
2717
2716
|
}
|
|
2718
2717
|
|
|
2719
2718
|
function resolveSkillPath(role = "buyer") {
|
|
2720
|
-
const skillDirName =
|
|
2721
|
-
const envPath =
|
|
2719
|
+
const skillDirName = "itpay-buyer";
|
|
2720
|
+
const envPath = process.env.ITPAY_BUYER_SKILL_PATH;
|
|
2722
2721
|
const candidates = [
|
|
2723
2722
|
envPath,
|
|
2724
2723
|
process.env.ITPAY_CLI_SKILL_PATH,
|
|
@@ -2771,7 +2770,7 @@ async function doctorModelCheck(target, credentials) {
|
|
|
2771
2770
|
function runtimeConfigStatus(target) {
|
|
2772
2771
|
const paths = {
|
|
2773
2772
|
"claude-code": [path.join(os.homedir(), ".claude", "settings.json")],
|
|
2774
|
-
codex: [path.join(os.homedir(), ".codex", "config.toml"), path.join(CONFIG_DIR, "
|
|
2773
|
+
codex: [path.join(os.homedir(), ".codex", "config.toml"), path.join(CONFIG_DIR, "itpay.env")],
|
|
2775
2774
|
openclaw: [path.join(os.homedir(), ".openclaw", "config.json")]
|
|
2776
2775
|
};
|
|
2777
2776
|
return (paths[target] || []).map((file) => ({
|
|
@@ -3989,7 +3988,7 @@ function writeSessionCredentials(response) {
|
|
|
3989
3988
|
|
|
3990
3989
|
function storeSessionCredential(response) {
|
|
3991
3990
|
const token = response.session_token;
|
|
3992
|
-
const ref = `
|
|
3991
|
+
const ref = `itpay:session:${response.account_id}:${response.device_id}`;
|
|
3993
3992
|
const nativeStore = writeNativeSecret(ref, token);
|
|
3994
3993
|
if (nativeStore.ok) {
|
|
3995
3994
|
return {
|
|
@@ -4071,7 +4070,7 @@ function deleteGrantCredential(grantId) {
|
|
|
4071
4070
|
}
|
|
4072
4071
|
|
|
4073
4072
|
function grantSecretRef(grantId) {
|
|
4074
|
-
return `
|
|
4073
|
+
return `itpay:${grantId}`;
|
|
4075
4074
|
}
|
|
4076
4075
|
|
|
4077
4076
|
function detectNativeCredentialStore() {
|
|
@@ -4093,7 +4092,7 @@ function writeNativeSecret(ref, secret) {
|
|
|
4093
4092
|
"-a",
|
|
4094
4093
|
ref,
|
|
4095
4094
|
"-s",
|
|
4096
|
-
"
|
|
4095
|
+
"ItPay",
|
|
4097
4096
|
"-w",
|
|
4098
4097
|
secret,
|
|
4099
4098
|
"-U"
|
|
@@ -4107,9 +4106,9 @@ function writeNativeSecret(ref, secret) {
|
|
|
4107
4106
|
try {
|
|
4108
4107
|
execFileSync("secret-tool", [
|
|
4109
4108
|
"store",
|
|
4110
|
-
"--label=
|
|
4109
|
+
"--label=ItPay",
|
|
4111
4110
|
"service",
|
|
4112
|
-
"
|
|
4111
|
+
"ItPay",
|
|
4113
4112
|
"account",
|
|
4114
4113
|
ref
|
|
4115
4114
|
], { input: secret, stdio: ["pipe", "ignore", "ignore"] });
|
|
@@ -4141,7 +4140,7 @@ function readNativeSecret(store, ref) {
|
|
|
4141
4140
|
"-a",
|
|
4142
4141
|
ref,
|
|
4143
4142
|
"-s",
|
|
4144
|
-
"
|
|
4143
|
+
"ItPay",
|
|
4145
4144
|
"-w"
|
|
4146
4145
|
], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
4147
4146
|
}
|
|
@@ -4149,7 +4148,7 @@ function readNativeSecret(store, ref) {
|
|
|
4149
4148
|
return execFileSync("secret-tool", [
|
|
4150
4149
|
"lookup",
|
|
4151
4150
|
"service",
|
|
4152
|
-
"
|
|
4151
|
+
"ItPay",
|
|
4153
4152
|
"account",
|
|
4154
4153
|
ref
|
|
4155
4154
|
], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
@@ -4168,14 +4167,14 @@ function deleteNativeSecret(store, ref) {
|
|
|
4168
4167
|
"-a",
|
|
4169
4168
|
ref,
|
|
4170
4169
|
"-s",
|
|
4171
|
-
"
|
|
4170
|
+
"ItPay"
|
|
4172
4171
|
], { stdio: "ignore" });
|
|
4173
4172
|
}
|
|
4174
4173
|
if (store === "secret-tool") {
|
|
4175
4174
|
execFileSync("secret-tool", [
|
|
4176
4175
|
"clear",
|
|
4177
4176
|
"service",
|
|
4178
|
-
"
|
|
4177
|
+
"ItPay",
|
|
4179
4178
|
"account",
|
|
4180
4179
|
ref
|
|
4181
4180
|
], { stdio: "ignore" });
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"When auth completes, CLI should claim and store the buyer session so a repeat purchase by the same agent/device can skip auth."
|
|
95
95
|
],
|
|
96
96
|
"forbidden": [
|
|
97
|
-
"
|
|
97
|
+
"Use the UCP cart and buyer checkout commands for external-agent tests.",
|
|
98
98
|
"Do not pass a different variant in checkout after cart is created.",
|
|
99
99
|
"Do not create a second cart/checkout after a retryable timeout unless the user asks to abandon the previous one.",
|
|
100
100
|
"Do not buy enterprise precise lookup with only a vague brand/short name unless you have resolved the exact registered company name.",
|
|
@@ -30,12 +30,13 @@
|
|
|
30
30
|
"The claim link is short-lived and single-use.",
|
|
31
31
|
"The agent may explain that ItPay intentionally separates agent operations from raw secret access.",
|
|
32
32
|
"If the human wants the agent to analyze or use the delivered result, ask them to click 'Give to Agent / 一键给 Agent' in the ItPay page and confirm with Passkey.",
|
|
33
|
-
"After the human grants access, the agent should discover the grant with `itp buyer vault grants list
|
|
33
|
+
"After the human grants access, the agent should discover the grant with `itp buyer vault grants list --checkout <checkout_id> --json`; do not ask the human to paste the grant id. The CLI restores the buyer agent session from the checkout auth handoff when possible.",
|
|
34
34
|
"If the human later wants the agent to install or use something, wait for an explicit human-granted install/capability flow."
|
|
35
35
|
],
|
|
36
36
|
"forbidden": [
|
|
37
37
|
"Do not ask the human to paste the claim link or key into chat.",
|
|
38
38
|
"Do not fetch the claim page with CLI, curl, browser automation, or an agent tool.",
|
|
39
|
+
"Do not ask the human for auth session IDs, display tokens, buyer session tokens, or portal tokens.",
|
|
39
40
|
"Do not store raw content in local run files."
|
|
40
41
|
],
|
|
41
42
|
"next_docs": [
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"A checkout/order has delivered a vault artifact and the human granted agent-readable access."
|
|
11
11
|
],
|
|
12
12
|
"required_state": {
|
|
13
|
-
"needs": ["authenticated buyer account session", "current agent device binding", "order_id or checkout_id or vault_artifact_id"],
|
|
13
|
+
"needs": ["checkout_id or authenticated buyer account session", "current agent device binding", "order_id or checkout_id or vault_artifact_id"],
|
|
14
14
|
"must_not_need": ["human portal token", "claim token", "passkey credential", "raw protected payload", "grant id copied by the human"]
|
|
15
15
|
},
|
|
16
16
|
"commands": [
|
|
@@ -37,14 +37,16 @@
|
|
|
37
37
|
],
|
|
38
38
|
"agent_rules": [
|
|
39
39
|
"Only read through `buyer vault` commands after the human has explicitly approved the agent grant in the ItPay portal.",
|
|
40
|
+
"Prefer `itp buyer vault grants list --checkout <checkout_id> --json`; the CLI can automatically restore the buyer agent session from the checkout auth handoff when possible.",
|
|
40
41
|
"Do not ask the human to copy or paste `agent_read_grant_id`; discover it with `buyer vault grants list`.",
|
|
41
42
|
"If no grant is returned, tell the human to open their ItPay account portal, reveal with Passkey, choose fields, and confirm one-key agent authorization.",
|
|
42
43
|
"Use only fields returned in the grant view. Do not infer that unreturned fields are accessible.",
|
|
43
|
-
"If the command returns 401 or buyer_session_invalid
|
|
44
|
+
"If the command still returns 401 or buyer_session_invalid after using `--checkout`, run `itp buyer checkout status <checkout_id> --json`, then retry `itp buyer vault grants list --checkout <checkout_id> --json`. Ask the human to reauthorize only if the checkout/auth handoff has expired."
|
|
44
45
|
],
|
|
45
46
|
"forbidden": [
|
|
47
|
+
"Do not open, click through, scrape, or automate the human web UI yourself.",
|
|
46
48
|
"Do not open, scrape, or screenshot the human portal to obtain protected content.",
|
|
47
|
-
"Do not request passkey credentials, portal tokens, claim links, claim tokens, storage refs, provider AppCode, provider keys, or raw payloads.",
|
|
49
|
+
"Do not request passkey credentials, portal tokens, claim links, claim tokens, auth session IDs, display tokens, session tokens, storage refs, provider AppCode, provider keys, or raw payloads.",
|
|
48
50
|
"Do not use another agent's grant id. Grants are scoped to the exact buyer account session and agent device.",
|
|
49
51
|
"Do not cache selected fields beyond the task context unless the user explicitly asks you to create a local artifact."
|
|
50
52
|
],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itpay/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "ItPay CLI, buyer skill, and agent-readable docs for agent-native commerce.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,8 +14,6 @@
|
|
|
14
14
|
"docs/",
|
|
15
15
|
"install.sh",
|
|
16
16
|
"install.ps1",
|
|
17
|
-
"smoke.sh",
|
|
18
|
-
"e2e-local.sh",
|
|
19
17
|
"README.md",
|
|
20
18
|
"LICENSE"
|
|
21
19
|
],
|
|
@@ -28,7 +26,6 @@
|
|
|
28
26
|
},
|
|
29
27
|
"keywords": [
|
|
30
28
|
"itpay",
|
|
31
|
-
"voltagent",
|
|
32
29
|
"agent",
|
|
33
30
|
"cli",
|
|
34
31
|
"claude-code",
|
|
@@ -126,9 +126,11 @@ registered name or run fuzzy search first.
|
|
|
126
126
|
`delivery_claimable`, `check_email`, and `claim_link_sent`, but must not
|
|
127
127
|
fetch or reveal protected content.
|
|
128
128
|
11. If the human uses Passkey to authorize agent-readable vault access, do not
|
|
129
|
-
ask them to paste content, portal text, claim links,
|
|
129
|
+
ask them to paste content, portal text, claim links, session tokens, auth
|
|
130
|
+
session IDs, display tokens, or grant IDs. Run
|
|
130
131
|
`itp buyer vault grants list ...` and then `itp buyer vault read ...`.
|
|
131
|
-
|
|
132
|
+
The CLI automatically restores the buyer agent session from the checkout
|
|
133
|
+
auth handoff when possible. Use only the fields returned by that command.
|
|
132
134
|
12. If the user asks you to analyze, compare, summarize, install, or otherwise
|
|
133
135
|
use a delivered result, you may ask them to open the ItPay claim/account
|
|
134
136
|
page, click "Give to Agent / 一键给 Agent", choose fields, and confirm with
|
|
@@ -140,6 +142,10 @@ registered name or run fuzzy search first.
|
|
|
140
142
|
broad keyword. For enterprise precise lookup, `company_name_or_credit_no`
|
|
141
143
|
must be exact; otherwise warn the user that the query may waste the paid
|
|
142
144
|
lookup.
|
|
145
|
+
15. Do not operate ItPay by opening the human web UI yourself. Use the CLI for
|
|
146
|
+
catalog, cart, checkout, payment wait, delivery status, grant discovery,
|
|
147
|
+
and vault reads. Browser/UI pages are for the human to scan, pay, claim,
|
|
148
|
+
reveal, and approve.
|
|
143
149
|
|
|
144
150
|
## Docs Directory
|
|
145
151
|
|
package/e2e-local.sh
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env sh
|
|
2
|
-
set -eu
|
|
3
|
-
|
|
4
|
-
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
5
|
-
API_BASE=${VOLTAGENT_API_BASE:-http://localhost:3000}
|
|
6
|
-
NODE=${NODE:-$(command -v node)}
|
|
7
|
-
|
|
8
|
-
TMP_HOME=$(mktemp -d "${TMPDIR:-/tmp}/voltagent-itp-e2e-home.XXXXXX")
|
|
9
|
-
TMP_SETUP_HOME=$(mktemp -d "${TMPDIR:-/tmp}/voltagent-itp-setup-home.XXXXXX")
|
|
10
|
-
TMP_SETUP_NOWAIT_HOME=$(mktemp -d "${TMPDIR:-/tmp}/voltagent-itp-setup-nowait-home.XXXXXX")
|
|
11
|
-
|
|
12
|
-
cleanup() {
|
|
13
|
-
rm -rf "$TMP_HOME" "$TMP_SETUP_HOME" "$TMP_SETUP_NOWAIT_HOME"
|
|
14
|
-
}
|
|
15
|
-
trap cleanup EXIT INT TERM
|
|
16
|
-
|
|
17
|
-
itp_home() {
|
|
18
|
-
itp_home_dir="$1"
|
|
19
|
-
shift
|
|
20
|
-
attempt=0
|
|
21
|
-
while :; do
|
|
22
|
-
err_file="$itp_home_dir/itp-error.log"
|
|
23
|
-
if out=$(HOME="$itp_home_dir" PATH=/nonexistent VOLTAGENT_API_BASE="$API_BASE" "$NODE" "$ROOT/bin/itp" "$@" 2>"$err_file"); then
|
|
24
|
-
printf '%s' "$out"
|
|
25
|
-
return 0
|
|
26
|
-
fi
|
|
27
|
-
status=$?
|
|
28
|
-
error_text=$(cat "$err_file" 2>/dev/null || true)
|
|
29
|
-
attempt=$((attempt + 1))
|
|
30
|
-
if [ "$attempt" -lt 6 ] && printf '%s' "$error_text" | grep -q 'request failed: 429'; then
|
|
31
|
-
sleep "$attempt"
|
|
32
|
-
continue
|
|
33
|
-
fi
|
|
34
|
-
printf '%s\n' "$error_text" >&2
|
|
35
|
-
return "$status"
|
|
36
|
-
done
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
itp() {
|
|
40
|
-
itp_home "$TMP_HOME" "$@"
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
json_get() {
|
|
44
|
-
"$NODE" -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const v=JSON.parse(d); const path=process.argv[1].split('.'); let cur=v; for (const p of path) cur=cur?.[p]; if (cur === undefined || cur === null) process.exit(2); process.stdout.write(String(cur));})" "$1"
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
json_assert() {
|
|
48
|
-
"$NODE" -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const v=JSON.parse(d); const fn = new Function('v', process.argv[1]); if (!fn(v)) process.exit(1);})" "$1"
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
printf 'checking server %s\n' "$API_BASE" >&2
|
|
52
|
-
curl -fsS "$API_BASE/api/status" >/dev/null
|
|
53
|
-
curl -fsS "$API_BASE/api/itp/plans" | json_assert "return v.success === true && v.data.plans.some(p => p.plan_id === 'credit-100') && v.data.unit_rule === '1 credit = 1 CNY'" >/dev/null
|
|
54
|
-
|
|
55
|
-
printf 'running one-command setup flow\n' >&2
|
|
56
|
-
SETUP=$(itp_home "$TMP_SETUP_HOME" setup --credits 100 --method fake --mock-approve --allow-fake --offline --json)
|
|
57
|
-
printf '%s' "$SETUP" | json_assert "return v.status === 'grant_ready' && v.target === 'generic' && v.grant_id && v.base_url && v.openai_base_url && v.credential && v.credential.stored === true && v.auth && v.auth.session_stored === true && v.runtime_install.status === 'skipped'"
|
|
58
|
-
test ! -f "$TMP_SETUP_HOME/.codex/config.toml"
|
|
59
|
-
TOKEN_FROM_SETUP=$(HOME="$TMP_SETUP_HOME" PATH=/nonexistent VOLTAGENT_API_BASE="$API_BASE" "$NODE" "$ROOT/bin/itp" token issue --grant "$(printf '%s' "$SETUP" | json_get grant_id)" --stdout)
|
|
60
|
-
case "$TOKEN_FROM_SETUP" in
|
|
61
|
-
sk-*) ;;
|
|
62
|
-
*) echo "setup token issue did not return sk-* token" >&2; exit 1 ;;
|
|
63
|
-
esac
|
|
64
|
-
|
|
65
|
-
printf 'checking setup no-wait auth handoff\n' >&2
|
|
66
|
-
SETUP_NOWAIT=$(itp_home "$TMP_SETUP_NOWAIT_HOME" setup --credits 100 --method fake --allow-fake --no-wait --json)
|
|
67
|
-
printf '%s' "$SETUP_NOWAIT" | json_assert "return v.status === 'waiting_human_auth' && v.action === 'scan_alipay_auth' && v.run_id && v.auth_id && v.user_code && v.verification_uri_complete && v.human_action && v.human_action.display.length > 0"
|
|
68
|
-
SETUP_NOWAIT_STATUS=$(itp_home "$TMP_SETUP_NOWAIT_HOME" status --json)
|
|
69
|
-
printf '%s' "$SETUP_NOWAIT_STATUS" | json_assert "return v.status === 'waiting_human_auth' && v.run_id && v.auth_id && v.next && v.next.command.includes('resume')"
|
|
70
|
-
SETUP_RESUMED=$(itp_home "$TMP_SETUP_NOWAIT_HOME" resume --mock-approve --allow-fake --offline --json)
|
|
71
|
-
printf '%s' "$SETUP_RESUMED" | json_assert "return v.status === 'grant_ready' && v.run_id && v.grant_id && v.credential && v.credential.stored === true"
|
|
72
|
-
|
|
73
|
-
USERNAME="e2e-$(date +%Y%m%d%H%M%S)-$$"
|
|
74
|
-
printf 'registering %s\n' "$USERNAME" >&2
|
|
75
|
-
AUTH=$(itp auth register --runtime codex --mock-approve --allow-fake --alipay-user-id "2088$RANDOM$RANDOM" --json)
|
|
76
|
-
printf '%s' "$AUTH" | json_assert "return v.account_id && v.device_id && v.session_stored === true"
|
|
77
|
-
|
|
78
|
-
ACCOUNT=$(itp account show --json)
|
|
79
|
-
printf '%s' "$ACCOUNT" | json_assert "return v.password_set === false && v.account && v.account.account_id"
|
|
80
|
-
|
|
81
|
-
printf 'setting first password\n' >&2
|
|
82
|
-
printf 'secret123\n' | itp account set-password --password-stdin --json | json_assert "return v.password_set === true"
|
|
83
|
-
ACCOUNT=$(itp account show --json)
|
|
84
|
-
printf '%s' "$ACCOUNT" | json_assert "return v.password_set === true"
|
|
85
|
-
|
|
86
|
-
printf 'creating fake checkout\n' >&2
|
|
87
|
-
CHECKOUT=$(itp checkout create --credits 100 --method fake --allow-fake --idempotency-key "e2e-$USERNAME" --json)
|
|
88
|
-
CHECKOUT_ID=$(printf '%s' "$CHECKOUT" | json_get checkout_id)
|
|
89
|
-
GRANT_ID=$(printf '%s' "$CHECKOUT" | json_get grant_id)
|
|
90
|
-
printf '%s' "$CHECKOUT" | json_assert "return v.status === 'grant_issued' && v.grant_id && !v.payment.cashier_url"
|
|
91
|
-
|
|
92
|
-
printf 'waiting payment %s\n' "$CHECKOUT_ID" >&2
|
|
93
|
-
PAYMENT=$(itp payment wait "$CHECKOUT_ID" --timeout 10 --json)
|
|
94
|
-
printf '%s' "$PAYMENT" | json_assert "return v.status === 'grant_issued' && v.grant_id"
|
|
95
|
-
|
|
96
|
-
printf 'installing grant %s\n' "$GRANT_ID" >&2
|
|
97
|
-
INSTALL=$(itp grants install "$GRANT_ID" --target codex --json)
|
|
98
|
-
printf '%s' "$INSTALL" | json_assert "return v.grant_id && v.credential && v.credential.credential_store"
|
|
99
|
-
|
|
100
|
-
printf 'installing codex profile in temp HOME\n' >&2
|
|
101
|
-
itp install codex --grant "$GRANT_ID" --offline --no-test --json | json_assert "return v.target === 'codex' && v.grant_id"
|
|
102
|
-
test -f "$TMP_HOME/.codex/config.toml"
|
|
103
|
-
test -f "$TMP_HOME/.itp/voltagent.env"
|
|
104
|
-
|
|
105
|
-
BALANCE=$(itp balance --json)
|
|
106
|
-
printf '%s' "$BALANCE" | json_assert "return v.active_grants === 1 && v.credits_remaining === '100.000000'"
|
|
107
|
-
|
|
108
|
-
ORDERS=$(itp checkout list --limit 5 --json)
|
|
109
|
-
printf '%s' "$ORDERS" | json_assert "return Array.isArray(v.orders) && v.orders.some(o => o.checkout_id === '$CHECKOUT_ID')"
|
|
110
|
-
|
|
111
|
-
USAGE=$(itp usage --grant "$GRANT_ID" --json)
|
|
112
|
-
printf '%s' "$USAGE" | json_assert "return v.grant_id === '$GRANT_ID' && v.total && v.total.requests === 0"
|
|
113
|
-
|
|
114
|
-
printf 'rotating grant key\n' >&2
|
|
115
|
-
ROTATE=$(itp keys rotate --grant "$GRANT_ID" --json)
|
|
116
|
-
printf '%s' "$ROTATE" | json_assert "return v.grant_id === '$GRANT_ID' && v.credential && v.credential.credential_store"
|
|
117
|
-
TOKEN=$(itp token issue --grant "$GRANT_ID" --stdout)
|
|
118
|
-
case "$TOKEN" in
|
|
119
|
-
sk-*) ;;
|
|
120
|
-
*) echo "token issue did not return sk-* token" >&2; exit 1 ;;
|
|
121
|
-
esac
|
|
122
|
-
|
|
123
|
-
printf 'revoking grant\n' >&2
|
|
124
|
-
REVOKE=$(itp grants revoke "$GRANT_ID" --json)
|
|
125
|
-
printf '%s' "$REVOKE" | json_assert "return v.status === 'revoked'"
|
|
126
|
-
BALANCE=$(itp balance --json)
|
|
127
|
-
printf '%s' "$BALANCE" | json_assert "return v.active_grants === 0"
|
|
128
|
-
|
|
129
|
-
printf 'voltagent local e2e ok\n'
|
|
130
|
-
printf 'server: %s\n' "$API_BASE"
|
|
131
|
-
printf 'account: %s\n' "$(printf '%s' "$AUTH" | json_get account_id)"
|
|
132
|
-
printf 'checkout: %s\n' "$CHECKOUT_ID"
|
|
133
|
-
printf 'grant: %s\n' "$GRANT_ID"
|
|
134
|
-
printf 'temp_home_cleaned_on_exit: %s\n' "$TMP_HOME"
|