@kashdao/cli 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +96 -0
- package/README.md +78 -27
- package/dist/{account-CVJZ3HVT.js → account-QHPOIZ6I.js} +9 -9
- package/dist/{auth-V5WVZBCO.js → auth-TE7BCN5Q.js} +12 -12
- package/dist/{auth-V5WVZBCO.js.map → auth-TE7BCN5Q.js.map} +1 -1
- package/dist/{chunk-MQUJG224.js → chunk-2HFKULGG.js} +2 -2
- package/dist/{chunk-BVFDUI73.js → chunk-2SR77BYA.js} +10 -9
- package/dist/chunk-2SR77BYA.js.map +1 -0
- package/dist/chunk-6QSTFU4R.js +9 -0
- package/dist/{chunk-KAM6KOFD.js.map → chunk-6QSTFU4R.js.map} +1 -1
- package/dist/{chunk-R4NGUZKN.js → chunk-6UU43MF5.js} +5 -3
- package/dist/chunk-6UU43MF5.js.map +1 -0
- package/dist/{chunk-A3IH5HUV.js → chunk-AROIP4EQ.js} +3 -3
- package/dist/chunk-AROIP4EQ.js.map +1 -0
- package/dist/{chunk-QALDZHTX.js → chunk-H2QGFDZP.js} +9 -11
- package/dist/chunk-H2QGFDZP.js.map +1 -0
- package/dist/{chunk-L4AFV2HK.js → chunk-IOLKGV45.js} +3 -3
- package/dist/chunk-IOLKGV45.js.map +1 -0
- package/dist/{chunk-KADB3OKE.js → chunk-VFBQVUL4.js} +2 -2
- package/dist/{chunk-X2VM6VKM.js → chunk-WL7XUYPH.js} +19 -20
- package/dist/chunk-WL7XUYPH.js.map +1 -0
- package/dist/{chunk-N26VGVGB.js → chunk-ZSMI63N3.js} +109 -37
- package/dist/chunk-ZSMI63N3.js.map +1 -0
- package/dist/client-REDY6K7J.js +14 -0
- package/dist/{completion-PK74S3EZ.js → completion-MXTJYK3D.js} +4 -4
- package/dist/{config-T5U6E6KU.js → config-TIJXUJ5N.js} +32 -23
- package/dist/config-TIJXUJ5N.js.map +1 -0
- package/dist/{config-store-UTFDUCKW.js → config-store-INJY4DDT.js} +6 -4
- package/dist/{docs-VA7CJ2ZF.js → docs-M5RZAQ6P.js} +5 -5
- package/dist/{eoa-RL3NLN3H.js → eoa-N55DYIN3.js} +18 -16
- package/dist/eoa-N55DYIN3.js.map +1 -0
- package/dist/{errors-2NNXRNES.js → errors-SMR7X5DU.js} +2 -2
- package/dist/{explain-3SPMWDYX.js → explain-HIDEZVLV.js} +31 -8
- package/dist/explain-HIDEZVLV.js.map +1 -0
- package/dist/{global-options-WFQ6CEFK.js → global-options-YONBFB7N.js} +4 -4
- package/dist/{health-2KD6XUTJ.js → health-LSJKP3NH.js} +9 -9
- package/dist/index.js +31 -28
- package/dist/index.js.map +1 -1
- package/dist/{markets-H6EK2XDE.js → markets-AGL6Q2ES.js} +23 -12
- package/dist/markets-AGL6Q2ES.js.map +1 -0
- package/dist/{output-FGTXXZEQ.js → output-M4NFSWGW.js} +4 -4
- package/dist/{portfolio-R63D5QB2.js → portfolio-RBRVUEYP.js} +11 -11
- package/dist/{portfolio-R63D5QB2.js.map → portfolio-RBRVUEYP.js.map} +1 -1
- package/dist/{protocol-ZB6R25IM.js → protocol-WYQAKN6N.js} +18 -16
- package/dist/protocol-WYQAKN6N.js.map +1 -0
- package/dist/{quote-OF6BTAPQ.js → quote-6CNHU36A.js} +10 -10
- package/dist/redeem-JESSTZ46.js +108 -0
- package/dist/redeem-JESSTZ46.js.map +1 -0
- package/dist/{schema-3HQN4RFL.js → schema-6EXADLRW.js} +12 -8
- package/dist/{schema-3HQN4RFL.js.map → schema-6EXADLRW.js.map} +1 -1
- package/dist/{setup-IIPQXLPL.js → setup-H46GTSVW.js} +8 -8
- package/dist/{trace-WMO3KDHS.js → trace-VDBWWTYL.js} +10 -10
- package/dist/{trade-YBCI6XD2.js → trade-FTSY23QG.js} +10 -10
- package/dist/trade-FTSY23QG.js.map +1 -0
- package/dist/{version-WB2ACHMU.js → version-JTLF3464.js} +7 -7
- package/dist/{version-check-PRAVPFC6.js → version-check-7JGBY6FZ.js} +4 -4
- package/dist/{webhooks-DVS55O4T.js → webhooks-JEWNX5HZ.js} +10 -10
- package/dist/{webhooks-DVS55O4T.js.map → webhooks-JEWNX5HZ.js.map} +1 -1
- package/dist/{with-retry-2MD2CNK2.js → with-retry-DYSHNLG7.js} +12 -6
- package/dist/with-retry-DYSHNLG7.js.map +1 -0
- package/package.json +5 -5
- package/dist/chunk-A3IH5HUV.js.map +0 -1
- package/dist/chunk-BVFDUI73.js.map +0 -1
- package/dist/chunk-KAM6KOFD.js +0 -9
- package/dist/chunk-L4AFV2HK.js.map +0 -1
- package/dist/chunk-N26VGVGB.js.map +0 -1
- package/dist/chunk-QALDZHTX.js.map +0 -1
- package/dist/chunk-R4NGUZKN.js.map +0 -1
- package/dist/chunk-X2VM6VKM.js.map +0 -1
- package/dist/client-75FVHGOH.js +0 -14
- package/dist/config-T5U6E6KU.js.map +0 -1
- package/dist/eoa-RL3NLN3H.js.map +0 -1
- package/dist/explain-3SPMWDYX.js.map +0 -1
- package/dist/markets-H6EK2XDE.js.map +0 -1
- package/dist/protocol-ZB6R25IM.js.map +0 -1
- package/dist/trade-YBCI6XD2.js.map +0 -1
- package/dist/with-retry-2MD2CNK2.js.map +0 -1
- /package/dist/{account-CVJZ3HVT.js.map → account-QHPOIZ6I.js.map} +0 -0
- /package/dist/{chunk-MQUJG224.js.map → chunk-2HFKULGG.js.map} +0 -0
- /package/dist/{chunk-KADB3OKE.js.map → chunk-VFBQVUL4.js.map} +0 -0
- /package/dist/{client-75FVHGOH.js.map → client-REDY6K7J.js.map} +0 -0
- /package/dist/{completion-PK74S3EZ.js.map → completion-MXTJYK3D.js.map} +0 -0
- /package/dist/{config-store-UTFDUCKW.js.map → config-store-INJY4DDT.js.map} +0 -0
- /package/dist/{docs-VA7CJ2ZF.js.map → docs-M5RZAQ6P.js.map} +0 -0
- /package/dist/{errors-2NNXRNES.js.map → errors-SMR7X5DU.js.map} +0 -0
- /package/dist/{global-options-WFQ6CEFK.js.map → global-options-YONBFB7N.js.map} +0 -0
- /package/dist/{health-2KD6XUTJ.js.map → health-LSJKP3NH.js.map} +0 -0
- /package/dist/{output-FGTXXZEQ.js.map → output-M4NFSWGW.js.map} +0 -0
- /package/dist/{quote-OF6BTAPQ.js.map → quote-6CNHU36A.js.map} +0 -0
- /package/dist/{setup-IIPQXLPL.js.map → setup-H46GTSVW.js.map} +0 -0
- /package/dist/{trace-WMO3KDHS.js.map → trace-VDBWWTYL.js.map} +0 -0
- /package/dist/{version-WB2ACHMU.js.map → version-JTLF3464.js.map} +0 -0
- /package/dist/{version-check-PRAVPFC6.js.map → version-check-7JGBY6FZ.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/trade/index.ts","../src/commands/trade/buy.ts","../src/commands/trade/place.ts","../src/commands/trade/confirm.ts","../src/commands/trade/list.ts","../src/commands/trade/sell.ts","../src/commands/trade/status.ts"],"sourcesContent":["/**\n * `kash trade` — place and manage trades.\n *\n * Every subcommand requires authentication: trades are written to a\n * specific actor (user or organization) determined by the API key's\n * scope. The buy/sell helpers honor `--wait` so scripts can block on\n * settlement without rolling their own poller.\n */\n\nimport { Command } from 'commander';\n\nimport { buyCommand } from './buy.js';\nimport { confirmCommand } from './confirm.js';\nimport { listTradesCommand } from './list.js';\nimport { sellCommand } from './sell.js';\nimport { statusCommand } from './status.js';\n\nexport const tradeCommand = new Command('trade')\n .description('Place trades and inspect their status.')\n .addCommand(buyCommand)\n .addCommand(sellCommand)\n .addCommand(statusCommand)\n .addCommand(listTradesCommand)\n .addCommand(confirmCommand);\n","/**\n * `kash trade buy <marketId>` — purchase outcome tokens.\n */\n\nimport { Command } from 'commander';\n\nimport { readGlobals } from '../../utils/global-options.js';\n\nimport { placeTrade, type PlaceTradeOptions } from './place.js';\n\nexport const buyCommand = new Command('buy')\n .description('Buy outcome tokens for a market.')\n .argument('<marketId>', 'market UUID')\n .requiredOption('-o, --outcome <index>', 'outcome index (0-based)')\n .requiredOption('-a, --amount <usdc>', 'USDC amount as a decimal (max 6 fractional digits)')\n .option('--wait', 'block until the trade reaches a terminal state')\n .option(\n '--wait-timeout-ms, --timeout <ms>',\n 'wait timeout in milliseconds (default 60000) — distinct from the global --timeout-ms (per-HTTP-request)'\n )\n .option(\n '--poll-interval-ms, --poll-interval <ms>',\n 'wait poll interval in milliseconds (default 2000)'\n )\n .option('--idempotency-key <key>', 'sets the Idempotency-Key HTTP header')\n .option(\n '--auto-idempotency-key',\n 'auto-generate an Idempotency-Key (UUID v4) and surface it in the response'\n )\n .option('--client-request-id <id>', 'sets the body-level clientRequestId for replay safety')\n .option(\n '--dry-run',\n 'preview the request without sending — emits the would-be body and resolved headers'\n )\n .addHelpText(\n 'after',\n `\nExamples:\n $ kash trade buy 9f0b... --outcome 0 --amount 10\n $ kash trade buy 9f0b... --outcome 1 --amount 25 --wait\n $ kash trade buy 9f0b... --outcome 0 --amount 5 --auto-idempotency-key --json --quiet\n $ kash trade buy 9f0b... --outcome 0 --amount 10 --dry-run --json\n`\n )\n .action(async (marketId: string, options: PlaceTradeOptions, cmd: Command) => {\n const globals = readGlobals(cmd);\n await placeTrade({ marketId, side: 'buy', options, globals });\n });\n","/**\n * Shared \"place a trade\" implementation for `buy` and `sell`.\n *\n * The two commands differ only in the `side` field, so the heavy\n * lifting — input validation, idempotency wiring, optional polling,\n * 202-confirmation rendering — lives here.\n */\n\nimport ora from 'ora';\n\nimport { CliValidationError, toCliError } from '../../errors.js';\nimport { buildClient } from '../../utils/client.js';\nimport { capitalize, colorStatus, formatUsdcDecimal, shortId } from '../../utils/formatting.js';\nimport { parsePositiveInt } from '../../utils/global-options.js';\nimport { isQuiet, log, print, printJson, style } from '../../utils/output.js';\nimport { parseOutcomeIndex, validateUsdcDecimalShape } from '../../utils/trade-input.js';\n\nimport type { GlobalOptions } from '../../utils/global-options.js';\nimport type { KashClient, TradeResource, TradeSide } from '@kashdao/sdk';\n\nexport type PlaceTradeOptions = {\n outcome: string;\n amount: string;\n wait?: boolean;\n idempotencyKey?: string;\n autoIdempotencyKey?: boolean;\n clientRequestId?: string;\n timeout?: string;\n pollInterval?: string;\n dryRun?: boolean;\n};\n\nexport type PlaceTradeArgs = {\n marketId: string;\n side: TradeSide;\n options: PlaceTradeOptions;\n globals: GlobalOptions;\n};\n\nexport async function placeTrade({\n marketId,\n side,\n options,\n globals,\n}: PlaceTradeArgs): Promise<void> {\n const json = globals.json;\n // marketId is enforced as a required positional by Commander; no\n // defensive check needed here. Same for --outcome / --amount, which\n // are `requiredOption`s — but we re-validate their *values* (range,\n // format) below since Commander only enforces presence.\n // Markets carry 2-8 outcomes (per @kashdao/constants), so a valid\n // outcome index is in [0, 7]. Bouncing out-of-range values here\n // gives the agent INVALID_INPUT with a clear field name, instead of\n // a server-side `KashValidationError` half a round-trip later.\n const outcomeIndex = parseOutcomeIndex(options.outcome);\n const amount = options.amount?.trim();\n if (!amount) {\n throw new CliValidationError(\n '--amount is required.',\n 'Use a USDC decimal like \"10\" or \"12.50\".'\n );\n }\n // Shape-only validator — public-API mode ships the decimal string\n // verbatim to the SDK rather than converting to atomic units, so we\n // only need the shape pin here. `validateUsdcDecimalShape` is the\n // shared primitive that backs `decimalToAtomicUsdc` in SA/EOA mode.\n validateUsdcDecimalShape(amount, 'amount');\n\n const timeoutMs = options.timeout ? parsePositiveInt(options.timeout, 'timeout') : 60_000;\n const pollIntervalMs = options.pollInterval\n ? parsePositiveInt(options.pollInterval, 'poll-interval')\n : 2_000;\n\n // Resolve idempotency key. Explicit --idempotency-key wins; otherwise\n // --auto-idempotency-key generates a UUID v4. The resolved value is\n // surfaced on the response so an agent that retries a transient\n // failure can re-use the same key.\n let resolvedIdempotencyKey = options.idempotencyKey;\n if (resolvedIdempotencyKey === undefined && options.autoIdempotencyKey) {\n // `@kashdao/cli` is a published npm package and cannot depend on\n // workspace-only `@kashdao/utils`. `crypto.randomUUID()` is the\n // right primitive here.\n // eslint-disable-next-line no-restricted-properties\n resolvedIdempotencyKey = crypto.randomUUID();\n if (!json) {\n log.detail('Generated Idempotency-Key', resolvedIdempotencyKey);\n }\n }\n\n // --dry-run short-circuits before the network call. We still validate\n // inputs (above) and resolve the idempotency key so the envelope\n // accurately reflects what a real invocation would send. Crucially,\n // we do NOT instantiate the SDK client — `buildClient` would fail on\n // a missing API key, but a dry-run preview should work without auth\n // so agents can plan trades against an unconfigured profile.\n if (options.dryRun === true) {\n const wouldSend = {\n marketId,\n outcomeIndex,\n amount,\n side,\n ...(options.clientRequestId === undefined\n ? {}\n : { clientRequestId: options.clientRequestId }),\n };\n const envelope = {\n wouldSend,\n idempotencyKey: resolvedIdempotencyKey ?? null,\n endpoint: { method: 'POST' as const, path: '/v1/trades' as const },\n };\n if (json) {\n printJson(envelope);\n return;\n }\n print('');\n print(style.bold('Dry run — no request sent.'));\n print(` ${style.dim('Endpoint ')} POST /v1/trades`);\n print(` ${style.dim('Market ')} ${marketId}`);\n print(` ${style.dim('Side ')} ${side}`);\n print(` ${style.dim('Outcome ')} ${String(outcomeIndex)}`);\n print(` ${style.dim('Amount ')} ${amount} USDC`);\n if (options.clientRequestId !== undefined) {\n print(` ${style.dim('Client req ')} ${options.clientRequestId}`);\n }\n if (resolvedIdempotencyKey !== undefined) {\n print(` ${style.dim('Idem. key ')} ${resolvedIdempotencyKey}`);\n }\n print('');\n print(style.dim('Re-run without --dry-run to submit.'));\n return;\n }\n\n let response;\n try {\n const { client } = await buildClient({ requireAuth: true, globals });\n response = await client.trades.create(\n {\n marketId,\n outcomeIndex,\n amount,\n side,\n ...(options.clientRequestId === undefined\n ? {}\n : { clientRequestId: options.clientRequestId }),\n },\n resolvedIdempotencyKey === undefined ? {} : { idempotencyKey: resolvedIdempotencyKey }\n );\n\n // Attach the idempotency key (if we generated one) so agents that\n // retry can re-use it. Empty when neither flag was provided.\n const augment = <T extends object>(payload: T): T & { idempotencyKey?: string } =>\n resolvedIdempotencyKey === undefined\n ? (payload as T & { idempotencyKey?: string })\n : { ...payload, idempotencyKey: resolvedIdempotencyKey };\n\n // The flat result (TradeResource + idempotent + optional confirmation) —\n // see SDK's `TradeCreateResult`.\n if (response.confirmation) {\n if (json) {\n printJson(augment(response));\n return;\n }\n log.warn(\n 'High-value trade — server returned 202. Confirm with `kash trade confirm <id> <token>`.'\n );\n print(` ${style.dim('Trade id ')} ${response.id}`);\n print(` ${style.dim('Status ')} ${colorStatus(response.status)}`);\n print(` ${style.dim('Token ')} ${response.confirmation.token}`);\n print(` ${style.dim('Expires ')} ${response.confirmation.expiresAt}`);\n return;\n }\n\n if (!options.wait) {\n if (json) {\n printJson(augment(response));\n return;\n }\n log.success(\n `${capitalize(side)} accepted${response.idempotent ? ' (idempotent replay)' : ''}: ${shortId(response.id)}`\n );\n print(` ${style.dim('Trade id ')} ${response.id}`);\n print(` ${style.dim('Status ')} ${colorStatus(response.status)}`);\n print(` ${style.dim('Amount ')} ${formatUsdcDecimal(response.amount)}`);\n log.info(\"Run 'kash trade status <id> --poll' to follow it to completion.\");\n return;\n }\n\n // --wait: poll until terminal. Partial-completion guard: the\n // `client.trades.create` above already succeeded — `response.id`\n // is server-assigned and the trade exists in the API. If the\n // poll fails (timeout, transient network), surface the id so\n // the operator can resume via `kash trade status <id> --poll`\n // instead of losing the pointer to a created-but-unconfirmed\n // trade. Symmetric to the SA-mode userop / EOA-mode tx hash\n // partial-completion seams.\n let completed: TradeResource;\n try {\n completed = await waitForCompletion(client, response, {\n timeoutMs,\n pollIntervalMs,\n json,\n });\n } catch (waitCause) {\n if (json) {\n printJson({\n id: response.id,\n status: response.status,\n waited: false,\n partial: true,\n });\n } else {\n log.warn(\n `Trade created but poll did not reach a terminal state. Resume with: kash trade status ${response.id} --poll`\n );\n }\n throw waitCause;\n }\n\n if (json) {\n printJson(augment({ ...completed, idempotent: response.idempotent, waited: true }));\n return;\n }\n renderTerminal(completed);\n } catch (cause) {\n throw toCliError(cause);\n }\n}\n\nasync function waitForCompletion(\n client: KashClient,\n initial: TradeResource,\n opts: { timeoutMs: number; pollIntervalMs: number; json: boolean }\n): Promise<TradeResource> {\n // Wire SIGINT/SIGTERM to abort the in-flight poll cleanly. The\n // shape mirrors `trade status --poll`: register on entry, unwire\n // in the finally block. Without this, Ctrl-C during a 60s wait\n // leaves the in-flight HTTP request stranded — process tear-down\n // works via the global SIGPIPE handler but the SDK fetch never\n // sees the cancellation.\n const ac = new AbortController();\n const onSig = (): void => ac.abort();\n process.on('SIGINT', onSig);\n process.on('SIGTERM', onSig);\n try {\n if (opts.json || isQuiet()) {\n return await client.trades.waitForCompletion(initial.id, {\n timeoutMs: opts.timeoutMs,\n pollIntervalMs: opts.pollIntervalMs,\n signal: ac.signal,\n });\n }\n const spinner = ora({\n text: `Waiting for trade ${shortId(initial.id)}…`,\n stream: process.stderr,\n }).start();\n try {\n const completed = await client.trades.waitForCompletion(initial.id, {\n timeoutMs: opts.timeoutMs,\n pollIntervalMs: opts.pollIntervalMs,\n signal: ac.signal,\n onStatus: (trade) => {\n spinner.text = `Trade ${shortId(trade.id)} — status: ${trade.status}`;\n },\n });\n if (completed.status === 'completed') {\n spinner.succeed(`Trade ${shortId(completed.id)} completed.`);\n } else {\n spinner.fail(`Trade ${shortId(completed.id)} ${completed.status}.`);\n }\n return completed;\n } catch (cause) {\n spinner.fail('Polling stopped.');\n throw cause;\n }\n } finally {\n process.off('SIGINT', onSig);\n process.off('SIGTERM', onSig);\n }\n}\n\nfunction renderTerminal(trade: TradeResource): void {\n print('');\n print(` ${style.dim('Trade id ')} ${trade.id}`);\n print(` ${style.dim('Status ')} ${colorStatus(trade.status)}`);\n print(` ${style.dim('Amount ')} ${formatUsdcDecimal(trade.amount)}`);\n // Use explicit null checks for nullable fields rather than truthy\n // checks. `if (trade.errorMessage)` would silently hide a literal\n // empty-string error (`\"\"`), which the API can return for \"errored\n // but with no human message.\" Same for `tokensOut`/`txHash`: `\"0\"`\n // is a valid string but `Boolean(\"0\") === true`, so truthy checks\n // happen to work today but are accidentally correct.\n if (trade.txHash !== null) {\n print(` ${style.dim('Tx hash ')} ${trade.txHash}`);\n }\n if (trade.tokensOut !== null) {\n print(` ${style.dim('Tokens out ')} ${trade.tokensOut}`);\n }\n if (trade.errorMessage !== null || trade.errorCode !== null) {\n print(\n ` ${style.dim('Error ')} [${trade.errorCode ?? 'UNKNOWN'}] ${trade.errorMessage ?? ''}`\n );\n log.info(`Inspect the full timeline: kash trace ${trade.correlationId}`);\n }\n}\n\n// `parseOutcomeIndex` and `validateUsdcDecimalShape` live in\n// `utils/trade-input.ts` — single source of truth across SA, EOA,\n// and public-API modes. Imports at the top of this file.\n","/**\n * `kash trade confirm <id> [token]` — confirm a high-value trade.\n *\n * High-value trades come back from `POST /v1/trades` with HTTP 202 and\n * a one-time confirmation token. This command POSTs that token to\n * `/v1/trades/{id}/confirm` to release the trade for execution.\n *\n * Token input is sensitive (a captured token plus the trade id is\n * sufficient to release real money), so we support three input\n * paths that don't taint shell history:\n *\n * 1. Positional `<token>` — convenient, but lands in `~/.bash_history`.\n * Kept for backward compat.\n * 2. `--token-stdin` — pipe a token (the natural shape after\n * capturing the 202 envelope into a file or secret store).\n * 3. Interactive prompt — TTY only, prompt goes to stderr so the\n * stdout pipe stays clean.\n */\n\nimport { Command } from 'commander';\n\nimport { CliValidationError, toCliError } from '../../errors.js';\nimport { buildClient } from '../../utils/client.js';\nimport { colorStatus, formatUsdcDecimal } from '../../utils/formatting.js';\nimport { readGlobals } from '../../utils/global-options.js';\nimport { log, print, printJson, style } from '../../utils/output.js';\n\ntype ConfirmOptions = {\n tokenStdin?: boolean;\n};\n\nexport const confirmCommand = new Command('confirm')\n .description('Confirm a high-value trade using its one-time token.')\n .argument('<id>', 'trade UUID')\n .argument('[token]', 'confirmation token (omit to read from stdin or prompt)')\n .option('--token-stdin', 'read the confirmation token from stdin')\n .addHelpText(\n 'after',\n `\nExamples:\n # Interactive prompt (no shell history footprint).\n $ kash trade confirm 9f0b...\n\n # Pipe from a captured token.\n $ cat token.txt | kash trade confirm 9f0b... --token-stdin\n\n # Positional (convenient but shows up in ~/.bash_history).\n $ kash trade confirm 9f0b... eyJ...\n $ kash trade confirm 9f0b... eyJ... --json --quiet\n`\n )\n .action(\n async (id: string, positional: string | undefined, options: ConfirmOptions, cmd: Command) => {\n const globals = readGlobals(cmd);\n const token = await resolveToken(positional, options);\n\n let trade;\n try {\n const { client } = await buildClient({ requireAuth: true, globals });\n trade = await client.trades.confirm(id, { token });\n } catch (cause) {\n throw toCliError(cause);\n }\n\n if (globals.json) {\n printJson(trade);\n return;\n }\n\n log.success(`Trade ${trade.id} confirmed.`);\n print(` ${style.dim('Status ')} ${colorStatus(trade.status)}`);\n print(` ${style.dim('Amount ')} ${formatUsdcDecimal(trade.amount)}`);\n log.info(\"Run 'kash trade status <id> --poll' to follow it to completion.\");\n }\n );\n\nasync function resolveToken(\n positional: string | undefined,\n options: ConfirmOptions\n): Promise<string> {\n // Mutual-exclusion guard: `kash trade confirm <id> <token> --token-stdin`\n // would silently take the positional and drop the piped value.\n // That's a real footgun on a real-money path — refuse to guess.\n if (positional !== undefined && positional !== '' && options.tokenStdin === true) {\n throw new CliValidationError(\n 'Pass either the positional [token] OR --token-stdin, not both.',\n 'The positional token would silently win and the piped value would be dropped.',\n 'token'\n );\n }\n if (positional !== undefined && positional !== '') {\n return positional;\n }\n if (options.tokenStdin === true) {\n // BOM-aware read — see `utils/stdin.ts` for rationale.\n const { readStdinTrimmed } = await import('../../utils/stdin.js');\n return readStdinTrimmed();\n }\n if (process.stdin.isTTY) {\n // Use the masked-input prompt — confirmation tokens are sensitive\n // (a captured token + the trade id is enough to release real\n // money) and `readline.question` would echo every keystroke.\n const { password } = await import('@inquirer/prompts');\n const answer = await password({\n message: 'Paste the confirmation token:',\n mask: '*',\n });\n return answer.trim();\n }\n throw new CliValidationError(\n 'No confirmation token provided.',\n 'Pass the token positionally, use --token-stdin to read it from a pipe, or run interactively for a prompt.',\n 'token'\n );\n}\n","/**\n * `kash trade list` — list the authenticating user's trades.\n *\n * Status filter is forwarded as-is to the API (the API accepts\n * comma-separated multiplicities, e.g. `pending,completed`).\n */\n\nimport { Command } from 'commander';\n\nimport { CliValidationError, toCliError } from '../../errors.js';\nimport { buildClient } from '../../utils/client.js';\nimport { colorStatus, createTable, formatUsdcDecimal, shortId } from '../../utils/formatting.js';\nimport { parsePositiveInt, readGlobals } from '../../utils/global-options.js';\nimport { log, print, printJson, writeNdjson } from '../../utils/output.js';\n\nimport type { TradeResource } from '@kashdao/sdk';\n\ntype ListOptions = {\n status?: readonly string[];\n market?: string;\n limit?: string;\n cursor?: string;\n all?: boolean;\n ndjson?: boolean;\n};\n\nexport const listTradesCommand = new Command('list')\n .description('List your trades.')\n .option(\n '-s, --status <status...>',\n 'status filter; repeat or comma-separate (e.g. --status pending --status executing | --status pending,executing)'\n )\n .option('-m, --market <id>', 'filter by market id')\n .option('-l, --limit <n>', 'page size (1-100)', '20')\n .option('-c, --cursor <cursor>', 'pagination cursor')\n .option('-a, --all', 'walk every page (use with --json for export)')\n .option(\n '--ndjson',\n 'stream results as newline-delimited JSON (one record per line); implies --all'\n )\n .addHelpText(\n 'after',\n `\nExamples:\n $ kash trade list\n $ kash trade list --status pending,executing --json --quiet\n $ kash trade list --status pending --status executing --json --quiet\n $ kash trade list --market 9f0b... --json --quiet | jq '.data[] | select(.status==\"completed\")'\n $ kash trade list --all --ndjson | while read -r line; do echo \"$line\" | jq -r .id; done\n`\n )\n .action(async (options: ListOptions, cmd: Command) => {\n const globals = readGlobals(cmd);\n const limit = options.limit === undefined ? 20 : parsePositiveInt(options.limit, 'limit');\n if (limit < 1 || limit > 100) {\n throw new CliValidationError('--limit must be between 1 and 100.', undefined, 'limit');\n }\n\n // Normalise --status: accept both `--status a,b,c` (single comma-string)\n // and `--status a --status b` (repeated). Both collapse to the\n // same comma-string the API expects. Mirrors `webhooks list`.\n const status = normaliseStatus(options.status);\n\n let response;\n try {\n const { client } = await buildClient({ requireAuth: true, globals });\n response = await client.trades.list({\n limit,\n ...(options.cursor === undefined ? {} : { cursor: options.cursor }),\n ...(status === undefined ? {} : { status }),\n ...(options.market === undefined ? {} : { marketId: options.market }),\n });\n\n if (options.ndjson) {\n for await (const trade of response) {\n writeNdjson(trade);\n }\n return;\n }\n\n if (options.all) {\n const collected: TradeResource[] = [];\n for await (const trade of response) collected.push(trade);\n if (globals.json) {\n // Canonical `{data, pagination, count}` — see markets/list.ts\n // for rationale. Same shape across single-page and walked\n // modes lets agents pin to one schema.\n printJson({\n data: collected,\n pagination: { hasMore: false, cursor: null },\n count: collected.length,\n });\n } else {\n renderTable(collected);\n }\n return;\n }\n } catch (cause) {\n throw toCliError(cause);\n }\n\n if (globals.json) {\n printJson({\n data: response.data,\n pagination: response.pagination,\n count: response.data.length,\n });\n return;\n }\n\n if (response.data.length === 0) {\n log.info('No trades matched the filter.');\n return;\n }\n renderTable(response.data);\n\n if (response.pagination.hasMore && response.pagination.cursor) {\n log.detail('Next page', `--cursor ${response.pagination.cursor}`);\n }\n });\n\n/**\n * Flatten + dedupe `--status`. Each entry may itself be a comma list.\n * Returns the canonical comma-string the API expects, or `undefined`\n * when no values were supplied (preserving the \"no filter\" default).\n */\nfunction normaliseStatus(raw: readonly string[] | undefined): string | undefined {\n if (!raw || raw.length === 0) return undefined;\n const seen = new Set<string>();\n for (const entry of raw) {\n for (const piece of entry.split(',')) {\n const trimmed = piece.trim();\n if (trimmed.length > 0) seen.add(trimmed);\n }\n }\n return seen.size === 0 ? undefined : [...seen].join(',');\n}\n\nfunction renderTable(trades: readonly TradeResource[]): void {\n const table = createTable(['Trade', 'Market', 'Side', 'Out#', 'Amount', 'Status', 'Tx']);\n for (const t of trades) {\n table.push([\n shortId(t.id),\n shortId(t.marketId),\n t.side,\n String(t.outcomeIndex),\n formatUsdcDecimal(t.amount),\n colorStatus(t.status),\n t.txHash ? shortId(t.txHash, 10) : '-',\n ]);\n }\n print(table.toString());\n}\n","/**\n * `kash trade sell <marketId>` — sell outcome tokens back to the AMM.\n */\n\nimport { Command } from 'commander';\n\nimport { readGlobals } from '../../utils/global-options.js';\n\nimport { placeTrade, type PlaceTradeOptions } from './place.js';\n\nexport const sellCommand = new Command('sell')\n .description('Sell outcome tokens back to the market (Kash-orchestrated / hosted-API flow).')\n .argument('<marketId>', 'market UUID')\n .requiredOption('-o, --outcome <index>', 'outcome index (0-based)')\n .requiredOption(\n '-a, --amount <usdc>',\n 'target USDC to receive (decimal, max 6 fractional digits) — NOT tokens-in. ' +\n 'Use `kash quote sell --tokens <n>` if you have a tokens-in figure and want a USDC quote first.'\n )\n .option('--wait', 'block until the trade reaches a terminal state')\n .option(\n '--wait-timeout-ms, --timeout <ms>',\n 'wait timeout in milliseconds (default 60000) — distinct from the global --timeout-ms (per-HTTP-request)'\n )\n .option(\n '--poll-interval-ms, --poll-interval <ms>',\n 'wait poll interval in milliseconds (default 2000)'\n )\n .option('--idempotency-key <key>', 'sets the Idempotency-Key HTTP header')\n .option(\n '--auto-idempotency-key',\n 'auto-generate an Idempotency-Key (UUID v4) and surface it in the response'\n )\n .option('--client-request-id <id>', 'sets the body-level clientRequestId for replay safety')\n .option(\n '--dry-run',\n 'preview the request without sending — emits the would-be body and resolved headers'\n )\n .addHelpText(\n 'after',\n `\nExamples:\n $ kash trade sell 9f0b... --outcome 0 --amount 10 # target $10 USDC out\n $ kash trade sell 9f0b... --outcome 1 --amount 25 --wait\n $ kash trade sell 9f0b... --outcome 0 --amount 5 --auto-idempotency-key --json --quiet\n $ kash trade sell 9f0b... --outcome 0 --amount 10 --dry-run --json\n\nNotes:\n --amount is in USDC for both buy AND sell:\n BUY — USDC you spend (input).\n SELL — USDC you target receiving (output).\n This is the API contract, not a CLI choice — \\`kash quote sell --tokens N\\`\n takes a tokens-in figure if that's the shape you have on hand.\n`\n )\n .action(async (marketId: string, options: PlaceTradeOptions, cmd: Command) => {\n const globals = readGlobals(cmd);\n await placeTrade({ marketId, side: 'sell', options, globals });\n });\n","/**\n * `kash trade status <id>` — fetch a single trade. With `--poll`, block\n * until the trade reaches a terminal state (uses the SDK's polling\n * helper).\n */\n\nimport { Command } from 'commander';\nimport ora from 'ora';\n\nimport { toCliError } from '../../errors.js';\nimport { buildClient } from '../../utils/client.js';\nimport { colorStatus, formatDate, formatUsdcDecimal, shortId } from '../../utils/formatting.js';\nimport { parsePositiveInt, readGlobals } from '../../utils/global-options.js';\nimport { isQuiet, log, print, printJson, style } from '../../utils/output.js';\n\nimport type { TradeResource } from '@kashdao/sdk';\n\ntype StatusOptions = {\n poll?: boolean;\n timeout?: string;\n pollInterval?: string;\n};\n\nexport const statusCommand = new Command('status')\n .description('Show the status of a trade. Use --poll to block until terminal.')\n .argument('<id>', 'trade UUID')\n .addHelpText(\n 'after',\n `\nExamples:\n $ kash trade status 9f0b...\n $ kash trade status 9f0b... --poll\n $ kash trade status 9f0b... --poll --json --quiet | jq -r '.txHash'\n`\n )\n .option('--poll', 'poll until the trade reaches a terminal state')\n .option(\n '--wait-timeout-ms, --timeout <ms>',\n 'poll timeout in milliseconds (default 60000) — distinct from the global --timeout-ms (per-HTTP-request)'\n )\n .option(\n '--poll-interval-ms, --poll-interval <ms>',\n 'poll interval in milliseconds (default 2000)'\n )\n .action(async (id: string, options: StatusOptions, cmd: Command) => {\n const globals = readGlobals(cmd);\n let trade: TradeResource;\n try {\n const { client } = await buildClient({ requireAuth: true, globals });\n if (options.poll) {\n const timeoutMs = options.timeout ? parsePositiveInt(options.timeout, 'timeout') : 60_000;\n const pollIntervalMs = options.pollInterval\n ? parsePositiveInt(options.pollInterval, 'poll-interval')\n : 2_000;\n\n // Wire SIGINT/SIGTERM to abort the in-flight poll cleanly.\n // Without this, Ctrl-C during a 60s wait leaves the SDK\n // mid-fetch — the polling loop's own signal-handler tears\n // down the process, but any in-flight HTTP request stays\n // unaborted. Pattern mirrors `kash protocol watch`.\n const ac = new AbortController();\n const onSig = (): void => ac.abort();\n process.on('SIGINT', onSig);\n process.on('SIGTERM', onSig);\n try {\n if (globals.json || isQuiet()) {\n trade = await client.trades.waitForCompletion(id, {\n timeoutMs,\n pollIntervalMs,\n signal: ac.signal,\n });\n } else {\n const spinner = ora({\n text: `Polling trade ${shortId(id)}…`,\n stream: process.stderr,\n }).start();\n try {\n trade = await client.trades.waitForCompletion(id, {\n timeoutMs,\n pollIntervalMs,\n signal: ac.signal,\n onStatus: (current) => {\n spinner.text = `Trade ${shortId(current.id)} — status: ${current.status}`;\n },\n });\n if (trade.status === 'completed') {\n spinner.succeed(`Trade ${shortId(trade.id)} completed.`);\n } else {\n spinner.fail(`Trade ${shortId(trade.id)} ${trade.status}.`);\n }\n } catch (cause) {\n spinner.fail('Polling stopped.');\n throw cause;\n }\n }\n } finally {\n process.off('SIGINT', onSig);\n process.off('SIGTERM', onSig);\n }\n } else {\n trade = await client.trades.get(id);\n }\n } catch (cause) {\n throw toCliError(cause);\n }\n\n if (globals.json) {\n printJson(trade);\n return;\n }\n\n print('');\n print(` ${style.dim('Trade id ')} ${trade.id}`);\n print(` ${style.dim('Market id ')} ${trade.marketId}`);\n print(` ${style.dim('Side ')} ${trade.side}`);\n print(` ${style.dim('Outcome idx ')} ${String(trade.outcomeIndex)}`);\n print(` ${style.dim('Amount ')} ${formatUsdcDecimal(trade.amount)}`);\n print(` ${style.dim('Status ')} ${colorStatus(trade.status)}`);\n // Use explicit null checks rather than truthy checks — empty\n // strings are valid SDK values that we'd otherwise silently hide.\n if (trade.txHash !== null) {\n print(` ${style.dim('Tx hash ')} ${trade.txHash}`);\n }\n if (trade.tokensOut !== null) {\n print(` ${style.dim('Tokens out ')} ${trade.tokensOut}`);\n }\n if (trade.errorCode !== null || trade.errorMessage !== null) {\n print(\n ` ${style.dim('Error ')} [${trade.errorCode ?? 'UNKNOWN'}] ${trade.errorMessage ?? ''}`\n );\n // Cross-link to the correlation timeline so an operator\n // diagnosing a failure can see every event the API and the\n // execution engine emitted for this trade.\n log.info(`Inspect the full timeline: kash trace ${trade.correlationId}`);\n }\n print(` ${style.dim('Created ')} ${formatDate(trade.createdAt)}`);\n print(` ${style.dim('Updated ')} ${formatDate(trade.updatedAt)}`);\n if (trade.webhookDelivery !== null) {\n // Surface webhook delivery state when present — agents and\n // humans both need this to debug \"did the integration receive\n // the event?\" without dropping into JSON mode.\n const wh = trade.webhookDelivery;\n const failure = wh.lastFailureCode === null ? '' : ` (${wh.lastFailureCode})`;\n const httpStatus = wh.lastStatusCode === null ? '' : ` HTTP ${String(wh.lastStatusCode)}`;\n print(\n ` ${style.dim('Webhook ')} ${colorStatus(wh.status)} attempts=${String(wh.attempts)}${httpStatus}${failure}`\n );\n if (wh.lastAttemptedAt !== null) {\n print(` ${style.dim(' Last try ')} ${formatDate(wh.lastAttemptedAt)}`);\n }\n }\n if (trade.status === 'pending_confirmation') {\n log.warn('Trade is awaiting high-value confirmation. Run `kash trade confirm <id> <token>`.');\n }\n });\n"],"mappings":"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","names":["Command","Command","Command","Command","Command","Command","Command","Command","ora","Command","ora","Command"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/with-retry.ts"],"sourcesContent":["/**\n * `kash with-retry [opts] -- <command> [args...]` — wrap any kash\n * command in a retry loop driven by the structured error envelope.\n *\n * **Why a built-in.** The `examples/trade-replay.sh` recipe shows the\n * pattern, but every agent / CI script ends up reimplementing it\n * slightly differently. Promoting it to a first-class command means:\n * - Idempotency-key handling is consistent (the inner command's\n * own --auto-idempotency-key / --idempotency-key still applies;\n * retries are at the CLI-invocation level so the inner command's\n * guarantees stay intact).\n * - `recoverable`/`retryAfterMs` from the error envelope drive the\n * wait — same logic that powers the SDK's own retries, surfaced\n * to scripts.\n *\n * **Mechanics.** Spawns the same kash binary as a child process,\n * forwards stdout/stderr live (so the user sees progress), and\n * captures stderr to parse the `CliErrorEnvelope` when the inner\n * command fails. If the inner command emitted `--json`, we get\n * structured `code` / `recoverable` / `retryAfterMs` and use them.\n * Otherwise we fall back to a fixed exponential schedule.\n *\n * Bare success (exit 0) returns immediately. Non-recoverable failure\n * also returns immediately — there's no point retrying an\n * `INVALID_INPUT` or `AUTH_REQUIRED`. Recoverable failure\n * (`RATE_LIMITED`, `NETWORK`, `TIMEOUT`, `MAINTENANCE`, `SERVER_ERROR`)\n * triggers the next attempt up to `--max-attempts`.\n */\n\nimport { spawn } from 'node:child_process';\n\nimport { Command } from 'commander';\n\nimport { CliError, toCliError } from '../errors.js';\nimport { parsePositiveInt, readGlobals } from '../utils/global-options.js';\nimport { log, style } from '../utils/output.js';\n\ntype WithRetryOptions = {\n maxAttempts?: string;\n initialDelayMs?: string;\n maxDelayMs?: string;\n retryWithoutJson?: boolean;\n};\n\n/** Codes whose envelopes are worth retrying. Mirrors the SDK's policy. */\nconst RETRYABLE_CODES: ReadonlySet<string> = new Set([\n 'RATE_LIMITED',\n 'NETWORK',\n 'TIMEOUT',\n 'MAINTENANCE',\n 'SERVER_ERROR',\n]);\n\n/** Codes that are explicitly NOT worth retrying — fail fast. */\nconst TERMINAL_CODES: ReadonlySet<string> = new Set([\n 'INVALID_INPUT',\n 'AUTH_REQUIRED',\n 'INSUFFICIENT_SCOPE',\n 'NOT_FOUND',\n 'CONFLICT',\n 'CONFIGURATION',\n 'DIRECT_CONFIG_MISSING',\n 'SIGNER_FAILED',\n]);\n\nexport const withRetryCommand = new Command('with-retry')\n .description('Re-run a kash command on recoverable failures (rate limits, transient errors).')\n .option('--max-attempts <n>', 'maximum total attempts including the first (default 5)', '5')\n .option(\n '--initial-delay-ms <n>',\n 'starting backoff when the envelope has no retryAfterMs (default 1000)',\n '1000'\n )\n .option('--max-delay-ms <n>', 'cap on the per-attempt wait (default 30000)', '30000')\n .option(\n '--retry-without-json',\n 'retry even when the inner command did not emit --json (no envelope to discriminate ' +\n 'terminal from transient — opt in if you trust the inner command failed transiently)'\n )\n // Allow unknown options to flow through as positional args so the\n // wrapped command's flags don't get parsed by Commander as our own.\n .allowUnknownOption()\n .argument('<command...>', 'kash command + args; pass after `--` to disambiguate')\n .addHelpText(\n 'after',\n `\nExamples:\n $ kash with-retry --max-attempts 5 -- markets list --status ACTIVE --json\n $ kash with-retry -- trade buy <id> --outcome 0 --amount 10 \\\\\n --auto-idempotency-key --json --quiet\n $ kash with-retry --max-attempts 3 -- health --json --quiet\n\nNotes:\n - The wrapped command MUST come after \\`--\\` so its flags don't collide\n with with-retry's own.\n - When the wrapped command emits --json, the error envelope's\n \\`code\\` / \\`recoverable\\` / \\`retryAfterMs\\` drive the retry. Recoverable\n codes (RATE_LIMITED, NETWORK, TIMEOUT, MAINTENANCE, SERVER_ERROR)\n retry; terminal codes (INVALID_INPUT, AUTH_REQUIRED, NOT_FOUND, …)\n fail fast.\n - Without --json on the inner command, we DO NOT retry by default —\n no envelope means we can't tell INVALID_INPUT from a transient\n NETWORK error, and retrying validation 5× wastes time. Pass\n --retry-without-json to force exponential backoff regardless.\n`\n )\n .action(async (commandArgs: string[], options: WithRetryOptions, cmd: Command) => {\n const globals = readGlobals(cmd);\n\n const maxAttempts = options.maxAttempts\n ? parsePositiveInt(options.maxAttempts, 'max-attempts')\n : 5;\n const initialDelay = options.initialDelayMs\n ? parsePositiveInt(options.initialDelayMs, 'initial-delay-ms')\n : 1_000;\n const maxDelay = options.maxDelayMs\n ? parsePositiveInt(options.maxDelayMs, 'max-delay-ms')\n : 30_000;\n\n if (commandArgs.length === 0) {\n throw new CliError('with-retry needs a command to run.', {\n code: 'INVALID_INPUT',\n recoverable: true,\n suggestion:\n 'Pass the kash command after `--`, e.g. `kash with-retry -- health --json --quiet`.',\n });\n }\n\n try {\n await runWithRetry(commandArgs, {\n maxAttempts,\n initialDelay,\n maxDelay,\n humanMode: !globals.json,\n retryWithoutJson: options.retryWithoutJson === true,\n });\n } catch (cause) {\n throw toCliError(cause);\n }\n });\n\ntype RunOptions = {\n readonly maxAttempts: number;\n readonly initialDelay: number;\n readonly maxDelay: number;\n readonly humanMode: boolean;\n readonly retryWithoutJson: boolean;\n};\n\nasync function runWithRetry(args: readonly string[], opts: RunOptions): Promise<void> {\n for (let attempt = 1; attempt <= opts.maxAttempts; attempt++) {\n const result = await runOnce(args);\n\n if (result.exitCode === 0) {\n // Success — replay captured stdout (stderr was already streamed\n // live by runOnce). The inner command's JSON / table output is\n // what the user wants on the success path.\n if (result.stdout) process.stdout.write(result.stdout);\n process.exit(0);\n }\n\n // Non-zero exit — the JSON error envelope (if --json was set on\n // the inner command) lives on stdout. Fall back to stderr for\n // commands that route errors that way.\n const envelope = parseErrorEnvelope(result.stdout) ?? parseErrorEnvelope(result.stderr);\n const code = envelope?.error.code;\n const recoverable = decideRetry(code, envelope?.error.recoverable, {\n retryWithoutJson: opts.retryWithoutJson,\n });\n\n if (!recoverable || attempt === opts.maxAttempts) {\n // Final attempt failed (or non-recoverable). Replay captured\n // stdout — that's where the JSON envelope lives in --json mode\n // and any successful prefix lives in human mode. Stderr already\n // streamed live during the attempt, so don't double-print it.\n if (result.stdout) process.stdout.write(result.stdout);\n if (opts.humanMode) {\n const reason = !recoverable ? 'non-recoverable error' : 'max attempts exhausted';\n log.error(`with-retry: ${reason} after attempt ${String(attempt)} (${code ?? 'unknown'}).`);\n }\n process.exit(result.exitCode);\n }\n\n // Recoverable — compute wait and retry. Drop the inner output\n // for failed-but-retried attempts so the user only sees the\n // final outcome.\n const wait = computeWait(envelope?.error.retryAfterMs, attempt, opts);\n if (opts.humanMode) {\n process.stderr.write(\n `${style.dim(\n `with-retry: attempt ${String(attempt)}/${String(opts.maxAttempts)} failed (${code ?? 'unknown'}); retrying in ${String(wait)}ms…`\n )}\\n`\n );\n }\n await sleep(wait);\n }\n}\n\ntype RunResult = {\n readonly exitCode: number;\n readonly stdout: string;\n readonly stderr: string;\n};\n\n/**\n * Spawn the kash CLI as a child process. Captures both stdout and\n * stderr — stdout because that's where the JSON error envelope lives\n * (via `printJson`); stderr because human-mode error rendering goes\n * there. We replay the captured streams to the user only after the\n * retry decision is made, to avoid showing transient failures.\n */\nasync function runOnce(args: readonly string[]): Promise<RunResult> {\n return await new Promise((resolve, reject) => {\n // process.argv[1] points at `dist/index.js` (or the published\n // bin's entry). Re-invoking via `process.execPath` avoids relying\n // on the shell `kash` lookup, which may not be on PATH for the\n // child (e.g. when `kash` was invoked via npx).\n const node = process.execPath;\n const entry = process.argv[1];\n if (!entry) {\n reject(new Error('with-retry: cannot determine kash entry path.'));\n return;\n }\n\n const child = spawn(node, [entry, ...args], {\n // stdin: 'ignore' (NOT 'inherit'). With inherit, an inner\n // command that prompts for input (`kash auth set-key` without\n // --from-stdin, an interactive `kash setup`) would silently\n // hang on retry — the prompt waits for input but the parent's\n // stdin has already been consumed (or is non-interactive in\n // the CI case). A retry loop should never be holding an\n // interactive prompt; if the inner command needs stdin, the\n // user runs it directly. `kash with-retry` is for commands\n // that read auth/config from env or pre-set state.\n // stdout + stderr stay piped so we can capture and re-emit.\n stdio: ['ignore', 'pipe', 'pipe'],\n // Inherit env so the child sees KASH_API_KEY, KASH_PROFILE, etc.\n env: process.env,\n });\n\n let stdout = '';\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString('utf8');\n });\n\n let stderr = '';\n child.stderr.on('data', (chunk: Buffer) => {\n const text = chunk.toString('utf8');\n stderr += text;\n // Stream stderr through live too — the inner command's progress\n // logs (spinners, retries) shouldn't get held back. The final\n // envelope at the very end is what we'll re-parse.\n process.stderr.write(text);\n });\n\n child.on('error', (err) => reject(err));\n child.on('exit', (exitCode, signal) => {\n // A signal-killed child gets exitCode null. Map to a non-zero\n // sentinel so the retry loop treats it as a failure.\n const code = exitCode ?? (signal === null ? 1 : 128);\n resolve({ exitCode: code, stdout, stderr });\n });\n });\n}\n\n/**\n * Look for a `CliErrorEnvelope` in the captured stderr. Robust to\n * non-JSON stderr (e.g. interleaved log lines) — scans for the last\n * line that parses as JSON with the `{ ok: false, error: { code } }`\n * shape.\n */\nexport function parseErrorEnvelope(\n stderr: string\n): { error: { code: string; recoverable?: boolean; retryAfterMs?: number } } | undefined {\n // Search from the end backwards — the envelope is emitted last.\n const lines = stderr.split('\\n').filter((l) => l.trim().startsWith('{'));\n for (let i = lines.length - 1; i >= 0; i--) {\n try {\n const parsed = JSON.parse(lines[i]!) as unknown;\n if (\n typeof parsed === 'object' &&\n parsed !== null &&\n 'error' in parsed &&\n typeof (parsed as { error?: unknown }).error === 'object'\n ) {\n const error = (\n parsed as { error: { code?: unknown; recoverable?: unknown; retryAfterMs?: unknown } }\n ).error;\n if (typeof error.code === 'string') {\n return {\n error: {\n code: error.code,\n ...(typeof error.recoverable === 'boolean' ? { recoverable: error.recoverable } : {}),\n ...(typeof error.retryAfterMs === 'number'\n ? { retryAfterMs: error.retryAfterMs }\n : {}),\n },\n };\n }\n }\n } catch {\n // Not JSON — skip.\n }\n }\n // The envelope is also commonly pretty-printed across multiple lines.\n // Try parsing the entire stderr as JSON if the multi-line scan fails.\n const trimmed = stderr.trim();\n if (trimmed.startsWith('{') && trimmed.endsWith('}')) {\n try {\n const parsed = JSON.parse(trimmed) as {\n error?: { code?: unknown; recoverable?: unknown; retryAfterMs?: unknown };\n };\n if (parsed.error && typeof parsed.error.code === 'string') {\n return {\n error: {\n code: parsed.error.code,\n ...(typeof parsed.error.recoverable === 'boolean'\n ? { recoverable: parsed.error.recoverable }\n : {}),\n ...(typeof parsed.error.retryAfterMs === 'number'\n ? { retryAfterMs: parsed.error.retryAfterMs }\n : {}),\n },\n };\n }\n } catch {\n // ignore\n }\n }\n return undefined;\n}\n\n/**\n * Decide whether to retry based on the envelope. Sources of truth, in\n * order:\n * 1. Explicit allow-list of retryable codes — wins.\n * 2. Explicit deny-list of terminal codes — fails fast.\n * 3. The envelope's own `recoverable: boolean` — fallback for codes\n * we don't have a strong opinion about.\n * 4. No envelope at all (non-JSON inner command, or stdout that\n * doesn't parse) — DO NOT retry by default. Without an envelope\n * we can't tell INVALID_INPUT (terminal) from a transient\n * NETWORK timeout, and retrying validation errors 5× wastes\n * time and confuses operators. Users who want unconditional\n * retries can pass `--retry-without-json`.\n *\n * The \"no-envelope = no-retry\" default is the safe one. The previous\n * behaviour (assume-retryable) caused `with-retry -- trade buy <bad-id>\n * --outcome 0 --amount 10` to retry 5× on INVALID_INPUT — observable\n * confusion in the wild.\n */\nexport function decideRetry(\n code: string | undefined,\n envelopeRecoverable: boolean | undefined,\n options: { retryWithoutJson: boolean } = { retryWithoutJson: false }\n): boolean {\n if (code === undefined) {\n // No envelope. Caller's policy wins.\n return options.retryWithoutJson;\n }\n if (RETRYABLE_CODES.has(code)) return true;\n if (TERMINAL_CODES.has(code)) return false;\n return envelopeRecoverable ?? true;\n}\n\n/**\n * Wait time for the next attempt. Server-driven `retryAfterMs` wins;\n * otherwise exponential (initial * 2^(attempt-1), capped at maxDelay).\n */\nexport function computeWait(\n retryAfterMs: number | undefined,\n attempt: number,\n opts: { initialDelay: number; maxDelay: number }\n): number {\n if (retryAfterMs !== undefined && retryAfterMs > 0) {\n return Math.min(retryAfterMs, opts.maxDelay);\n }\n const exp = opts.initialDelay * 2 ** (attempt - 1);\n return Math.min(exp, opts.maxDelay);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA6BA,SAAS,aAAa;AAEtB,SAAS,eAAe;AAcxB,IAAM,kBAAuC,oBAAI,IAAI;AAAA,EACnD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,IAAM,iBAAsC,oBAAI,IAAI;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,IAAM,mBAAmB,IAAI,QAAQ,YAAY,EACrD,YAAY,gFAAgF,EAC5F,OAAO,sBAAsB,0DAA0D,GAAG,EAC1F;AAAA,EACC;AAAA,EACA;AAAA,EACA;AACF,EACC,OAAO,sBAAsB,+CAA+C,OAAO,EACnF;AAAA,EACC;AAAA,EACA;AAEF,EAGC,mBAAmB,EACnB,SAAS,gBAAgB,sDAAsD,EAC/E;AAAA,EACC;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBF,EACC,OAAO,OAAO,aAAuB,SAA2B,QAAiB;AAChF,QAAM,UAAU,YAAY,GAAG;AAE/B,QAAM,cAAc,QAAQ,cACxB,iBAAiB,QAAQ,aAAa,cAAc,IACpD;AACJ,QAAM,eAAe,QAAQ,iBACzB,iBAAiB,QAAQ,gBAAgB,kBAAkB,IAC3D;AACJ,QAAM,WAAW,QAAQ,aACrB,iBAAiB,QAAQ,YAAY,cAAc,IACnD;AAEJ,MAAI,YAAY,WAAW,GAAG;AAC5B,UAAM,IAAI,SAAS,sCAAsC;AAAA,MACvD,MAAM;AAAA,MACN,aAAa;AAAA,MACb,YACE;AAAA,IACJ,CAAC;AAAA,EACH;AAEA,MAAI;AACF,UAAM,aAAa,aAAa;AAAA,MAC9B;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,CAAC,QAAQ;AAAA,MACpB,kBAAkB,QAAQ,qBAAqB;AAAA,IACjD,CAAC;AAAA,EACH,SAAS,OAAO;AACd,UAAM,WAAW,KAAK;AAAA,EACxB;AACF,CAAC;AAUH,eAAe,aAAa,MAAyB,MAAiC;AACpF,WAAS,UAAU,GAAG,WAAW,KAAK,aAAa,WAAW;AAC5D,UAAM,SAAS,MAAM,QAAQ,IAAI;AAEjC,QAAI,OAAO,aAAa,GAAG;AAIzB,UAAI,OAAO,OAAQ,SAAQ,OAAO,MAAM,OAAO,MAAM;AACrD,cAAQ,KAAK,CAAC;AAAA,IAChB;AAKA,UAAM,WAAW,mBAAmB,OAAO,MAAM,KAAK,mBAAmB,OAAO,MAAM;AACtF,UAAM,OAAO,UAAU,MAAM;AAC7B,UAAM,cAAc,YAAY,MAAM,UAAU,MAAM,aAAa;AAAA,MACjE,kBAAkB,KAAK;AAAA,IACzB,CAAC;AAED,QAAI,CAAC,eAAe,YAAY,KAAK,aAAa;AAKhD,UAAI,OAAO,OAAQ,SAAQ,OAAO,MAAM,OAAO,MAAM;AACrD,UAAI,KAAK,WAAW;AAClB,cAAM,SAAS,CAAC,cAAc,0BAA0B;AACxD,YAAI,MAAM,eAAe,MAAM,kBAAkB,OAAO,OAAO,CAAC,KAAK,QAAQ,SAAS,IAAI;AAAA,MAC5F;AACA,cAAQ,KAAK,OAAO,QAAQ;AAAA,IAC9B;AAKA,UAAM,OAAO,YAAY,UAAU,MAAM,cAAc,SAAS,IAAI;AACpE,QAAI,KAAK,WAAW;AAClB,cAAQ,OAAO;AAAA,QACb,GAAG,MAAM;AAAA,UACP,uBAAuB,OAAO,OAAO,CAAC,IAAI,OAAO,KAAK,WAAW,CAAC,YAAY,QAAQ,SAAS,kBAAkB,OAAO,IAAI,CAAC;AAAA,QAC/H,CAAC;AAAA;AAAA,MACH;AAAA,IACF;AACA,UAAM,MAAM,IAAI;AAAA,EAClB;AACF;AAeA,eAAe,QAAQ,MAA6C;AAClE,SAAO,MAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AAK5C,UAAM,OAAO,QAAQ;AACrB,UAAM,QAAQ,QAAQ,KAAK,CAAC;AAC5B,QAAI,CAAC,OAAO;AACV,aAAO,IAAI,MAAM,+CAA+C,CAAC;AACjE;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,MAAM,CAAC,OAAO,GAAG,IAAI,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAW1C,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA;AAAA,MAEhC,KAAK,QAAQ;AAAA,IACf,CAAC;AAED,QAAI,SAAS;AACb,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,gBAAU,MAAM,SAAS,MAAM;AAAA,IACjC,CAAC;AAED,QAAI,SAAS;AACb,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,YAAM,OAAO,MAAM,SAAS,MAAM;AAClC,gBAAU;AAIV,cAAQ,OAAO,MAAM,IAAI;AAAA,IAC3B,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,QAAQ,OAAO,GAAG,CAAC;AACtC,UAAM,GAAG,QAAQ,CAAC,UAAU,WAAW;AAGrC,YAAM,OAAO,aAAa,WAAW,OAAO,IAAI;AAChD,cAAQ,EAAE,UAAU,MAAM,QAAQ,OAAO,CAAC;AAAA,IAC5C,CAAC;AAAA,EACH,CAAC;AACH;AAQO,SAAS,mBACd,QACuF;AAEvF,QAAM,QAAQ,OAAO,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,CAAC;AACvE,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,MAAM,CAAC,CAAE;AACnC,UACE,OAAO,WAAW,YAClB,WAAW,QACX,WAAW,UACX,OAAQ,OAA+B,UAAU,UACjD;AACA,cAAM,QACJ,OACA;AACF,YAAI,OAAO,MAAM,SAAS,UAAU;AAClC,iBAAO;AAAA,YACL,OAAO;AAAA,cACL,MAAM,MAAM;AAAA,cACZ,GAAI,OAAO,MAAM,gBAAgB,YAAY,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;AAAA,cACnF,GAAI,OAAO,MAAM,iBAAiB,WAC9B,EAAE,cAAc,MAAM,aAAa,IACnC,CAAC;AAAA,YACP;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,QAAM,UAAU,OAAO,KAAK;AAC5B,MAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,GAAG;AACpD,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,OAAO;AAGjC,UAAI,OAAO,SAAS,OAAO,OAAO,MAAM,SAAS,UAAU;AACzD,eAAO;AAAA,UACL,OAAO;AAAA,YACL,MAAM,OAAO,MAAM;AAAA,YACnB,GAAI,OAAO,OAAO,MAAM,gBAAgB,YACpC,EAAE,aAAa,OAAO,MAAM,YAAY,IACxC,CAAC;AAAA,YACL,GAAI,OAAO,OAAO,MAAM,iBAAiB,WACrC,EAAE,cAAc,OAAO,MAAM,aAAa,IAC1C,CAAC;AAAA,UACP;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAqBO,SAAS,YACd,MACA,qBACA,UAAyC,EAAE,kBAAkB,MAAM,GAC1D;AACT,MAAI,SAAS,QAAW;AAEtB,WAAO,QAAQ;AAAA,EACjB;AACA,MAAI,gBAAgB,IAAI,IAAI,EAAG,QAAO;AACtC,MAAI,eAAe,IAAI,IAAI,EAAG,QAAO;AACrC,SAAO,uBAAuB;AAChC;AAMO,SAAS,YACd,cACA,SACA,MACQ;AACR,MAAI,iBAAiB,UAAa,eAAe,GAAG;AAClD,WAAO,KAAK,IAAI,cAAc,KAAK,QAAQ;AAAA,EAC7C;AACA,QAAM,MAAM,KAAK,eAAe,MAAM,UAAU;AAChD,SAAO,KAAK,IAAI,KAAK,KAAK,QAAQ;AACpC;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;","names":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|