@candledottv/cli 0.13.23 → 0.13.25
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/README.md +25 -18
- package/dist/index.js +287 -100
- 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.
|
|
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
|
|
113
|
-
| `candle keys resume <prefix>` |
|
|
112
|
+
| `candle keys stop <prefix\|label>` | Pause one key using the device token. Its requests are refused with `PROFILE_PAUSED` until the owner resumes it in the signed-in console; the key and its local credential are kept. Repeating the command succeeds. |
|
|
113
|
+
| `candle keys resume <prefix\|label>` | A device token cannot resume a stopped key: the server answers `LOOSEN_REQUIRES_SESSION` and the key stays stopped. The owner resumes it in the signed-in console. 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. |
|
|
@@ -120,9 +120,9 @@ chosen words from your saved copy. Clear your terminal's scrollback too: the CLI
|
|
|
120
120
|
| `candle wallets authorize` | Gives agents access to the account's embedded wallets. The grant is yours to make in a browser (each wallet's owner is you, so neither the CLI nor the API can attach Candle's signer): this opens the Agents page's grant-only step, creates no key, and with an API key waits until the wallets show as delegated. On a new account, run it before `candle auth login`. |
|
|
121
121
|
| `candle wallets trust\|untrust <label\|address\|id\|prefix*>...` | Mark linked wallets as yours so an agent's Withdraw key can move funds into them, or clear the mark. Owner only, over the device token, with a typed confirm; a trailing `*`, as in `'tr-*'`, selects by prefix. |
|
|
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
|
-
| `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`. |
|
|
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 (`swap:write` or `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`
|
|
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. |
|
|
@@ -240,7 +240,7 @@ transparency log: [Verify a Candle release](https://docs.candle.tv/developers/ve
|
|
|
240
240
|
|
|
241
241
|
Where the API has `OWNER_CONFIRM_ENABLED` on, an owner step made with the device token that widens what the account's keys can do also needs one approval in your browser, signed in as the account owner. The API answers `403 OWNER_CONFIRM_REQUIRED` and changes nothing; the CLI then:
|
|
242
242
|
|
|
243
|
-
1. Prints what you are approving (a summary the server built), `Open <url>` and `Code: XXXX-XXXX` on stderr, with "Only approve if you just ran this command." It opens the browser only with `--open`. Under `--json` it writes one line to stderr instead, `{"ok":false,"pending":true,"code":"OWNER_CONFIRM_REQUIRED","ownerConfirm":{"userCode","verificationUriComplete","expiresIn","step","summary"}}`, and keeps stdout for the final result.
|
|
243
|
+
1. Prints what you are approving (a summary the server built), `Open <url>` and `Code: XXXX-XXXX` on stderr, with "Only approve if you just ran this command." When the API names the account the request was made as (0.13.24+), the first line reads `This needs your approval in the browser as <display name> (<short wallet>): <summary>` and a following line says the browser must be signed in as that account — so a wrong-account browser is obvious before it answers "Request not found". It opens the browser only with `--open`. Under `--json` it writes one line to stderr instead, `{"ok":false,"pending":true,"code":"OWNER_CONFIRM_REQUIRED","ownerConfirm":{"userCode","verificationUriComplete","expiresIn","step","summary","account":{"wallet","displayName"}}}`, and keeps stdout for the final result.
|
|
244
244
|
2. Waits, polling every few seconds. The code expires 5 minutes after it was issued.
|
|
245
245
|
3. When you approve at `/dev/agent/confirm`, sends the identical request again and prints its result. Deny it and the command exits 1 with `Denied in the browser; nothing changed.`; let it expire and it exits 1 with `The confirmation expired; nothing changed. Run the command again.`
|
|
246
246
|
4. With `--no-wait` it prints the code and exits 7 without waiting. The command does not resume later: run it again without `--no-wait`, or make the change in the console.
|
|
@@ -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
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
|
306
|
-
|
|
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
|
-
|
|
312
|
-
|
|
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 (
|
|
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.
|
|
473
|
-
|
|
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
|
}
|
|
@@ -728,6 +734,23 @@ var init_agent_key_access = __esm(() => {
|
|
|
728
734
|
];
|
|
729
735
|
});
|
|
730
736
|
|
|
737
|
+
// src/terminal-text.ts
|
|
738
|
+
function escapeTerminalText(value) {
|
|
739
|
+
return value.replace(/[\u0000-\u0008\u000a-\u001f\u007f-\u009f]/g, (char) => {
|
|
740
|
+
if (char === `
|
|
741
|
+
`)
|
|
742
|
+
return "\\n";
|
|
743
|
+
if (char === "\r")
|
|
744
|
+
return "\\r";
|
|
745
|
+
return `\\x${char.charCodeAt(0).toString(16).padStart(2, "0")}`;
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
function terminalLines(value) {
|
|
749
|
+
return value.split(`
|
|
750
|
+
`).map(escapeTerminalText).join(`
|
|
751
|
+
`);
|
|
752
|
+
}
|
|
753
|
+
|
|
731
754
|
// src/terminal-theme.ts
|
|
732
755
|
var TERMINAL_TOKENS;
|
|
733
756
|
var init_terminal_theme = __esm(() => {
|
|
@@ -755,7 +778,7 @@ var init_terminal_theme = __esm(() => {
|
|
|
755
778
|
});
|
|
756
779
|
|
|
757
780
|
// src/version.ts
|
|
758
|
-
var CLI_VERSION = "0.13.
|
|
781
|
+
var CLI_VERSION = "0.13.25";
|
|
759
782
|
|
|
760
783
|
// src/presentation.ts
|
|
761
784
|
function terminalStyle(deps, stream, machine = false) {
|
|
@@ -775,6 +798,7 @@ function terminalStyle(deps, stream, machine = false) {
|
|
|
775
798
|
};
|
|
776
799
|
}
|
|
777
800
|
function accent(text, style, role = "brand") {
|
|
801
|
+
text = terminalLines(text);
|
|
778
802
|
if (!style.color)
|
|
779
803
|
return text;
|
|
780
804
|
const token = TERMINAL_TOKENS[style.theme][role];
|
|
@@ -938,6 +962,7 @@ function symbols(style) {
|
|
|
938
962
|
return style.styled ? UNICODE_SYMBOLS : ASCII_SYMBOLS;
|
|
939
963
|
}
|
|
940
964
|
function sgr(text, style, code) {
|
|
965
|
+
text = terminalLines(text);
|
|
941
966
|
return style.color && text ? `\x1B[${code}m${text}\x1B[0m` : text;
|
|
942
967
|
}
|
|
943
968
|
function bold(text, style) {
|
|
@@ -947,30 +972,32 @@ function dim(text, style) {
|
|
|
947
972
|
return sgr(text, style, "2");
|
|
948
973
|
}
|
|
949
974
|
function heading(text, style) {
|
|
950
|
-
return wrapText(text, proseWidth(style)).map((line) => bold(line, style)).join(`
|
|
975
|
+
return wrapText(terminalLines(text), proseWidth(style)).map((line) => bold(line, style)).join(`
|
|
951
976
|
`);
|
|
952
977
|
}
|
|
953
978
|
function fields(rows, style, indent = 2) {
|
|
979
|
+
rows = rows.map(([label, value]) => [escapeTerminalText(label), value]);
|
|
954
980
|
const budget = proseWidth(style);
|
|
955
981
|
const pad = " ".repeat(Math.min(indent, Math.max(0, budget)));
|
|
956
982
|
const labelWidth = Math.max(0, ...rows.map(([label]) => displayWidth(label)));
|
|
957
983
|
const sideWidth = budget - pad.length - labelWidth - 2;
|
|
958
984
|
const out = [];
|
|
959
985
|
for (const [label, raw] of rows) {
|
|
960
|
-
const
|
|
986
|
+
const paint = (text) => typeof raw === "object" && raw !== null ? accent(text, style, raw.role) : text;
|
|
987
|
+
const value = raw === undefined || raw === null || raw === "" ? symbols(style).missing : escapeTerminalText(typeof raw === "object" ? raw.text : String(raw));
|
|
961
988
|
if (sideWidth >= 1 && fitsWidth(value, sideWidth)) {
|
|
962
989
|
const [first = "", ...rest] = value.split(`
|
|
963
990
|
`).flatMap((line) => wrapText(line, sideWidth));
|
|
964
|
-
out.push(`${pad}${dim(padDisplay(label, labelWidth), style)} ${first}`);
|
|
991
|
+
out.push(`${pad}${dim(padDisplay(label, labelWidth), style)} ${paint(first)}`);
|
|
965
992
|
for (const line of rest)
|
|
966
|
-
out.push(`${pad}${" ".repeat(labelWidth + 2)}${line}`);
|
|
993
|
+
out.push(`${pad}${" ".repeat(labelWidth + 2)}${paint(line)}`);
|
|
967
994
|
} else {
|
|
968
|
-
out.push(...stackedField(label, value, style, pad, budget));
|
|
995
|
+
out.push(...stackedField(label, value, style, pad, budget, paint));
|
|
969
996
|
}
|
|
970
997
|
}
|
|
971
998
|
return out;
|
|
972
999
|
}
|
|
973
|
-
function stackedField(label, value, style, pad, budget) {
|
|
1000
|
+
function stackedField(label, value, style, pad, budget, paint) {
|
|
974
1001
|
const labelBudget = Math.max(1, budget - pad.length);
|
|
975
1002
|
const out = wrapText(label, labelBudget).map((line) => `${pad}${dim(line, style)}`);
|
|
976
1003
|
const nested = budget - pad.length - 2;
|
|
@@ -987,11 +1014,12 @@ function stackedField(label, value, style, pad, budget) {
|
|
|
987
1014
|
for (const part of value.split(`
|
|
988
1015
|
`)) {
|
|
989
1016
|
for (const line of wrapText(part, valueWidth))
|
|
990
|
-
out.push(line ? `${valuePad}${line}` : "");
|
|
1017
|
+
out.push(line ? `${valuePad}${paint(line)}` : "");
|
|
991
1018
|
}
|
|
992
1019
|
return out;
|
|
993
1020
|
}
|
|
994
1021
|
function renderNotice(level, text, style, indent = 2) {
|
|
1022
|
+
text = terminalLines(text);
|
|
995
1023
|
const sym = symbols(style);
|
|
996
1024
|
const mark = level === "note" ? sym.note : level === "warning" ? sym.warning : sym.danger;
|
|
997
1025
|
const budget = proseWidth(style);
|
|
@@ -1021,6 +1049,7 @@ ${renderNotice(level, text, style).join(`
|
|
|
1021
1049
|
`);
|
|
1022
1050
|
}
|
|
1023
1051
|
function success(text, style) {
|
|
1052
|
+
text = terminalLines(text);
|
|
1024
1053
|
const mark = symbols(style).ok;
|
|
1025
1054
|
const hang = " ".repeat(displayWidth(mark) + 1);
|
|
1026
1055
|
const budget = Math.max(1, proseWidth(style) - displayWidth(mark) - 1);
|
|
@@ -1042,17 +1071,19 @@ var init_presentation = __esm(() => {
|
|
|
1042
1071
|
});
|
|
1043
1072
|
|
|
1044
1073
|
// src/render.ts
|
|
1045
|
-
function formatScopesForSummary(scopes) {
|
|
1046
|
-
return sortAgentKeyScopes(scopes).map((scope) => scope === "swap:write" ? `${scope} (${SWAP_WRITE_NOTE})` : scope === "transfer:write" ? `${scope} (${TRANSFER_WRITE_NOTE})` : scope === "transfer:bound" ? `${scope} (${TRANSFER_BOUND_NOTE})` : scope === "perps:write" ? `${scope} (${PERPS_WRITE_NOTE})` : scope).join(", ");
|
|
1047
|
-
}
|
|
1048
1074
|
function terminalText(value) {
|
|
1049
1075
|
return value.replace(/[\u0000-\u001f\u007f-\u009f]/g, "");
|
|
1050
1076
|
}
|
|
1077
|
+
function formatScopesForSummary(scopes) {
|
|
1078
|
+
return sortAgentKeyScopes(scopes).map((scope) => scope === "swap:write" ? `${scope} (${SWAP_WRITE_NOTE})` : scope === "transfer:write" ? `${scope} (${TRANSFER_WRITE_NOTE})` : scope === "transfer:bound" ? `${scope} (${TRANSFER_BOUND_NOTE})` : scope === "perps:write" ? `${scope} (${PERPS_WRITE_NOTE})` : scope).join(", ");
|
|
1079
|
+
}
|
|
1051
1080
|
function numericColumn(rows, col) {
|
|
1052
1081
|
const known = rows.map((row) => row[col] ?? "").filter((cell) => cell !== "" && cell !== "–" && cell !== "-");
|
|
1053
1082
|
return known.length > 0 && known.every((cell) => NUMERIC_CELL.test(cell));
|
|
1054
1083
|
}
|
|
1055
1084
|
function renderTable(headers, rows, options) {
|
|
1085
|
+
headers = headers.map(escapeTerminalText);
|
|
1086
|
+
rows = rows.map((row) => row.map((cell) => escapeTerminalText(cell ?? "")));
|
|
1056
1087
|
if (!options)
|
|
1057
1088
|
return renderPlainTable(headers, rows);
|
|
1058
1089
|
const specs = headers.map((_, col) => options.columns?.[col] ?? {});
|
|
@@ -1213,6 +1244,14 @@ function errorParts(result, ctx) {
|
|
|
1213
1244
|
return { message: result.message, fixes: result.uiHint ? [{ label: result.uiHint }] : [] };
|
|
1214
1245
|
}
|
|
1215
1246
|
function renderErrorBlock(parts, style) {
|
|
1247
|
+
parts = {
|
|
1248
|
+
message: terminalLines(parts.message),
|
|
1249
|
+
fixes: parts.fixes.map((fix) => ({
|
|
1250
|
+
...fix,
|
|
1251
|
+
label: terminalLines(fix.label),
|
|
1252
|
+
...fix.command ? { command: terminalLines(fix.command) } : {}
|
|
1253
|
+
}))
|
|
1254
|
+
};
|
|
1216
1255
|
const budget = proseWidth(style);
|
|
1217
1256
|
const out = [...errorMessageLines(parts.message, style, budget)];
|
|
1218
1257
|
if (parts.fixes.length === 0)
|
|
@@ -1280,7 +1319,7 @@ function writeFailure(deps, result, ctx, json) {
|
|
|
1280
1319
|
deps.stdout.write(`${JSON.stringify(errorEnvelope(result, ctx))}
|
|
1281
1320
|
`);
|
|
1282
1321
|
else
|
|
1283
|
-
deps.stderr.write(`${renderError(result, ctx)}
|
|
1322
|
+
deps.stderr.write(`${terminalLines(renderError(result, ctx))}
|
|
1284
1323
|
`);
|
|
1285
1324
|
}
|
|
1286
1325
|
function writeLocalFailure(deps, failure2, json) {
|
|
@@ -1292,7 +1331,7 @@ function writeLocalFailure(deps, failure2, json) {
|
|
|
1292
1331
|
const separator = failure2.suggestion?.includes(`
|
|
1293
1332
|
`) ? `
|
|
1294
1333
|
` : " ";
|
|
1295
|
-
deps.stderr.write(`${failure2.suggestion ? `${failure2.message}${separator}${failure2.suggestion}` : failure2.message}
|
|
1334
|
+
deps.stderr.write(`${terminalLines(failure2.suggestion ? `${failure2.message}${separator}${failure2.suggestion}` : failure2.message)}
|
|
1296
1335
|
`);
|
|
1297
1336
|
}
|
|
1298
1337
|
function writeUsageFailure(deps, message, json) {
|
|
@@ -1300,7 +1339,7 @@ function writeUsageFailure(deps, message, json) {
|
|
|
1300
1339
|
deps.stdout.write(`${JSON.stringify({ ok: false, code: "USAGE", message })}
|
|
1301
1340
|
`);
|
|
1302
1341
|
else
|
|
1303
|
-
deps.stderr.write(`${message}
|
|
1342
|
+
deps.stderr.write(`${terminalLines(message)}
|
|
1304
1343
|
`);
|
|
1305
1344
|
}
|
|
1306
1345
|
function portalDevicesUrl(apiUrl, portalOrigin) {
|
|
@@ -6852,7 +6891,7 @@ var init_launch_definition = __esm(() => {
|
|
|
6852
6891
|
{
|
|
6853
6892
|
key: "curve.virtualQuote",
|
|
6854
6893
|
type: "decimal",
|
|
6855
|
-
help: "exp-custom's virtual quote, in whole quote units: 1 to 100 SOL or 66000 to 6600000 CNDL; requires --curve-g
|
|
6894
|
+
help: "exp-custom's virtual quote, in whole quote units: 1 to 100 SOL or 66000 to 6600000 CNDL; requires --curve-g.",
|
|
6856
6895
|
modes: [
|
|
6857
6896
|
"exp-custom"
|
|
6858
6897
|
],
|
|
@@ -6891,7 +6930,7 @@ var init_launch_definition = __esm(() => {
|
|
|
6891
6930
|
{
|
|
6892
6931
|
key: "curve.threshold",
|
|
6893
6932
|
type: "decimal",
|
|
6894
|
-
help: "exp-custom's graduation threshold, in whole quote units: 1 to 250 SOL or 66000 to 16500000 CNDL; requires --curve-v
|
|
6933
|
+
help: "exp-custom's graduation threshold, in whole quote units: 1 to 250 SOL or 66000 to 16500000 CNDL; requires --curve-v.",
|
|
6895
6934
|
modes: [
|
|
6896
6935
|
"exp-custom"
|
|
6897
6936
|
],
|
|
@@ -20209,10 +20248,13 @@ function credentialEnvOverrides(env) {
|
|
|
20209
20248
|
return ["CANDLE_API_KEY_FILE", "CANDLE_API_KEY", "CANDLE_DEVICE_TOKEN"].filter((name) => env[name]?.trim());
|
|
20210
20249
|
}
|
|
20211
20250
|
function identityLine(profile, account, apiUrl, overrides, username, options = {}) {
|
|
20212
|
-
|
|
20251
|
+
profile = profile === undefined ? undefined : escapeTerminalText(profile);
|
|
20252
|
+
username = username === undefined ? undefined : escapeTerminalText(username);
|
|
20253
|
+
overrides = overrides?.map(escapeTerminalText);
|
|
20254
|
+
const address = account === undefined ? undefined : escapeTerminalText(shortId(account, options.full));
|
|
20213
20255
|
const shown = overrides?.length ? address ? `${address} (${overrides.join(", ")} override)` : `unknown (${overrides.join(", ")} override)` : username && address ? `${username} (${address})` : address ?? "unknown";
|
|
20214
20256
|
const name = profile ?? "none";
|
|
20215
|
-
const host = apiUrl.replace(/^https:\/\//i, "").replace(/\/+$/, "");
|
|
20257
|
+
const host = escapeTerminalText(apiUrl).replace(/^https:\/\//i, "").replace(/\/+$/, "");
|
|
20216
20258
|
const sep2 = options.style ? symbols(options.style).note : "·";
|
|
20217
20259
|
return `Profile ${options.style ? bold(name, options.style) : name} ${sep2} Account ${shown} ${sep2} ${host}`;
|
|
20218
20260
|
}
|
|
@@ -25682,6 +25724,76 @@ var init_hood_legs = __esm(() => {
|
|
|
25682
25724
|
V4_SWAP_PARAMS = { tuple: [V4_POOL_KEY, "bool", "uint", "uint", "uint", "bytes"] };
|
|
25683
25725
|
});
|
|
25684
25726
|
|
|
25727
|
+
// src/hood-operation-wait.ts
|
|
25728
|
+
async function waitForHoodOperation(ctx, key, id, evidence) {
|
|
25729
|
+
const deadline = ctx.deps.now() + WAIT_MS;
|
|
25730
|
+
const hint = `candle swap status --operation ${safeText(id)}`;
|
|
25731
|
+
const readCtx = {
|
|
25732
|
+
...ctx,
|
|
25733
|
+
deps: {
|
|
25734
|
+
...ctx.deps,
|
|
25735
|
+
fetch: (input, init) => ctx.deps.fetch(input, {
|
|
25736
|
+
...init,
|
|
25737
|
+
signal: AbortSignal.timeout(Math.max(1, deadline - ctx.deps.now()))
|
|
25738
|
+
})
|
|
25739
|
+
}
|
|
25740
|
+
};
|
|
25741
|
+
for (;; ) {
|
|
25742
|
+
let result2;
|
|
25743
|
+
try {
|
|
25744
|
+
result2 = responseSchema.parse(await request(readCtx, key, `/api/v1/agent/hood-tee/operations/${encodeURIComponent(id)}`));
|
|
25745
|
+
if (result2.operation.operationId !== id)
|
|
25746
|
+
throw new Error("The server returned a different operation");
|
|
25747
|
+
} catch (error) {
|
|
25748
|
+
throw new TradingError("HOOD_WAIT_UNAVAILABLE", `Could not follow Hood settlement. Do not repeat the swap. Read ${hint}.`, {
|
|
25749
|
+
exitCode: 3,
|
|
25750
|
+
details: {
|
|
25751
|
+
...evidence,
|
|
25752
|
+
operationId: id,
|
|
25753
|
+
readCode: error instanceof TradingError ? error.code : "INVALID_RESPONSE"
|
|
25754
|
+
}
|
|
25755
|
+
});
|
|
25756
|
+
}
|
|
25757
|
+
const op = result2.operation;
|
|
25758
|
+
const held = op.guard?.recoveryRequired === true || op.recovery?.recoveryRequired === true;
|
|
25759
|
+
const completed = op.status === "completed" && op.legs.length === op.plannedLegCount && op.legs.every((leg) => leg.status === "landed");
|
|
25760
|
+
const failed = op.status === "failed" || op.status === "expired";
|
|
25761
|
+
const final = !held && (completed || failed);
|
|
25762
|
+
if (final || ctx.deps.now() >= deadline) {
|
|
25763
|
+
ctx.deps.stdout.write(`${JSON.stringify(result2, null, ctx.json ? undefined : 2)}
|
|
25764
|
+
`);
|
|
25765
|
+
if (!final)
|
|
25766
|
+
ctx.deps.stderr.write(`Still not final after ten minutes. Do not repeat the swap. Read ${hint}.
|
|
25767
|
+
`);
|
|
25768
|
+
else if (failed)
|
|
25769
|
+
ctx.deps.stderr.write(`Hood operation ${safeText(op.status)}. Do not repeat the swap. Read ${hint}.
|
|
25770
|
+
`);
|
|
25771
|
+
if (!final)
|
|
25772
|
+
return 3;
|
|
25773
|
+
if (completed)
|
|
25774
|
+
return 0;
|
|
25775
|
+
return op.legs.some((leg) => leg.kind === "trade" && leg.status === "landed") ? 3 : 1;
|
|
25776
|
+
}
|
|
25777
|
+
await ctx.deps.sleep(Math.min(POLL_MS, deadline - ctx.deps.now()));
|
|
25778
|
+
}
|
|
25779
|
+
}
|
|
25780
|
+
var WAIT_MS, POLL_MS = 5000, responseSchema;
|
|
25781
|
+
var init_hood_operation_wait = __esm(() => {
|
|
25782
|
+
init_zod();
|
|
25783
|
+
init_trading();
|
|
25784
|
+
WAIT_MS = 10 * 60000;
|
|
25785
|
+
responseSchema = exports_external.object({
|
|
25786
|
+
operation: exports_external.object({
|
|
25787
|
+
operationId: exports_external.string(),
|
|
25788
|
+
status: exports_external.enum(["active", "completed", "failed", "expired"]),
|
|
25789
|
+
plannedLegCount: exports_external.number().int().positive(),
|
|
25790
|
+
legs: exports_external.array(exports_external.object({ kind: exports_external.string(), status: exports_external.string() }).passthrough()),
|
|
25791
|
+
guard: exports_external.object({ recoveryRequired: exports_external.boolean() }).passthrough().optional(),
|
|
25792
|
+
recovery: exports_external.object({ recoveryRequired: exports_external.boolean() }).passthrough().optional()
|
|
25793
|
+
}).passthrough()
|
|
25794
|
+
}).passthrough();
|
|
25795
|
+
});
|
|
25796
|
+
|
|
25685
25797
|
// src/human-output.ts
|
|
25686
25798
|
function writeFields(deps, stream, rows, title) {
|
|
25687
25799
|
const style = terminalStyle(deps, stream);
|
|
@@ -26005,7 +26117,7 @@ function walletRecordCheck(row, trading, records) {
|
|
|
26005
26117
|
differences
|
|
26006
26118
|
} : pass("record");
|
|
26007
26119
|
}
|
|
26008
|
-
async function inspectReport(ctx, key, row, listing, records, payable, name) {
|
|
26120
|
+
async function inspectReport(ctx, key, row, listing, records, payable, name, chain2) {
|
|
26009
26121
|
const checks = [pass("key"), payable ? failure2("payable", payable) : pass("payable")];
|
|
26010
26122
|
checks.push(listing.scopes.includes("swap:write") ? pass("scope") : failure2("scope", new TradingError("SCOPE_MISSING", "The bound key needs swap:write.")));
|
|
26011
26123
|
const report = {
|
|
@@ -26018,7 +26130,7 @@ async function inspectReport(ctx, key, row, listing, records, payable, name) {
|
|
|
26018
26130
|
return { report };
|
|
26019
26131
|
}
|
|
26020
26132
|
checks.push(walletRecordCheck(row, listing.rows, records));
|
|
26021
|
-
const inspected = await inspectTradingWallet(ctx, row, listing.appId, "swap:write", "solana", {
|
|
26133
|
+
const inspected = await inspectTradingWallet(ctx, row, listing.appId, "swap:write", chain2 ?? rowChain(row) ?? "solana", {
|
|
26022
26134
|
apiKey: key,
|
|
26023
26135
|
keyPrefix: listing.keyPrefix
|
|
26024
26136
|
});
|
|
@@ -26211,14 +26323,18 @@ async function buildCheck(ctx, key, wallet3, report, from, to, amount, slippage,
|
|
|
26211
26323
|
}
|
|
26212
26324
|
}
|
|
26213
26325
|
async function teeCheck(args, ctx) {
|
|
26214
|
-
const parsed = parseArgs(args, {
|
|
26326
|
+
const parsed = parseArgs(args, {
|
|
26327
|
+
valueFlags: ["--label-prefix", "--max", "--rpc-url", "--chain"],
|
|
26328
|
+
booleanFlags: ["--sign"]
|
|
26329
|
+
});
|
|
26215
26330
|
if ("error" in parsed) {
|
|
26216
26331
|
writeUsageFailure(ctx.deps, parsed.error, ctx.json);
|
|
26217
26332
|
return 2;
|
|
26218
26333
|
}
|
|
26219
26334
|
const max = Number(parsed.values["--max"] ?? "10");
|
|
26220
|
-
|
|
26221
|
-
|
|
26335
|
+
const chainFlag = parsed.values["--chain"];
|
|
26336
|
+
if (!parsed.positionals.length && parsed.values["--label-prefix"] === undefined || !Number.isSafeInteger(max) || max < 1 || chainFlag !== undefined && chainFlag !== "solana" && chainFlag !== "hood") {
|
|
26337
|
+
writeUsageFailure(ctx.deps, "Usage: candle tee check <address|id|label>... [--label-prefix <p>] [--chain solana|hood] [--sign] [--max <n>] [--rpc-url <url>]", ctx.json);
|
|
26222
26338
|
return 2;
|
|
26223
26339
|
}
|
|
26224
26340
|
try {
|
|
@@ -26250,8 +26366,13 @@ async function teeCheck(args, ctx) {
|
|
|
26250
26366
|
for (const name of targets) {
|
|
26251
26367
|
const matches = listing.rows.filter((row) => matchesName(row, name));
|
|
26252
26368
|
const payable = matches.length === 1 ? undefined : new TradingError("TEE_WALLET_REQUIRED", `"${safeText(name)}" matches ${matches.length} TEE wallets. Bound to this key: ${listing.rows.map(describeWallet).join("; ") || "no TEE wallets bound to this key"}.`);
|
|
26253
|
-
const { report, wallet: wallet3 } = await inspectReport(ctx, key, matches.length === 1 ? matches[0] : undefined, listing, records, payable, name);
|
|
26254
|
-
if (sign4 && wallet3)
|
|
26369
|
+
const { report, wallet: wallet3 } = await inspectReport(ctx, key, matches.length === 1 ? matches[0] : undefined, listing, records, payable, name, chainFlag);
|
|
26370
|
+
if (sign4 && wallet3?.chain === "evm")
|
|
26371
|
+
report.checks.push({
|
|
26372
|
+
...skipped("sign", "hood"),
|
|
26373
|
+
message: "Signing is checked on Solana wallets only; a Hood test build would hold the wallet."
|
|
26374
|
+
});
|
|
26375
|
+
else if (sign4 && wallet3)
|
|
26255
26376
|
await buildCheck(ctx, key, wallet3, report, "SOL", "USDC", "0.000001", 50, parsed.values["--rpc-url"], true);
|
|
26256
26377
|
else
|
|
26257
26378
|
report.checks.push(skipped("sign", sign4 ? report.checks.find((check) => check.status === "fail")?.id ?? "signer" : "--sign not set"));
|
|
@@ -26288,7 +26409,7 @@ async function swapCheck(args, ctx) {
|
|
|
26288
26409
|
if (from.chain !== "solana" || to.chain !== "solana")
|
|
26289
26410
|
throw new TradingError("VALIDATION_FAILED", "Dry run checks Solana TEE pairs; Hood and bridge pairs are unsupported.");
|
|
26290
26411
|
if (parsed.values["--to"] !== undefined || parsed.booleans.has("--wait")) {
|
|
26291
|
-
writeUsageFailure(ctx.deps, "--to
|
|
26412
|
+
writeUsageFailure(ctx.deps, "--to selects a bridge destination; --wait follows a bridge or a Hood TEE swap without --dry-run. Follow a Hood swap with candle swap status --operation <id>, or a job with candle swap status <client-trade-id>.", ctx.json);
|
|
26292
26413
|
return 2;
|
|
26293
26414
|
}
|
|
26294
26415
|
if (from.asset === to.asset || !from.base && !to.base)
|
|
@@ -26311,7 +26432,7 @@ async function swapCheck(args, ctx) {
|
|
|
26311
26432
|
payable = error;
|
|
26312
26433
|
}
|
|
26313
26434
|
const records = await readWalletRecords(ctx, key);
|
|
26314
|
-
const { report, wallet: wallet3 } = await inspectReport(ctx, key, row, listing, records, payable, parsed.values["--wallet"]);
|
|
26435
|
+
const { report, wallet: wallet3 } = await inspectReport(ctx, key, row, listing, records, payable, parsed.values["--wallet"], from.chain);
|
|
26315
26436
|
if (wallet3)
|
|
26316
26437
|
await buildCheck(ctx, key, wallet3, report, from.asset, to.asset, parsed.values["--amount"], slippage, parsed.values["--rpc-url"], false);
|
|
26317
26438
|
else
|
|
@@ -26446,7 +26567,7 @@ function printTradingResult(ctx, result2) {
|
|
|
26446
26567
|
}
|
|
26447
26568
|
const printed = ctx.json && (result2.kind === "swap" || result2.kind === "trade") ? swapJsonResult(result2) : result2;
|
|
26448
26569
|
ctx.deps.stdout.write(ctx.json ? `${JSON.stringify(printed)}
|
|
26449
|
-
` : `${JSON.stringify(result2, null, 2)}
|
|
26570
|
+
` : `${terminalLines(JSON.stringify(result2, null, 2))}
|
|
26450
26571
|
`);
|
|
26451
26572
|
const job = result2.job;
|
|
26452
26573
|
const operation = job?.operation;
|
|
@@ -26495,11 +26616,11 @@ async function lookupOperation(ctx, key, id, kind) {
|
|
|
26495
26616
|
async function swapStatus(args, ctx) {
|
|
26496
26617
|
const parsed = parseArgs(args, { valueFlags: ["--kind", "--operation"], booleanFlags: ["--wait"] });
|
|
26497
26618
|
if (!("error" in parsed) && parsed.values["--operation"] !== undefined) {
|
|
26498
|
-
if (parsed.positionals.length || parsed.values["--kind"]
|
|
26499
|
-
writeUsageFailure(ctx.deps, "Usage: candle swap status --operation <operationId>", ctx.json);
|
|
26619
|
+
if (parsed.positionals.length || parsed.values["--kind"]) {
|
|
26620
|
+
writeUsageFailure(ctx.deps, "Usage: candle swap status --operation <operationId> [--wait]", ctx.json);
|
|
26500
26621
|
return 2;
|
|
26501
26622
|
}
|
|
26502
|
-
return hoodOperationRead(ctx, parsed.values["--operation"]);
|
|
26623
|
+
return hoodOperationRead(ctx, parsed.values["--operation"], undefined, false, parsed.booleans.has("--wait"));
|
|
26503
26624
|
}
|
|
26504
26625
|
if ("error" in parsed || parsed.positionals.length !== 1 || !validClientId(parsed.positionals[0] ?? "") || parsed.values["--kind"] !== undefined && !["trade", "swap", "launch"].includes(parsed.values["--kind"])) {
|
|
26505
26626
|
writeUsageFailure(ctx.deps, "Usage: candle swap status <id> [--kind trade|swap|launch] [--wait]", ctx.json);
|
|
@@ -26572,7 +26693,7 @@ async function swapReceipt(args, ctx) {
|
|
|
26572
26693
|
}
|
|
26573
26694
|
describeReceipt(ctx, body, answer.job);
|
|
26574
26695
|
ctx.deps.stdout.write(ctx.json ? `${JSON.stringify(body)}
|
|
26575
|
-
` : `${JSON.stringify(body, null, 2)}
|
|
26696
|
+
` : `${terminalLines(JSON.stringify(body, null, 2))}
|
|
26576
26697
|
`);
|
|
26577
26698
|
return 0;
|
|
26578
26699
|
} catch (error) {
|
|
@@ -26583,7 +26704,7 @@ function describeReceipt(ctx, body, job) {
|
|
|
26583
26704
|
const settlement = body.settlement;
|
|
26584
26705
|
if (settlement === null || typeof settlement !== "object") {
|
|
26585
26706
|
if (typeof body.next === "string")
|
|
26586
|
-
ctx.deps.stderr.write(`${body.next}
|
|
26707
|
+
ctx.deps.stderr.write(`${terminalLines(body.next)}
|
|
26587
26708
|
`);
|
|
26588
26709
|
return;
|
|
26589
26710
|
}
|
|
@@ -26706,8 +26827,9 @@ async function swap2(args, ctx) {
|
|
|
26706
26827
|
slippage,
|
|
26707
26828
|
...bridge
|
|
26708
26829
|
});
|
|
26709
|
-
|
|
26710
|
-
|
|
26830
|
+
const unsupportedWait = "--to is for a bridge: SOL or USDC to ETH or USDG, or back. --wait follows a bridge or a Hood TEE swap. Follow a Hood swap with candle swap status --operation <id>; use candle swap status <client-trade-id> for a job.";
|
|
26831
|
+
if (flags["--to"] !== undefined)
|
|
26832
|
+
throw new TradingUsage(unsupportedWait);
|
|
26711
26833
|
const chain2 = pairChain(fromAsset, toAsset);
|
|
26712
26834
|
const walletFlag = flags["--wallet"];
|
|
26713
26835
|
const named = walletFlag === undefined ? undefined : walletNameChain(walletFlag);
|
|
@@ -26715,6 +26837,7 @@ async function swap2(args, ctx) {
|
|
|
26715
26837
|
throw chainMismatch(`--wallet ${safeText(walletFlag)}`, named, chain2);
|
|
26716
26838
|
if (chain2 === "hood")
|
|
26717
26839
|
return await hoodSwap(ctx, {
|
|
26840
|
+
wait: parsed.booleans.has("--wait"),
|
|
26718
26841
|
flags,
|
|
26719
26842
|
yes: parsed.booleans.has("--yes"),
|
|
26720
26843
|
id,
|
|
@@ -26722,6 +26845,8 @@ async function swap2(args, ctx) {
|
|
|
26722
26845
|
from: fromAsset,
|
|
26723
26846
|
to: toAsset
|
|
26724
26847
|
});
|
|
26848
|
+
if (parsed.booleans.has("--wait"))
|
|
26849
|
+
throw new TradingUsage(unsupportedWait);
|
|
26725
26850
|
const from = fromAsset.asset;
|
|
26726
26851
|
const to = toAsset.asset;
|
|
26727
26852
|
if (from === to)
|
|
@@ -26947,9 +27072,19 @@ async function hoodSwap(ctx, args) {
|
|
|
26947
27072
|
const kind = from.base && to.base ? "swap" : "trade";
|
|
26948
27073
|
const key = await tradingKey(ctx);
|
|
26949
27074
|
const prior = await lookupOperation(ctx, key, id, kind);
|
|
27075
|
+
const replay = async (result2) => {
|
|
27076
|
+
if (!args.wait)
|
|
27077
|
+
return printTradingResult(ctx, result2);
|
|
27078
|
+
const operation = result2.job?.operation;
|
|
27079
|
+
if (typeof operation?.operationId !== "string")
|
|
27080
|
+
throw new TradingUsage(`No Hood TEE operationId is available. Follow with candle swap status ${id}; a Hood TEE swap uses candle swap status --operation <id>.`);
|
|
27081
|
+
return waitForHoodOperation(ctx, key, operation.operationId);
|
|
27082
|
+
};
|
|
26950
27083
|
if (prior)
|
|
26951
|
-
return
|
|
27084
|
+
return replay(prior);
|
|
26952
27085
|
const payer = await tradingPayer(ctx, key, flags["--wallet"], "swap:write", "hood");
|
|
27086
|
+
if (args.wait && payer.kind !== "tee")
|
|
27087
|
+
throw new TradingUsage(`--wait follows a bridge or a Hood TEE swap. Follow this swap with candle swap status ${id}; a Hood TEE swap uses candle swap status --operation <id>.`);
|
|
26953
27088
|
const payerAddress = payer.kind === "tee" ? payer.wallet.address : payer.address;
|
|
26954
27089
|
const rpc = lazyEvmRpc(ctx, flags["--rpc-url"]);
|
|
26955
27090
|
const decimals = await hoodDecimals(from, rpc);
|
|
@@ -26989,7 +27124,7 @@ async function hoodSwap(ctx, args) {
|
|
|
26989
27124
|
...payer.kind === "embedded" ? { deferExecution: true } : {}
|
|
26990
27125
|
});
|
|
26991
27126
|
if (built.job || built.status === "executed")
|
|
26992
|
-
return
|
|
27127
|
+
return replay({ ...built, clientTradeId: id, kind });
|
|
26993
27128
|
const body = kind === "swap" ? built.payload : built;
|
|
26994
27129
|
const data = swapBuildSchema.parse(body);
|
|
26995
27130
|
const echoed = kind === "swap" ? body?.recipient : body?.walletAddress;
|
|
@@ -27073,7 +27208,7 @@ async function hoodSwap(ctx, args) {
|
|
|
27073
27208
|
treasury: () => hoodFeeTreasury(rpc()),
|
|
27074
27209
|
curve: () => registeredCurveFor(rpc(), token2)
|
|
27075
27210
|
});
|
|
27076
|
-
const
|
|
27211
|
+
const execute = () => runSequencedLegs(ctx, key, {
|
|
27077
27212
|
wallet: wallet3,
|
|
27078
27213
|
first: sequenced,
|
|
27079
27214
|
submitPath: kind === "swap" ? "/api/v1/agent/swap/submit" : "/api/v1/trade/agent/submit",
|
|
@@ -27089,6 +27224,16 @@ async function hoodSwap(ctx, args) {
|
|
|
27089
27224
|
notices.push(notice2);
|
|
27090
27225
|
}
|
|
27091
27226
|
});
|
|
27227
|
+
let run;
|
|
27228
|
+
try {
|
|
27229
|
+
run = await execute();
|
|
27230
|
+
} catch (error) {
|
|
27231
|
+
if (args.wait && error instanceof TradingError && (error.exitCode === 3 || error.details?.stage === "reverted"))
|
|
27232
|
+
return waitForHoodOperation(ctx, key, sequenced.operationId, error.details);
|
|
27233
|
+
throw error;
|
|
27234
|
+
}
|
|
27235
|
+
if (args.wait)
|
|
27236
|
+
return waitForHoodOperation(ctx, key, sequenced.operationId);
|
|
27092
27237
|
const settled = kind === "swap" ? reportSettlement(ctx, run.final.settlement, { asset: to.asset, decimals: outDecimals }, id) : {};
|
|
27093
27238
|
return printTradingResult(ctx, {
|
|
27094
27239
|
...run.final,
|
|
@@ -27347,18 +27492,21 @@ async function bridgeSwap(ctx, args) {
|
|
|
27347
27492
|
return printTradingResult(ctx, { ...receipt, waited: { final: false, error: reason } });
|
|
27348
27493
|
}
|
|
27349
27494
|
}
|
|
27350
|
-
async function hoodOperationRead(ctx, id, candidateHash, recover = false) {
|
|
27495
|
+
async function hoodOperationRead(ctx, id, candidateHash, recover = false, wait = false) {
|
|
27351
27496
|
if (!validClientId(id)) {
|
|
27352
27497
|
writeUsageFailure(ctx.deps, "An operationId is required", ctx.json);
|
|
27353
27498
|
return 2;
|
|
27354
27499
|
}
|
|
27355
27500
|
try {
|
|
27356
27501
|
const key = await tradingKey(ctx);
|
|
27502
|
+
if (wait)
|
|
27503
|
+
return await waitForHoodOperation(ctx, key, id);
|
|
27357
27504
|
const path = `/api/v1/agent/hood-tee/operations/${encodeURIComponent(id)}${recover ? "/recover" : ""}`;
|
|
27358
27505
|
const result2 = await request(ctx, key, path, recover ? candidateHash ? { candidateHash } : {} : undefined);
|
|
27359
27506
|
const operation = result2.operation;
|
|
27360
27507
|
const guard = operation?.guard;
|
|
27361
|
-
|
|
27508
|
+
const output = JSON.stringify(result2, null, ctx.json ? undefined : 2);
|
|
27509
|
+
ctx.deps.stdout.write(`${ctx.json ? output : terminalLines(output)}
|
|
27362
27510
|
`);
|
|
27363
27511
|
if (guard)
|
|
27364
27512
|
ctx.deps.stderr.write(`${safeText(guard.reason)}: ${safeText(guard.clearingCondition)} Read: candle swap status --operation ${id}.
|
|
@@ -27390,6 +27538,7 @@ var init_swap = __esm(() => {
|
|
|
27390
27538
|
init_client();
|
|
27391
27539
|
init_evm_lite();
|
|
27392
27540
|
init_hood_legs();
|
|
27541
|
+
init_hood_operation_wait();
|
|
27393
27542
|
init_human_output();
|
|
27394
27543
|
init_presentation();
|
|
27395
27544
|
init_profiles();
|
|
@@ -47607,11 +47756,10 @@ function mintedByLabel(mintedBy, ownDeviceTokenPrefix) {
|
|
|
47607
47756
|
function labelCell(label) {
|
|
47608
47757
|
if (!label)
|
|
47609
47758
|
return "";
|
|
47610
|
-
const cleaned = Array.from(label).map((ch) => {
|
|
47759
|
+
const cleaned = Array.from(escapeTerminalText(label)).map((ch) => {
|
|
47611
47760
|
const code = ch.codePointAt(0) ?? 0;
|
|
47612
|
-
const isControl = code < 32 || code >= 127 && code <= 159;
|
|
47613
47761
|
const isBidiOrInvisible = code >= 8203 && code <= 8207 || code >= 8234 && code <= 8238 || code >= 8294 && code <= 8297 || code === 65279;
|
|
47614
|
-
return
|
|
47762
|
+
return isBidiOrInvisible ? " " : ch;
|
|
47615
47763
|
}).join("");
|
|
47616
47764
|
return cleaned.replace(/\s+/g, " ").trim();
|
|
47617
47765
|
}
|
|
@@ -47895,17 +48043,27 @@ async function keysRevoke(args, ctx) {
|
|
|
47895
48043
|
return 2;
|
|
47896
48044
|
}
|
|
47897
48045
|
if (parsed.positionals.length !== 1) {
|
|
47898
|
-
deps.stderr.write(`Usage: candle keys revoke <prefix>
|
|
48046
|
+
deps.stderr.write(`Usage: candle keys revoke <prefix|label>
|
|
47899
48047
|
`);
|
|
47900
48048
|
return 2;
|
|
47901
48049
|
}
|
|
47902
|
-
const
|
|
48050
|
+
const target = parsed.positionals[0];
|
|
47903
48051
|
await printIdentity(ctx);
|
|
47904
48052
|
const deviceToken = await resolveDeviceToken(deps, ctx.profile);
|
|
47905
48053
|
if (!deviceToken) {
|
|
47906
48054
|
writeLocalFailure(deps, NO_DEVICE_TOKEN, json);
|
|
47907
48055
|
return 1;
|
|
47908
48056
|
}
|
|
48057
|
+
const resolved = await resolveTargetKey(ctx, deviceToken, target, {
|
|
48058
|
+
codePrefix: "KEY_REVOKE",
|
|
48059
|
+
writeListFailure: (failure3) => {
|
|
48060
|
+
writeFailure(deps, failure3, { apiUrl, authType: "device" }, json);
|
|
48061
|
+
return 1;
|
|
48062
|
+
}
|
|
48063
|
+
});
|
|
48064
|
+
if (!resolved.ok)
|
|
48065
|
+
return resolved.code;
|
|
48066
|
+
const prefix = resolved.keyPrefix;
|
|
47909
48067
|
const result2 = await apiRequest(`${KEYS_PATH3}/${encodeURIComponent(prefix)}`, {
|
|
47910
48068
|
method: "DELETE",
|
|
47911
48069
|
auth: "device",
|
|
@@ -47955,6 +48113,7 @@ var init_keys = __esm(() => {
|
|
|
47955
48113
|
init_secret_store();
|
|
47956
48114
|
init_key_output();
|
|
47957
48115
|
init_keys_embedded_wallet();
|
|
48116
|
+
init_tee_rebind();
|
|
47958
48117
|
ACCESS_LEVELS = {
|
|
47959
48118
|
read: "read",
|
|
47960
48119
|
write: "readwrite",
|
|
@@ -77034,7 +77193,7 @@ var LAUNCH_DEFINITION2 = {
|
|
|
77034
77193
|
{
|
|
77035
77194
|
key: "curve.virtualQuote",
|
|
77036
77195
|
type: "decimal",
|
|
77037
|
-
help: "exp-custom's virtual quote, in whole quote units: 1 to 100 SOL or 66000 to 6600000 CNDL; requires --curve-g
|
|
77196
|
+
help: "exp-custom's virtual quote, in whole quote units: 1 to 100 SOL or 66000 to 6600000 CNDL; requires --curve-g.",
|
|
77038
77197
|
modes: [
|
|
77039
77198
|
"exp-custom"
|
|
77040
77199
|
],
|
|
@@ -77073,7 +77232,7 @@ var LAUNCH_DEFINITION2 = {
|
|
|
77073
77232
|
{
|
|
77074
77233
|
key: "curve.threshold",
|
|
77075
77234
|
type: "decimal",
|
|
77076
|
-
help: "exp-custom's graduation threshold, in whole quote units: 1 to 250 SOL or 66000 to 16500000 CNDL; requires --curve-v
|
|
77235
|
+
help: "exp-custom's graduation threshold, in whole quote units: 1 to 250 SOL or 66000 to 16500000 CNDL; requires --curve-v.",
|
|
77077
77236
|
modes: [
|
|
77078
77237
|
"exp-custom"
|
|
77079
77238
|
],
|
|
@@ -78065,7 +78224,7 @@ var HELP = {
|
|
|
78065
78224
|
group: "Trade",
|
|
78066
78225
|
intro: "Quote, swap and bridge on Solana or Hood; read an operation",
|
|
78067
78226
|
summary: "Quote, confirm and swap on Solana or Hood, bridge between them; read an operation by id",
|
|
78068
|
-
description: "Swaps run through a TEE wallet's bound key: the quote is shown and confirmed before anything is sent. The first buy after a launch is this command, unless the launch carried it with candle launch --buy. The assets decide the chain (ETH, USDG or a 0x token is Hood; SOL, USDC, CNDL or a mint is Solana) and a named wallet must be on it. A Hood TEE wallet signs one leg at a time: approve, Permit2, trade, then the fee, each only after the one before it landed. SOL or USDC to ETH or USDG, or back, is a bridge through Relay from a TEE wallet into this key's own TEE wallet on the other chain (--to names it when there are several); Candle charges no fee, only configured raw caps and USD limits apply (unset means unlimited, zero still binds), and this machine checks Relay's deposit before it is signed. The bridge prints its deposit and returns while Relay fills; --wait follows it for up to ten minutes. While it is open, a sweep of either wallet refuses BRIDGE_IN_FLIGHT for two hours. Vault to vault: fund the Solana TEE wallet from the vault, bridge, then disable and sweep the Hood TEE wallet into its vault. Every plan can trade Candle-launched tokens and base assets, and buy or sell a token that was not launched on Candle. Each trade pays the plan's agent fee on top; a base-pair swap or a bridge between this key's own wallets pays none. candle plans shows the fees in force. With --dry-run, stdout reports per-check results and the quote; nothing is signed or sent. Otherwise with --json, stdout is one JSON value
|
|
78227
|
+
description: "Swaps run through a TEE wallet's bound key: the quote is shown and confirmed before anything is sent. The first buy after a launch is this command, unless the launch carried it with candle launch --buy. The assets decide the chain (ETH, USDG or a 0x token is Hood; SOL, USDC, CNDL or a mint is Solana) and a named wallet must be on it. A Hood TEE wallet signs one leg at a time: approve, Permit2, trade, then the fee, each only after the one before it landed. SOL or USDC to ETH or USDG, or back, is a bridge through Relay from a TEE wallet into this key's own TEE wallet on the other chain (--to names it when there are several); Candle charges no fee, only configured raw caps and USD limits apply (unset means unlimited, zero still binds), and this machine checks Relay's deposit before it is signed. The bridge prints its deposit and returns while Relay fills; --wait follows it for up to ten minutes. While it is open, a sweep of either wallet refuses BRIDGE_IN_FLIGHT for two hours. On a Hood TEE swap, --wait follows the trade and fee legs for up to ten minutes and prints the same operation response as candle swap status --operation <id>; exit 0 means completed, 1 failed, and 3 partial execution, unresolved settlement or timeout. Embedded and Solana same-chain swaps use candle swap status <client-trade-id>. Vault to vault: fund the Solana TEE wallet from the vault, bridge, then disable and sweep the Hood TEE wallet into its vault. Every plan can trade Candle-launched tokens and base assets, and buy or sell a token that was not launched on Candle. Each trade pays the plan's agent fee on top; a base-pair swap or a bridge between this key's own wallets pays none. candle plans shows the fees in force. With --dry-run, stdout reports per-check results and the quote; nothing is signed or sent. Otherwise with --json, stdout is one JSON value: swaps, trades, replays and job status reads carry kind, status, clientTradeId, signature (the primary transaction, or null), signatures (a list) and raw (the original response); Hood --wait and explicit --operation reads return the operation response. Every human line is on stderr. The keys older versions printed stay beside them.",
|
|
78069
78228
|
usage: [
|
|
78070
78229
|
"candle swap <from> <to> [flags]",
|
|
78071
78230
|
"candle swap status <id>",
|
|
@@ -78080,8 +78239,8 @@ var HELP = {
|
|
|
78080
78239
|
description: "Quote, confirm and swap on Solana or Hood, or bridge between them"
|
|
78081
78240
|
},
|
|
78082
78241
|
{
|
|
78083
|
-
invocation: "status <id> [--kind trade|swap|launch] [--wait] | status --operation <operationId>",
|
|
78084
|
-
description: "Read without resending; --wait follows a bridge. --operation reads a Hood TEE guard
|
|
78242
|
+
invocation: "status <id> [--kind trade|swap|launch] [--wait] | status --operation <operationId> [--wait]",
|
|
78243
|
+
description: "Read without resending; --wait follows a bridge. --operation reads a Hood TEE guard; add --wait to follow all legs for up to ten minutes (0 completed, 1 failed, 3 partial or unresolved)"
|
|
78085
78244
|
},
|
|
78086
78245
|
{
|
|
78087
78246
|
invocation: "recover --operation <operationId> [--hash <0x…>]",
|
|
@@ -78928,13 +79087,19 @@ var HELP = {
|
|
|
78928
79087
|
invocation: "self embedded-wallet deny",
|
|
78929
79088
|
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
79089
|
},
|
|
78931
|
-
{
|
|
78932
|
-
|
|
79090
|
+
{
|
|
79091
|
+
invocation: "stop <prefix|label>",
|
|
79092
|
+
description: "Pause one key with the device token (starting it again is done in the signed-in console)"
|
|
79093
|
+
},
|
|
79094
|
+
{
|
|
79095
|
+
invocation: "resume <prefix|label>",
|
|
79096
|
+
description: "Resume a stopped key in the signed-in console, because the server refuses a device token here (LOOSEN_REQUIRES_SESSION)"
|
|
79097
|
+
},
|
|
78933
79098
|
{
|
|
78934
79099
|
invocation: "rotate <prefix|label> ([--grace <duration> | --now] [--force] [--yes] | --finish)",
|
|
78935
79100
|
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
79101
|
},
|
|
78937
|
-
{ invocation: "revoke <prefix>", description: "Revoke an API key permanently" },
|
|
79102
|
+
{ invocation: "revoke <prefix|label>", description: "Revoke an API key permanently" },
|
|
78938
79103
|
{
|
|
78939
79104
|
invocation: "signer approve <code> --key <prefix|label> [--reject]",
|
|
78940
79105
|
description: "Approve (or reject) a key's signer request with the device token; type the full fingerprint the trading machine printed"
|
|
@@ -79018,7 +79183,7 @@ var HELP = {
|
|
|
79018
79183
|
},
|
|
79019
79184
|
{
|
|
79020
79185
|
invocation: "close-empty [--wallet embedded|<tee address|label>] [--keep <mint>]... [--client-trade-id <id>] [--preview] [--yes]",
|
|
79021
|
-
description: "Close a wallet's empty token accounts and return their rent to it, after a preview: the embedded wallet (transfer:write), or a TEE wallet bound to this key, signed by its key signer (swap:write). --preview prints what would close (one JSON document on stdout under --json) and closes nothing"
|
|
79186
|
+
description: "Close a wallet's empty token accounts and return their rent to it, after a preview: the embedded wallet (swap:write or transfer:write), or a TEE wallet bound to this key, signed by its key signer (swap:write). --preview prints what would close (one JSON document on stdout under --json) and closes nothing"
|
|
79022
79187
|
}
|
|
79023
79188
|
],
|
|
79024
79189
|
flags: [
|
|
@@ -79232,8 +79397,8 @@ var HELP = {
|
|
|
79232
79397
|
usage: ["candle tee <subcommand> [flags]"],
|
|
79233
79398
|
rows: [
|
|
79234
79399
|
{
|
|
79235
|
-
invocation: "check <address|id|label>... [--label-prefix <p>] [--sign] [--max <n>] [--rpc-url <url>]",
|
|
79236
|
-
description: "Check payable membership, record parity and this machine's signer without spending. --sign builds a tiny SOL/USDC quote, checks spend, signs and simulates; never broadcasts (default cap 10). Needs swap:write"
|
|
79400
|
+
invocation: "check <address|id|label>... [--label-prefix <p>] [--chain solana|hood] [--sign] [--max <n>] [--rpc-url <url>]",
|
|
79401
|
+
description: "Check payable membership, record parity and this machine's signer without spending. Each wallet is checked on its own chain; --chain checks every wallet against one chain (CHAIN_MISMATCH otherwise). --sign builds a tiny SOL/USDC quote on a Solana wallet, checks spend, signs and simulates; never broadcasts (default cap 10). A Hood wallet skips sign: a Hood build holds the wallet. Needs swap:write"
|
|
79237
79402
|
},
|
|
79238
79403
|
{
|
|
79239
79404
|
invocation: "new [--label <name>]",
|
|
@@ -79299,6 +79464,7 @@ var HELP = {
|
|
|
79299
79464
|
examples: [
|
|
79300
79465
|
"candle tee check --label-prefix pool- --json",
|
|
79301
79466
|
"candle tee check AgentOne --sign --rpc-url <url>",
|
|
79467
|
+
"candle tee check hood-1 --chain hood --json",
|
|
79302
79468
|
"candle tee new --label AgentOne",
|
|
79303
79469
|
"candle tee status AgentOneAddress",
|
|
79304
79470
|
"candle tee sweep AgentOneAddress",
|
|
@@ -85264,6 +85430,13 @@ init_evm_lite();
|
|
|
85264
85430
|
init_client();
|
|
85265
85431
|
var EXIT_OWNER_CONFIRM_PENDING = 7;
|
|
85266
85432
|
var OWN_NO_WAIT_COMMANDS = [["tee"], ["lab"], ["wallets", "authorize"]];
|
|
85433
|
+
function shortWallet(wallet3) {
|
|
85434
|
+
return wallet3.length >= 12 ? `${wallet3.slice(0, 4)}…${wallet3.slice(-4)}` : wallet3;
|
|
85435
|
+
}
|
|
85436
|
+
function accountLabel(account) {
|
|
85437
|
+
const short = shortWallet(account.wallet);
|
|
85438
|
+
return account.displayName ? `${account.displayName} (${short})` : short;
|
|
85439
|
+
}
|
|
85267
85440
|
function ownerConfirmPendingLine(challenge) {
|
|
85268
85441
|
return JSON.stringify({
|
|
85269
85442
|
ok: false,
|
|
@@ -85274,7 +85447,8 @@ function ownerConfirmPendingLine(challenge) {
|
|
|
85274
85447
|
verificationUriComplete: challenge.verificationUriComplete,
|
|
85275
85448
|
expiresIn: challenge.expiresIn,
|
|
85276
85449
|
step: challenge.step,
|
|
85277
|
-
summary: challenge.summary
|
|
85450
|
+
summary: challenge.summary,
|
|
85451
|
+
...challenge.account ? { account: challenge.account } : {}
|
|
85278
85452
|
}
|
|
85279
85453
|
});
|
|
85280
85454
|
}
|
|
@@ -85291,10 +85465,12 @@ function cliOwnerConfirmHooks(deps, opts, state) {
|
|
|
85291
85465
|
`);
|
|
85292
85466
|
} else {
|
|
85293
85467
|
const minutes = Math.max(1, Math.round(challenge.expiresIn / 60));
|
|
85468
|
+
const as = challenge.account ? ` as ${accountLabel(challenge.account)}` : "";
|
|
85294
85469
|
deps.stderr.write([
|
|
85295
|
-
`This needs your approval in the browser: ${challenge.summary}`,
|
|
85470
|
+
`This needs your approval in the browser${as}: ${challenge.summary}`,
|
|
85296
85471
|
`Open ${challenge.verificationUriComplete}`,
|
|
85297
85472
|
`Code: ${challenge.userCode}`,
|
|
85473
|
+
...challenge.account ? [`The browser must be signed in as ${accountLabel(challenge.account)}.`] : [],
|
|
85298
85474
|
"Only approve if you just ran this command.",
|
|
85299
85475
|
opts.wait ? `Waiting for approval (expires in ${minutes} minute${minutes === 1 ? "" : "s"})...` : ""
|
|
85300
85476
|
].filter(Boolean).join(`
|
|
@@ -86262,7 +86438,7 @@ ${USAGE6}`, json);
|
|
|
86262
86438
|
} catch (error) {
|
|
86263
86439
|
if (!(error instanceof CoreClientError))
|
|
86264
86440
|
throw error;
|
|
86265
|
-
deps.stderr.write(`${error.code}: ${error.message}
|
|
86441
|
+
deps.stderr.write(`${terminalLines(`${error.code}: ${error.message}`)}
|
|
86266
86442
|
`);
|
|
86267
86443
|
return 1;
|
|
86268
86444
|
}
|
|
@@ -91975,7 +92151,7 @@ async function help(args, ctx) {
|
|
|
91975
92151
|
}
|
|
91976
92152
|
const topic = renderTopic(word3, terminalStyle(ctx.deps, "stdout", ctx.json), args.includes("--more"));
|
|
91977
92153
|
if (topic === undefined) {
|
|
91978
|
-
ctx.deps.stderr.write(`Unknown command: ${word3}
|
|
92154
|
+
ctx.deps.stderr.write(`Unknown command: ${escapeTerminalText(word3)}
|
|
91979
92155
|
`);
|
|
91980
92156
|
ctx.deps.stderr.write(renderTopLevel());
|
|
91981
92157
|
return 1;
|
|
@@ -92305,7 +92481,7 @@ async function keysAccessHistory(target, ctx) {
|
|
|
92305
92481
|
`);
|
|
92306
92482
|
return 0;
|
|
92307
92483
|
}
|
|
92308
|
-
const rows = result2.body.changes ?? [];
|
|
92484
|
+
const rows = (result2.body.changes ?? []).filter((row2) => row2.eventKind !== "profile_edit" && row2.change !== "profile_edit");
|
|
92309
92485
|
if (rows.length === 0) {
|
|
92310
92486
|
notice(deps, "stdout", "note", `No access changes on key ${resolved.keyPrefix}.`);
|
|
92311
92487
|
return 0;
|
|
@@ -92615,7 +92791,7 @@ function writeRefusal(ctx, refusal3, suggestion) {
|
|
|
92615
92791
|
`);
|
|
92616
92792
|
return;
|
|
92617
92793
|
}
|
|
92618
|
-
ctx.deps.stderr.write(`${suggestion ? `${message} ${suggestion}` : message}
|
|
92794
|
+
ctx.deps.stderr.write(`${escapeTerminalText(suggestion ? `${message} ${suggestion}` : message)}
|
|
92619
92795
|
`);
|
|
92620
92796
|
}
|
|
92621
92797
|
function writeApiFailure(ctx, result2, authType) {
|
|
@@ -92710,8 +92886,8 @@ function identity2(who) {
|
|
|
92710
92886
|
if (who.kind === "candle")
|
|
92711
92887
|
return "Candle";
|
|
92712
92888
|
if (who.kind === "agent")
|
|
92713
|
-
return
|
|
92714
|
-
return
|
|
92889
|
+
return escapeTerminalText(who.name ?? "Agent");
|
|
92890
|
+
return escapeTerminalText(who.name ?? who.account);
|
|
92715
92891
|
}
|
|
92716
92892
|
function sectionText(bytes, section2) {
|
|
92717
92893
|
if (bytes.length > TOKEN_PAGE_BODY_CAP_BYTES)
|
|
@@ -92750,7 +92926,7 @@ function factValue(fact) {
|
|
|
92750
92926
|
if (fact.raw === undefined)
|
|
92751
92927
|
return fact.status;
|
|
92752
92928
|
const value = fact.decimals ? rawUnits2(fact.raw, fact.decimals) : fact.raw;
|
|
92753
|
-
return `${value}${fact.unit === "token" ? "" : ` ${fact.unit}`} (${fact.status}, ${fact.coverage}, ${
|
|
92929
|
+
return `${value}${fact.unit === "token" ? "" : ` ${fact.unit}`} (${fact.status}, ${fact.coverage}, ${escapeTerminalText(fact.source)}${fact.measuredAt ? `, ${fact.measuredAt}` : ""})`;
|
|
92754
92930
|
}
|
|
92755
92931
|
function rawUnits2(raw, decimals) {
|
|
92756
92932
|
if (!/^\d+$/.test(raw))
|
|
@@ -92792,7 +92968,7 @@ function writeAbout(ctx, about) {
|
|
|
92792
92968
|
if (v.linksWithheld)
|
|
92793
92969
|
notice(ctx.deps, "stdout", "note", "Links are withheld pending review.");
|
|
92794
92970
|
ctx.deps.stdout.write(`
|
|
92795
|
-
${
|
|
92971
|
+
${escapeTerminalText(v.text)}
|
|
92796
92972
|
`);
|
|
92797
92973
|
}
|
|
92798
92974
|
writeHeading(ctx.deps, "stdout", "Supply, measured by Candle");
|
|
@@ -92801,12 +92977,7 @@ ${terminalTextKeepingLines(v.text)}
|
|
|
92801
92977
|
ctx.deps.stdout.write(` Measurements unavailable
|
|
92802
92978
|
`);
|
|
92803
92979
|
else
|
|
92804
|
-
writeFields(ctx.deps, "stdout", facts.map((f) => [
|
|
92805
|
-
}
|
|
92806
|
-
function terminalTextKeepingLines(text3) {
|
|
92807
|
-
return text3.split(`
|
|
92808
|
-
`).map((line) => terminalText(line.replace(/\t/g, " "))).join(`
|
|
92809
|
-
`);
|
|
92980
|
+
writeFields(ctx.deps, "stdout", facts.map((f) => [escapeTerminalText(f.metric), factValue(f)]));
|
|
92810
92981
|
}
|
|
92811
92982
|
async function tokenAbout(args, ctx) {
|
|
92812
92983
|
const usage4 = usageFor(ctx, USAGE9.about);
|
|
@@ -93120,12 +93291,12 @@ async function tokenHistory(args, ctx) {
|
|
|
93120
93291
|
]);
|
|
93121
93292
|
if (item.text !== null)
|
|
93122
93293
|
ctx.deps.stdout.write(`
|
|
93123
|
-
${
|
|
93294
|
+
${escapeTerminalText(item.text)}
|
|
93124
93295
|
`);
|
|
93125
93296
|
}
|
|
93126
93297
|
if (data.nextCursor)
|
|
93127
93298
|
ctx.deps.stdout.write(`
|
|
93128
|
-
More: --cursor ${data.nextCursor}
|
|
93299
|
+
More: --cursor ${escapeTerminalText(data.nextCursor)}
|
|
93129
93300
|
`);
|
|
93130
93301
|
return 0;
|
|
93131
93302
|
}
|
|
@@ -93311,12 +93482,12 @@ async function tokenComments(args, ctx) {
|
|
|
93311
93482
|
]);
|
|
93312
93483
|
if (item.text !== null)
|
|
93313
93484
|
ctx.deps.stdout.write(`
|
|
93314
|
-
${
|
|
93485
|
+
${escapeTerminalText(item.text)}
|
|
93315
93486
|
`);
|
|
93316
93487
|
}
|
|
93317
93488
|
if (data.nextCursor)
|
|
93318
93489
|
ctx.deps.stdout.write(`
|
|
93319
|
-
More: --cursor ${data.nextCursor}
|
|
93490
|
+
More: --cursor ${escapeTerminalText(data.nextCursor)}
|
|
93320
93491
|
`);
|
|
93321
93492
|
return 0;
|
|
93322
93493
|
}
|
|
@@ -93591,6 +93762,7 @@ init_presentation();
|
|
|
93591
93762
|
init_profiles();
|
|
93592
93763
|
init_key_output();
|
|
93593
93764
|
init_keys();
|
|
93765
|
+
init_tee_rebind();
|
|
93594
93766
|
function keysStop(args, ctx) {
|
|
93595
93767
|
return setKeyPaused(args, ctx, true);
|
|
93596
93768
|
}
|
|
@@ -93599,14 +93771,14 @@ function keysResume(args, ctx) {
|
|
|
93599
93771
|
}
|
|
93600
93772
|
async function setKeyPaused(args, ctx, paused) {
|
|
93601
93773
|
const { deps, apiUrl, json } = ctx;
|
|
93602
|
-
const parsed = parseArgs(args, {});
|
|
93774
|
+
const parsed = parseArgs(args, { keyPrefixPositional: true });
|
|
93603
93775
|
if ("error" in parsed) {
|
|
93604
93776
|
writeScreenUsageFailure(deps, parsed.error, json);
|
|
93605
93777
|
return 2;
|
|
93606
93778
|
}
|
|
93607
|
-
const
|
|
93608
|
-
if (!
|
|
93609
|
-
writeScreenUsageFailure(deps, `Usage: candle keys ${paused ? "stop" : "resume"} <prefix>`, json);
|
|
93779
|
+
const target = parsed.positionals[0];
|
|
93780
|
+
if (!target?.trim() || parsed.positionals.length !== 1) {
|
|
93781
|
+
writeScreenUsageFailure(deps, `Usage: candle keys ${paused ? "stop" : "resume"} <prefix|label>`, json);
|
|
93610
93782
|
return 2;
|
|
93611
93783
|
}
|
|
93612
93784
|
await printIdentity(ctx);
|
|
@@ -93615,6 +93787,16 @@ async function setKeyPaused(args, ctx, paused) {
|
|
|
93615
93787
|
writeScreenLocalFailure(deps, NO_DEVICE_TOKEN, json);
|
|
93616
93788
|
return 1;
|
|
93617
93789
|
}
|
|
93790
|
+
const resolved = await resolveTargetKey(ctx, deviceToken, target, {
|
|
93791
|
+
codePrefix: paused ? "KEY_STOP" : "KEY_RESUME",
|
|
93792
|
+
writeListFailure: (failure3) => {
|
|
93793
|
+
writeScreenFailure(deps, failure3, { apiUrl, authType: "device" }, json);
|
|
93794
|
+
return 1;
|
|
93795
|
+
}
|
|
93796
|
+
});
|
|
93797
|
+
if (!resolved.ok)
|
|
93798
|
+
return resolved.code;
|
|
93799
|
+
const prefix = resolved.keyPrefix;
|
|
93618
93800
|
const result2 = await apiRequest(`/api/v1/agent/keys/${encodeURIComponent(prefix)}/stop`, {
|
|
93619
93801
|
method: "POST",
|
|
93620
93802
|
auth: "device",
|
|
@@ -101426,7 +101608,7 @@ async function read2(ctx, argv, command) {
|
|
|
101426
101608
|
for (const warning of result2.warnings)
|
|
101427
101609
|
notice(ctx.deps, "stderr", "note", `${safeText(warning)}
|
|
101428
101610
|
`.trim());
|
|
101429
|
-
const out = (line) => ctx.deps.stdout.write(`${line}
|
|
101611
|
+
const out = (line) => ctx.deps.stdout.write(`${terminalLines(line)}
|
|
101430
101612
|
`);
|
|
101431
101613
|
out(`Hyperliquid ${result2.network}; OI floor $${result2.gate.minOpenInterestUsd} (${result2.gate.source}). Tradeability is Candle's build gate at read time, not a promise. OI caps may still refuse an order.`);
|
|
101432
101614
|
if ("market" in result2) {
|
|
@@ -102844,7 +103026,7 @@ async function runPaidRead(ctx, input) {
|
|
|
102844
103026
|
ctx.deps.stdout.write(`${JSON.stringify({ ok: true, data, ...paid.payment ? { payment: paid.payment } : {}, ...paid.servedFromCredit ? { servedFromCredit: true } : {} })}
|
|
102845
103027
|
`);
|
|
102846
103028
|
} else {
|
|
102847
|
-
ctx.deps.stdout.write(`${JSON.stringify(data, null, 2)}
|
|
103029
|
+
ctx.deps.stdout.write(`${terminalLines(JSON.stringify(data, null, 2))}
|
|
102848
103030
|
`);
|
|
102849
103031
|
}
|
|
102850
103032
|
return 0;
|
|
@@ -104258,6 +104440,9 @@ function financialHeading(text4, style) {
|
|
|
104258
104440
|
function signed(text4, style) {
|
|
104259
104441
|
return /^\+/.test(text4) ? accent(text4, style, "positive") : /^[-−]/.test(text4) ? accent(text4, style, "negative") : text4;
|
|
104260
104442
|
}
|
|
104443
|
+
function signedField(text4) {
|
|
104444
|
+
return /^\+/.test(text4) ? { text: text4, role: "positive" } : /^[-−]/.test(text4) ? { text: text4, role: "negative" } : text4;
|
|
104445
|
+
}
|
|
104261
104446
|
function financialTable(ctx, headers2, rows, columns) {
|
|
104262
104447
|
const style = terminalStyle(ctx.deps, "stdout");
|
|
104263
104448
|
const clean3 = rows.map((row2) => row2.map(terminalText));
|
|
@@ -104274,7 +104459,7 @@ function financialTable(ctx, headers2, rows, columns) {
|
|
|
104274
104459
|
if (style.width >= 60 && table.split(`
|
|
104275
104460
|
`).every((line) => displayWidth(line) <= style.width))
|
|
104276
104461
|
return table;
|
|
104277
|
-
return clean3.map((row2) => fields(headers2.map((label, col) => [label, specs[col]?.signed ?
|
|
104462
|
+
return clean3.map((row2) => fields(headers2.map((label, col) => [label, specs[col]?.signed ? signedField(row2[col] ?? "") : row2[col]]), style).join(`
|
|
104278
104463
|
`)).join(`
|
|
104279
104464
|
|
|
104280
104465
|
`);
|
|
@@ -104288,7 +104473,7 @@ function writeFinancialLines(ctx, rows) {
|
|
|
104288
104473
|
for (const [label = "", value = "", note = ""] of rows) {
|
|
104289
104474
|
const head = ` ${dim(padDisplay(label, labelWidth), style)} ${" ".repeat(valueWidth - displayWidth(value))}${signed(value, style)}`;
|
|
104290
104475
|
if (displayWidth(head) > style.width) {
|
|
104291
|
-
ctx.deps.stdout.write(`${fields([[label,
|
|
104476
|
+
ctx.deps.stdout.write(`${fields([[label, signedField(value)]], style).join(`
|
|
104292
104477
|
`)}
|
|
104293
104478
|
`);
|
|
104294
104479
|
if (note)
|
|
@@ -106278,10 +106463,12 @@ async function quantAsk(args, ctx) {
|
|
|
106278
106463
|
})}
|
|
106279
106464
|
`);
|
|
106280
106465
|
} else {
|
|
106281
|
-
ctx.deps.stdout.write(`${
|
|
106466
|
+
ctx.deps.stdout.write(`${result2.paragraphs.map(escapeTerminalText).join(`
|
|
106467
|
+
|
|
106468
|
+
`)}
|
|
106282
106469
|
`);
|
|
106283
106470
|
if (remaining !== undefined) {
|
|
106284
|
-
ctx.deps.stderr.write(`${remaining} Quant answer${remaining === 1 ? "" : "s"} left today${resetsAt !== undefined ? `, resets at ${resetsAt}` : ""}.
|
|
106471
|
+
ctx.deps.stderr.write(`${remaining} Quant answer${remaining === 1 ? "" : "s"} left today${resetsAt !== undefined ? `, resets at ${escapeTerminalText(String(resetsAt))}` : ""}.
|
|
106285
106472
|
`);
|
|
106286
106473
|
}
|
|
106287
106474
|
}
|
|
@@ -108435,10 +108622,10 @@ function eventLine(event, widths) {
|
|
|
108435
108622
|
const { doing, detail } = action(event);
|
|
108436
108623
|
return [
|
|
108437
108624
|
formatTime(event.at).padEnd(widths.time),
|
|
108438
|
-
event.kind.padEnd(widths.kind),
|
|
108439
|
-
who(event).padEnd(widths.who),
|
|
108440
|
-
doing,
|
|
108441
|
-
detail
|
|
108625
|
+
escapeTerminalText(event.kind).padEnd(widths.kind),
|
|
108626
|
+
escapeTerminalText(who(event)).padEnd(widths.who),
|
|
108627
|
+
escapeTerminalText(doing),
|
|
108628
|
+
escapeTerminalText(detail)
|
|
108442
108629
|
].filter((cell) => cell.trim().length > 0).join(" ").trimEnd();
|
|
108443
108630
|
}
|
|
108444
108631
|
async function tokenEvents(args, ctx) {
|
|
@@ -108519,7 +108706,7 @@ async function tokenEvents(args, ctx) {
|
|
|
108519
108706
|
const widths = {
|
|
108520
108707
|
time: Math.max(...events.map((event) => formatTime(event.at).length)),
|
|
108521
108708
|
kind: Math.max(...events.map((event) => event.kind.length)),
|
|
108522
|
-
who: Math.max(...events.map((event) => who(event).length))
|
|
108709
|
+
who: Math.max(...events.map((event) => escapeTerminalText(who(event)).length))
|
|
108523
108710
|
};
|
|
108524
108711
|
for (const event of events)
|
|
108525
108712
|
out.write(`${eventLine(event, widths)}
|
|
@@ -108717,7 +108904,7 @@ ${retry}
|
|
|
108717
108904
|
["Result", payload.changed ? "edited" : "nothing changed"],
|
|
108718
108905
|
["Version", payload.version === null ? "-" : String(payload.version)],
|
|
108719
108906
|
...payload.changed ? [["Changed", changedOf(body)]] : [],
|
|
108720
|
-
["Description", payload.description ?
|
|
108907
|
+
["Description", payload.description ? escapeTerminalText(payload.description) : "-"],
|
|
108721
108908
|
...linkRows(payload.socials)
|
|
108722
108909
|
]);
|
|
108723
108910
|
return 0;
|
|
@@ -108771,18 +108958,18 @@ async function tokenHistory2(args, ctx) {
|
|
|
108771
108958
|
writeHeading(ctx.deps, "stdout", `version ${item.version}`);
|
|
108772
108959
|
writeFields(ctx.deps, "stdout", [
|
|
108773
108960
|
["At", item.createdAt],
|
|
108774
|
-
["By", item.author?.name ?
|
|
108961
|
+
["By", item.author?.name ? escapeTerminalText(item.author.name) : "Agent"],
|
|
108775
108962
|
["Changed", item.changedFields.join(", ")],
|
|
108776
108963
|
...item.changedFields.map((field) => {
|
|
108777
108964
|
const now = field === "description" ? item.description : item.socials[field.slice("socials.".length)];
|
|
108778
108965
|
const was = field === "description" ? item.prevDescription : item.prevSocials[field.slice("socials.".length)];
|
|
108779
|
-
return [field, `${was ?
|
|
108966
|
+
return [field, `${was ? escapeTerminalText(was) : "-"} -> ${now ? escapeTerminalText(now) : "-"}`];
|
|
108780
108967
|
})
|
|
108781
108968
|
]);
|
|
108782
108969
|
}
|
|
108783
108970
|
if (data.nextCursor)
|
|
108784
108971
|
ctx.deps.stdout.write(`
|
|
108785
|
-
More: --cursor ${data.nextCursor}
|
|
108972
|
+
More: --cursor ${escapeTerminalText(data.nextCursor)}
|
|
108786
108973
|
`);
|
|
108787
108974
|
return 0;
|
|
108788
108975
|
}
|
|
@@ -118681,7 +118868,7 @@ async function runWithConfirm(argv, deps) {
|
|
|
118681
118868
|
async function runCommand(argv, deps) {
|
|
118682
118869
|
const extracted = extractGlobalFlags(argv);
|
|
118683
118870
|
if ("error" in extracted) {
|
|
118684
|
-
deps.stderr.write(`${extracted.error}
|
|
118871
|
+
deps.stderr.write(`${escapeTerminalText(extracted.error)}
|
|
118685
118872
|
`);
|
|
118686
118873
|
return 2;
|
|
118687
118874
|
}
|
|
@@ -118802,7 +118989,7 @@ async function runCommand(argv, deps) {
|
|
|
118802
118989
|
return 1;
|
|
118803
118990
|
}
|
|
118804
118991
|
if (verdict.warning)
|
|
118805
|
-
deps.stderr.write(`${verdict.warning}
|
|
118992
|
+
deps.stderr.write(`${terminalLines(verdict.warning)}
|
|
118806
118993
|
`);
|
|
118807
118994
|
}
|
|
118808
118995
|
if (ROUTED_COMMANDS.has(word3) && routesToCommand(cmd, sub))
|
|
@@ -118833,7 +119020,7 @@ function splitFix(message) {
|
|
|
118833
119020
|
}
|
|
118834
119021
|
function unknownCommand(deps, token2, word3) {
|
|
118835
119022
|
if (token2 !== undefined)
|
|
118836
|
-
deps.stderr.write(`Unknown command: ${token2}
|
|
119023
|
+
deps.stderr.write(`Unknown command: ${escapeTerminalText(token2)}
|
|
118837
119024
|
`);
|
|
118838
119025
|
deps.stderr.write((word3 === undefined ? undefined : renderTopic(word3)) ?? renderTopLevel());
|
|
118839
119026
|
return 1;
|
|
@@ -119049,12 +119236,12 @@ if (isMainModule) {
|
|
|
119049
119236
|
process.exitCode = 1;
|
|
119050
119237
|
if (stream === process.stderr)
|
|
119051
119238
|
return;
|
|
119052
|
-
process.stderr.write(`Unexpected error: ${err?.message ?? String(err)}
|
|
119239
|
+
process.stderr.write(`Unexpected error: ${escapeTerminalText(err?.message ?? String(err))}
|
|
119053
119240
|
`);
|
|
119054
119241
|
});
|
|
119055
119242
|
}
|
|
119056
119243
|
await main().catch((err) => {
|
|
119057
|
-
process.stderr.write(`Unexpected error: ${err instanceof Error ? err.message : String(err)}
|
|
119244
|
+
process.stderr.write(`Unexpected error: ${escapeTerminalText(err instanceof Error ? err.message : String(err))}
|
|
119058
119245
|
`);
|
|
119059
119246
|
process.exitCode = 1;
|
|
119060
119247
|
});
|
package/package.json
CHANGED