@allscale/cli 0.1.0 → 1.0.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.
Files changed (39) hide show
  1. package/README.md +113 -41
  2. package/bin/dev.js +11 -1
  3. package/bin/run-oclif.js +216 -27
  4. package/bin/run.js +19 -1
  5. package/dist/commands/build-info.js +13 -13
  6. package/dist/commands/claim-link/claim.js +14 -14
  7. package/dist/commands/claim-link/create.js +40 -37
  8. package/dist/commands/claim-link/get.js +26 -15
  9. package/dist/commands/claim-link/list.js +15 -15
  10. package/dist/commands/claim-link/status.js +13 -13
  11. package/dist/commands/describe.js +13 -13
  12. package/dist/commands/device-login.js +23 -24
  13. package/dist/commands/invoice/get.js +15 -15
  14. package/dist/commands/invoice/list.js +16 -16
  15. package/dist/commands/invoice/pay.js +40 -41
  16. package/dist/commands/invoice/received.js +16 -16
  17. package/dist/commands/invoice/send.js +42 -34
  18. package/dist/commands/invoice/sent.js +15 -15
  19. package/dist/commands/invoice/update.js +17 -15
  20. package/dist/commands/logout.js +14 -14
  21. package/dist/commands/operations.js +13 -13
  22. package/dist/commands/otp-login.js +21 -20
  23. package/dist/commands/otp-send.js +13 -13
  24. package/dist/commands/payout/send.js +14 -14
  25. package/dist/commands/payout/status.js +15 -15
  26. package/dist/commands/scope.js +17 -16
  27. package/dist/commands/store/create.js +17 -17
  28. package/dist/commands/transaction/get.js +15 -15
  29. package/dist/commands/transaction/list.js +15 -15
  30. package/dist/commands/wallet/list.js +15 -15
  31. package/dist/commands/wallet/send.js +40 -43
  32. package/dist/commands/whoami.js +14 -14
  33. package/dist/hooks/version-suffix.js +1 -1
  34. package/dist/index.js +14 -14
  35. package/dist/lib/help.js +7 -2
  36. package/dist/lib/output/audit-lifecycle.js +7 -0
  37. package/oclif.manifest.json +123 -292
  38. package/package.json +4 -6
  39. package/dist/commands/claim-link/preview.js +0 -18
@@ -3,7 +3,7 @@
3
3
  "build-info": {
4
4
  "aliases": [],
5
5
  "args": {},
6
- "description": "Show which API base (if any) this build is bound to, and its signing-key id. Never prints the signing key itself — only its (non-secret, already-forgeable) id.",
6
+ "description": "Show which API base (if any) this build is bound to, and its signing-key id. Never prints the signing key itself — only its non-secret id.",
7
7
  "examples": [
8
8
  "$ allscale build-info"
9
9
  ],
@@ -18,6 +18,7 @@
18
18
  "profile": {
19
19
  "description": "Named profile from ~/.allscale/config.toml",
20
20
  "helpGroup": "GLOBAL",
21
+ "hidden": true,
21
22
  "name": "profile",
22
23
  "hasDynamicHelp": false,
23
24
  "multiple": false,
@@ -148,7 +149,7 @@
148
149
  "device-login": {
149
150
  "aliases": [],
150
151
  "args": {},
151
- "description": "Log in via browser-confirmed device pairing. Use an existing web session or sign in there with Passkey or Email (enter the Email OTP on the webpage), then approve the CLI. The backend mints a scoped agent key — the only credential this CLI issues; no web sign-in secret is entered in the terminal.",
152
+ "description": "Log in via browser-confirmed device pairing. Use an existing web session or sign in there with Passkey or Email (enter the Email OTP on the webpage), then approve the CLI. The backend mints a scoped agent key — the credential issued by the current CLI login flows; no web sign-in secret is entered in the terminal.",
152
153
  "examples": [
153
154
  "$ allscale device-login",
154
155
  "$ allscale device-login --device-label tom-mbp-2026",
@@ -203,7 +204,7 @@
203
204
  "type": "option"
204
205
  },
205
206
  "scopes": {
206
- "description": "Requested scopes for the eventual agent key, repeatable. Scope strings use `<category>:<tier>`; `:all` implies `:read_only` for the same category. Pairs the approval screen can grant: invoice:{read_only,all}, contact:{read_only,all}, transaction:read_only, wallet:read_only, claim_link:{read_only,all}, store:all. The final set is chosen — and can be overridden — on that screen, and is ENFORCED server-side on every request. The Wallets toggle is on by default and grants `wallet:read_only`, which `invoice pay` and `wallet list` both need.",
207
+ "description": "Requested scopes for the eventual agent key, repeatable. Scope strings use `<category>:<tier>`; `:all` implies `:read_only` for the same category. Pairs the approval screen can grant: invoice:{read_only,all}, contact:{read_only,all}, transaction:read_only, wallet:read_only, claim_link:{read_only,all}, store:{read_only,all}. The final set is chosen — and can be overridden — on that screen, and is checked by the backend on each request. The Wallets toggle is on by default and grants `wallet:read_only`, which `wallet list` needs and `invoice pay` needs as ONE of its two scopes — `invoice pay` also needs `invoice:all` — request it here with `--scopes invoice:all` (it is forwarded to the approval screen), and tick the Invoices write tier there, since that toggle starts at Read. Values are checked locally first: an unrecognised `<category>:<tier>` string exits 2 before any pairing session is registered — the same check, and the same exit code, as `otp-login --scopes`. A recognised scope the screen declines to grant is reported after the key is minted, not up front.",
207
208
  "name": "scopes",
208
209
  "hasDynamicHelp": false,
209
210
  "multiple": true,
@@ -407,12 +408,12 @@
407
408
  "otp-login": {
408
409
  "aliases": [],
409
410
  "args": {},
410
- "description": "Terminal-only Email OTP login for headless / scripted shells. Works with any account email (external domains included; the account must already exist). Sends the code (unless --otp-id is supplied), reads it in the terminal, and mints a scope-ENFORCED agent API key — the only credential this CLI issues. Scopes are set with --scopes (required; no full-permission default exists).",
411
+ "description": "Terminal-only Email OTP login for headless / scripted shells. Works with any account email (external domains included; the account must already exist). Sends the code (unless --otp-id is supplied), reads it in the terminal, and mints a scoped agent API key whose granted scopes the backend checks on each request — the credential issued by the current CLI login flows. Scopes are set with --scopes (required; no full-permission default exists).",
411
412
  "examples": [
412
413
  "$ allscale otp-login --email me@example.com --scopes invoice:read_only # interactive: prompts on stdin",
413
414
  "$ printf '%s' \"$OTP\" | allscale otp-login --email me@example.com --scopes invoice:all --otp-stdin",
414
415
  "$ ALLSCALE_OTP=$(read-from-mailbox) allscale otp-login --email me@example.com --scopes claim_link:all",
415
- "$ allscale otp-login --email me@example.com --scopes invoice:read_only --otp-id <id> --otp <code>"
416
+ "$ allscale otp-login --email me@example.com --scopes invoice:read_only --otp-id otp-request-id --otp 123456"
416
417
  ],
417
418
  "flags": {
418
419
  "json": {
@@ -466,7 +467,7 @@
466
467
  "type": "option"
467
468
  },
468
469
  "scopes": {
469
- "description": "Scopes the minted agent key should carry, repeatable. Scope strings use `<category>:<tier>`; `:all` implies `:read_only` for the same category. Pairs that reach a real operation today: invoice:{read_only,all}, contact:{read_only,all}, claim_link:{read_only,all}, transaction:read_only, store:all, wallet:read_only. Other pairs are accepted by the catalog but grant nothing, and this command does NOT warn when a requested scope is clipped — verify with `allscale scope` after minting. Required — the backend deliberately has no full-permission default. Requested scopes are clipped to your account's role; keys are ENFORCED server-side on every request.",
470
+ "description": "Scopes the minted agent key should carry, repeatable. Scope strings use `<category>:<tier>`; `:all` implies `:read_only` for the same category. Pairs that reach a real operation today: invoice:{read_only,all}, contact:{read_only,all}, claim_link:{read_only,all}, transaction:{read_only,all}, store:{read_only,all}, wallet:{read_only,all}. transaction:all, wallet:all reach only their category's READS — no write operation is wired to either today, so the `:read_only` tier grants exactly as much; prefer it unless you have a reason to hold the wider scope. Other pairs are accepted by the catalog but grant nothing, and this command does NOT warn when a requested scope is clipped — verify with `allscale scope` after minting. Values are checked locally first: an unrecognised `<category>:<tier>` string exits 2 before any OTP is sent — the same check, and the same exit code, as `device-login --scopes`. Required — the backend deliberately has no full-permission default. Requested scopes are clipped to your account's role; granted scopes are checked by the backend on each request.",
470
471
  "helpGroup": "REQUIRED",
471
472
  "name": "scopes",
472
473
  "required": true,
@@ -624,7 +625,7 @@
624
625
  "scope": {
625
626
  "aliases": [],
626
627
  "args": {},
627
- "description": "Show the capabilities granted to the current CLI session (chosen on the browser approval screen at `device-login`, or via --scopes on `otp-login`). Scopes are ENFORCED server-side on every request. Read from the cached token bundle; no network call.",
628
+ "description": "Show the capabilities granted to the current CLI session (chosen on the browser approval screen at `device-login`, or via --scopes on `otp-login`). Scopes are checked by the backend on each request. Scope controls reduce access but do not prevent credential compromise, misuse within a granted scope, configuration errors, or unauthorized actions by an otherwise authenticated caller. Read from the cached token bundle; no network call.",
628
629
  "examples": [
629
630
  "$ allscale scope",
630
631
  "$ allscale scope --profile prod"
@@ -646,7 +647,7 @@
646
647
  "type": "option"
647
648
  },
648
649
  "token": {
649
- "description": "Override the access token for this invocation (precedence: --token > ALLSCALE_TOKEN > --profile)",
650
+ "description": "Not supported by this command. `scope` reads scopes recorded with a stored profile, so an arbitrary token has no authoritative scope source and is rejected locally. Use --profile to select the stored credential whose scopes should be shown.",
650
651
  "helpGroup": "GLOBAL",
651
652
  "hidden": true,
652
653
  "name": "token",
@@ -764,10 +765,10 @@
764
765
  "claim-link:claim": {
765
766
  "aliases": [],
766
767
  "args": {},
767
- "description": "Claim a Claim Link. Anonymous claims require --to; authenticated business receivers can use --to-wallet. Only an on-chain-proven claimed outcome exits 0.",
768
+ "description": "Claim a Claim Link. Path-A claims (no AllScale account sign-in) require --to; authenticated business receivers can use --to-wallet. Only an on-chain-proven claimed outcome exits 0.",
768
769
  "examples": [
769
- "$ allscale claim-link claim --claim-token <token> --to 0xabc...",
770
- "$ allscale claim-link claim --claim-url https://app.allscale.io/claim/<token> --to-wallet"
770
+ "$ allscale claim-link claim --claim-token claim-token-from-share-link --to 0x1234567890abcdef1234567890abcdef12345678",
771
+ "$ allscale claim-link claim --claim-url https://app.allscale.io/claim/claim-token-from-share-link --to-wallet"
771
772
  ],
772
773
  "flags": {
773
774
  "json": {
@@ -812,7 +813,7 @@
812
813
  },
813
814
  "claim-token": {
814
815
  "description": "Raw bearer claim token. Secret: prefer env/history-safe invocation.",
815
- "helpGroup": "REQUIRED (ONE OF)",
816
+ "helpGroup": "REQUIRED SOURCE (ONE OF)",
816
817
  "name": "claim-token",
817
818
  "hasDynamicHelp": false,
818
819
  "multiple": false,
@@ -820,18 +821,18 @@
820
821
  },
821
822
  "claim-url": {
822
823
  "description": "Canonical claim URL, or a same-origin /s/ short URL resolved with one unauthenticated 302.",
823
- "helpGroup": "REQUIRED (ONE OF)",
824
+ "helpGroup": "REQUIRED SOURCE (ONE OF)",
824
825
  "name": "claim-url",
825
826
  "hasDynamicHelp": false,
826
827
  "multiple": false,
827
828
  "type": "option"
828
829
  },
829
830
  "to": {
830
- "description": "Receiver EVM address for anonymous Path-A claims.",
831
+ "description": "Receiver EVM address for Path-A claims (no AllScale account sign-in). This does not make the transaction anonymous: network, request, compliance, and public blockchain data may still be processed or observable.",
831
832
  "exclusive": [
832
833
  "to-wallet"
833
834
  ],
834
- "helpGroup": "REQUIRED (ONE OF)",
835
+ "helpGroup": "REQUIRED DESTINATION (ONE OF)",
835
836
  "name": "to",
836
837
  "hasDynamicHelp": false,
837
838
  "multiple": false,
@@ -842,7 +843,7 @@
842
843
  "exclusive": [
843
844
  "to"
844
845
  ],
845
- "helpGroup": "REQUIRED (ONE OF)",
846
+ "helpGroup": "REQUIRED DESTINATION (ONE OF)",
846
847
  "name": "to-wallet",
847
848
  "allowNo": false,
848
849
  "type": "boolean"
@@ -855,7 +856,7 @@
855
856
  "pluginName": "@allscale/cli",
856
857
  "pluginType": "core",
857
858
  "strict": true,
858
- "usage": "claim-link claim (--claim-token <value> | --claim-url <value>) [--json] [--profile <value>] [--insecure-storage] [--to <value> | --to-wallet]",
859
+ "usage": "claim-link claim (--claim-token <value> | --claim-url <value>) [--json] [--profile <value>] [--insecure-storage] (--to <value> | --to-wallet)",
859
860
  "enableJsonFlag": true,
860
861
  "legacyStagingNoticeShown": false,
861
862
  "emitsOwnOutput": false,
@@ -997,7 +998,7 @@
997
998
  "type": "option"
998
999
  },
999
1000
  "bridge-timeout": {
1000
- "description": "Shorten the browser confirmation deadline, in milliseconds. By default the CLI waits for the broker op TTL.",
1001
+ "description": "Shorten the browser confirmation deadline, in milliseconds. By default the CLI waits the broker op's remaining life. It can only shorten the wait — a value above that is clamped to it, because the confirmation link cannot outlive its broker op. If the server's response carries no timestamp to measure that life against, the CLI says so on stderr and bounds the wait instead: with no --bridge-timeout this receipt-aware flow waits up to 240s and may be shortened by the best-effort bound from the stated expiry; an explicit value is honoured up to that same bound (clamped to 60s-15min), or 15min when the expiry itself is unreadable.",
1001
1002
  "name": "bridge-timeout",
1002
1003
  "hasDynamicHelp": false,
1003
1004
  "multiple": false,
@@ -1042,15 +1043,15 @@
1042
1043
  "aliases": [],
1043
1044
  "args": {
1044
1045
  "id": {
1045
- "description": "Claim Link id_str.",
1046
+ "description": "Claim Link id.",
1046
1047
  "name": "id",
1047
1048
  "required": true
1048
1049
  }
1049
1050
  },
1050
- "description": "Fetch one of your own Claim Links by id. Unknown or non-owned ids are indistinguishable on the backend.",
1051
+ "description": "Fetch one of your own Claim Links by id. Unknown or non-owned ids are indistinguishable on the backend. Requires claim_link:read_only for an agent-key session.",
1051
1052
  "examples": [
1052
- "$ allscale claim-link get 65ab1234567890abcdef0123",
1053
- "$ allscale claim-link get 65ab... --select 'id status amount token_symbol chain'"
1053
+ "Fetch the first Claim Link you own (requires jq and at least one existing link):\n$ CLAIM_LINK_ID=\"$(\n allscale claim-link list --input '{\"limit\":1}' |\n jq -er '.data.items[0].id'\n )\" &&\n allscale claim-link get \"$CLAIM_LINK_ID\"",
1054
+ "Fetch selected fields from the first Claim Link you own (requires jq and at least one existing link):\n$ CLAIM_LINK_ID=\"$(\n allscale claim-link list --input '{\"limit\":1}' |\n jq -er '.data.items[0].id'\n )\" &&\n allscale claim-link get \"$CLAIM_LINK_ID\" \\\n --select 'id status amount token_symbol chain'"
1054
1055
  ],
1055
1056
  "flags": {
1056
1057
  "json": {
@@ -1132,7 +1133,7 @@
1132
1133
  "claim-link:list": {
1133
1134
  "aliases": [],
1134
1135
  "args": {},
1135
- "description": "List your own Claim Links newest first. Defaults to the first 50 rows; pass --input or --all to override.",
1136
+ "description": "List your own Claim Links newest first. Defaults to the first 50 rows; pass --input or --all to override. Requires claim_link:read_only for an agent-key session.",
1136
1137
  "examples": [
1137
1138
  "$ allscale claim-link list",
1138
1139
  "$ allscale claim-link list --status link_sent --input '{\"limit\":20}'",
@@ -1180,7 +1181,7 @@
1180
1181
  "type": "boolean"
1181
1182
  },
1182
1183
  "status": {
1183
- "description": "Optional ClaimLinkStatus filter. Accepts 1..6 or pending_deposit/link_sent/claiming/claimed/expired/cancelled.",
1184
+ "description": "Optional ClaimLinkStatus filter. Accepts an integer from 1 through 6 or pending_deposit/link_sent/claiming/claimed/expired/cancelled.",
1184
1185
  "name": "status",
1185
1186
  "hasDynamicHelp": false,
1186
1187
  "multiple": false,
@@ -1242,136 +1243,13 @@
1242
1243
  "list.js"
1243
1244
  ]
1244
1245
  },
1245
- "claim-link:preview": {
1246
- "aliases": [],
1247
- "args": {},
1248
- "description": "Preview Claim Link fees without creating a link. Mirrors create_claim_link validation.",
1249
- "examples": [
1250
- "$ allscale claim-link preview --amount 10 --chain base --per-transfer-gas 0.02"
1251
- ],
1252
- "flags": {
1253
- "json": {
1254
- "description": "Format output as json.",
1255
- "helpGroup": "GLOBAL",
1256
- "name": "json",
1257
- "allowNo": false,
1258
- "type": "boolean"
1259
- },
1260
- "profile": {
1261
- "description": "Named profile from ~/.allscale/config.toml",
1262
- "helpGroup": "GLOBAL",
1263
- "name": "profile",
1264
- "hasDynamicHelp": false,
1265
- "multiple": false,
1266
- "type": "option"
1267
- },
1268
- "token": {
1269
- "description": "Override the access token for this invocation (precedence: --token > ALLSCALE_TOKEN > --profile)",
1270
- "helpGroup": "GLOBAL",
1271
- "hidden": true,
1272
- "name": "token",
1273
- "hasDynamicHelp": false,
1274
- "multiple": false,
1275
- "type": "option"
1276
- },
1277
- "api-base": {
1278
- "description": "Override the API base URL (public builds accept only https://app.allscale.io)",
1279
- "helpGroup": "GLOBAL",
1280
- "hidden": true,
1281
- "name": "api-base",
1282
- "hasDynamicHelp": false,
1283
- "multiple": false,
1284
- "type": "option"
1285
- },
1286
- "insecure-storage": {
1287
- "description": "Force POSIX plaintext token storage at ~/.allscale/credentials.json (mode 0600) instead of the OS keychain — for headless / CI / agent use. Unavailable on Windows because Node cannot verify owner-only ACL/link safety. Also via ALLSCALE_INSECURE_STORAGE=1.",
1288
- "helpGroup": "GLOBAL",
1289
- "name": "insecure-storage",
1290
- "allowNo": false,
1291
- "type": "boolean"
1292
- },
1293
- "amount": {
1294
- "description": "Receiver amount as a decimal string in token units; at most 6 effective decimals, or 18 on BSC.",
1295
- "helpGroup": "REQUIRED",
1296
- "name": "amount",
1297
- "required": true,
1298
- "hasDynamicHelp": false,
1299
- "multiple": false,
1300
- "type": "option"
1301
- },
1302
- "chain": {
1303
- "description": "EVM chain for the claim link.",
1304
- "helpGroup": "REQUIRED",
1305
- "name": "chain",
1306
- "required": true,
1307
- "hasDynamicHelp": false,
1308
- "multiple": false,
1309
- "options": [
1310
- "ethereum",
1311
- "bsc",
1312
- "base",
1313
- "polygon",
1314
- "arbitrum",
1315
- "optimism"
1316
- ],
1317
- "type": "option"
1318
- },
1319
- "per-transfer-gas": {
1320
- "description": "One ERC-20 transfer's gas estimate in token units.",
1321
- "helpGroup": "REQUIRED",
1322
- "name": "per-transfer-gas",
1323
- "required": true,
1324
- "hasDynamicHelp": false,
1325
- "multiple": false,
1326
- "type": "option"
1327
- },
1328
- "stable-coin": {
1329
- "description": "Stablecoin for the link.",
1330
- "name": "stable-coin",
1331
- "default": "USDT",
1332
- "hasDynamicHelp": false,
1333
- "multiple": false,
1334
- "options": [
1335
- "USDT",
1336
- "USDC"
1337
- ],
1338
- "type": "option"
1339
- },
1340
- "show-doc": {
1341
- "description": "Print the GraphQL document to stderr.",
1342
- "name": "show-doc",
1343
- "allowNo": false,
1344
- "type": "boolean"
1345
- }
1346
- },
1347
- "hasDynamicHelp": false,
1348
- "hiddenAliases": [],
1349
- "id": "claim-link:preview",
1350
- "pluginAlias": "@allscale/cli",
1351
- "pluginName": "@allscale/cli",
1352
- "pluginType": "core",
1353
- "strict": true,
1354
- "enableJsonFlag": true,
1355
- "legacyStagingNoticeShown": false,
1356
- "emitsOwnOutput": false,
1357
- "requiresEscapeHatch": false,
1358
- "requiresInput": false,
1359
- "isLocalOnly": false,
1360
- "isESM": false,
1361
- "relativePath": [
1362
- "dist",
1363
- "commands",
1364
- "claim-link",
1365
- "preview.js"
1366
- ]
1367
- },
1368
1246
  "claim-link:status": {
1369
1247
  "aliases": [],
1370
1248
  "args": {},
1371
1249
  "description": "Read the public receiver-facing status snapshot for a Claim Link bearer token or URL.",
1372
1250
  "examples": [
1373
- "$ allscale claim-link status --claim-token <token>",
1374
- "$ allscale claim-link status --claim-url https://app.allscale.io/claim/<token>"
1251
+ "$ allscale claim-link status --claim-token claim-token-from-share-link",
1252
+ "$ allscale claim-link status --claim-url https://app.allscale.io/claim/claim-token-from-share-link"
1375
1253
  ],
1376
1254
  "flags": {
1377
1255
  "json": {
@@ -1457,7 +1335,7 @@
1457
1335
  "aliases": [],
1458
1336
  "args": {
1459
1337
  "id": {
1460
- "description": "Invoice id_str (the backend's Payment.id_str)",
1338
+ "description": "Invoice id, as returned by `invoice send` and listed by `invoice list`",
1461
1339
  "name": "id",
1462
1340
  "required": true
1463
1341
  }
@@ -1465,7 +1343,7 @@
1465
1343
  "description": "Fetch one invoice by id. Wraps the `payment` GraphQL query.",
1466
1344
  "examples": [
1467
1345
  "$ allscale invoice get 65ab1234567890abcdef0123",
1468
- "$ allscale invoice get 65ab... --select 'id status amount currency'"
1346
+ "$ allscale invoice get 65ab1234567890abcdef0123 --select 'id status amount_coins display_amount payment_type'"
1469
1347
  ],
1470
1348
  "flags": {
1471
1349
  "json": {
@@ -1550,10 +1428,10 @@
1550
1428
  "description": "List invoices for a business. Wraps the `payments` GraphQL query. Defaults to the first 50 rows; pass --input '{\"limit\":N,\"skip\":M}' to paginate, or --all to request one unpaginated response. Filter with --status / --payment-type (repeatable), the creation-date range --from / --to (half-open: --from inclusive, --to exclusive), and --to-email, which narrows this both-directions view to the invoices you ISSUED to that contact.",
1551
1429
  "examples": [
1552
1430
  "$ allscale invoice list",
1553
- "$ allscale invoice list --business-id <id>",
1431
+ "$ allscale invoice list --business-id 65ab1234567890abcdef0123",
1554
1432
  "$ allscale invoice list --input '{\"limit\":50,\"skip\":50}' # second page",
1555
1433
  "$ allscale invoice list --all # everything (be careful)",
1556
- "$ allscale invoice list --input '{\"limit\":5}' --select 'count items { id status amount }'",
1434
+ "$ allscale invoice list --payment-type USDT --input '{\"limit\":5}' --select 'count items { id status amount_coins }'",
1557
1435
  "$ allscale invoice list --status SENT --status OVERDUE # unpaid work",
1558
1436
  "$ allscale invoice list --payment-type USDC # only USDC invoices",
1559
1437
  "$ allscale invoice list --from 2026-07-01 --to 2026-08-01 # exactly July (--to is exclusive)",
@@ -1602,7 +1480,7 @@
1602
1480
  },
1603
1481
  "business-id": {
1604
1482
  "char": "b",
1605
- "description": "Business id_str. Defaults to the logged-in user's business when omitted.",
1483
+ "description": "Business id. Defaults to the logged-in user's business when omitted.",
1606
1484
  "name": "business-id",
1607
1485
  "hasDynamicHelp": false,
1608
1486
  "multiple": false,
@@ -1703,15 +1581,15 @@
1703
1581
  "aliases": [],
1704
1582
  "args": {
1705
1583
  "id": {
1706
- "description": "Invoice id_str (the backend Payment.id_str) to pay.",
1584
+ "description": "Invoice id to pay, as listed by `invoice sent`.",
1707
1585
  "name": "id",
1708
1586
  "required": true
1709
1587
  }
1710
1588
  },
1711
- "description": "Pay a received invoice from your AllScale wallet. Resolves the invoice's destination + amount, moves the funds via the same signing path as `wallet send` (confirmed in the browser at /cli), then reports and confirms the payment. Needs TWO scopes: `invoice:all` to create, report and confirm the pay intent, and `wallet:read_only` to look up the address you are paying FROM. Neither flow grants `invoice:all` for you: on `device-login` the Invoices toggle starts at Read, so tick its write tier on the approval screen; on `otp-login` pass BOTH --scopes invoice:all --scopes wallet:read_only, because naming any scope turns every unnamed category off.",
1589
+ "description": "Pay an invoice where you are the payer, from your AllScale wallet. Use `allscale invoice sent` to list the invoices you owe (add --status SENT --status ACKNOWLEDGED --status FAILED for the ones you can start paying). An OVERDUE invoice cannot start a new payment: if an earlier attempt of yours may still be in flight, reconcile that first — otherwise ask the issuer to extend its due date, which revives it. Resolves the invoice's destination + amount, moves the funds via the same signing path as `wallet send` (the transfer is confirmed in the browser), then reports and confirms the payment. Needs TWO scopes: `invoice:all` to create, report and confirm the pay intent, and `wallet:read_only` to look up the address you are paying FROM. Neither LOGIN flow grants `invoice:all` for you: on `device-login` the Invoices toggle starts at Read, so tick its write tier on the approval screen; on `otp-login` pass BOTH --scopes invoice:all --scopes wallet:read_only, because naming any scope turns every unnamed category off.",
1712
1590
  "examples": [
1713
1591
  "$ allscale invoice pay 65ab1234567890abcdef0123",
1714
- "$ allscale invoice pay 65ab... --chain bsc --stable-coin USDT"
1592
+ "$ allscale invoice pay 65ab1234567890abcdef0123 --chain bsc --stable-coin USDT"
1715
1593
  ],
1716
1594
  "flags": {
1717
1595
  "json": {
@@ -1755,6 +1633,7 @@
1755
1633
  "type": "boolean"
1756
1634
  },
1757
1635
  "business-id": {
1636
+ "char": "b",
1758
1637
  "description": "Your own business id. Optional and redundant — payment always comes from the wallet of the business you are signed in as. Supplying a different business id is rejected rather than silently paying from your own wallet; omit the flag unless a script needs to assert which business it is acting as.",
1759
1638
  "name": "business-id",
1760
1639
  "hasDynamicHelp": false,
@@ -1788,60 +1667,15 @@
1788
1667
  "type": "option"
1789
1668
  },
1790
1669
  "browser-base": {
1791
- "description": "Override the base URL of the /cli confirmation link (defaults to the session API base). (Browser-bridge only — not valid with --headless.)",
1792
- "exclusive": [
1793
- "headless"
1794
- ],
1670
+ "description": "Override the base URL of the /cli confirmation link (defaults to the session API base).",
1795
1671
  "name": "browser-base",
1796
1672
  "hasDynamicHelp": false,
1797
1673
  "multiple": false,
1798
1674
  "type": "option"
1799
1675
  },
1800
1676
  "bridge-timeout": {
1801
- "description": "Override the confirmation deadline in milliseconds (default 180000). (Browser-bridge only not valid with --headless.)",
1802
- "exclusive": [
1803
- "headless"
1804
- ],
1677
+ "description": "Shorten the confirmation deadline, in milliseconds. By default the CLI waits the broker op's remaining life (~5 min). The browser only returns after the on-chain receipt confirms (it may poll for up to ~2 min after signing), so a shortened deadline must budget for the signing ceremony PLUS that receipt wait. Capped by the broker op TTL: a larger value is clamped, because the confirmation link cannot outlive its broker op. If no usable server-relative TTL is available, a withdraw waits up to 240s and may be shortened by the best-effort bound from the stated expiry.",
1805
1678
  "name": "bridge-timeout",
1806
- "default": 180000,
1807
- "hasDynamicHelp": false,
1808
- "multiple": false,
1809
- "type": "option"
1810
- },
1811
- "headless": {
1812
- "description": "Pay without a browser via Turnkey email-OTP (accounts with email-OTP enabled and no passkey; passkey accounts must use the browser flow). Supply the OTP via --otp-stdin (recommended for automation) or interactively; --otp / ALLSCALE_OTP work ONLY combined with --otp-id. This removes the in-browser human confirmation, so use it only on a trusted machine. Requires an existing legacy JWT session, which this build cannot create — use the browser-bridge flow instead.",
1813
- "name": "headless",
1814
- "allowNo": false,
1815
- "type": "boolean"
1816
- },
1817
- "otp": {
1818
- "dependsOn": [
1819
- "headless"
1820
- ],
1821
- "description": "Turnkey signing OTP code (with --headless). Only works combined with --otp-id (an externally-triggered flow): without --otp-id the CLI emails a FRESH code at run time, so any pre-set --otp / ALLSCALE_OTP value is stale and fails with 'Invalid or expired OTP code'. For automation prefer --otp-stdin (CLI emails; pipe the fresh code back). Passing --otp as a flag also exposes it in shell history AND the process list (ps / /proc/<pid>/cmdline).",
1822
- "exclusive": [
1823
- "otp-stdin"
1824
- ],
1825
- "name": "otp",
1826
- "hasDynamicHelp": false,
1827
- "multiple": false,
1828
- "type": "option"
1829
- },
1830
- "otp-stdin": {
1831
- "dependsOn": [
1832
- "headless"
1833
- ],
1834
- "description": "Read the Turnkey signing OTP as one line from stdin (with --headless).",
1835
- "name": "otp-stdin",
1836
- "allowNo": false,
1837
- "type": "boolean"
1838
- },
1839
- "otp-id": {
1840
- "dependsOn": [
1841
- "headless"
1842
- ],
1843
- "description": "Skip the OTP-send step and reuse a prior Turnkey otpId (with --headless). The OTP code itself is still required via --otp / ALLSCALE_OTP / --otp-stdin.",
1844
- "name": "otp-id",
1845
1679
  "hasDynamicHelp": false,
1846
1680
  "multiple": false,
1847
1681
  "type": "option"
@@ -1874,7 +1708,7 @@
1874
1708
  "description": "List invoices you are the PAYEE on — where the money is owed to your business. Wraps `recipient_payments` (the backend's `recipient` is the payee). This INCLUDES every invoice you issued with `invoice send`. Defaults to the first 50 rows; pass --input or --all to override. Filter with --status / --payment-type (repeatable), the creation-date range --from / --to (half-open: --from inclusive, --to exclusive), and --to-email to narrow to the invoices you billed to one contact.",
1875
1709
  "examples": [
1876
1710
  "$ allscale invoice received",
1877
- "$ allscale invoice received --business-id <id> --input '{\"limit\":10}'",
1711
+ "$ allscale invoice received --business-id 65ab1234567890abcdef0123 --input '{\"limit\":10}'",
1878
1712
  "$ allscale invoice received --all",
1879
1713
  "$ allscale invoice received --status SENT --status OVERDUE",
1880
1714
  "$ allscale invoice received --from 2026-07-01 --to 2026-08-01",
@@ -1923,7 +1757,7 @@
1923
1757
  },
1924
1758
  "business-id": {
1925
1759
  "char": "b",
1926
- "description": "Business id_str. Defaults to the logged-in user's business when omitted.",
1760
+ "description": "Business id. Defaults to the logged-in user's business when omitted.",
1927
1761
  "name": "business-id",
1928
1762
  "hasDynamicHelp": false,
1929
1763
  "multiple": false,
@@ -2023,15 +1857,15 @@
2023
1857
  "invoice:send": {
2024
1858
  "aliases": [],
2025
1859
  "args": {},
2026
- "description": "Send an invoice by email. Resolves the contact, selects receiving wallets, renders a minimal HTML invoice, and submits create_payment through the authenticated session. No browser ceremony — the payer signs when they pay, not when you issue. Returns a fixed summary (payment_id, contact, amount, wallet count) rather than a GraphQL selection, which is why there is no --select here; read the created invoice back with `invoice get <payment_id> --select ...`. Note the direction: an invoice you send lists YOU as the payee, so it appears under `invoice received`, not `invoice sent`.",
1860
+ "description": "Send an invoice by email. Resolves the contact, selects receiving wallets, renders a minimal HTML invoice, and submits create_payment through the authenticated session. No browser ceremony — the payer signs when they pay, not when you issue. Returns a fixed summary (payment_id, contact, amount, wallet count) rather than a GraphQL selection, which is why there is no --select here; read the created invoice back with `invoice get \"$PAYMENT_ID\" --select 'id status amount_coins display_amount payment_type'`, using the payment_id returned by this command. The follow-up reflects the default USDT path; display_amount remains the denomination-aware value. Note the direction: an invoice you send lists YOU as the payee, so it appears under `invoice received`, not `invoice sent`.",
2027
1861
  "examples": [
2028
1862
  "$ allscale invoice send --to-email client@example.com --amount 1.00",
2029
- "$ allscale invoice send --to-email client@example.com --amount 250 --payment-type 2 --currency-label USDC --memo \"January retainer\" --due 2026-06-01",
1863
+ "$ allscale invoice send --to-email client@example.com --amount 250 --payment-type 2 --currency-label USDC --memo \"January retainer\" --due \"$(node -p 'new Date(Date.now()+30*864e5).toISOString().slice(0,10)')\"",
2030
1864
  "$ allscale invoice send --to-contact-id 695da2cd3b9598f5edcdcb56 --amount 50 --line \"Design|1|30.00\" --line \"Copy|1|20.00\"",
2031
1865
  "$ allscale invoice send --to-email client@example.com --amount 75 --line \"Consulting retainer\"",
2032
1866
  "$ allscale invoice send --to-email client@example.com --amount 350 --line \"Discovery (4h)|4|25.00\" --line \"Implementation (10h)|10|25.00\"",
2033
1867
  "$ allscale invoice send --to-email client@example.com --line \"Discovery (4h)|4|25.00\" --line \"Implementation (10h)|10|25.00\"",
2034
- "$ allscale invoice send --to-email client@example.com --amount 100 --wallet-id 66ac....",
1868
+ "$ allscale invoice send --to-email client@example.com --amount 100 --wallet-id 66ac1234567890abcdef0123",
2035
1869
  "$ allscale invoice send --to-email newclient@example.com --amount 100 --auto-create-contact",
2036
1870
  "$ allscale invoice send --to-email billing@acme.com --amount 100 --auto-create-contact --contact-name \"Acme Inc\""
2037
1871
  ],
@@ -2088,7 +1922,7 @@
2088
1922
  "type": "option"
2089
1923
  },
2090
1924
  "to-contact-id": {
2091
- "description": "Contact id_str directly (skip the email lookup). Mutually exclusive with --to-email.",
1925
+ "description": "Contact id directly (skip the email lookup). Mutually exclusive with --to-email.",
2092
1926
  "exclusive": [
2093
1927
  "to-email"
2094
1928
  ],
@@ -2099,7 +1933,7 @@
2099
1933
  "type": "option"
2100
1934
  },
2101
1935
  "amount": {
2102
- "description": "Decimal amount in coin units (e.g. \"1.00\" for one USDT; minimum 0.10 for USDT/USDC). Optional when every --line is the three-field \"<description>|<quantity>|<amount>\" form — in that case the CLI sums the lines to derive the invoice total. Required otherwise.",
1936
+ "description": "Exact decimal amount: USDT/USDC accept up to 6 effective decimal places (minimum 0.10), while fiat accepts up to 2. Redundant trailing zeroes are allowed and canonicalized; the CLI never rounds or truncates. Optional when every --line is the three-field \"<description>|<quantity>|<amount>\" form — in that case the CLI sums the lines to derive the invoice total. Required otherwise.",
2103
1937
  "helpGroup": "CONDITIONAL",
2104
1938
  "name": "amount",
2105
1939
  "hasDynamicHelp": false,
@@ -2116,7 +1950,7 @@
2116
1950
  "type": "boolean"
2117
1951
  },
2118
1952
  "contact-name": {
2119
- "description": "Display name to use when --auto-create-contact creates a new contact. Optional — defaults to the full email address (unique enough to avoid the BE's per-business name-uniqueness collision). Has no effect when the contact already exists or when --auto-create-contact is not set.",
1953
+ "description": "Display name to use when --auto-create-contact creates a new contact. Optional — defaults to the full email address (unique enough to avoid a per-business name collision). Has no effect when the contact already exists or when --auto-create-contact is not set.",
2120
1954
  "exclusive": [
2121
1955
  "to-contact-id"
2122
1956
  ],
@@ -2126,15 +1960,14 @@
2126
1960
  "type": "option"
2127
1961
  },
2128
1962
  "currency-label": {
2129
- "description": "Label shown next to the amount in the rendered invoice (e.g. USDT, USDC). Display only; select the stablecoin with --payment-type=1 (USDT) or --payment-type=2 (USDC). --currency-int applies only to --payment-type=0 fiat invoices. Default: USDT.",
1963
+ "description": "Label shown after each amount in the rendered invoice (e.g. \"250.00 USDT\"). Display only it changes no amount, no asset, and nothing the payer is charged. Which stablecoins the invoice ACCEPTS is decided by --payment-type when you pass it explicitly (=1 USDT, =2 USDC); omit that flag and the invoice accepts whatever your business is configured for. Defaults to the label matching --payment-type (USDT or USDC; USD for --payment-type=0 fiat pass this flag explicitly for a non-USD --currency-int).",
2130
1964
  "name": "currency-label",
2131
- "default": "USDT",
2132
1965
  "hasDynamicHelp": false,
2133
1966
  "multiple": false,
2134
1967
  "type": "option"
2135
1968
  },
2136
1969
  "currency-int": {
2137
- "description": "Backend Currency enum value (USD=1, AED=2, ...). Only sent when --payment-type=0 (CURRENCY/fiat invoice); ignored for stablecoin payment types. Default: 1 (USD).",
1970
+ "description": "Fiat currency code, such as USD=1 or AED=2. Only sent when --payment-type=0 (fiat invoice); ignored for stablecoin payment types. Default: 1 (USD).",
2138
1971
  "name": "currency-int",
2139
1972
  "default": 1,
2140
1973
  "hasDynamicHelp": false,
@@ -2142,7 +1975,7 @@
2142
1975
  "type": "option"
2143
1976
  },
2144
1977
  "payment-type": {
2145
- "description": "CreatePaymentInput.payment_type. Run `allscale describe create_payment` for the enum. Default: 1.",
1978
+ "description": "What the invoice is denominated in AND which stablecoin it may be paid with: 0 = fiat (pair with --currency-int), 1 = USDT, 2 = USDC. Passing 1 or 2 EXPLICITLY pins the invoice to that coin — a payment arriving in the other stablecoin is rejected, and the payer's email names only this coin. Omit the flag and the invoice accepts whichever stablecoins your business is configured for (it is still denominated in USDT, the default). For 0 the amount is fiat but settlement is still in a stablecoin, so the payer may likewise use any coin your business accepts. Run `allscale describe create_payment` for the full list. Default: 1 (denomination only — omitting the flag pins nothing).",
2146
1979
  "name": "payment-type",
2147
1980
  "default": 1,
2148
1981
  "hasDynamicHelp": false,
@@ -2150,14 +1983,14 @@
2150
1983
  "type": "option"
2151
1984
  },
2152
1985
  "memo": {
2153
- "description": "Free-form invoice memo / description shown to the payer.",
1986
+ "description": "Free-form invoice memo / description shown to the payer. When omitted, defaults to the same note the web invoice editor generates: \"Thank you for your business! Payment is due within N days.\" with N derived from the due date (30 unless --due says otherwise).",
2154
1987
  "name": "memo",
2155
1988
  "hasDynamicHelp": false,
2156
1989
  "multiple": false,
2157
1990
  "type": "option"
2158
1991
  },
2159
1992
  "due": {
2160
- "description": "Invoice due date / datetime (ISO 8601), e.g. 2026-06-01, 2026-06-01T09:30:00, 2026-06-01T09:30:00Z, or 2026-06-01T09:30:00+08:00. A value without a UTC offset is read as UTC (so 2026-06-01 means 2026-06-01T00:00:00Z) — stated explicitly because interpreting it in the local timezone would make the same command mean different things on different machines. Rendered in the HTML body exactly as you typed it.",
1993
+ "description": "Invoice due date / datetime (ISO 8601), e.g. YYYY-MM-DD, YYYY-MM-DDTHH:mm:ss, YYYY-MM-DDTHH:mm:ssZ, or YYYY-MM-DDTHH:mm:ss+08:00. A value without a UTC offset is read as UTC (so a date-only value means midnight UTC) — stated explicitly because interpreting it in the local timezone would make the same command mean different things on different machines. When omitted, defaults to 30 days from now, matching the web invoice editor. The rendered document shows the NORMALIZED date in the web's display format (\"MMM DD, YYYY\"), not the raw flag text. The examples compute the date with POSIX command substitution, which sh/bash/zsh and PowerShell expand but cmd.exe does not; on cmd.exe pass an explicit date instead.",
2161
1994
  "name": "due",
2162
1995
  "hasDynamicHelp": false,
2163
1996
  "multiple": false,
@@ -2171,21 +2004,22 @@
2171
2004
  "type": "option"
2172
2005
  },
2173
2006
  "line": {
2174
- "description": "Line item on the rendered invoice. Repeatable. Two forms: \"<description>\" (qty=1, amount=full --amount) or \"<description>|<quantity>|<amount>\" (all explicit; same decimal units as --amount). Use the three-field form for more than one line: the single-field form gives EVERY line the full --amount, so two such lines sum to twice --amount and trigger the totals-mismatch warning. With three-field lines you may omit --amount entirely and the CLI sums them. The backend charges --amount regardless; --line only shapes the rendered HTML.",
2007
+ "description": "Line item on the rendered invoice. Repeatable. Two forms: \"<description>\" (qty=1, amount=full --amount) or \"<description>|<quantity>|<amount>\" (all explicit; same exact decimal precision as --amount). Use the three-field form for more than one line: the single-field form gives EVERY line the full --amount, so two such lines sum to twice --amount and trigger the totals-mismatch warning. With three-field lines you may omit --amount entirely and the CLI sums them. Every line total (quantity x amount) must fit the selected denomination's precision, whether or not --amount is given; the CLI never rounds it.",
2175
2008
  "name": "line",
2176
2009
  "hasDynamicHelp": false,
2177
2010
  "multiple": true,
2178
2011
  "type": "option"
2179
2012
  },
2180
2013
  "wallet-id": {
2181
- "description": "id_str of a wallet to receive payment. Repeatable. When given, the backend resolves and validates the ids (no wallet read needed — works with an invoice-scope-only agent key). When omitted, the CLI fetches your wallets via the narrow `business_wallets` query, which requires the wallet:read_only scope on an agent key.",
2014
+ "description": "id of a wallet to receive payment, as listed by `wallet list`. Repeatable. When given, the backend resolves and validates the ids (no wallet read needed — works with an invoice-scope-only agent key). When omitted, the CLI fetches your wallets via the narrow `business_wallets` query, which requires the wallet:read_only scope on an agent key.",
2182
2015
  "name": "wallet-id",
2183
2016
  "hasDynamicHelp": false,
2184
2017
  "multiple": true,
2185
2018
  "type": "option"
2186
2019
  },
2187
2020
  "business-id": {
2188
- "description": "Your own business id_str. Optional and redundant — the invoice is always issued as the business you are signed in as, and contacts and wallets always resolve against it. Supplying a different business id is rejected rather than silently ignored; omit the flag unless a script needs to assert which business it is acting as.",
2021
+ "char": "b",
2022
+ "description": "Your own business id. Optional and redundant — the invoice is always issued as the business you are signed in as, and contacts and wallets always resolve against it. Supplying a different business id is rejected rather than silently ignored; omit the flag unless a script needs to assert which business it is acting as.",
2189
2023
  "name": "business-id",
2190
2024
  "hasDynamicHelp": false,
2191
2025
  "multiple": false,
@@ -2225,7 +2059,7 @@
2225
2059
  "description": "List invoices you are the PAYER on — where your business owes the money. Wraps `sender_payments` (the backend's `sender` is the payer). NOTE: invoices you issued with `invoice send` are NOT here — you are the payee on those, so they appear under `invoice received`. Defaults to the first 50 rows; pass --input or --all to override. Filter with --status / --payment-type (repeatable) and the creation-date range --from / --to, which is half-open: --from is inclusive, --to exclusive. There is no --to-email here: an invoice's contact record belongs to the business that issued it, so filtering the invoices you OWE by your own contacts is not possible — use `invoice received --to-email`.",
2226
2060
  "examples": [
2227
2061
  "$ allscale invoice sent",
2228
- "$ allscale invoice sent --business-id <id> --input '{\"limit\":10}'",
2062
+ "$ allscale invoice sent --business-id 65ab1234567890abcdef0123 --input '{\"limit\":10}'",
2229
2063
  "$ allscale invoice sent --all",
2230
2064
  "$ allscale invoice sent --status SENT --status OVERDUE",
2231
2065
  "$ allscale invoice sent --from 2026-07-01 --to 2026-08-01"
@@ -2273,7 +2107,7 @@
2273
2107
  },
2274
2108
  "business-id": {
2275
2109
  "char": "b",
2276
- "description": "Business id_str. Defaults to the logged-in user's business when omitted.",
2110
+ "description": "Business id. Defaults to the logged-in user's business when omitted.",
2277
2111
  "name": "business-id",
2278
2112
  "hasDynamicHelp": false,
2279
2113
  "multiple": false,
@@ -2375,16 +2209,15 @@
2375
2209
  "aliases": [],
2376
2210
  "args": {
2377
2211
  "id": {
2378
- "description": "Invoice id_str (the backend's Payment.id_str)",
2212
+ "description": "Invoice id, as returned by `invoice send` and listed by `invoice list`",
2379
2213
  "name": "id",
2380
2214
  "required": true
2381
2215
  }
2382
2216
  },
2383
- "description": "Update an existing invoice's memo, payer, or (for an overdue invoice) its due date. Only those three fields can be changed; anything else is rejected by the backend.",
2217
+ "description": "Update an existing invoice's memo or (for an overdue invoice) its due date. Although sender_business_id exists in the GraphQL input type, the backend rejects attempts to change it; do not send that field.",
2384
2218
  "examples": [
2385
- "$ allscale invoice update 65ab... --input '{\"memo\":\"corrected memo\"}'",
2386
- "$ allscale invoice update 65ab... --input '{\"due_datetime\":\"2026-12-31T00:00:00Z\"}'",
2387
- "$ allscale invoice update 65ab... --input '{\"sender_business_id\":\"6a288fb5f9b4689d8b09a50a\"}'"
2219
+ "$ allscale invoice update 65ab1234567890abcdef0123 --input '{\"memo\":\"corrected memo\"}'",
2220
+ "OVERDUE only: due_datetime may move FORWARD only (example: 30 days out):\n$ DUE=\"$(node -p 'new Date(Date.now()+30*864e5).toISOString()')\"\n$ allscale invoice update 65ab1234567890abcdef0123 -i \"{\\\"due_datetime\\\":\\\"$DUE\\\"}\""
2388
2221
  ],
2389
2222
  "flags": {
2390
2223
  "json": {
@@ -2429,7 +2262,7 @@
2429
2262
  },
2430
2263
  "input": {
2431
2264
  "char": "i",
2432
- "description": "JSON object of fields to change. Exactly three are accepted: \"memo\" (free text), \"sender_business_id\" (the payer's business id), and \"due_datetime\" (ISO 8601, e.g. 2026-12-31 or 2026-12-31T00:00:00Z — a value without a UTC offset is read as UTC, exactly as `invoice send --due` does). Note that due_datetime only applies to an OVERDUE invoice and may only move FORWARD; extending past now also revives the invoice so buyers can pay again. Any other field is rejected by the backend.",
2265
+ "description": "JSON object of fields to change. Use \"memo\" (free text) or \"due_datetime\" (ISO 8601, e.g. YYYY-MM-DD or YYYY-MM-DDTHH:mm:ssZ — a value without a UTC offset is read as UTC, exactly as `invoice send --due` does). The GraphQL input also exposes \"sender_business_id\", but the backend rejects attempts to change it; do not send that field. Note that due_datetime only applies to an OVERDUE invoice and may only move FORWARD; extending past now also revives the invoice so buyers can pay again. Any other field is rejected by the backend. The example computes the value with POSIX command substitution, which sh/bash/zsh and PowerShell expand but cmd.exe does not; on cmd.exe pass an explicit ISO value.",
2433
2266
  "helpGroup": "REQUIRED",
2434
2267
  "name": "input",
2435
2268
  "required": true,
@@ -2478,8 +2311,8 @@
2478
2311
  "args": {},
2479
2312
  "description": "Create and auto-fund a claim link from this store's enabled auto-payout wallet. Authenticates with the store API key/secret (NOT your login), so it needs a store that has completed Payout onboarding. Idempotent on --reference-id.",
2480
2313
  "examples": [
2481
- "$ ALLSCALE_STORE_API_SECRET=… allscale payout send --api-key ak_… --amount 10 --chain base --reference-id order-1042",
2482
- "$ allscale payout send --payout-api-base https://openapi.allscale.io --api-key ak_… --api-secret --amount 25 --stable-coin USDC --chain polygon --reference-id inv-77 --receiver-email client@example.com"
2314
+ "$ ALLSCALE_STORE_API_SECRET=your_store_api_secret allscale payout send --api-key ak_your_api_key --amount 10 --chain base --reference-id order-1042",
2315
+ "$ allscale payout send --payout-api-base https://openapi.allscale.io --api-key ak_your_api_key --api-secret your_store_api_secret --amount 25 --stable-coin USDC --chain polygon --reference-id inv-77 --receiver-email client@example.com"
2483
2316
  ],
2484
2317
  "flags": {
2485
2318
  "json": {
@@ -2721,11 +2554,11 @@
2721
2554
  "store:create": {
2722
2555
  "aliases": [],
2723
2556
  "args": {},
2724
- "description": "Create a store for the logged-in business. Returns the API key and one-time API secret in stdout; capture the result securely. Business must have checkout enabled; only one sandbox store is allowed per business.",
2557
+ "description": "Create a store for the logged-in business. Returns the API key and an API secret that is shown only once (it cannot be retrieved later — capture it now). The secret itself stays valid for use — it just cannot be displayed again. Business must have checkout enabled; only one sandbox store is allowed per business.",
2725
2558
  "examples": [
2726
2559
  "$ allscale store create --name \"My Shop\"",
2727
2560
  "$ allscale store create --name \"Live Shop\" --live",
2728
- "$ allscale store create --name \"Acme\" --business-id 665f0c..."
2561
+ "$ allscale store create --name \"Acme\" --business-id 665f0c1234567890abcdef12"
2729
2562
  ],
2730
2563
  "flags": {
2731
2564
  "json": {
@@ -2778,6 +2611,7 @@
2778
2611
  "type": "option"
2779
2612
  },
2780
2613
  "business-id": {
2614
+ "char": "b",
2781
2615
  "description": "Owning business id. Defaults to the logged-in business (decoded from the session token). Admins creating for another business must pass this.",
2782
2616
  "name": "business-id",
2783
2617
  "hasDynamicHelp": false,
@@ -2791,7 +2625,7 @@
2791
2625
  "type": "boolean"
2792
2626
  },
2793
2627
  "type": {
2794
- "description": "Store type (backend StoreType int). Omit for the default.",
2628
+ "description": "Store type. 1 = checkout (the default). Payout credential stores are provisioned by Payout onboarding, not by this command. Omit for checkout.",
2795
2629
  "name": "type",
2796
2630
  "hasDynamicHelp": false,
2797
2631
  "multiple": false,
@@ -2830,7 +2664,7 @@
2830
2664
  "aliases": [],
2831
2665
  "args": {
2832
2666
  "id": {
2833
- "description": "Transaction id_str",
2667
+ "description": "Transaction id",
2834
2668
  "name": "id",
2835
2669
  "required": true
2836
2670
  }
@@ -2838,7 +2672,7 @@
2838
2672
  "description": "Fetch one transaction by id. Wraps the `transaction` query.",
2839
2673
  "examples": [
2840
2674
  "$ allscale transaction get 65ab1234567890abcdef0123",
2841
- "$ allscale transaction get 65ab... --select 'id status type direction'"
2675
+ "$ allscale transaction get 65ab1234567890abcdef0123 --select 'id status transaction_type direction'"
2842
2676
  ],
2843
2677
  "flags": {
2844
2678
  "json": {
@@ -2920,11 +2754,15 @@
2920
2754
  "transaction:list": {
2921
2755
  "aliases": [],
2922
2756
  "args": {},
2923
- "description": "List transactions. --scope picks between mine (default; backend my_transactions), business (business_transaction_records), or activities. All scopes default to the first 50 rows; pass --input or --all to override.",
2757
+ "description": "List transactions. --scope picks between mine (default; backend my_transactions), business (business_transaction_records), or activities. All scopes default to the first 50 rows; pass --input or --all to override. Filter by time with --from / --to (all scopes; half-open: --from inclusive, --to exclusive), and with --direction / --transaction-type / --status under --scope business.",
2924
2758
  "examples": [
2925
2759
  "$ allscale transaction list",
2760
+ "$ allscale transaction list --from 2026-07-01 --to 2026-08-01 # exactly July (--to is exclusive)",
2761
+ "$ allscale transaction list --scope business --direction INFLOW --status SUCCESS",
2762
+ "$ allscale transaction list --scope business --transaction-type CLAIM_LINK",
2926
2763
  "$ allscale transaction list --scope business --input '{\"limit\":20}'",
2927
2764
  "$ allscale transaction list --scope activities --skip 0 --limit 25",
2765
+ "$ allscale transaction list --scope activities --from 2026-07-01",
2928
2766
  "$ allscale transaction list --scope activities --all"
2929
2767
  ],
2930
2768
  "flags": {
@@ -3009,12 +2847,50 @@
3009
2847
  "type": "option"
3010
2848
  },
3011
2849
  "limit": {
3012
- "description": "Pagination limit (activities scope only). Defaults to 50; --all requests one unpaginated response.",
2850
+ "description": "Positive pagination limit (activities scope only). Defaults to 50; --all requests one unpaginated response.",
2851
+ "exclusive": [
2852
+ "all"
2853
+ ],
3013
2854
  "name": "limit",
3014
2855
  "hasDynamicHelp": false,
3015
2856
  "multiple": false,
3016
2857
  "type": "option"
3017
2858
  },
2859
+ "from": {
2860
+ "description": "Only transactions at or after this time. INCLUSIVE. Filters created_at under --scope mine/business and transaction_time under --scope activities. Accepts a date (2026-08-05), a naive datetime (2026-08-05T12:30:00, read as UTC), or an offset datetime (2026-08-05T20:30:00+08:00); the CLI converts to UTC and pads it to the backend's required YYYY-MM-DDTHH:MM:SS.ffffffZ.",
2861
+ "name": "from",
2862
+ "hasDynamicHelp": false,
2863
+ "multiple": false,
2864
+ "type": "option"
2865
+ },
2866
+ "to": {
2867
+ "description": "Only transactions strictly before this time. EXCLUSIVE — --from 2026-07-01 --to 2026-08-01 is exactly July and consecutive ranges never overlap. Filters created_at under --scope mine/business and transaction_time under --scope activities. Accepts a date (2026-08-05), a naive datetime (2026-08-05T12:30:00, read as UTC), or an offset datetime (2026-08-05T20:30:00+08:00); the CLI converts to UTC and pads it to the backend's required YYYY-MM-DDTHH:MM:SS.ffffffZ.",
2868
+ "name": "to",
2869
+ "hasDynamicHelp": false,
2870
+ "multiple": false,
2871
+ "type": "option"
2872
+ },
2873
+ "direction": {
2874
+ "description": "Only transactions in this direction (--scope business only). Accepted: INFLOW, OUTFLOW (case-insensitive), or a raw integer (1=INFLOW, -1=OUTFLOW).",
2875
+ "name": "direction",
2876
+ "hasDynamicHelp": false,
2877
+ "multiple": false,
2878
+ "type": "option"
2879
+ },
2880
+ "transaction-type": {
2881
+ "description": "Only transactions of this type (--scope business only). Accepted: PAYMENT, REWARD, WITHDRAWAL, SERVICE_FEE, CHECKOUT_INTENT_EPHEMERAL, CHECKOUT_INTENT, ON_CHAIN_TX, CHECKOUT_INTENT_EPHEMERAL_REFUND, CHECKOUT_INTENT_SERVICE_FEE, CHECKOUT_INTENT_EPHEMERAL_NET_INCOME, CLAIM_LINK (case-insensitive), or a raw integer.",
2882
+ "name": "transaction-type",
2883
+ "hasDynamicHelp": false,
2884
+ "multiple": false,
2885
+ "type": "option"
2886
+ },
2887
+ "status": {
2888
+ "description": "Only transactions in this status (--scope business only). Accepted: NEW, SUCCESS, FAILED, CONFIRMING (case-insensitive), or a raw integer. These are transaction statuses, not invoice statuses.",
2889
+ "name": "status",
2890
+ "hasDynamicHelp": false,
2891
+ "multiple": false,
2892
+ "type": "option"
2893
+ },
3018
2894
  "select": {
3019
2895
  "char": "s",
3020
2896
  "description": "Override the default selection set with a GraphQL fragment. Default (--scope mine, --scope business): 'count items { created_at updated_at id owner_type owner_business_id owner_employee_id transaction_type direction reference_type payment_id counterparty_business_id counterparty_employee_id counterparty_name payment_type currency amount_cents amount_coins status memo transaction_time tx_hash tx_block_chain tx_from tx_to tx_block_number total_fee network_fee service_fee }'. Default (--scope activities): 'count items { transaction_id amount_coins amount_cents transaction_time created_at transaction_status payment_id transaction_type direction wallet_address tx_hash tx_block_chain tx_block_number memo payment_type currency total_fee network_fee service_fee counterparty_business_id claim_link_id }'. Pass --show-doc to print the exact GraphQL document that gets sent.",
@@ -3139,13 +3015,12 @@
3139
3015
  "wallet:send": {
3140
3016
  "aliases": [],
3141
3017
  "args": {},
3142
- "description": "Withdraw a stablecoin from your AllScale wallet to an EVM address. Confirms in the browser by default; the CLI prints the transaction hash and explorer URL when the network accepts the transfer. Accounts with Turnkey email-OTP enabled and no passkey can sign without a browser via --headless. Requires an existing legacy JWT session, which this build cannot create — use the browser-bridge flow instead.",
3018
+ "description": "Withdraw a stablecoin from your AllScale wallet to an EVM address. The transfer is confirmed in the browser; the CLI prints the transaction hash and explorer URL only after its on-chain receipt confirms.",
3143
3019
  "examples": [
3144
- "$ allscale wallet send --idempotency-key order-1042 --to 0xabc... --amount 100 --chain ethereum",
3145
- "$ allscale wallet send --idempotency-key order-1043 --to 0xabc... --amount 50 --chain polygon --stable-coin USDC",
3146
- "$ allscale wallet send --idempotency-key order-1044 --to 0xabc... --amount 1 --chain base --bridge-timeout 300000",
3147
- "$ allscale wallet send --idempotency-key order-1045 --to 0xabc... --amount 1 --chain bsc # over SSH: open the printed URL on any device, paste the tx hash back",
3148
- "$ allscale wallet send --to 0xabc... --amount 1 --chain base --headless --otp-stdin --idempotency-key order-1042 # reuse the same key only when retrying this operation"
3020
+ "$ allscale wallet send --idempotency-key order-1042 --to 0x1234567890abcdef1234567890abcdef12345678 --amount 100 --chain ethereum",
3021
+ "$ allscale wallet send --idempotency-key order-1043 --to 0x1234567890abcdef1234567890abcdef12345678 --amount 50 --chain polygon --stable-coin USDC",
3022
+ "$ allscale wallet send --idempotency-key order-1044 --to 0x1234567890abcdef1234567890abcdef12345678 --amount 1 --chain base --bridge-timeout 240000",
3023
+ "$ allscale wallet send --idempotency-key order-1045 --to 0x1234567890abcdef1234567890abcdef12345678 --amount 1 --chain bsc # over SSH: open the printed URL on any device, paste the full result JSON back"
3149
3024
  ],
3150
3025
  "flags": {
3151
3026
  "json": {
@@ -3189,7 +3064,7 @@
3189
3064
  "type": "boolean"
3190
3065
  },
3191
3066
  "to": {
3192
- "description": "Recipient EVM address (0x 40-hex).",
3067
+ "description": "Recipient EVM address (0x followed by 40 hex characters).",
3193
3068
  "helpGroup": "REQUIRED",
3194
3069
  "name": "to",
3195
3070
  "required": true,
@@ -3245,62 +3120,18 @@
3245
3120
  "type": "option"
3246
3121
  },
3247
3122
  "bridge-timeout": {
3248
- "description": "Shorten the confirmation deadline, in milliseconds. By default the CLI waits the full broker op TTL (~5 min, shared by the loopback and paste channels); pass this to abort sooner. (Browser-bridge only — not valid with --headless.)",
3249
- "exclusive": [
3250
- "headless"
3251
- ],
3123
+ "description": "Shorten the confirmation deadline, in milliseconds. By default the CLI waits the broker op's remaining life (~5 min, shared by the loopback and paste channels); pass this to abort sooner. The browser only returns after the on-chain receipt confirms (it may poll for up to ~2 min after signing), so a shortened deadline must budget for the signing ceremony PLUS that receipt wait. It can only shorten the wait a value above the op's remaining life is clamped to it, because the confirmation link cannot outlive its broker op. If the server's response carries no timestamp to measure that life against, the CLI says so on stderr and bounds the wait instead: with no --bridge-timeout a withdraw waits up to 240s (budgeting the signing ceremony plus the receipt wait) and may be shortened by the best-effort bound from the stated expiry; an explicit value is honoured up to that same bound (clamped to 60s-15min), or 15min when the expiry itself is unreadable.",
3252
3124
  "name": "bridge-timeout",
3253
3125
  "hasDynamicHelp": false,
3254
3126
  "multiple": false,
3255
3127
  "type": "option"
3256
3128
  },
3257
3129
  "browser-base": {
3258
- "description": "Override the base URL of the printed /cli confirmation link (defaults to the session API base). (Browser-bridge only not valid with --headless.)",
3259
- "exclusive": [
3260
- "headless"
3261
- ],
3130
+ "description": "Override the base URL of the printed /cli confirmation link (defaults to the session API base). Accepts only the trusted AllScale origin; invalid values report the allowed origin.",
3262
3131
  "name": "browser-base",
3263
3132
  "hasDynamicHelp": false,
3264
3133
  "multiple": false,
3265
3134
  "type": "option"
3266
- },
3267
- "headless": {
3268
- "description": "Sign without a browser via Turnkey email-OTP (accounts with email-OTP enabled and no passkey; passkey accounts must use the browser flow). Supply the OTP via --otp-stdin (recommended for automation) or interactively; --otp / ALLSCALE_OTP work ONLY combined with --otp-id. This removes the in-browser human confirmation, so use it only on a trusted machine. Requires an existing legacy JWT session, which this build cannot create — use the browser-bridge flow instead.",
3269
- "name": "headless",
3270
- "allowNo": false,
3271
- "type": "boolean"
3272
- },
3273
- "otp": {
3274
- "dependsOn": [
3275
- "headless"
3276
- ],
3277
- "description": "Turnkey signing OTP code (with --headless). Only works combined with --otp-id (an externally-triggered flow): without --otp-id the CLI emails a FRESH code at run time, so any pre-set --otp / ALLSCALE_OTP value is stale and fails with 'Invalid or expired OTP code'. For automation prefer --otp-stdin (CLI emails; pipe the fresh code back). Passing --otp as a flag also exposes it in shell history AND the process list (ps / /proc/<pid>/cmdline).",
3278
- "exclusive": [
3279
- "otp-stdin"
3280
- ],
3281
- "name": "otp",
3282
- "hasDynamicHelp": false,
3283
- "multiple": false,
3284
- "type": "option"
3285
- },
3286
- "otp-stdin": {
3287
- "dependsOn": [
3288
- "headless"
3289
- ],
3290
- "description": "Read the Turnkey signing OTP as one line from stdin (with --headless).",
3291
- "name": "otp-stdin",
3292
- "allowNo": false,
3293
- "type": "boolean"
3294
- },
3295
- "otp-id": {
3296
- "dependsOn": [
3297
- "headless"
3298
- ],
3299
- "description": "Skip the OTP-send step and reuse a prior Turnkey otpId (with --headless). The OTP code itself is still required via --otp / ALLSCALE_OTP / --otp-stdin.",
3300
- "name": "otp-id",
3301
- "hasDynamicHelp": false,
3302
- "multiple": false,
3303
- "type": "option"
3304
3135
  }
3305
3136
  },
3306
3137
  "hasDynamicHelp": false,
@@ -3325,5 +3156,5 @@
3325
3156
  ]
3326
3157
  }
3327
3158
  },
3328
- "version": "0.1.0"
3159
+ "version": "1.0.1"
3329
3160
  }