@candledottv/cli 0.13.22 → 0.13.24

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 (3) hide show
  1. package/README.md +23 -16
  2. package/dist/index.js +53 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @candledottv/cli
2
2
 
3
- The Candle CLI (current version 0.13.0): authorize a device from your browser, keep your own keys
3
+ The Candle CLI (current version 0.13.23): authorize a device from your browser, keep your own keys
4
4
  in an encrypted vault on this machine, hand an agent a TEE wallet it can trade, and manage API
5
5
  keys, wallets, and setup health from the terminal. Zero runtime dependencies; the whole thing is
6
6
  one self-contained `dist/index.js` that runs under plain Node.
@@ -109,10 +109,10 @@ chosen words from your saved copy. Clear your terminal's scrollback too: the CLI
109
109
  | `candle keys limits <prefix> <asset>=<raw> [<asset>=<raw> ...]` | Sets a key's per-asset caps (`sol`, `usdc`, `cndl`, `eth`, `usdg`, in base units per transaction) with the profile's own API key. It can only tighten; raising or removing a cap is `LOOSEN_REQUIRES_SESSION`, and the owner does that in the web key manager. A device token cannot set these caps. |
110
110
  | `candle keys update <prefix\|label> --embedded-wallet allow\|deny [--yes]`, `candle keys self embedded-wallet deny` | Allow or deny a key the account's embedded wallet, with the device token (allowing needs the prefix typed back at a terminal). `self embedded-wallet deny` lets the profile's own key give it up with no prompt; a key can never allow itself. |
111
111
  | `candle keys signer approve <code> --key <prefix\|label> [--reject]`, `candle keys signer move <prefix\|label> [--to-key <prefix\|label>]` | Key signers for bots on a different machine from the vault. `approve` accepts (or rejects) a key's signer request with the device token, after you type the full fingerprint the trading machine printed. `move`, on the machine holding a wallet's current signer, moves every wallet on the key onto its active signer; `--to-key` moves a revoked key's wallets to a live key. |
112
- | `candle keys stop <prefix>` | Pause one key using the device token. Its requests are refused with `PROFILE_PAUSED` until resumed; the key and its local credential are kept. Repeating the command succeeds. |
113
- | `candle keys resume <prefix>` | Resume a stopped key using the device token. Repeating the command succeeds. An API key cannot stop or resume itself; a revoked key cannot be resumed. |
112
+ | `candle keys stop <prefix\|label>` | Pause one key using the device token. Its requests are refused with `PROFILE_PAUSED` until resumed; the key and its local credential are kept. Repeating the command succeeds. |
113
+ | `candle keys resume <prefix\|label>` | Resume a stopped key using the device token. Repeating the command succeeds. An API key cannot stop or resume itself; a revoked key cannot be resumed. |
114
114
  | `candle keys rotate <prefix\|label> ([--grace <duration> \| --now] [--force] [--yes] [--idempotency-key <key>] \| --finish)` | Gives a key a new secret and keeps its prefix, label, access, limits, wallets and history. The old secret keeps working through `--grace` (default `1h`, at most `24h`) or stops at once with `--now`; `--finish` ends an open window early. When the key is the CLI's own, the new secret replaces it in the keychain. A copy in `CANDLE_API_KEY_FILE`, `CANDLE_API_KEY` or anywhere else must be updated by hand before the window ends; `candle doctor` warns while the old secret is in use. Each run sends an `Idempotency-Key` and retries a dropped connection once; if there is still no answer, rerun with the printed `--idempotency-key` to recover the first secret. |
115
- | `candle keys revoke <prefix>` | Revokes an API key by prefix. Revoking the CLI's own stored key also clears it locally. A prefix can start with `-`; the key commands accept it as typed, and `--` (`candle keys revoke -- -QVeHyie`) ends the options on any command. |
115
+ | `candle keys revoke <prefix\|label>` | Revokes an API key by prefix or label. A label resolves to its prefix the way `keys access` does; two live keys with the same label refuse and list their prefixes. Revoking the CLI's own stored key also clears it locally. A prefix can start with `-`; the key commands accept it as typed, and `--` (`candle keys revoke -- -QVeHyie`) ends the options on any command. |
116
116
  | `candle keys wallets <prefix>`, `keys wallets set <prefix> --wallets <id,id>`, `keys wallets scope <prefix> --scope all\|selected` | Each key is an agent profile with its own wallet set: list it, replace it, or limit the profile to its assigned wallets. |
117
117
  | `candle wallets` (alias `candle wallet`) | Shows the account's embedded (launch) wallets and any linked wallets, using the API key, with a `Signer` column saying whether this machine holds each linked wallet's signing key and a `Trusted` column saying whether it is yours (linked while signed in, or marked with `trust`). It links and revokes; it never generates or prints a key, which the vault now derives. |
118
118
  | `candle wallets import --chain solana\|evm`, `wallets revoke <wallet-id>` | Link a wallet you own (key from `--key-file` or a hidden prompt, sealed to Privy before it leaves the machine), or unlink one. |
@@ -122,7 +122,7 @@ chosen words from your saved copy. Clear your terminal's scrollback too: the CLI
122
122
  | `candle wallets allow-launch\|disallow-launch <label\|address\|id\|prefix*>...` | Deprecated. Where the server has `TEE_LAUNCH_BY_SCOPE_ENABLED` on, a key's `launch:write` decides TEE launches: `allow-launch` saves the value and exits 0, and `disallow-launch` saves it, prints the server's `ALLOW_LAUNCH_NOT_ENFORCED` message and exits 1, because no launch is stopped. A server with the switch off still lets TEE wallets pay for a launch (`candle launch`) only after `allow-launch`. Owner only, over the device token; the screen shows each wallet's label, address and bound key before a typed confirm. `disallow-launch --yes` skips the confirm. |
123
123
  | `candle wallets close-empty [--wallet embedded\|<tee address\|label>] [--keep <mint>]... [--client-trade-id <id>] [--preview] [--yes]` | Closes a wallet's empty token accounts and returns their rent to it, after a preview: the embedded wallet (`transfer:write`), or a Solana TEE wallet bound to the key (`swap:write`), whose close transactions Candle builds, this machine checks and the key signer approves. `--preview` prints what would close — the whole preview as one JSON document on stdout under `--json` — and closes, signs and prompts nothing; headless without `--yes`, the `CONFIRMATION_REQUIRED` refusal carries that same preview under `details`. |
124
124
  | `candle vault init\|status\|list\|new-key\|rename\|phrase show\|restore\|reconcile-exposure` | The encrypted local vault, `vault.enc` (Tier 1). `init` creates it with a passphrase factor and a 24-word recovery phrase (and no key), labels its read-back derivation as verification, and leaves security-key enrollment to the separate optional `candle vault enroll security-key` command; `new-key` derives a key inside it (`--count`, `--labels-from <file>` for many under one unlock), `list [filter] [--balances]` shows one line per key, `rename` changes a label only, `phrase show` displays the phrase on a terminal (never in `--json`, a log or a pipe), and `restore --phrase` rebuilds the derived keys on another machine with a new passphrase (`--count`, `--tee-count`, `--external-count`, `--evm-count` and `--evm-tee-count` for EVM keys and Hood TEE wallets, or a Solana gap scan with `--rpc-url`); a restored vault never allocates new keys. `reconcile-exposure` re-reads the account and adds exposure records, clearing nothing. No key ever leaves the vault to reach Candle. See [CLI custody](https://docs.candle.tv/developers/cli-custody). |
125
- | `candle vault factor list\|add passphrase\|add security-key\|add touch-id\|add passkey\|remove` (or `vault enroll <kind>`), `vault backup --to <path>\|icloud`, `vault verify-backup <path>` | What can open the vault, and proving a copy of it works: `backup` verifies the copy in full before reporting (a copy is sealed, keeping only the passphrase and security-key envelopes, unless it goes to a recognised local disk or removable drive, so a cloud folder and any path the CLI cannot place are both sealed unless `--accept-shared-domain` is passed), and `verify-backup` re-checks an existing one against this vault's key and address set. `add security-key` enrolls a FIDO2 security key (CTAP2 `hmac-secret`, user-verified with the key's PIN or biometric) through the bundled `candle-fido2` helper (`--install-helper` fetches and verifies this release's helper first); one key is not a recoverable factor, two keys are a pair, an enrolled key can authorize adding another, and the passphrase stays the recovery floor. `add touch-id` and `add passkey` are refused with `VAULT_FACTOR_UNSUPPORTED_ON_PLATFORM` until the Apple-signed helper ships. Every vault command then takes `--factor <id\|passphrase\|security-key>` to say which envelope opens it and `--device <id>` to name the key when several are attached. |
125
+ | `candle vault factor list\|add passphrase\|add security-key\|add touch-id\|add passkey\|remove` (or `vault enroll <kind>`), `vault backup --to <path>\|icloud`, `vault verify-backup <path>` | What can open the vault, and proving a copy of it works: `backup` verifies the copy in full before reporting (a copy is sealed, keeping only the passphrase and security-key envelopes, unless it goes to a recognised local disk or removable drive, so a cloud folder and any path the CLI cannot place are both sealed unless `--accept-shared-domain` is passed), and `verify-backup` re-checks an existing one against this vault's key and address set. `add security-key` enrolls a FIDO2 security key (CTAP2 `hmac-secret`, user-verified with the key's PIN or biometric) through the bundled `candle-fido2` helper (`--install-helper` fetches and verifies this release's helper first); one key is not a recoverable factor, two keys are a pair, an enrolled key can authorize adding another, and the passphrase stays the recovery floor. `add touch-id` is available in 0.13.23 on supported Macs with the signed `candle-enclave.app`; npm ships no native helper. `add passkey` remains unavailable (`VAULT_FACTOR_UNSUPPORTED_ON_PLATFORM`): the shipped helper lacks the associated-domain entitlement. Every vault command then takes `--factor <id\|passphrase\|security-key\|touch-id>` to say which envelope opens it and `--device <id>` to name the key when several are attached. |
126
126
  | `candle vault transfer\|promote\|promote-batch\|fund\|demote\|export-key` | Moving value and authority. `transfer` and `fund` sign locally after showing the decoded transaction (`transfer` from a vault key or a promoted TEE wallet, which warns when an agent may be trading it and reports the finalized transfer to Candle's history; `fund` reaches a TEE wallet from its pinned vault key, or an external wallet, with no `--yes`; both verify once, at the unlock, which names the send, then show the destination grouped in fours and take a typed `confirm`); `promote --from <cold label>` derives a fresh TEE wallet pinned to that key (an EVM vault key becomes a Hood TEE wallet), shows the pinned vault address and asks you to type `confirm` to promote the new wallet (promotion moves no funds), `promote --in-place <label> --sweep-to <cold label>` promotes a key at its own address after a typed warning, `promote-batch --pairs-from <file>` does many in place under one unlock, `--to-key` binds the result to another of your keys, and `demote` (disable then sweep; `--emergency` sweeps even when the stop request fails) sends it back to its pinned vault; `export-key` is the one ceremony that writes a single private key to a file you name. `demote` takes several addresses, or `--all --empty` for every Solana TEE wallet with nothing left to sweep, under one unlock and one typed `confirm` (`--yes` skips it), with a result line per wallet and one `--json` document. |
127
127
  | `candle external new\|list\|sweep` | External wallets for outside tools: keys on the vault's third branch (`m/44'/501'/n'/2'`), never delegated to Privy and never registered with Candle. `new` allocates one (and moves the vault to format version 3; refused in a restored vault), `list` shows them, and `sweep <external> --to <vault>` sends everything back to a named vault key, signed locally, confirmed by a typed `confirm` after the vault key's address is shown. See [Bring your own services](https://docs.candle.tv/developers/cli#bring-your-own-services). |
128
128
  | `candle sign [--file <path>] --wallet <external>... [--broadcast] [--yes]`, `candle sign message --wallet <external> [--file <path>] [--yes]` | The generic signer: a base64 transaction any tool built, legacy or v0, decoded (lookup tables resolved) and simulated over your own RPC before it is displayed and signed with an external wallet only. A vault key, a TEE wallet or an unnamed signer is refused; a failing simulation is refused with no override; `--yes` skips the confirmation prompt and nothing else. `sign message` signs the exact bytes of a file or of stdin. |
@@ -294,22 +294,28 @@ The npm package ships no native helper: there the factor is refused with `VAULT_
294
294
  until a release build's `candle-fido2` is installed and `CANDLE_FIDO2_HELPER` points at it. No
295
295
  other factor is ever substituted for one that is refused.
296
296
 
297
- The Touch ID factor (`candle vault factor add touch-id`; not yet released) needs
298
- the signed `candle-enclave.app` helper, which the darwin tarball, the installer script and Homebrew
299
- place beside `candle` once a release ships it; a release cut before Apple's approval ships no
300
- helper and the CLI refuses the factor with a typed code that says so. The CLI verifies the
297
+ The Touch ID factor (`candle vault factor add touch-id`) is available in
298
+ [0.13.23](https://github.com/candledottv/cli/releases/tag/cli-v0.13.23) on Apple silicon Macs and Intel
299
+ Macs with a T2 chip. It needs the signed `candle-enclave.app` helper, which the darwin tarball,
300
+ the installer script and Homebrew place beside `candle`. The npm package ships no native helper:
301
+ install the matching signed bundle and set `CANDLE_ENCLAVE_HELPER`, or the command refuses with
302
+ `VAULT_HELPER_MISSING`. An untrusted helper is `VAULT_HELPER_UNTRUSTED`; Linux and a Mac without
303
+ a Secure Enclave are `VAULT_FACTOR_UNSUPPORTED_ON_PLATFORM`. The CLI verifies the
301
304
  helper's code signature with `/usr/bin/codesign` before trusting it, and `CANDLE_ENCLAVE_HELPER`
302
305
  points at a bundle elsewhere. The factor opens the vault on one Mac only and is never a recovery
303
306
  factor; the passphrase remains the recovery floor.
304
307
 
305
- The synced passkey factor (`candle vault factor add passkey`; not yet released)
306
- runs through the same signed helper on macOS 15 or later, and needs the helper's
308
+ The synced passkey factor (`candle vault factor add passkey`) remains unavailable in 0.13.23:
309
+ the shipped helper deliberately omits `webcredentials:cli.candle.tv`, so it refuses with
310
+ `VAULT_FACTOR_UNSUPPORTED_ON_PLATFORM` and explains the missing entitlement. Passkey enrollment
311
+ has not yet been checked on real Macs. A future release needs macOS 15 or later and the helper's
307
312
  associated-domains entitlement for `webcredentials:cli.candle.tv` with an embedded provisioning
308
313
  profile, plus an `apple-app-site-association` served at
309
314
  `https://cli.candle.tv/.well-known/apple-app-site-association` (status 200, no redirect,
310
315
  `Content-Type: application/json`, `{"webcredentials":{"apps":["<TEAM ID>.tv.candle.cli.enclave"]}}`).
311
- Each missing piece is a typed refusal before any passkey sheet, and the refusal names what must be
312
- served. The passkey follows the Apple account, so it is a recoverable factor; two are one factor,
316
+ A missing association refuses with `VAULT_FACTOR_UNAVAILABLE` before any passkey sheet and names
317
+ what must be served; serving it allows real enrollment when the local requirements are met.
318
+ The passkey follows the Apple account, so it is a recoverable factor; two are one factor,
313
319
  and a copy of the vault is sealed (passphrase and security-key envelopes only) by default unless it goes to a
314
320
  recognised local disk or removable drive, so that one account never holds
315
321
  both the blob and a factor that opens it. iCloud Drive and other cloud folders are sealed for that
@@ -434,7 +440,7 @@ the profile they were migrated into.
434
440
  `CANDLE_DEVICE_TOKEN` and `CANDLE_API_KEY` together mean CI needs no storage backend at all: set
435
441
  both and every command works without ever touching a keychain or the encrypted file.
436
442
 
437
- ### External key files (unreleased source support)
443
+ ### External key files (CLI 0.13.22 and later)
438
444
 
439
445
  A host may supply an absolute path with both raw key variables absent:
440
446
 
@@ -469,8 +475,9 @@ The key stays in private memory; it is never inserted into environment variables
469
475
  config or saved files. Ordinary browser/helper/hook/keychain children retain the nonsecret path;
470
476
  plugins keep their environment allowlist and receive neither the path nor key. This reduces
471
477
  environment exposure, not access by root or another process with the same UID. Extended ACLs
472
- are not audited. Source readiness does not imply an installed npm/binary release supports this;
473
- publication is a separately authorized human release.
478
+ are not audited. This is available in `candle` and the bundled `candle mcp` from CLI 0.13.22.
479
+ For standalone `candle-mcp` (`@candledottv/mcp`), this remains unreleased source support:
480
+ version 0.10.32 does not include it and needs its own next release.
474
481
 
475
482
  ## What this CLI deliberately does not do
476
483
 
package/dist/index.js CHANGED
@@ -225,6 +225,11 @@ function ownerConfirmChallengeOf(result) {
225
225
  const verificationUriComplete = str("verificationUriComplete");
226
226
  if (!confirmId || !userCode || !verificationUriComplete)
227
227
  return null;
228
+ const accountRaw = oc.account;
229
+ const account = accountRaw && typeof accountRaw === "object" && !Array.isArray(accountRaw) && typeof accountRaw.wallet === "string" ? {
230
+ wallet: accountRaw.wallet,
231
+ displayName: typeof accountRaw.displayName === "string" ? accountRaw.displayName : null
232
+ } : undefined;
228
233
  return {
229
234
  confirmId,
230
235
  userCode,
@@ -234,6 +239,7 @@ function ownerConfirmChallengeOf(result) {
234
239
  interval: Math.max(1, num("interval") ?? 5),
235
240
  step: str("step") ?? "",
236
241
  summary: str("summary") ?? "",
242
+ ...account ? { account } : {},
237
243
  reason: str("reason") ?? "required"
238
244
  };
239
245
  }
@@ -755,7 +761,7 @@ var init_terminal_theme = __esm(() => {
755
761
  });
756
762
 
757
763
  // src/version.ts
758
- var CLI_VERSION = "0.13.22";
764
+ var CLI_VERSION = "0.13.24";
759
765
 
760
766
  // src/presentation.ts
761
767
  function terminalStyle(deps, stream, machine = false) {
@@ -47895,17 +47901,27 @@ async function keysRevoke(args, ctx) {
47895
47901
  return 2;
47896
47902
  }
47897
47903
  if (parsed.positionals.length !== 1) {
47898
- deps.stderr.write(`Usage: candle keys revoke <prefix>
47904
+ deps.stderr.write(`Usage: candle keys revoke <prefix|label>
47899
47905
  `);
47900
47906
  return 2;
47901
47907
  }
47902
- const prefix = parsed.positionals[0];
47908
+ const target = parsed.positionals[0];
47903
47909
  await printIdentity(ctx);
47904
47910
  const deviceToken = await resolveDeviceToken(deps, ctx.profile);
47905
47911
  if (!deviceToken) {
47906
47912
  writeLocalFailure(deps, NO_DEVICE_TOKEN, json);
47907
47913
  return 1;
47908
47914
  }
47915
+ const resolved = await resolveTargetKey(ctx, deviceToken, target, {
47916
+ codePrefix: "KEY_REVOKE",
47917
+ writeListFailure: (failure3) => {
47918
+ writeFailure(deps, failure3, { apiUrl, authType: "device" }, json);
47919
+ return 1;
47920
+ }
47921
+ });
47922
+ if (!resolved.ok)
47923
+ return resolved.code;
47924
+ const prefix = resolved.keyPrefix;
47909
47925
  const result2 = await apiRequest(`${KEYS_PATH3}/${encodeURIComponent(prefix)}`, {
47910
47926
  method: "DELETE",
47911
47927
  auth: "device",
@@ -47955,6 +47971,7 @@ var init_keys = __esm(() => {
47955
47971
  init_secret_store();
47956
47972
  init_key_output();
47957
47973
  init_keys_embedded_wallet();
47974
+ init_tee_rebind();
47958
47975
  ACCESS_LEVELS = {
47959
47976
  read: "read",
47960
47977
  write: "readwrite",
@@ -78928,13 +78945,16 @@ var HELP = {
78928
78945
  invocation: "self embedded-wallet deny",
78929
78946
  description: "The profile's own key gives up the embedded wallet, with no prompt. A key can never allow itself: the owner does that with keys update"
78930
78947
  },
78931
- { invocation: "stop <prefix>", description: "Pause one key with the device token; reversible with keys resume" },
78932
- { invocation: "resume <prefix>", description: "Resume a stopped key with the device token" },
78948
+ {
78949
+ invocation: "stop <prefix|label>",
78950
+ description: "Pause one key with the device token; reversible with keys resume"
78951
+ },
78952
+ { invocation: "resume <prefix|label>", description: "Resume a stopped key with the device token" },
78933
78953
  {
78934
78954
  invocation: "rotate <prefix|label> ([--grace <duration> | --now] [--force] [--yes] | --finish)",
78935
78955
  description: "Give a key a new secret, keeping its prefix, label, access, limits, wallets and history. The old secret works through --grace (default 1h, at most 24h) or stops at once with --now; --finish ends an open window early. The profile's own key is replaced in the keychain; update CANDLE_API_KEY or CANDLE_API_KEY_FILE yourself and restart MCP. --force rotates again while a window is open"
78936
78956
  },
78937
- { invocation: "revoke <prefix>", description: "Revoke an API key permanently" },
78957
+ { invocation: "revoke <prefix|label>", description: "Revoke an API key permanently" },
78938
78958
  {
78939
78959
  invocation: "signer approve <code> --key <prefix|label> [--reject]",
78940
78960
  description: "Approve (or reject) a key's signer request with the device token; type the full fingerprint the trading machine printed"
@@ -85264,6 +85284,13 @@ init_evm_lite();
85264
85284
  init_client();
85265
85285
  var EXIT_OWNER_CONFIRM_PENDING = 7;
85266
85286
  var OWN_NO_WAIT_COMMANDS = [["tee"], ["lab"], ["wallets", "authorize"]];
85287
+ function shortWallet(wallet3) {
85288
+ return wallet3.length >= 12 ? `${wallet3.slice(0, 4)}…${wallet3.slice(-4)}` : wallet3;
85289
+ }
85290
+ function accountLabel(account) {
85291
+ const short = shortWallet(account.wallet);
85292
+ return account.displayName ? `${account.displayName} (${short})` : short;
85293
+ }
85267
85294
  function ownerConfirmPendingLine(challenge) {
85268
85295
  return JSON.stringify({
85269
85296
  ok: false,
@@ -85274,7 +85301,8 @@ function ownerConfirmPendingLine(challenge) {
85274
85301
  verificationUriComplete: challenge.verificationUriComplete,
85275
85302
  expiresIn: challenge.expiresIn,
85276
85303
  step: challenge.step,
85277
- summary: challenge.summary
85304
+ summary: challenge.summary,
85305
+ ...challenge.account ? { account: challenge.account } : {}
85278
85306
  }
85279
85307
  });
85280
85308
  }
@@ -85291,10 +85319,12 @@ function cliOwnerConfirmHooks(deps, opts, state) {
85291
85319
  `);
85292
85320
  } else {
85293
85321
  const minutes = Math.max(1, Math.round(challenge.expiresIn / 60));
85322
+ const as = challenge.account ? ` as ${accountLabel(challenge.account)}` : "";
85294
85323
  deps.stderr.write([
85295
- `This needs your approval in the browser: ${challenge.summary}`,
85324
+ `This needs your approval in the browser${as}: ${challenge.summary}`,
85296
85325
  `Open ${challenge.verificationUriComplete}`,
85297
85326
  `Code: ${challenge.userCode}`,
85327
+ ...challenge.account ? [`The browser must be signed in as ${accountLabel(challenge.account)}.`] : [],
85298
85328
  "Only approve if you just ran this command.",
85299
85329
  opts.wait ? `Waiting for approval (expires in ${minutes} minute${minutes === 1 ? "" : "s"})...` : ""
85300
85330
  ].filter(Boolean).join(`
@@ -93591,6 +93621,7 @@ init_presentation();
93591
93621
  init_profiles();
93592
93622
  init_key_output();
93593
93623
  init_keys();
93624
+ init_tee_rebind();
93594
93625
  function keysStop(args, ctx) {
93595
93626
  return setKeyPaused(args, ctx, true);
93596
93627
  }
@@ -93599,14 +93630,14 @@ function keysResume(args, ctx) {
93599
93630
  }
93600
93631
  async function setKeyPaused(args, ctx, paused) {
93601
93632
  const { deps, apiUrl, json } = ctx;
93602
- const parsed = parseArgs(args, {});
93633
+ const parsed = parseArgs(args, { keyPrefixPositional: true });
93603
93634
  if ("error" in parsed) {
93604
93635
  writeScreenUsageFailure(deps, parsed.error, json);
93605
93636
  return 2;
93606
93637
  }
93607
- const prefix = parsed.positionals[0];
93608
- if (!prefix?.trim() || parsed.positionals.length !== 1) {
93609
- writeScreenUsageFailure(deps, `Usage: candle keys ${paused ? "stop" : "resume"} <prefix>`, json);
93638
+ const target = parsed.positionals[0];
93639
+ if (!target?.trim() || parsed.positionals.length !== 1) {
93640
+ writeScreenUsageFailure(deps, `Usage: candle keys ${paused ? "stop" : "resume"} <prefix|label>`, json);
93610
93641
  return 2;
93611
93642
  }
93612
93643
  await printIdentity(ctx);
@@ -93615,6 +93646,16 @@ async function setKeyPaused(args, ctx, paused) {
93615
93646
  writeScreenLocalFailure(deps, NO_DEVICE_TOKEN, json);
93616
93647
  return 1;
93617
93648
  }
93649
+ const resolved = await resolveTargetKey(ctx, deviceToken, target, {
93650
+ codePrefix: paused ? "KEY_STOP" : "KEY_RESUME",
93651
+ writeListFailure: (failure3) => {
93652
+ writeScreenFailure(deps, failure3, { apiUrl, authType: "device" }, json);
93653
+ return 1;
93654
+ }
93655
+ });
93656
+ if (!resolved.ok)
93657
+ return resolved.code;
93658
+ const prefix = resolved.keyPrefix;
93618
93659
  const result2 = await apiRequest(`/api/v1/agent/keys/${encodeURIComponent(prefix)}/stop`, {
93619
93660
  method: "POST",
93620
93661
  auth: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@candledottv/cli",
3
- "version": "0.13.22",
3
+ "version": "0.13.24",
4
4
  "description": "The Candle CLI: authorize a device from your browser, then manage API keys, wallets, and setup health from the terminal",
5
5
  "type": "module",
6
6
  "bin": {