@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/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 || process.env.VOLTAGENT_API_BASE || "http://localhost:3000";
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, "voltagent.env");
2459
+ const envPath = path.join(CONFIG_DIR, "itpay.env");
2460
2460
  const existing = readText(configPath, "");
2461
2461
  const block = [
2462
- 'model_provider = "voltagent"',
2462
+ 'model_provider = "itpay"',
2463
2463
  'model = "openai-code-default"',
2464
2464
  "",
2465
- "[model_providers.voltagent]",
2466
- 'name = "VoltaGent"',
2465
+ "[model_providers.itpay]",
2466
+ 'name = "ItPay"',
2467
2467
  `base_url = "${escapeTomlString(credentials.openai_base_url)}"`,
2468
- 'env_key = "VOLTAGENT_API_KEY"'
2468
+ 'env_key = "ITPAY_API_KEY"'
2469
2469
  ].join("\n");
2470
- const nextConfig = replaceManagedBlock(existing, "voltagent", block);
2470
+ const nextConfig = replaceManagedBlock(existing, "itpay", block);
2471
2471
  const configWrite = writeTextWithBackup(configPath, nextConfig, 0o600, dryRun);
2472
- const envWrite = writeTextWithBackup(envPath, `export VOLTAGENT_API_KEY=${quoteShell(credentials.key)}\n`, 0o600, dryRun);
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 VOLTAGENT_API_KEY from its process environment; source ~/.itp/voltagent.env before starting Codex if your launcher does not load it."
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.voltagent = {
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: role === "voltagent" ? "voltagent" : "itpay-buyer", role, path: skillPath, content });
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: role === "voltagent" ? "voltagent" : "itpay-buyer", role, path: skillPath });
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 = role === "voltagent" ? "voltagent" : "itpay-buyer";
2721
- const envPath = role === "buyer" ? process.env.ITPAY_BUYER_SKILL_PATH : process.env.ITPAY_CLI_SKILL_PATH;
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, "voltagent.env")],
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 = `voltagent:session:${response.account_id}:${response.device_id}`;
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 `voltagent:${grantId}`;
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
- "VoltaGent",
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=VoltaGent",
4109
+ "--label=ItPay",
4111
4110
  "service",
4112
- "VoltaGent",
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
- "VoltaGent",
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
- "VoltaGent",
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
- "VoltaGent"
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
- "VoltaGent",
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
- "Do not call legacy checkout directly for CORE-028 external-agent tests.",
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 ...`; do not ask the human to paste the grant id.",
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, run `itp buyer auth status --json` and ask the human to reauthorize through a normal checkout/account flow."
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.0",
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, or grant IDs. Run
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
- Use only the fields returned by that command.
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"