@candledottv/cli 0.13.10 → 0.13.12

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 +10 -7
  2. package/dist/index.js +158 -31
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -7,6 +7,7 @@ one self-contained `dist/index.js` that runs under plain Node.
7
7
 
8
8
  The full custody guide is [CLI custody](https://docs.candle.tv/developers/cli-custody), and every
9
9
  command and flag is in the [Candle CLI reference](https://docs.candle.tv/developers/cli).
10
+ Not sure this is the right tool? [How to choose: SDK, MCP or CLI](https://docs.candle.tv/developers/choose-a-surface).
10
11
 
11
12
  `candle vault rename --pairs-from <file> [--dry-run]` renames 1–256 vault or external keys under
12
13
  one unlock and one commit. Use `<label|address|id> <new-label>` lines or CSV columns `from,to`
@@ -84,7 +85,7 @@ emergency procedure, rebinding, trusted wallets and API key access levels are al
84
85
  | Command | What it does |
85
86
  | --- | --- |
86
87
  | `candle setup [--no-browser]` | The onboarding wizard: authorizes this device (skipped when already authorized), prints the agent wallets as funding destinations plus the paste-into-your-agent brief, shows the skill/MCP install lines, runs the full doctor check (setup's exit code is doctor's), and links the web console. Safe to re-run. |
87
- | `candle auth login [--api-url <url>] [--profile <name>] [--scopes <a,b,c>] [--label <name>] [--embedded-wallet deny\|allow] [--no-browser]` | Authorizes this device: prints the API host and a code, opens (or prints) an approval URL, polls until approved, then stores the resulting device token and API key. A new profile needs `--api-url` or a prompt. `--label` names both the device and the key. `--embedded-wallet` defaults to deny. `transfer:bound` is refused here and granted afterwards with `keys access <prefix> --access withdraw`. |
88
+ | `candle auth login [--api-url <url>] [--profile <name>] [--scopes <a,b,c>] [--label <name>] [--embedded-wallet deny\|allow] [--no-browser]` | Authorizes this device: prints the API host, a code and its expiry, opens (or prints) an approval URL, polls until approved, then stores the resulting device token and API key. A new profile needs `--api-url` or a prompt. `--label` names both the device and the key. `--embedded-wallet` defaults to deny. `transfer:bound` is refused here and granted afterwards with `keys access <prefix> --access withdraw`. |
88
89
  | `candle auth status` | Shows which storage backend is in use, both credential prefixes, the config file path, and a live validity check for each credential. |
89
90
  | `candle auth logout [--keep-key]` | Revokes the stored API key (skipped with `--keep-key`), clears local credentials and config, and prints the portal URL for revoking the device itself. |
90
91
  | `candle keys list [--scopes]` | Lists this account's API keys: prefix, name, Read, Write or Withdraw access, environment, timestamps, and which device minted each one. `--scopes` adds the raw scopes, sorted. |
@@ -92,6 +93,8 @@ emergency procedure, rebinding, trusted wallets and API key access levels are al
92
93
  | `candle keys access <prefix\|label\|self> (--access read\|write\|withdraw [--yes] \| --history)` | Changes an existing key's level in place (same key, wallets and caps). Widening needs the device token and the prefix typed back at a terminal; `--yes` skips the prompt when narrowing; `self` narrows the profile's own key and can never widen it. `--history` lists the key's changes and who made them. |
93
94
  | `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. |
94
95
  | `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. |
96
+ | `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. |
97
+ | `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. |
95
98
  | `candle keys revoke <prefix>` | Revokes an API key by prefix. Revoking the CLI's own stored key also clears it locally. |
96
99
  | `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. |
97
100
  | `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. |
@@ -101,7 +104,7 @@ emergency procedure, rebinding, trusted wallets and API key access levels are al
101
104
  | `candle wallets close-empty [--wallet embedded\|<tee address\|label>] [--keep <mint>]... [--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. |
102
105
  | `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), `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). |
103
106
  | `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. |
104
- | `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), `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. |
107
+ | `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), `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. |
105
108
  | `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). |
106
109
  | `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. |
107
110
  | `candle secrets set\|list\|remove <name>` | Your own third-party API keys, in a keychain namespace separate from Candle's credentials (`tv.candle.cli.secrets`, or `secrets.enc` on the encrypted-file fallback). Typed on a hidden prompt, never sent to Candle, never printed after `set`; `list` shows names only. A plug-in receives one as `CANDLE_SECRET_<NAME>` only when named with `--secret`. |
@@ -116,8 +119,8 @@ emergency procedure, rebinding, trusted wallets and API key access levels are al
116
119
  | `candle tee new\|enable` | `new` adds a key to an existing `tee-wallets.enc` and refuses when that store is absent (`vault new-key --chain solana`, then `vault promote --from <label> --to-key <prefix>`). `--factor` does not apply. `enable <address> --vault <address>` delegates an existing key with a pinned sweep vault. `vault import-legacy --tee` migrates an existing store. |
117
120
  | `candle swap <from> <to> --amount <n>\|--percent <n> --wallet <tee> [--to <tee>] [--wait]` | Quote, confirm and swap through the TEE wallet's bound key; the first buy after a launch is this command, unless `candle launch --buy` made it. The assets decide the chain (ETH, USDG or a 0x token is Hood; SOL, USDC, CNDL or a mint is Solana). SOL or USDC to ETH or USDG, or back, is a bridge through Relay into this key's own TEE wallet on the other chain (`--to` names it when there are several); `--wait` follows it for up to ten minutes. With `--json`, stdout is exactly one JSON value for swaps, token trades, replays and `swap status`: `kind`, `status`, `clientTradeId`, `signature` (the primary transaction, or `null`), `signatures` (a list) and `raw` (the original response). Every human line is on stderr. The keys older versions printed stay beside them. |
118
121
  | `candle swap status <id> [--kind trade\|swap\|launch] [--wait]` | Read an operation without resending it; `--wait` follows a bridge until it ends. |
119
- | `candle transfer --to <address\|wallet name\|vault> --asset <SOL\|USDC\|CNDL\|ETH\|USDG>\|--mint <mint>\|--token <0x...> --amount <decimal\|max> [--wallet <tee>] [--rpc-url <url>] [--yes]` | Move funds out of a TEE wallet through its bound Withdraw key, to the wallet's own pinned vault (`--to vault`, any token, `max` allowed, one SOL transfer at most the server's backstop ceiling, 50 SOL by default) or to another of the account's wallets you linked while signed in or marked trusted (a base asset, counted against the key's caps, which must include a per-asset cap and a USD `--tx-limit`). The asset decides the chain: ETH, USDG or `--token` moves from a Hood TEE wallet. The destination and its kind are shown before you confirm; Candle builds, the relay signs, Candle broadcasts. |
120
- | `candle launch --name <name> --symbol <symbol> --image <path>\|--image-url <url> --wallet <tee>\|embedded [--banner <file\|URL>] [--description <text>] [--website <url>] [--x <url>] [--telegram <url>] [--discord <url>] [--buy <amount>] [--quote-asset <asset>] [--dex-version v3\|v4] [--mode <mode>] [--curve-v <V> --curve-g <G>] [--creator-fee-bps 0\|100\|200] [--staker-allocation-bps <bps>] [--visibility production\|test\|local\|hidden] [--pool-fee 25\|50\|100] [--pool-fee-from <bps> --pool-fee-minutes <min>] [--pool-open legacy\|curve_price] [--exp-pool-shape floor\|full_range] [--dry-run] [--check] [--client-trade-id <id>] [--yes]` | Create a token from the named wallet, with an optional first buy. `--wallet embedded` (or `main`) uses `/launch/headless` on Solana or Hood, with or without `--buy`; every plan can use it with `launch:write` and the key's embedded-wallet allowance, without `swap:write`. It previews through `/launch/headless/dry-run`, shows the quote for confirmation, then sends one headless request. A Solana buy is a creator bundle signed by Candle, preserving `bundleId` in the result; a Hood buy is best effort after creation. The server's slippage floor applies; there is no separate CLI build-floor comparison. TEE wallets use `/launch/self` and need Pro or Max, `allowLaunch`, the bound key and verified signing authority. A TEE first buy needs `TEE_LAUNCH_BUYS_ENABLED` (off by default); Solana creator buys also need `AGENT_TRADING_ENABLED` and a configured Jito engine. No gate or plan default changes. `--buy` is a decimal in whole quote units (SOL/USDC/CNDL on Solana, ETH on Hood; USDG has no first buy); zero or empty means no buy. A Hood launch needs `--dex-version v3\|v4`. `--description` and the four absolute http(s) socials flags travel with every preview/build: `--x` maps to `socials.twitter`. `--staker-allocation-bps` accepts 50–1000; `--visibility` is production, test, local or hidden (test keys need test or hidden). `--mode exp-custom` needs `--curve-v` and `--curve-g` in whole quote units; ranges and common help come from the shared launch definition and appear in `candle launch --help`. `--pool-fee` sets the Solana graduated pool fee (default 50 bps); `--pool-fee-from` and `--pool-fee-minutes` add a fall to that rate. Preset `--pool-open curve_price` opens at the curve's last price and burns the rest; omitted it applies when allocations fit `graduation.maxStakerCarveBps`, otherwise falls back to legacy. Exp `--exp-pool-shape` is floor or full_range. `--image-url` is https; `--image` uploads a local PNG/JPEG/GIF/WebP up to 10MB. `--banner` takes a wide local file or https URL. Uploads are public and permanent before confirmation, even on a dry run or decline. `--dry-run` stops at the preview without claiming the launch id. Reuse `--client-trade-id` for recovery, and always pass `--json` in scripts. `--check` answers which launch path works before any of it, in one call (`GET /api/v1/launch/preflight`): the key's tier and embedded-wallet allowance, each bound TEE wallet's allowLaunch and verified-active state, and the server's `TEE_LAUNCH_BUYS_ENABLED` and `AGENT_TRADING_ENABLED` switches. No name, symbol or image is needed, nothing is uploaded or sent, `--wallet <name>` narrows the answer to one wallet, and it exits 1 when no launch path works. |
122
+ | `candle transfer --to <address\|wallet name\|allowlist label\|vault> --asset <SOL\|USDC\|CNDL\|ETH\|USDG>\|--mint <mint>\|--token <0x...> --amount <decimal\|max> [--wallet <tee>] [--rpc-url <url>] [--yes]` | Move funds out of a TEE wallet through its bound Withdraw key, to the wallet's own pinned vault (`--to vault`, any token, `max` allowed, one SOL transfer at most the server's backstop ceiling, 50 SOL by default) or to another of the account's wallets you linked while signed in or marked trusted (a base asset, counted against the key's caps, which must include a per-asset cap and a USD `--tx-limit`). Where the server has `TEE_WITHDRAWALS_ENABLED` on (it ships off), also to an address on the account's withdrawal allowlist, named by its label, id or address, with the same base-asset rule. The asset decides the chain: ETH, USDG or `--token` moves from a Hood TEE wallet. The destination and its kind are shown before you confirm; Candle builds, the relay signs, Candle broadcasts. |
123
+ | `candle launch --name <name> --symbol <symbol> --image <path>\|--image-url <url> --wallet <tee>\|embedded [--banner <file\|URL>] [--description <text>] [--website <url>] [--x <url>] [--telegram <url>] [--discord <url>] [--buy <amount>] [--quote-asset <asset>] [--dex-version v3\|v4] [--mode <mode>] [--curve-v <V> --curve-g <G>] [--creator-fee-bps 0\|100\|200] [--staker-allocation-bps <bps>] [--visibility production\|test\|local\|hidden] [--pool-fee 25\|50\|100] [--pool-fee-from <bps> --pool-fee-minutes <min>] [--pool-open legacy\|curve_price] [--exp-pool-shape floor\|full_range] [--fee-wallet <address>\|--fee-wallet-multisig <address> [--fee-wallet-multisig-vault-index <n>]] [--require-fee-wallet] [--dry-run] [--check] [--client-trade-id <id>] [--yes]` | Create a token from the named wallet, with an optional first buy. `--wallet embedded` (or `main`) uses `/launch/headless` on Solana or Hood, with or without `--buy`; every plan can use it with `launch:write` and the key's embedded-wallet allowance, without `swap:write`. It previews through `/launch/headless/dry-run`, shows the quote for confirmation, then sends one headless request. A Solana buy is a creator bundle signed by Candle, preserving `bundleId` in the result; a Hood buy is best effort after creation. The server's slippage floor applies; there is no separate CLI build-floor comparison. TEE wallets use `/launch/self` and need Pro or Max, `allowLaunch`, the bound key and verified signing authority. A TEE first buy needs `TEE_LAUNCH_BUYS_ENABLED` (off by default); Solana creator buys also need `AGENT_TRADING_ENABLED` and a configured Jito engine. No gate or plan default changes. `--buy` is a decimal in whole quote units (SOL/USDC/CNDL on Solana, ETH on Hood; USDG has no first buy); zero or empty means no buy. A Hood launch needs `--dex-version v3\|v4`. `--description` and the four absolute http(s) socials flags travel with every preview/build: `--x` maps to `socials.twitter`. `--staker-allocation-bps` accepts 50–1000; `--visibility` is production, test, local or hidden (test keys need test or hidden). `--mode exp-custom` needs `--curve-v` and `--curve-g` in whole quote units; ranges and common help come from the shared launch definition and appear in `candle launch --help`. `--pool-fee` sets the Solana graduated pool fee (default 50 bps); `--pool-fee-from` and `--pool-fee-minutes` add a fall to that rate. Preset `--pool-open curve_price` opens at the curve's last price and burns the rest; omitted it applies when allocations fit `graduation.maxStakerCarveBps`, otherwise falls back to legacy. Exp `--exp-pool-shape` is floor or full_range. `--image-url` is https; `--image` uploads a local PNG/JPEG/GIF/WebP up to 10MB. `--banner` takes a wide local file or https URL. Uploads are public and permanent before confirmation, even on a dry run or decline. `--dry-run` stops at the preview without claiming the launch id. Reuse `--client-trade-id` for recovery, and always pass `--json` in scripts. `--check` answers which launch path works before any of it, in one call (`GET /api/v1/launch/preflight`): the key's tier and embedded-wallet allowance, each bound TEE wallet's allowLaunch and verified-active state, and the server's `TEE_LAUNCH_BUYS_ENABLED` and `AGENT_TRADING_ENABLED` switches. No name, symbol or image is needed, nothing is uploaded or sent, `--wallet <name>` narrows the answer to one wallet, and it exits 1 when no launch path works. |
121
124
  | `candle launch status <mint-or-client-launch-id> [--wait]` | One launch state, `build`, `signed`, `confirmed`, `trading`, `bonded` or `migrated`, for a mint or a `clientLaunchId`. Reads `GET /api/v1/launch/status/{id}` (`launch:read`) and reports the market's own `lifecycle` and `migration` fields. `--wait` follows it until the token is trading, the launch has failed, or ten minutes pass. Nothing is sent. |
122
125
  | `candle image upload <path>` | Uploads a PNG, JPEG, GIF or WebP file, at most 10MB, and prints the `https://` URL Candle hosts it at, ready to be a launch's `imageUrl`. The type is read from the file's first bytes. The file becomes public and permanent. Needs `launch:write`; spends nothing and does not count against the launch cap. `--json` adds the content type and the size. |
123
126
  | `candle pnl [--profile <name>]` | P&L: realized net, fees, unrealized and total, then open positions, the same figures the console shows. Without `--profile`, the account's books (every profile, the web app and the CLI, one ledger; needs the Read scope), with a By agent table (each key, then Manual, then the account total). With `--profile <name>`, that profile's key's share of the same P&L: realized on its sales and the open positions that belong to it, with a row per wallet that sums to the total. Each wallet is its own average-cost pool, and a recorded move between the account's wallets carries its cost. Both show a Solana block, a Hood block, then the combined figure, the closed positions as money made, minus money lost, plus partial sells, and a `CHAIN` column (and `WALLET`, where the API names it) on positions. Unpriced positions are never valued at zero; dust (worth under one cent) is marked and still counted. A bridge between Solana and Hood is not a trade, and bridge and Relay costs are not netted. DAMM v2 LP positions are included: realized (withdrawals against the cost basis at the add, plus claimed fees), unrealized (open positions at their share of the pool plus unclaimed fees, against that basis) and vs holding (what the deposited tokens would be worth held); `Total` stays the console's token figure and `Total with LP` covers tokens and LP. |
@@ -159,7 +162,7 @@ Every command accepts these global options:
159
162
  | `--device <id>` | Vault commands: the security key to use, by id. |
160
163
  | `--no-verify-account` | Skips the check that the stored key belongs to the profile's account. |
161
164
  | `--full` | Prints ids in full where a table would shorten them (`9EAq…9Hd7`). |
162
- | `--json` | Machine-readable output instead of a formatted table or summary, generally the underlying API response. One exception: `auth login`'s JSON output still omits the plaintext device token and API key, matching its human-readable summary, since login never displays either value in any mode. |
165
+ | `--json` | Machine-readable output instead of a formatted table or summary, generally the underlying API response. Two exceptions: `auth login`'s JSON result still omits the plaintext device token and API key, matching its human-readable summary, since login never displays either value in any mode; and its device-code progress (code, approval URL, expiry) is printed to stderr as one JSON object with `"stage":"device_code"`, keeping stdout a single parseable JSON value. |
163
166
  | `--help`, `-h` | Prints usage. |
164
167
  | `--version`, `-v` | Prints the CLI version. |
165
168
 
@@ -552,7 +555,7 @@ MIT, see [LICENSE](LICENSE). Third-party material, including the EFF long wordli
552
555
  generated vault passphrases (CC BY 3.0 US), is listed in
553
556
  [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
554
557
 
555
- ### Immutable token updates (unreleased source)
558
+ ### Immutable token updates
556
559
 
557
560
  `candle token update <address> --file ./update.txt --chain solana --idempotency-key <saved-key>`
558
561
  posts 1–1000 characters with the account's `token-pages:write` key and fresh 0.5% eligibility.
@@ -563,7 +566,7 @@ soft-deletes a post owned by that key's account, including posts from its agents
563
566
  Both commands support `--json` and default-off server refusals. Package release remains a separate
564
567
  human-owned step.
565
568
 
566
- ### Token comments (unreleased source)
569
+ ### Token comments
567
570
 
568
571
  `candle token comment <address> --file ./comment.txt --chain solana --idempotency-key <saved-key>`
569
572
  posts 1–500 characters under the same `token-pages:write` key and fresh 0.5% eligibility as a
package/dist/index.js CHANGED
@@ -3476,7 +3476,7 @@ var init_terminal_theme = __esm(() => {
3476
3476
  });
3477
3477
 
3478
3478
  // src/version.ts
3479
- var CLI_VERSION = "0.13.10";
3479
+ var CLI_VERSION = "0.13.12";
3480
3480
 
3481
3481
  // src/presentation.ts
3482
3482
  function terminalStyle(deps, stream, machine = false) {
@@ -15771,7 +15771,9 @@ var init_trading = __esm(() => {
15771
15771
  transaction: exports_external.string().min(1),
15772
15772
  maxDebitLamports: exports_external.string().regex(/^\d+$/),
15773
15773
  expiresAt: exports_external.number().finite(),
15774
- fee: feeSchema.optional()
15774
+ fee: feeSchema.optional(),
15775
+ feeWallet: exports_external.string().optional(),
15776
+ feeWalletSource: exports_external.string().optional()
15775
15777
  });
15776
15778
  walletSchema = exports_external.object({
15777
15779
  id: exports_external.string(),
@@ -38464,7 +38466,7 @@ var init_token_clusters = __esm(() => {
38464
38466
  });
38465
38467
 
38466
38468
  // ../mcp/src/version.ts
38467
- var SERVER_VERSION = "0.10.28";
38469
+ var SERVER_VERSION = "0.10.29";
38468
38470
 
38469
38471
  // ../mcp/src/update-notice.ts
38470
38472
  function newer(a, b) {
@@ -39722,7 +39724,7 @@ var init_launch_definition = __esm(() => {
39722
39724
  {
39723
39725
  key: "feeWallet",
39724
39726
  type: "string",
39725
- help: "Solana only: the wallet this launch's curve and pool fees earn to, an on-curve wallet or a Squads v4 vault; defaults to the account wallet.",
39727
+ help: "Solana only: the wallet this launch's curve and pool fees earn to, an on-curve wallet or a Squads v4 vault; defaults to the wallet that launches the token (the account wallet on a server without LAUNCH_FEE_WALLET_FROM_LAUNCHER, except an embedded launch).",
39726
39728
  chains: [
39727
39729
  "solana"
39728
39730
  ],
@@ -44683,6 +44685,18 @@ function buildRequest(name, args, cfg) {
44683
44685
  init: { method: "POST", headers: jsonHeaders(apiKey), body: JSON.stringify(body) }
44684
44686
  };
44685
44687
  }
44688
+ case "candle_atomic_launch_dry_run": {
44689
+ const apiKey = requireApiKey2(cfg);
44690
+ const { payer, chain: _chain, buyAmount: _buyAmount, ...rest } = args;
44691
+ return {
44692
+ url: `${base2}/api/v1/launch/atomic/dry-run`,
44693
+ init: {
44694
+ method: "POST",
44695
+ headers: jsonHeaders(apiKey),
44696
+ body: JSON.stringify({ ...rest, chain: "solana", payer: payer ?? { type: "main" } })
44697
+ }
44698
+ };
44699
+ }
44686
44700
  case "candle_get_market": {
44687
44701
  const { chain: chain2, mint } = args;
44688
44702
  return {
@@ -45355,7 +45369,7 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
45355
45369
  }, async (args) => callAndRelay("candle_claim_staking_reward", args, getConfig(), doFetch));
45356
45370
  register("candle_get_fees", {
45357
45371
  title: "Read the creator fees this account's tokens can pay",
45358
- description: "The tokens launched by the account's own wallets, with what each could pay now in creator fees " + "(raw units from direct chain reads) and `claimable: true` or the `reason` it is not. Reads only; " + "moves nothing. It returns no earned or paid-out totals. An unreadable token is marked, never " + "shown as zero. Paged: pass `continueCursor` back as `cursor`.",
45372
+ description: "The tokens launched by the account's own wallets, with what each could pay now in creator fees " + "(raw units from direct chain reads) and a `reason` saying who can claim: `claimable_by_key` on a " + "claimable Solana row, and otherwise why this key cannot (including `claimable_via_web_session` " + "and `fee_wallet_designated`, which name whose authority collects). Reads only; " + "moves nothing. It returns no earned or paid-out totals. An unreadable token is marked, never " + "shown as zero. Paged: pass `continueCursor` back as `cursor`.",
45359
45373
  inputSchema: getFeesShape
45360
45374
  }, async (args) => callAndRelay("candle_get_fees", args, getConfig(), doFetch));
45361
45375
  register("candle_claim_fees", {
@@ -45468,6 +45482,12 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
45468
45482
  const result = await executeTrade(args, getConfig(), doFetch);
45469
45483
  return { content: [{ type: "text", text: result.text }], ...result.isError ? { isError: true } : {} };
45470
45484
  });
45485
+ register("candle_atomic_launch_dry_run", {
45486
+ title: "Dry-run an atomic Solana launch",
45487
+ description: "Check a Solana launch bundled with first buys and price each buy, without building anything. Runs the " + "same checks as the atomic build, in the same order (payers, Believer check, fee and spend limits, the " + "key's transaction cap), then returns the launch preview (curveTerms) and one fill per first buy: the " + "tokens it would receive and the floor the build would sign it at. Reads only; moves nothing, reserves " + "nothing, and the clientLaunchId stays unused. It does not check balances, the daily cap, or the " + "transaction's size, so the real launch can still refuse. Needs launch:write, swap:write and Pro or Max.",
45488
+ inputSchema: atomicLaunchDryRunShape,
45489
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
45490
+ }, async (args) => callAndRelay("candle_atomic_launch_dry_run", args, getConfig(), doFetch));
45471
45491
  register("candle_launch_and_seed", {
45472
45492
  title: "Launch a token and seed it",
45473
45493
  description: "Launch a new token from the account's embedded wallet, with an optional dev-buy seed, then " + "return the fresh market state and token links in one result. On solana a seed makes this " + (options.headlessSeed === true ? "the seed is a separate first-buy transaction in the same Jito bundle as the launch, so " + "both land or neither does. That works on every plan, Free included, and needs no " + "swap:write. On hood the seed is " : "the atomic launch: the launch and the seed are two transactions Candle signs and sends as " + "one Jito bundle, so both land or neither does. That needs Pro or Max (TIER_REQUIRED " + "otherwise) and swap:write on the key, and the result carries bundleId. On hood the seed is ") + "a best-effort follow-up. A launch with no seed works on every plan, Free included. MOVES " + "REAL FUNDS unless dryRun; dry-run first, which spends nothing. Seeds above the platform " + "dev-buy ceiling are rejected (DEV_BUY_TOO_HIGH): at most 0.5 SOL / 500 USDC / 2,000,000 " + "CNDL, or on an exp- curve 10% of the threshold when that is lower; the server holds the " + "creator's own seed to the effective ceiling, so launch with the largest devBuy it allows " + "and top up with candle_trade. " + LAUNCH_SHARED_SENTENCES + (imageDir ? IMAGE_PATH_DISCLOSURE : ""),
@@ -45670,7 +45690,7 @@ async function registerEligibleProfileTool(server, env = process.env, doFetch =
45670
45690
  }
45671
45691
  return eligible;
45672
45692
  }
45673
- var VAMP_CANDIDATES_DESCRIPTION, localProfileTools, STAKING_DISCLOSURE = "No instant unstake. Read pool.withdrawalTimelockSeconds for the wait. Solana rewards accrue to the holding wallet. sCNDL in any eligible account wallet counts toward Pro and earns the account's Hood staking reward, except a wallet another account also claims, wallets past the 200-wallet cap, and an account with no EVM wallet. CNDL in a pending withdrawal counts toward nothing for Pro.", stakingWriteShape, TOOL_NAMES, LAB_TOOL_NAMES, PROFILE_EDIT_TOOL_NAMES, isProfileEditTool = (name) => PROFILE_EDIT_TOOL_NAMES.includes(name), OPERATION_NOT_FOUND_RULE, getMarketShape, TOKEN_EVENTS_LIMIT_MESSAGE = "limit must be a whole number from 1 to 1000", tokenEventsLimit, TOKEN_EVENTS_KIND_NOTE = "kept back from the request and applied to the returned page by this server, so a filtered page can be shorter than limit while older events of other kinds remain: page with nextCursor as before until it is null", tokenCardShape, tokenEventsShape, tokenForensicsShape, FEED_LIMIT_MESSAGE = "limit must be a whole number from 1 to 200", feedLimit, getFeedShape, reportActivityShape, getAgentProfileShape, listAgentsShape, createPostShape, deletePostShape, listAgentPostsShape, getAgentPositionsShape, getOperationShape, vampCandidatesShape, getFeesShape, claimFeesShape, resolveTokenShape, profileWalletsShape, profilePnlShape, profileTradesShape, setProfileWalletsShape, swapShape, tradeShape, tokenImageUrl, tokenBannerUrl, localImageShape, localLaunchTokenShape, localLaunchAndSeedShape, IMAGE_PATH_DISCLOSURE, transferShape, sweepShape, PROFILE_PNL_HYPERLIQUID;
45693
+ var VAMP_CANDIDATES_DESCRIPTION, localProfileTools, STAKING_DISCLOSURE = "No instant unstake. Read pool.withdrawalTimelockSeconds for the wait. Solana rewards accrue to the holding wallet. sCNDL in any eligible account wallet counts toward Pro and earns the account's Hood staking reward, except a wallet another account also claims, wallets past the 200-wallet cap, and an account with no EVM wallet. CNDL in a pending withdrawal counts toward nothing for Pro.", stakingWriteShape, TOOL_NAMES, LAB_TOOL_NAMES, PROFILE_EDIT_TOOL_NAMES, isProfileEditTool = (name) => PROFILE_EDIT_TOOL_NAMES.includes(name), OPERATION_NOT_FOUND_RULE, getMarketShape, TOKEN_EVENTS_LIMIT_MESSAGE = "limit must be a whole number from 1 to 1000", tokenEventsLimit, TOKEN_EVENTS_KIND_NOTE = "kept back from the request and applied to the returned page by this server, so a filtered page can be shorter than limit while older events of other kinds remain: page with nextCursor as before until it is null", tokenCardShape, tokenEventsShape, tokenForensicsShape, FEED_LIMIT_MESSAGE = "limit must be a whole number from 1 to 200", feedLimit, getFeedShape, reportActivityShape, getAgentProfileShape, listAgentsShape, createPostShape, deletePostShape, listAgentPostsShape, getAgentPositionsShape, getOperationShape, vampCandidatesShape, getFeesShape, atomicPayerShape, atomicLaunchDryRunShape, claimFeesShape, resolveTokenShape, profileWalletsShape, profilePnlShape, profileTradesShape, setProfileWalletsShape, swapShape, tradeShape, tokenImageUrl, tokenBannerUrl, localImageShape, localLaunchTokenShape, localLaunchAndSeedShape, IMAGE_PATH_DISCLOSURE, transferShape, sweepShape, PROFILE_PNL_HYPERLIQUID;
45674
45694
  var init_tools = __esm(() => {
45675
45695
  init_zod();
45676
45696
  init_client2();
@@ -45735,6 +45755,7 @@ var init_tools = __esm(() => {
45735
45755
  "candle_get_plans",
45736
45756
  "candle_trade",
45737
45757
  "candle_launch_and_seed",
45758
+ "candle_atomic_launch_dry_run",
45738
45759
  "candle_swap",
45739
45760
  "candle_transfer",
45740
45761
  "candle_sweep",
@@ -45886,6 +45907,22 @@ var init_tools = __esm(() => {
45886
45907
  cursor: exports_external.string().optional().describe("The previous page's continueCursor, verbatim"),
45887
45908
  limit: exports_external.number().optional().describe("Tokens per page, 1 to 50 (default 20). Each one costs chain reads")
45888
45909
  };
45910
+ atomicPayerShape = exports_external.union([
45911
+ exports_external.object({ type: exports_external.literal("main") }),
45912
+ exports_external.object({ type: exports_external.literal("linked"), linkedWalletId: exports_external.string().min(1) })
45913
+ ]);
45914
+ atomicLaunchDryRunShape = (() => {
45915
+ const { buyAmount: _buyAmount, chain: _chain, ...launch } = launchTokenShape;
45916
+ return {
45917
+ ...launch,
45918
+ clientLaunchId: exports_external.string().optional().describe("Not used by a dry run; send the id you will launch with"),
45919
+ payer: atomicPayerShape.optional().describe("Who pays for the launch: {type:'main'} (default) or {type:'linked', linkedWalletId}"),
45920
+ firstBuys: exports_external.array(exports_external.object({
45921
+ payer: atomicPayerShape,
45922
+ amountRaw: exports_external.string().regex(/^[1-9][0-9]*$/).describe("Quote-asset raw units")
45923
+ })).min(1).max(4).describe("One to four first buys, each priced on the curve this launch would deploy")
45924
+ };
45925
+ })();
45889
45926
  claimFeesShape = {
45890
45927
  mint: exports_external.string().describe("The launched token: a Solana mint or a Hood token address, as candle_get_fees lists it"),
45891
45928
  dryRun: exports_external.boolean().optional().describe("Check and read only. Sends nothing and writes nothing. Use it first to see what would be claimed")
@@ -74853,6 +74890,9 @@ init_keys_embedded_wallet();
74853
74890
  var DEVICE_CODE_PATH = "/api/v1/agent/device/code";
74854
74891
  var DEVICE_TOKEN_PATH = "/api/v1/agent/device/token";
74855
74892
  var MAX_CLIENT_NAME_LENGTH = 64;
74893
+ function expiresInWords(seconds) {
74894
+ return seconds % 60 === 0 ? `${seconds / 60}m` : `${seconds}s`;
74895
+ }
74856
74896
  function loginApiUrlFault(value, env) {
74857
74897
  let url;
74858
74898
  try {
@@ -74975,11 +75015,14 @@ async function authLogin(args, ctx) {
74975
75015
  const code = codeResult.body;
74976
75016
  const progress = json ? deps.stderr : deps.stdout;
74977
75017
  if (json) {
74978
- progress.write(`API: ${apiUrl}
74979
- `);
74980
- progress.write(`Your device code: ${code.userCode}
74981
- `);
74982
- progress.write(`Open this URL to approve: ${code.verificationUriComplete}
75018
+ progress.write(`${JSON.stringify({
75019
+ stage: "device_code",
75020
+ apiUrl,
75021
+ userCode: code.userCode,
75022
+ verificationUri: code.verificationUri,
75023
+ verificationUriComplete: code.verificationUriComplete,
75024
+ expiresIn: code.expiresIn
75025
+ })}
74983
75026
  `);
74984
75027
  } else {
74985
75028
  const style = terminalStyle(deps, "stdout");
@@ -74988,7 +75031,8 @@ async function authLogin(args, ctx) {
74988
75031
  ${fields([
74989
75032
  ["API", apiUrl],
74990
75033
  ["Device code", code.userCode],
74991
- ["Approve at", code.verificationUriComplete]
75034
+ ["Approve at", code.verificationUriComplete],
75035
+ ["Code expires in", expiresInWords(code.expiresIn)]
74992
75036
  ], style).join(`
74993
75037
  `)}
74994
75038
 
@@ -76100,7 +76144,7 @@ var LAUNCH_DEFINITION = {
76100
76144
  {
76101
76145
  key: "feeWallet",
76102
76146
  type: "string",
76103
- help: "Solana only: the wallet this launch's curve and pool fees earn to, an on-curve wallet or a Squads v4 vault; defaults to the account wallet.",
76147
+ help: "Solana only: the wallet this launch's curve and pool fees earn to, an on-curve wallet or a Squads v4 vault; defaults to the wallet that launches the token (the account wallet on a server without LAUNCH_FEE_WALLET_FROM_LAUNCHER, except an embedded launch).",
76104
76148
  chains: [
76105
76149
  "solana"
76106
76150
  ],
@@ -76756,7 +76800,7 @@ var HELP = {
76756
76800
  launch: {
76757
76801
  group: "Trade",
76758
76802
  summary: "Create a token on Solana or Hood, with an optional first buy",
76759
- description: "Creates a token from the wallet you name, with an optional first buy. Embedded launches use /launch/headless on every plan, with the key's embedded-wallet allowance and launch:write; no swap:write is needed. A Solana first buy is a creator bundle, signed by Candle. A Hood first buy is best effort after creation. TEE launches use /launch/self and require Pro or Max, allowLaunch, a bound key and verified authority. TEE buys need TEE_LAUNCH_BUYS_ENABLED on the server; Solana buys also need AGENT_TRADING_ENABLED and Jito. Existing server gates and ceilings apply. The embedded path previews the launch before confirmation; --dry-run stops at the preview. No buy creates only the token. Local image and banner uploads happen before confirmation and are public and permanent even if the launch is declined. Reuse --client-trade-id for recovery. On Solana, --fee-wallet <address> sets the wallet the launch's curve and pool fees earn to, an on-curve wallet by itself or a Squads v4 vault with --fee-wallet-multisig <address> and --fee-wallet-multisig-vault-index <n>; without it the fees earn to the account wallet, which the preview, the confirm and --check all say; a live launch also warns on stderr when it defaults, and --require-fee-wallet refuses the launch without one. --check answers first, before any of it: which launch path works on this key and server (embedded allowance, tier, allowLaunch, TEE_LAUNCH_BUYS_ENABLED) in one call, with nothing uploaded, built or sent; name a wallet with --wallet to narrow it, and it exits 1 when no path works. status reads one launch by its mint or clientLaunchId: build, signed, confirmed, trading, bonded, then migrated, with the market's lifecycle and migration fields. --wait follows it until the token is trading, the launch has failed, or ten minutes pass.",
76803
+ description: "Creates a token from the wallet you name, with an optional first buy. Embedded launches use /launch/headless on every plan, with the key's embedded-wallet allowance and launch:write; no swap:write is needed. A Solana first buy is a creator bundle, signed by Candle. A Hood first buy is best effort after creation. TEE launches use /launch/self and require Pro or Max, allowLaunch, a bound key and verified authority. TEE buys need TEE_LAUNCH_BUYS_ENABLED on the server; Solana buys also need AGENT_TRADING_ENABLED and Jito. Existing server gates and ceilings apply. The embedded path previews the launch before confirmation; --dry-run stops at the preview. No buy creates only the token. Local image and banner uploads happen before confirmation and are public and permanent even if the launch is declined. Reuse --client-trade-id for recovery. On Solana, --fee-wallet <address> sets the wallet the launch's curve and pool fees earn to, an on-curve wallet by itself or a Squads v4 vault with --fee-wallet-multisig <address> and --fee-wallet-multisig-vault-index <n>; without it the fees earn to the wallet that launches the token where the server has LAUNCH_FEE_WALLET_FROM_LAUNCHER on (an embedded launch already earns to its embedded wallet), otherwise to the account wallet, and the preview, the confirm and --check all say which; a live launch also warns on stderr when it defaults, and --require-fee-wallet refuses the launch without one. --check answers first, before any of it: which launch path works on this key and server (embedded allowance, tier, allowLaunch, TEE_LAUNCH_BUYS_ENABLED) in one call, with nothing uploaded, built or sent; name a wallet with --wallet to narrow it, and it exits 1 when no path works. status reads one launch by its mint or clientLaunchId: build, signed, confirmed, trading, bonded, then migrated, with the market's lifecycle and migration fields. --wait follows it until the token is trading, the launch has failed, or ten minutes pass.",
76760
76804
  usage: [LAUNCH_USAGE, "candle launch status <mint-or-client-launch-id> [--wait]"],
76761
76805
  rows: [
76762
76806
  {
@@ -77261,7 +77305,9 @@ var HELP = {
77261
77305
  invocation: "self embedded-wallet deny",
77262
77306
  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"
77263
77307
  },
77264
- { invocation: "revoke <prefix>", description: "Revoke an API key" },
77308
+ { invocation: "stop <prefix>", description: "Pause one key with the device token; reversible with keys resume" },
77309
+ { invocation: "resume <prefix>", description: "Resume a stopped key with the device token" },
77310
+ { invocation: "revoke <prefix>", description: "Revoke an API key permanently" },
77265
77311
  {
77266
77312
  invocation: "signer approve <code> --key <prefix|label> [--reject]",
77267
77313
  description: "Approve (or reject) a key's signer request with the device token; type the full fingerprint the trading machine printed"
@@ -77296,7 +77342,9 @@ var HELP = {
77296
77342
  "candle tee rebind --label-prefix dest- --to-key Ab3dEf9h",
77297
77343
  "candle keys signer approve ABCD-EFGH --key tr-2",
77298
77344
  "candle keys signer move tr-2",
77299
- "candle keys revoke ck_live_ab12"
77345
+ "candle keys revoke ck_live_ab12",
77346
+ "candle keys stop ck_live_ab12",
77347
+ "candle keys resume ck_live_ab12"
77300
77348
  ],
77301
77349
  env: ENV_API
77302
77350
  },
@@ -78207,6 +78255,7 @@ var TOOL_CLASSES = {
78207
78255
  candle_execution_status: READ,
78208
78256
  candle_get_operation: READ,
78209
78257
  candle_get_fees: READ,
78258
+ candle_atomic_launch_dry_run: READ,
78210
78259
  candle_get_staking: READ,
78211
78260
  candle_perps_markets: READ,
78212
78261
  candle_perps_market: READ,
@@ -89775,6 +89824,8 @@ var REASON_WORDS = {
89775
89824
  embedded_wallet_not_permitted: "this key may not use the embedded wallet",
89776
89825
  not_recipient: "the fees go to someone else, or the token has no fee vault",
89777
89826
  fee_wallet_designated: "the fees go to this launch's designated fee wallet, which claims them itself",
89827
+ claimable_by_key: "claimable by this key",
89828
+ claimable_via_web_session: "the fees go to this account's web wallet; a signed-in session claims them",
89778
89829
  insufficient_gas: "the recipient wallet cannot cover the network fee"
89779
89830
  };
89780
89831
  function reasonText(reason, missing) {
@@ -89809,7 +89860,7 @@ async function feesList(args, ctx) {
89809
89860
  notice(ctx.deps, "stdout", "note", "No launched tokens on this account.");
89810
89861
  else {
89811
89862
  const rows = page.map((row) => {
89812
- const status = row.unreadable ? "could not be read" : row.claimable ? `claimable: ${amountsText(row.claimableAmounts, missing)}` : `not claimable: ${reasonText(row.reason, missing)}`;
89863
+ const status = row.unreadable ? "could not be read" : row.claimable ? `${row.reason === "claimable_by_key" ? "claimable by this key" : "claimable"}: ${amountsText(row.claimableAmounts, missing)}` : `not claimable: ${reasonText(row.reason, missing)}`;
89813
89864
  return [
89814
89865
  safeText(String(row.symbol ?? row.name ?? row.mint)),
89815
89866
  safeText(String(row.chain)),
@@ -90388,6 +90439,63 @@ ${fields(rows, style).join(`
90388
90439
  return 0;
90389
90440
  }
90390
90441
 
90442
+ // src/commands/keys-stop.ts
90443
+ init_args();
90444
+ init_client();
90445
+ init_deps();
90446
+ init_presentation();
90447
+ init_profiles();
90448
+ init_key_output();
90449
+ init_keys();
90450
+ function keysStop(args, ctx) {
90451
+ return setKeyPaused(args, ctx, true);
90452
+ }
90453
+ function keysResume(args, ctx) {
90454
+ return setKeyPaused(args, ctx, false);
90455
+ }
90456
+ async function setKeyPaused(args, ctx, paused) {
90457
+ const { deps, apiUrl, json } = ctx;
90458
+ const parsed = parseArgs(args, {});
90459
+ if ("error" in parsed) {
90460
+ writeScreenUsageFailure(deps, parsed.error, json);
90461
+ return 2;
90462
+ }
90463
+ const prefix = parsed.positionals[0];
90464
+ if (!prefix?.trim() || parsed.positionals.length !== 1) {
90465
+ writeScreenUsageFailure(deps, `Usage: candle keys ${paused ? "stop" : "resume"} <prefix>`, json);
90466
+ return 2;
90467
+ }
90468
+ await printIdentity(ctx);
90469
+ const deviceToken = await resolveDeviceToken(deps, ctx.profile);
90470
+ if (!deviceToken) {
90471
+ writeScreenLocalFailure(deps, NO_DEVICE_TOKEN, json);
90472
+ return 1;
90473
+ }
90474
+ const result = await apiRequest(`/api/v1/agent/keys/${encodeURIComponent(prefix)}/stop`, {
90475
+ method: "POST",
90476
+ auth: "device",
90477
+ credentials: { deviceToken },
90478
+ apiUrl,
90479
+ fetch: deps.fetch,
90480
+ env: deps.env,
90481
+ body: { paused }
90482
+ });
90483
+ if (!result.ok) {
90484
+ writeScreenFailure(deps, result, { apiUrl, authType: "device" }, json);
90485
+ return 1;
90486
+ }
90487
+ if (json) {
90488
+ deps.stdout.write(`${JSON.stringify(result.body)}
90489
+ `);
90490
+ return 0;
90491
+ }
90492
+ const body = result.body;
90493
+ const state = body.paused ? "stopped" : "resumed";
90494
+ deps.stdout.write(`${success(`Key ${prefix} ${body.changed ? "" : "already "}${state}.`, terminalStyle(deps, "stdout"))}
90495
+ `);
90496
+ return 0;
90497
+ }
90498
+
90391
90499
  // src/commands/keys-wallets.ts
90392
90500
  init_args();
90393
90501
  init_client();
@@ -91028,7 +91136,8 @@ function decodeAddress(value, flag) {
91028
91136
  throw new Error(`${flag} takes a Solana address.`);
91029
91137
  }
91030
91138
  }
91031
- function feeWalletLines(selection, echo) {
91139
+ var fromLauncher = (echo) => echo?.feeWalletSource === "launching_wallet" && typeof echo.feeWallet === "string";
91140
+ function feeWalletLines(selection, echo, launcherDefault = false) {
91032
91141
  if (selection)
91033
91142
  return [
91034
91143
  `Fee wallet: ${selection.feeWallet} (earns this launch's fees; set by --fee-wallet)`,
@@ -91036,14 +91145,20 @@ function feeWalletLines(selection, echo) {
91036
91145
  `Fee wallet multisig: ${selection.feeWalletMultisig.address}, vault ${selection.feeWalletMultisig.vaultIndex} (the Squads v4 multisig behind the fee wallet)`
91037
91146
  ] : []
91038
91147
  ];
91148
+ if (fromLauncher(echo))
91149
+ return [
91150
+ `Fee wallet: ${echo.feeWallet} (defaults to the launching wallet; pass --fee-wallet <address> to change it)`
91151
+ ];
91152
+ if (launcherDefault)
91153
+ return ["Fee wallet: defaults to the launching wallet (pass --fee-wallet <address> to change it)."];
91039
91154
  return [
91040
- echo ? `Fee wallet: ${echo} (defaults to the account wallet; pass --fee-wallet <address> to change it)` : "Fee wallet: defaults to the account wallet (pass --fee-wallet <address> to change it)."
91155
+ echo?.feeWallet ? `Fee wallet: ${echo.feeWallet} (defaults to the account wallet; pass --fee-wallet <address> to change it)` : "Fee wallet: defaults to the account wallet (pass --fee-wallet <address> to change it)."
91041
91156
  ];
91042
91157
  }
91043
- var FEE_WALLET_DEFAULT_WARNING = "No --fee-wallet given: this launch's curve and pool fees earn to the account wallet. Pass --fee-wallet <address> to send them to another wallet.";
91044
- function feeWalletQuoteLine(selection) {
91158
+ var FEE_WALLET_DEFAULT_WARNING = "No --fee-wallet given: this launch's curve and pool fees earn to the launching wallet, or to the account wallet on a server that does not record the launching wallet; the confirmation names which. Pass --fee-wallet <address> to send them to another wallet.";
91159
+ function feeWalletQuoteLine(selection, echo) {
91045
91160
  if (!selection)
91046
- return "defaults to the account wallet";
91161
+ return fromLauncher(echo) ? `${echo.feeWallet} (the launching wallet, by default)` : "defaults to the account wallet";
91047
91162
  return `${selection.feeWallet}${selection.feeWalletMultisig ? ` (Squads vault of ${selection.feeWalletMultisig.address}, vault ${selection.feeWalletMultisig.vaultIndex})` : ""}`;
91048
91163
  }
91049
91164
 
@@ -91190,7 +91305,8 @@ var checkSchema = exports_external.object({
91190
91305
  switches: exports_external.object({
91191
91306
  headlessLaunch: exports_external.boolean(),
91192
91307
  agentTrading: exports_external.boolean(),
91193
- teeLaunchBuys: exports_external.boolean()
91308
+ teeLaunchBuys: exports_external.boolean(),
91309
+ feeWalletFromLauncher: exports_external.boolean().optional()
91194
91310
  }),
91195
91311
  embedded: exports_external.object({
91196
91312
  solana: exports_external.object({ address: exports_external.string(), delegated: exports_external.boolean().optional() }).nullable().optional(),
@@ -91302,8 +91418,9 @@ async function launchCheck(ctx, key, walletFlag, requestedChain, feeWallet = und
91302
91418
  const lines = [`Launch paths for key ${answer2.key.keyPrefix} (tier ${answer2.key.tier}):`];
91303
91419
  if (requestedChain)
91304
91420
  lines.push(`Requested launch chain: ${chainName(requestedChain)}.`);
91421
+ const launcherDefault = answer2.switches.feeWalletFromLauncher === true;
91305
91422
  if (!requestedChain || requestedChain === "solana")
91306
- lines.push(...feeWalletLines(feeWallet));
91423
+ lines.push(...feeWalletLines(feeWallet, undefined, launcherDefault));
91307
91424
  if (walletFlag === undefined || embeddedOnly)
91308
91425
  lines.push(...embeddedLines(answer2, requestedChain));
91309
91426
  for (const row of shownWallets)
@@ -91317,7 +91434,7 @@ async function launchCheck(ctx, key, walletFlag, requestedChain, feeWallet = und
91317
91434
  ...requestedChain ? { requestedChain } : {},
91318
91435
  key: answer2.key,
91319
91436
  switches: answer2.switches,
91320
- ...feeWallet ? { feeWallet: { ...feeWallet, set: true } } : { feeWallet: { default: "account wallet", set: false } },
91437
+ ...feeWallet ? { feeWallet: { ...feeWallet, set: true } } : { feeWallet: { default: launcherDefault ? "launching wallet" : "account wallet", set: false } },
91321
91438
  ...answer2.embedded ? { embedded: answer2.embedded } : {},
91322
91439
  wallets: shownWallets.map((row) => ({
91323
91440
  ...row,
@@ -91664,7 +91781,7 @@ async function launch(args, ctx) {
91664
91781
  fee: built.fee ?? { bps: 0, feeRaw: "0" },
91665
91782
  minimumReceived: buy ? "not quoted (the first buy on the new curve)" : "0 tokens (no first buy)",
91666
91783
  maxDebitLamports: built.maxDebitLamports,
91667
- feeWallet: feeWalletQuoteLine(feeWallet),
91784
+ feeWallet: feeWalletQuoteLine(feeWallet, built),
91668
91785
  tokenRisks: []
91669
91786
  };
91670
91787
  notice(ctx.deps, "stderr", "note", buyNotice(buy, "solana").trim());
@@ -91794,14 +91911,19 @@ var previewSchema = exports_external.object({
91794
91911
  }).passthrough().optional(),
91795
91912
  devBuyMax: exports_external.string().nullable().optional(),
91796
91913
  feeWallet: exports_external.string().optional(),
91914
+ feeWalletSource: exports_external.string().optional(),
91797
91915
  fills: exports_external.array(exports_external.object({ index: exports_external.number(), amountRaw: exports_external.string(), supplyPct: exports_external.number() }).passthrough()).optional()
91798
91916
  }).passthrough();
91799
91917
  var percent = (value) => `${Number(value.toFixed(4))}%`;
91918
+ function feeWalletEchoOf(answer2) {
91919
+ const parsed = exports_external.object({ feeWallet: exports_external.string().optional(), feeWalletSource: exports_external.string().optional() }).safeParse(answer2);
91920
+ return parsed.success ? parsed.data : undefined;
91921
+ }
91800
91922
  function previewLines(answer2, chain2, feeWallet = undefined) {
91801
91923
  const parsed = previewSchema.safeParse(answer2);
91802
91924
  if (!parsed.success)
91803
91925
  return "";
91804
- const { resolved, curveTerms: terms, devBuyMax, feeWallet: echo, fills } = parsed.data;
91926
+ const { resolved, curveTerms: terms, devBuyMax, feeWallet: echo, feeWalletSource, fills } = parsed.data;
91805
91927
  const quote = (resolved?.quoteAsset ?? LAUNCH_DEFINITION.defaultQuote[chain2]).toUpperCase();
91806
91928
  const decimals = (chain2 === "hood" ? HOOD_BASES[quote] : BASES[quote])?.decimals;
91807
91929
  const whole = (raw) => decimals === undefined ? `${raw} raw` : `${shortAmount(raw, decimals)} ${quote}`;
@@ -91820,7 +91942,7 @@ function previewLines(answer2, chain2, feeWallet = undefined) {
91820
91942
  if (devBuyMax)
91821
91943
  lines.push(`Dev-buy ceiling: ${whole(devBuyMax)}.`);
91822
91944
  if (chain2 === "solana")
91823
- lines.push(...feeWalletLines(feeWallet, echo));
91945
+ lines.push(...feeWalletLines(feeWallet, { feeWallet: echo, feeWalletSource }));
91824
91946
  return lines.length ? `${lines.join(`
91825
91947
  `)}
91826
91948
  ` : "";
@@ -91886,7 +92008,9 @@ var atomicLegSchema = exports_external.object({
91886
92008
  var atomicBuildSchema = exports_external.object({
91887
92009
  bundleId: exports_external.string().min(1),
91888
92010
  legs: exports_external.array(atomicLegSchema),
91889
- expiresAt: exports_external.number().finite()
92011
+ expiresAt: exports_external.number().finite(),
92012
+ feeWallet: exports_external.string().optional(),
92013
+ feeWalletSource: exports_external.string().optional()
91890
92014
  });
91891
92015
  var atomicOutcomeSchema = exports_external.discriminatedUnion("status", [
91892
92016
  exports_external.object({ status: exports_external.literal("landed"), bundleId: exports_external.string(), mint: exports_external.string(), signatures: exports_external.array(exports_external.string()) }).passthrough(),
@@ -91930,7 +92054,7 @@ async function atomicLaunch(ctx, key, args) {
91930
92054
  candleFee: reading.feeRaw > 0n ? `${decimalAmount(reading.feeRaw.toString(), decimals)} ${quoteAsset} (the tier fee on the first buy, in its leg)` : "none on this first buy",
91931
92055
  minimumReceived: minOut !== undefined ? `${minOut} raw units of the new token (the first buy's floor)` : "not quoted",
91932
92056
  maxLoss: quoteAsset === "SOL" ? sol : `${sol} and ${decimalAmount(reading.quoteRaw.toString(), decimals)} ${quoteAsset}`,
91933
- feeWallet: feeWalletQuoteLine(feeWalletSelection(flags)),
92057
+ feeWallet: feeWalletQuoteLine(feeWalletSelection(flags), built),
91934
92058
  tokenRisks: [],
91935
92059
  bundleId: built.bundleId
91936
92060
  };
@@ -92092,7 +92216,7 @@ async function embeddedLaunch(ctx, key, args) {
92092
92216
  venue: "curve launch (signed by Candle)",
92093
92217
  priceImpactPct: null,
92094
92218
  minimumReceived: "0 tokens (no first buy)",
92095
- ...chain2 === "solana" ? { feeWallet: feeWalletQuoteLine(feeWalletSelection(flags)) } : {},
92219
+ ...chain2 === "solana" ? { feeWallet: feeWalletQuoteLine(feeWalletSelection(flags), feeWalletEchoOf(answer2)) } : {},
92096
92220
  tokenRisks: []
92097
92221
  };
92098
92222
  if (buy && chain2 === "solana") {
@@ -92867,6 +92991,7 @@ var MCP_TOOL_NAMES = [
92867
92991
  "candle_execution_status",
92868
92992
  "candle_get_operation",
92869
92993
  "candle_get_fees",
92994
+ "candle_atomic_launch_dry_run",
92870
92995
  "candle_claim_fees",
92871
92996
  "candle_get_staking",
92872
92997
  "candle_stake",
@@ -115054,6 +115179,8 @@ var COMMANDS = {
115054
115179
  limit: keysLimit,
115055
115180
  self: keysSelf,
115056
115181
  revoke: keysRevoke,
115182
+ stop: keysStop,
115183
+ resume: keysResume,
115057
115184
  wallets: keysWallets,
115058
115185
  signer: keysSigner
115059
115186
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@candledottv/cli",
3
- "version": "0.13.10",
3
+ "version": "0.13.12",
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": {