@candledottv/cli 0.13.24 → 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 +4 -4
- package/dist/index.js +237 -91
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -109,8 +109,8 @@ 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\|label>` | Pause one key using the device token. Its requests are refused with `PROFILE_PAUSED` until
|
|
113
|
-
| `candle keys resume <prefix\|label>` |
|
|
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
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. |
|
|
@@ -120,7 +120,7 @@ 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
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. |
|
|
@@ -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.
|
package/dist/index.js
CHANGED
|
@@ -734,6 +734,23 @@ var init_agent_key_access = __esm(() => {
|
|
|
734
734
|
];
|
|
735
735
|
});
|
|
736
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
|
+
|
|
737
754
|
// src/terminal-theme.ts
|
|
738
755
|
var TERMINAL_TOKENS;
|
|
739
756
|
var init_terminal_theme = __esm(() => {
|
|
@@ -761,7 +778,7 @@ var init_terminal_theme = __esm(() => {
|
|
|
761
778
|
});
|
|
762
779
|
|
|
763
780
|
// src/version.ts
|
|
764
|
-
var CLI_VERSION = "0.13.
|
|
781
|
+
var CLI_VERSION = "0.13.25";
|
|
765
782
|
|
|
766
783
|
// src/presentation.ts
|
|
767
784
|
function terminalStyle(deps, stream, machine = false) {
|
|
@@ -781,6 +798,7 @@ function terminalStyle(deps, stream, machine = false) {
|
|
|
781
798
|
};
|
|
782
799
|
}
|
|
783
800
|
function accent(text, style, role = "brand") {
|
|
801
|
+
text = terminalLines(text);
|
|
784
802
|
if (!style.color)
|
|
785
803
|
return text;
|
|
786
804
|
const token = TERMINAL_TOKENS[style.theme][role];
|
|
@@ -944,6 +962,7 @@ function symbols(style) {
|
|
|
944
962
|
return style.styled ? UNICODE_SYMBOLS : ASCII_SYMBOLS;
|
|
945
963
|
}
|
|
946
964
|
function sgr(text, style, code) {
|
|
965
|
+
text = terminalLines(text);
|
|
947
966
|
return style.color && text ? `\x1B[${code}m${text}\x1B[0m` : text;
|
|
948
967
|
}
|
|
949
968
|
function bold(text, style) {
|
|
@@ -953,30 +972,32 @@ function dim(text, style) {
|
|
|
953
972
|
return sgr(text, style, "2");
|
|
954
973
|
}
|
|
955
974
|
function heading(text, style) {
|
|
956
|
-
return wrapText(text, proseWidth(style)).map((line) => bold(line, style)).join(`
|
|
975
|
+
return wrapText(terminalLines(text), proseWidth(style)).map((line) => bold(line, style)).join(`
|
|
957
976
|
`);
|
|
958
977
|
}
|
|
959
978
|
function fields(rows, style, indent = 2) {
|
|
979
|
+
rows = rows.map(([label, value]) => [escapeTerminalText(label), value]);
|
|
960
980
|
const budget = proseWidth(style);
|
|
961
981
|
const pad = " ".repeat(Math.min(indent, Math.max(0, budget)));
|
|
962
982
|
const labelWidth = Math.max(0, ...rows.map(([label]) => displayWidth(label)));
|
|
963
983
|
const sideWidth = budget - pad.length - labelWidth - 2;
|
|
964
984
|
const out = [];
|
|
965
985
|
for (const [label, raw] of rows) {
|
|
966
|
-
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));
|
|
967
988
|
if (sideWidth >= 1 && fitsWidth(value, sideWidth)) {
|
|
968
989
|
const [first = "", ...rest] = value.split(`
|
|
969
990
|
`).flatMap((line) => wrapText(line, sideWidth));
|
|
970
|
-
out.push(`${pad}${dim(padDisplay(label, labelWidth), style)} ${first}`);
|
|
991
|
+
out.push(`${pad}${dim(padDisplay(label, labelWidth), style)} ${paint(first)}`);
|
|
971
992
|
for (const line of rest)
|
|
972
|
-
out.push(`${pad}${" ".repeat(labelWidth + 2)}${line}`);
|
|
993
|
+
out.push(`${pad}${" ".repeat(labelWidth + 2)}${paint(line)}`);
|
|
973
994
|
} else {
|
|
974
|
-
out.push(...stackedField(label, value, style, pad, budget));
|
|
995
|
+
out.push(...stackedField(label, value, style, pad, budget, paint));
|
|
975
996
|
}
|
|
976
997
|
}
|
|
977
998
|
return out;
|
|
978
999
|
}
|
|
979
|
-
function stackedField(label, value, style, pad, budget) {
|
|
1000
|
+
function stackedField(label, value, style, pad, budget, paint) {
|
|
980
1001
|
const labelBudget = Math.max(1, budget - pad.length);
|
|
981
1002
|
const out = wrapText(label, labelBudget).map((line) => `${pad}${dim(line, style)}`);
|
|
982
1003
|
const nested = budget - pad.length - 2;
|
|
@@ -993,11 +1014,12 @@ function stackedField(label, value, style, pad, budget) {
|
|
|
993
1014
|
for (const part of value.split(`
|
|
994
1015
|
`)) {
|
|
995
1016
|
for (const line of wrapText(part, valueWidth))
|
|
996
|
-
out.push(line ? `${valuePad}${line}` : "");
|
|
1017
|
+
out.push(line ? `${valuePad}${paint(line)}` : "");
|
|
997
1018
|
}
|
|
998
1019
|
return out;
|
|
999
1020
|
}
|
|
1000
1021
|
function renderNotice(level, text, style, indent = 2) {
|
|
1022
|
+
text = terminalLines(text);
|
|
1001
1023
|
const sym = symbols(style);
|
|
1002
1024
|
const mark = level === "note" ? sym.note : level === "warning" ? sym.warning : sym.danger;
|
|
1003
1025
|
const budget = proseWidth(style);
|
|
@@ -1027,6 +1049,7 @@ ${renderNotice(level, text, style).join(`
|
|
|
1027
1049
|
`);
|
|
1028
1050
|
}
|
|
1029
1051
|
function success(text, style) {
|
|
1052
|
+
text = terminalLines(text);
|
|
1030
1053
|
const mark = symbols(style).ok;
|
|
1031
1054
|
const hang = " ".repeat(displayWidth(mark) + 1);
|
|
1032
1055
|
const budget = Math.max(1, proseWidth(style) - displayWidth(mark) - 1);
|
|
@@ -1048,17 +1071,19 @@ var init_presentation = __esm(() => {
|
|
|
1048
1071
|
});
|
|
1049
1072
|
|
|
1050
1073
|
// src/render.ts
|
|
1051
|
-
function formatScopesForSummary(scopes) {
|
|
1052
|
-
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(", ");
|
|
1053
|
-
}
|
|
1054
1074
|
function terminalText(value) {
|
|
1055
1075
|
return value.replace(/[\u0000-\u001f\u007f-\u009f]/g, "");
|
|
1056
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
|
+
}
|
|
1057
1080
|
function numericColumn(rows, col) {
|
|
1058
1081
|
const known = rows.map((row) => row[col] ?? "").filter((cell) => cell !== "" && cell !== "–" && cell !== "-");
|
|
1059
1082
|
return known.length > 0 && known.every((cell) => NUMERIC_CELL.test(cell));
|
|
1060
1083
|
}
|
|
1061
1084
|
function renderTable(headers, rows, options) {
|
|
1085
|
+
headers = headers.map(escapeTerminalText);
|
|
1086
|
+
rows = rows.map((row) => row.map((cell) => escapeTerminalText(cell ?? "")));
|
|
1062
1087
|
if (!options)
|
|
1063
1088
|
return renderPlainTable(headers, rows);
|
|
1064
1089
|
const specs = headers.map((_, col) => options.columns?.[col] ?? {});
|
|
@@ -1219,6 +1244,14 @@ function errorParts(result, ctx) {
|
|
|
1219
1244
|
return { message: result.message, fixes: result.uiHint ? [{ label: result.uiHint }] : [] };
|
|
1220
1245
|
}
|
|
1221
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
|
+
};
|
|
1222
1255
|
const budget = proseWidth(style);
|
|
1223
1256
|
const out = [...errorMessageLines(parts.message, style, budget)];
|
|
1224
1257
|
if (parts.fixes.length === 0)
|
|
@@ -1286,7 +1319,7 @@ function writeFailure(deps, result, ctx, json) {
|
|
|
1286
1319
|
deps.stdout.write(`${JSON.stringify(errorEnvelope(result, ctx))}
|
|
1287
1320
|
`);
|
|
1288
1321
|
else
|
|
1289
|
-
deps.stderr.write(`${renderError(result, ctx)}
|
|
1322
|
+
deps.stderr.write(`${terminalLines(renderError(result, ctx))}
|
|
1290
1323
|
`);
|
|
1291
1324
|
}
|
|
1292
1325
|
function writeLocalFailure(deps, failure2, json) {
|
|
@@ -1298,7 +1331,7 @@ function writeLocalFailure(deps, failure2, json) {
|
|
|
1298
1331
|
const separator = failure2.suggestion?.includes(`
|
|
1299
1332
|
`) ? `
|
|
1300
1333
|
` : " ";
|
|
1301
|
-
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)}
|
|
1302
1335
|
`);
|
|
1303
1336
|
}
|
|
1304
1337
|
function writeUsageFailure(deps, message, json) {
|
|
@@ -1306,7 +1339,7 @@ function writeUsageFailure(deps, message, json) {
|
|
|
1306
1339
|
deps.stdout.write(`${JSON.stringify({ ok: false, code: "USAGE", message })}
|
|
1307
1340
|
`);
|
|
1308
1341
|
else
|
|
1309
|
-
deps.stderr.write(`${message}
|
|
1342
|
+
deps.stderr.write(`${terminalLines(message)}
|
|
1310
1343
|
`);
|
|
1311
1344
|
}
|
|
1312
1345
|
function portalDevicesUrl(apiUrl, portalOrigin) {
|
|
@@ -6858,7 +6891,7 @@ var init_launch_definition = __esm(() => {
|
|
|
6858
6891
|
{
|
|
6859
6892
|
key: "curve.virtualQuote",
|
|
6860
6893
|
type: "decimal",
|
|
6861
|
-
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.",
|
|
6862
6895
|
modes: [
|
|
6863
6896
|
"exp-custom"
|
|
6864
6897
|
],
|
|
@@ -6897,7 +6930,7 @@ var init_launch_definition = __esm(() => {
|
|
|
6897
6930
|
{
|
|
6898
6931
|
key: "curve.threshold",
|
|
6899
6932
|
type: "decimal",
|
|
6900
|
-
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.",
|
|
6901
6934
|
modes: [
|
|
6902
6935
|
"exp-custom"
|
|
6903
6936
|
],
|
|
@@ -20215,10 +20248,13 @@ function credentialEnvOverrides(env) {
|
|
|
20215
20248
|
return ["CANDLE_API_KEY_FILE", "CANDLE_API_KEY", "CANDLE_DEVICE_TOKEN"].filter((name) => env[name]?.trim());
|
|
20216
20249
|
}
|
|
20217
20250
|
function identityLine(profile, account, apiUrl, overrides, username, options = {}) {
|
|
20218
|
-
|
|
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));
|
|
20219
20255
|
const shown = overrides?.length ? address ? `${address} (${overrides.join(", ")} override)` : `unknown (${overrides.join(", ")} override)` : username && address ? `${username} (${address})` : address ?? "unknown";
|
|
20220
20256
|
const name = profile ?? "none";
|
|
20221
|
-
const host = apiUrl.replace(/^https:\/\//i, "").replace(/\/+$/, "");
|
|
20257
|
+
const host = escapeTerminalText(apiUrl).replace(/^https:\/\//i, "").replace(/\/+$/, "");
|
|
20222
20258
|
const sep2 = options.style ? symbols(options.style).note : "·";
|
|
20223
20259
|
return `Profile ${options.style ? bold(name, options.style) : name} ${sep2} Account ${shown} ${sep2} ${host}`;
|
|
20224
20260
|
}
|
|
@@ -25688,6 +25724,76 @@ var init_hood_legs = __esm(() => {
|
|
|
25688
25724
|
V4_SWAP_PARAMS = { tuple: [V4_POOL_KEY, "bool", "uint", "uint", "uint", "bytes"] };
|
|
25689
25725
|
});
|
|
25690
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
|
+
|
|
25691
25797
|
// src/human-output.ts
|
|
25692
25798
|
function writeFields(deps, stream, rows, title) {
|
|
25693
25799
|
const style = terminalStyle(deps, stream);
|
|
@@ -26011,7 +26117,7 @@ function walletRecordCheck(row, trading, records) {
|
|
|
26011
26117
|
differences
|
|
26012
26118
|
} : pass("record");
|
|
26013
26119
|
}
|
|
26014
|
-
async function inspectReport(ctx, key, row, listing, records, payable, name) {
|
|
26120
|
+
async function inspectReport(ctx, key, row, listing, records, payable, name, chain2) {
|
|
26015
26121
|
const checks = [pass("key"), payable ? failure2("payable", payable) : pass("payable")];
|
|
26016
26122
|
checks.push(listing.scopes.includes("swap:write") ? pass("scope") : failure2("scope", new TradingError("SCOPE_MISSING", "The bound key needs swap:write.")));
|
|
26017
26123
|
const report = {
|
|
@@ -26024,7 +26130,7 @@ async function inspectReport(ctx, key, row, listing, records, payable, name) {
|
|
|
26024
26130
|
return { report };
|
|
26025
26131
|
}
|
|
26026
26132
|
checks.push(walletRecordCheck(row, listing.rows, records));
|
|
26027
|
-
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", {
|
|
26028
26134
|
apiKey: key,
|
|
26029
26135
|
keyPrefix: listing.keyPrefix
|
|
26030
26136
|
});
|
|
@@ -26217,14 +26323,18 @@ async function buildCheck(ctx, key, wallet3, report, from, to, amount, slippage,
|
|
|
26217
26323
|
}
|
|
26218
26324
|
}
|
|
26219
26325
|
async function teeCheck(args, ctx) {
|
|
26220
|
-
const parsed = parseArgs(args, {
|
|
26326
|
+
const parsed = parseArgs(args, {
|
|
26327
|
+
valueFlags: ["--label-prefix", "--max", "--rpc-url", "--chain"],
|
|
26328
|
+
booleanFlags: ["--sign"]
|
|
26329
|
+
});
|
|
26221
26330
|
if ("error" in parsed) {
|
|
26222
26331
|
writeUsageFailure(ctx.deps, parsed.error, ctx.json);
|
|
26223
26332
|
return 2;
|
|
26224
26333
|
}
|
|
26225
26334
|
const max = Number(parsed.values["--max"] ?? "10");
|
|
26226
|
-
|
|
26227
|
-
|
|
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);
|
|
26228
26338
|
return 2;
|
|
26229
26339
|
}
|
|
26230
26340
|
try {
|
|
@@ -26256,8 +26366,13 @@ async function teeCheck(args, ctx) {
|
|
|
26256
26366
|
for (const name of targets) {
|
|
26257
26367
|
const matches = listing.rows.filter((row) => matchesName(row, name));
|
|
26258
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"}.`);
|
|
26259
|
-
const { report, wallet: wallet3 } = await inspectReport(ctx, key, matches.length === 1 ? matches[0] : undefined, listing, records, payable, name);
|
|
26260
|
-
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)
|
|
26261
26376
|
await buildCheck(ctx, key, wallet3, report, "SOL", "USDC", "0.000001", 50, parsed.values["--rpc-url"], true);
|
|
26262
26377
|
else
|
|
26263
26378
|
report.checks.push(skipped("sign", sign4 ? report.checks.find((check) => check.status === "fail")?.id ?? "signer" : "--sign not set"));
|
|
@@ -26294,7 +26409,7 @@ async function swapCheck(args, ctx) {
|
|
|
26294
26409
|
if (from.chain !== "solana" || to.chain !== "solana")
|
|
26295
26410
|
throw new TradingError("VALIDATION_FAILED", "Dry run checks Solana TEE pairs; Hood and bridge pairs are unsupported.");
|
|
26296
26411
|
if (parsed.values["--to"] !== undefined || parsed.booleans.has("--wait")) {
|
|
26297
|
-
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);
|
|
26298
26413
|
return 2;
|
|
26299
26414
|
}
|
|
26300
26415
|
if (from.asset === to.asset || !from.base && !to.base)
|
|
@@ -26317,7 +26432,7 @@ async function swapCheck(args, ctx) {
|
|
|
26317
26432
|
payable = error;
|
|
26318
26433
|
}
|
|
26319
26434
|
const records = await readWalletRecords(ctx, key);
|
|
26320
|
-
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);
|
|
26321
26436
|
if (wallet3)
|
|
26322
26437
|
await buildCheck(ctx, key, wallet3, report, from.asset, to.asset, parsed.values["--amount"], slippage, parsed.values["--rpc-url"], false);
|
|
26323
26438
|
else
|
|
@@ -26452,7 +26567,7 @@ function printTradingResult(ctx, result2) {
|
|
|
26452
26567
|
}
|
|
26453
26568
|
const printed = ctx.json && (result2.kind === "swap" || result2.kind === "trade") ? swapJsonResult(result2) : result2;
|
|
26454
26569
|
ctx.deps.stdout.write(ctx.json ? `${JSON.stringify(printed)}
|
|
26455
|
-
` : `${JSON.stringify(result2, null, 2)}
|
|
26570
|
+
` : `${terminalLines(JSON.stringify(result2, null, 2))}
|
|
26456
26571
|
`);
|
|
26457
26572
|
const job = result2.job;
|
|
26458
26573
|
const operation = job?.operation;
|
|
@@ -26501,11 +26616,11 @@ async function lookupOperation(ctx, key, id, kind) {
|
|
|
26501
26616
|
async function swapStatus(args, ctx) {
|
|
26502
26617
|
const parsed = parseArgs(args, { valueFlags: ["--kind", "--operation"], booleanFlags: ["--wait"] });
|
|
26503
26618
|
if (!("error" in parsed) && parsed.values["--operation"] !== undefined) {
|
|
26504
|
-
if (parsed.positionals.length || parsed.values["--kind"]
|
|
26505
|
-
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);
|
|
26506
26621
|
return 2;
|
|
26507
26622
|
}
|
|
26508
|
-
return hoodOperationRead(ctx, parsed.values["--operation"]);
|
|
26623
|
+
return hoodOperationRead(ctx, parsed.values["--operation"], undefined, false, parsed.booleans.has("--wait"));
|
|
26509
26624
|
}
|
|
26510
26625
|
if ("error" in parsed || parsed.positionals.length !== 1 || !validClientId(parsed.positionals[0] ?? "") || parsed.values["--kind"] !== undefined && !["trade", "swap", "launch"].includes(parsed.values["--kind"])) {
|
|
26511
26626
|
writeUsageFailure(ctx.deps, "Usage: candle swap status <id> [--kind trade|swap|launch] [--wait]", ctx.json);
|
|
@@ -26578,7 +26693,7 @@ async function swapReceipt(args, ctx) {
|
|
|
26578
26693
|
}
|
|
26579
26694
|
describeReceipt(ctx, body, answer.job);
|
|
26580
26695
|
ctx.deps.stdout.write(ctx.json ? `${JSON.stringify(body)}
|
|
26581
|
-
` : `${JSON.stringify(body, null, 2)}
|
|
26696
|
+
` : `${terminalLines(JSON.stringify(body, null, 2))}
|
|
26582
26697
|
`);
|
|
26583
26698
|
return 0;
|
|
26584
26699
|
} catch (error) {
|
|
@@ -26589,7 +26704,7 @@ function describeReceipt(ctx, body, job) {
|
|
|
26589
26704
|
const settlement = body.settlement;
|
|
26590
26705
|
if (settlement === null || typeof settlement !== "object") {
|
|
26591
26706
|
if (typeof body.next === "string")
|
|
26592
|
-
ctx.deps.stderr.write(`${body.next}
|
|
26707
|
+
ctx.deps.stderr.write(`${terminalLines(body.next)}
|
|
26593
26708
|
`);
|
|
26594
26709
|
return;
|
|
26595
26710
|
}
|
|
@@ -26712,8 +26827,9 @@ async function swap2(args, ctx) {
|
|
|
26712
26827
|
slippage,
|
|
26713
26828
|
...bridge
|
|
26714
26829
|
});
|
|
26715
|
-
|
|
26716
|
-
|
|
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);
|
|
26717
26833
|
const chain2 = pairChain(fromAsset, toAsset);
|
|
26718
26834
|
const walletFlag = flags["--wallet"];
|
|
26719
26835
|
const named = walletFlag === undefined ? undefined : walletNameChain(walletFlag);
|
|
@@ -26721,6 +26837,7 @@ async function swap2(args, ctx) {
|
|
|
26721
26837
|
throw chainMismatch(`--wallet ${safeText(walletFlag)}`, named, chain2);
|
|
26722
26838
|
if (chain2 === "hood")
|
|
26723
26839
|
return await hoodSwap(ctx, {
|
|
26840
|
+
wait: parsed.booleans.has("--wait"),
|
|
26724
26841
|
flags,
|
|
26725
26842
|
yes: parsed.booleans.has("--yes"),
|
|
26726
26843
|
id,
|
|
@@ -26728,6 +26845,8 @@ async function swap2(args, ctx) {
|
|
|
26728
26845
|
from: fromAsset,
|
|
26729
26846
|
to: toAsset
|
|
26730
26847
|
});
|
|
26848
|
+
if (parsed.booleans.has("--wait"))
|
|
26849
|
+
throw new TradingUsage(unsupportedWait);
|
|
26731
26850
|
const from = fromAsset.asset;
|
|
26732
26851
|
const to = toAsset.asset;
|
|
26733
26852
|
if (from === to)
|
|
@@ -26953,9 +27072,19 @@ async function hoodSwap(ctx, args) {
|
|
|
26953
27072
|
const kind = from.base && to.base ? "swap" : "trade";
|
|
26954
27073
|
const key = await tradingKey(ctx);
|
|
26955
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
|
+
};
|
|
26956
27083
|
if (prior)
|
|
26957
|
-
return
|
|
27084
|
+
return replay(prior);
|
|
26958
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>.`);
|
|
26959
27088
|
const payerAddress = payer.kind === "tee" ? payer.wallet.address : payer.address;
|
|
26960
27089
|
const rpc = lazyEvmRpc(ctx, flags["--rpc-url"]);
|
|
26961
27090
|
const decimals = await hoodDecimals(from, rpc);
|
|
@@ -26995,7 +27124,7 @@ async function hoodSwap(ctx, args) {
|
|
|
26995
27124
|
...payer.kind === "embedded" ? { deferExecution: true } : {}
|
|
26996
27125
|
});
|
|
26997
27126
|
if (built.job || built.status === "executed")
|
|
26998
|
-
return
|
|
27127
|
+
return replay({ ...built, clientTradeId: id, kind });
|
|
26999
27128
|
const body = kind === "swap" ? built.payload : built;
|
|
27000
27129
|
const data = swapBuildSchema.parse(body);
|
|
27001
27130
|
const echoed = kind === "swap" ? body?.recipient : body?.walletAddress;
|
|
@@ -27079,7 +27208,7 @@ async function hoodSwap(ctx, args) {
|
|
|
27079
27208
|
treasury: () => hoodFeeTreasury(rpc()),
|
|
27080
27209
|
curve: () => registeredCurveFor(rpc(), token2)
|
|
27081
27210
|
});
|
|
27082
|
-
const
|
|
27211
|
+
const execute = () => runSequencedLegs(ctx, key, {
|
|
27083
27212
|
wallet: wallet3,
|
|
27084
27213
|
first: sequenced,
|
|
27085
27214
|
submitPath: kind === "swap" ? "/api/v1/agent/swap/submit" : "/api/v1/trade/agent/submit",
|
|
@@ -27095,6 +27224,16 @@ async function hoodSwap(ctx, args) {
|
|
|
27095
27224
|
notices.push(notice2);
|
|
27096
27225
|
}
|
|
27097
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);
|
|
27098
27237
|
const settled = kind === "swap" ? reportSettlement(ctx, run.final.settlement, { asset: to.asset, decimals: outDecimals }, id) : {};
|
|
27099
27238
|
return printTradingResult(ctx, {
|
|
27100
27239
|
...run.final,
|
|
@@ -27353,18 +27492,21 @@ async function bridgeSwap(ctx, args) {
|
|
|
27353
27492
|
return printTradingResult(ctx, { ...receipt, waited: { final: false, error: reason } });
|
|
27354
27493
|
}
|
|
27355
27494
|
}
|
|
27356
|
-
async function hoodOperationRead(ctx, id, candidateHash, recover = false) {
|
|
27495
|
+
async function hoodOperationRead(ctx, id, candidateHash, recover = false, wait = false) {
|
|
27357
27496
|
if (!validClientId(id)) {
|
|
27358
27497
|
writeUsageFailure(ctx.deps, "An operationId is required", ctx.json);
|
|
27359
27498
|
return 2;
|
|
27360
27499
|
}
|
|
27361
27500
|
try {
|
|
27362
27501
|
const key = await tradingKey(ctx);
|
|
27502
|
+
if (wait)
|
|
27503
|
+
return await waitForHoodOperation(ctx, key, id);
|
|
27363
27504
|
const path = `/api/v1/agent/hood-tee/operations/${encodeURIComponent(id)}${recover ? "/recover" : ""}`;
|
|
27364
27505
|
const result2 = await request(ctx, key, path, recover ? candidateHash ? { candidateHash } : {} : undefined);
|
|
27365
27506
|
const operation = result2.operation;
|
|
27366
27507
|
const guard = operation?.guard;
|
|
27367
|
-
|
|
27508
|
+
const output = JSON.stringify(result2, null, ctx.json ? undefined : 2);
|
|
27509
|
+
ctx.deps.stdout.write(`${ctx.json ? output : terminalLines(output)}
|
|
27368
27510
|
`);
|
|
27369
27511
|
if (guard)
|
|
27370
27512
|
ctx.deps.stderr.write(`${safeText(guard.reason)}: ${safeText(guard.clearingCondition)} Read: candle swap status --operation ${id}.
|
|
@@ -27396,6 +27538,7 @@ var init_swap = __esm(() => {
|
|
|
27396
27538
|
init_client();
|
|
27397
27539
|
init_evm_lite();
|
|
27398
27540
|
init_hood_legs();
|
|
27541
|
+
init_hood_operation_wait();
|
|
27399
27542
|
init_human_output();
|
|
27400
27543
|
init_presentation();
|
|
27401
27544
|
init_profiles();
|
|
@@ -47613,11 +47756,10 @@ function mintedByLabel(mintedBy, ownDeviceTokenPrefix) {
|
|
|
47613
47756
|
function labelCell(label) {
|
|
47614
47757
|
if (!label)
|
|
47615
47758
|
return "";
|
|
47616
|
-
const cleaned = Array.from(label).map((ch) => {
|
|
47759
|
+
const cleaned = Array.from(escapeTerminalText(label)).map((ch) => {
|
|
47617
47760
|
const code = ch.codePointAt(0) ?? 0;
|
|
47618
|
-
const isControl = code < 32 || code >= 127 && code <= 159;
|
|
47619
47761
|
const isBidiOrInvisible = code >= 8203 && code <= 8207 || code >= 8234 && code <= 8238 || code >= 8294 && code <= 8297 || code === 65279;
|
|
47620
|
-
return
|
|
47762
|
+
return isBidiOrInvisible ? " " : ch;
|
|
47621
47763
|
}).join("");
|
|
47622
47764
|
return cleaned.replace(/\s+/g, " ").trim();
|
|
47623
47765
|
}
|
|
@@ -77051,7 +77193,7 @@ var LAUNCH_DEFINITION2 = {
|
|
|
77051
77193
|
{
|
|
77052
77194
|
key: "curve.virtualQuote",
|
|
77053
77195
|
type: "decimal",
|
|
77054
|
-
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.",
|
|
77055
77197
|
modes: [
|
|
77056
77198
|
"exp-custom"
|
|
77057
77199
|
],
|
|
@@ -77090,7 +77232,7 @@ var LAUNCH_DEFINITION2 = {
|
|
|
77090
77232
|
{
|
|
77091
77233
|
key: "curve.threshold",
|
|
77092
77234
|
type: "decimal",
|
|
77093
|
-
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.",
|
|
77094
77236
|
modes: [
|
|
77095
77237
|
"exp-custom"
|
|
77096
77238
|
],
|
|
@@ -78082,7 +78224,7 @@ var HELP = {
|
|
|
78082
78224
|
group: "Trade",
|
|
78083
78225
|
intro: "Quote, swap and bridge on Solana or Hood; read an operation",
|
|
78084
78226
|
summary: "Quote, confirm and swap on Solana or Hood, bridge between them; read an operation by id",
|
|
78085
|
-
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.",
|
|
78086
78228
|
usage: [
|
|
78087
78229
|
"candle swap <from> <to> [flags]",
|
|
78088
78230
|
"candle swap status <id>",
|
|
@@ -78097,8 +78239,8 @@ var HELP = {
|
|
|
78097
78239
|
description: "Quote, confirm and swap on Solana or Hood, or bridge between them"
|
|
78098
78240
|
},
|
|
78099
78241
|
{
|
|
78100
|
-
invocation: "status <id> [--kind trade|swap|launch] [--wait] | status --operation <operationId>",
|
|
78101
|
-
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)"
|
|
78102
78244
|
},
|
|
78103
78245
|
{
|
|
78104
78246
|
invocation: "recover --operation <operationId> [--hash <0x…>]",
|
|
@@ -78947,9 +79089,12 @@ var HELP = {
|
|
|
78947
79089
|
},
|
|
78948
79090
|
{
|
|
78949
79091
|
invocation: "stop <prefix|label>",
|
|
78950
|
-
description: "Pause one key with the device token
|
|
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)"
|
|
78951
79097
|
},
|
|
78952
|
-
{ invocation: "resume <prefix|label>", description: "Resume a stopped key with the device token" },
|
|
78953
79098
|
{
|
|
78954
79099
|
invocation: "rotate <prefix|label> ([--grace <duration> | --now] [--force] [--yes] | --finish)",
|
|
78955
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"
|
|
@@ -79038,7 +79183,7 @@ var HELP = {
|
|
|
79038
79183
|
},
|
|
79039
79184
|
{
|
|
79040
79185
|
invocation: "close-empty [--wallet embedded|<tee address|label>] [--keep <mint>]... [--client-trade-id <id>] [--preview] [--yes]",
|
|
79041
|
-
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"
|
|
79042
79187
|
}
|
|
79043
79188
|
],
|
|
79044
79189
|
flags: [
|
|
@@ -79252,8 +79397,8 @@ var HELP = {
|
|
|
79252
79397
|
usage: ["candle tee <subcommand> [flags]"],
|
|
79253
79398
|
rows: [
|
|
79254
79399
|
{
|
|
79255
|
-
invocation: "check <address|id|label>... [--label-prefix <p>] [--sign] [--max <n>] [--rpc-url <url>]",
|
|
79256
|
-
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"
|
|
79257
79402
|
},
|
|
79258
79403
|
{
|
|
79259
79404
|
invocation: "new [--label <name>]",
|
|
@@ -79319,6 +79464,7 @@ var HELP = {
|
|
|
79319
79464
|
examples: [
|
|
79320
79465
|
"candle tee check --label-prefix pool- --json",
|
|
79321
79466
|
"candle tee check AgentOne --sign --rpc-url <url>",
|
|
79467
|
+
"candle tee check hood-1 --chain hood --json",
|
|
79322
79468
|
"candle tee new --label AgentOne",
|
|
79323
79469
|
"candle tee status AgentOneAddress",
|
|
79324
79470
|
"candle tee sweep AgentOneAddress",
|
|
@@ -86292,7 +86438,7 @@ ${USAGE6}`, json);
|
|
|
86292
86438
|
} catch (error) {
|
|
86293
86439
|
if (!(error instanceof CoreClientError))
|
|
86294
86440
|
throw error;
|
|
86295
|
-
deps.stderr.write(`${error.code}: ${error.message}
|
|
86441
|
+
deps.stderr.write(`${terminalLines(`${error.code}: ${error.message}`)}
|
|
86296
86442
|
`);
|
|
86297
86443
|
return 1;
|
|
86298
86444
|
}
|
|
@@ -92005,7 +92151,7 @@ async function help(args, ctx) {
|
|
|
92005
92151
|
}
|
|
92006
92152
|
const topic = renderTopic(word3, terminalStyle(ctx.deps, "stdout", ctx.json), args.includes("--more"));
|
|
92007
92153
|
if (topic === undefined) {
|
|
92008
|
-
ctx.deps.stderr.write(`Unknown command: ${word3}
|
|
92154
|
+
ctx.deps.stderr.write(`Unknown command: ${escapeTerminalText(word3)}
|
|
92009
92155
|
`);
|
|
92010
92156
|
ctx.deps.stderr.write(renderTopLevel());
|
|
92011
92157
|
return 1;
|
|
@@ -92335,7 +92481,7 @@ async function keysAccessHistory(target, ctx) {
|
|
|
92335
92481
|
`);
|
|
92336
92482
|
return 0;
|
|
92337
92483
|
}
|
|
92338
|
-
const rows = result2.body.changes ?? [];
|
|
92484
|
+
const rows = (result2.body.changes ?? []).filter((row2) => row2.eventKind !== "profile_edit" && row2.change !== "profile_edit");
|
|
92339
92485
|
if (rows.length === 0) {
|
|
92340
92486
|
notice(deps, "stdout", "note", `No access changes on key ${resolved.keyPrefix}.`);
|
|
92341
92487
|
return 0;
|
|
@@ -92645,7 +92791,7 @@ function writeRefusal(ctx, refusal3, suggestion) {
|
|
|
92645
92791
|
`);
|
|
92646
92792
|
return;
|
|
92647
92793
|
}
|
|
92648
|
-
ctx.deps.stderr.write(`${suggestion ? `${message} ${suggestion}` : message}
|
|
92794
|
+
ctx.deps.stderr.write(`${escapeTerminalText(suggestion ? `${message} ${suggestion}` : message)}
|
|
92649
92795
|
`);
|
|
92650
92796
|
}
|
|
92651
92797
|
function writeApiFailure(ctx, result2, authType) {
|
|
@@ -92740,8 +92886,8 @@ function identity2(who) {
|
|
|
92740
92886
|
if (who.kind === "candle")
|
|
92741
92887
|
return "Candle";
|
|
92742
92888
|
if (who.kind === "agent")
|
|
92743
|
-
return
|
|
92744
|
-
return
|
|
92889
|
+
return escapeTerminalText(who.name ?? "Agent");
|
|
92890
|
+
return escapeTerminalText(who.name ?? who.account);
|
|
92745
92891
|
}
|
|
92746
92892
|
function sectionText(bytes, section2) {
|
|
92747
92893
|
if (bytes.length > TOKEN_PAGE_BODY_CAP_BYTES)
|
|
@@ -92780,7 +92926,7 @@ function factValue(fact) {
|
|
|
92780
92926
|
if (fact.raw === undefined)
|
|
92781
92927
|
return fact.status;
|
|
92782
92928
|
const value = fact.decimals ? rawUnits2(fact.raw, fact.decimals) : fact.raw;
|
|
92783
|
-
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}` : ""})`;
|
|
92784
92930
|
}
|
|
92785
92931
|
function rawUnits2(raw, decimals) {
|
|
92786
92932
|
if (!/^\d+$/.test(raw))
|
|
@@ -92822,7 +92968,7 @@ function writeAbout(ctx, about) {
|
|
|
92822
92968
|
if (v.linksWithheld)
|
|
92823
92969
|
notice(ctx.deps, "stdout", "note", "Links are withheld pending review.");
|
|
92824
92970
|
ctx.deps.stdout.write(`
|
|
92825
|
-
${
|
|
92971
|
+
${escapeTerminalText(v.text)}
|
|
92826
92972
|
`);
|
|
92827
92973
|
}
|
|
92828
92974
|
writeHeading(ctx.deps, "stdout", "Supply, measured by Candle");
|
|
@@ -92831,12 +92977,7 @@ ${terminalTextKeepingLines(v.text)}
|
|
|
92831
92977
|
ctx.deps.stdout.write(` Measurements unavailable
|
|
92832
92978
|
`);
|
|
92833
92979
|
else
|
|
92834
|
-
writeFields(ctx.deps, "stdout", facts.map((f) => [
|
|
92835
|
-
}
|
|
92836
|
-
function terminalTextKeepingLines(text3) {
|
|
92837
|
-
return text3.split(`
|
|
92838
|
-
`).map((line) => terminalText(line.replace(/\t/g, " "))).join(`
|
|
92839
|
-
`);
|
|
92980
|
+
writeFields(ctx.deps, "stdout", facts.map((f) => [escapeTerminalText(f.metric), factValue(f)]));
|
|
92840
92981
|
}
|
|
92841
92982
|
async function tokenAbout(args, ctx) {
|
|
92842
92983
|
const usage4 = usageFor(ctx, USAGE9.about);
|
|
@@ -93150,12 +93291,12 @@ async function tokenHistory(args, ctx) {
|
|
|
93150
93291
|
]);
|
|
93151
93292
|
if (item.text !== null)
|
|
93152
93293
|
ctx.deps.stdout.write(`
|
|
93153
|
-
${
|
|
93294
|
+
${escapeTerminalText(item.text)}
|
|
93154
93295
|
`);
|
|
93155
93296
|
}
|
|
93156
93297
|
if (data.nextCursor)
|
|
93157
93298
|
ctx.deps.stdout.write(`
|
|
93158
|
-
More: --cursor ${data.nextCursor}
|
|
93299
|
+
More: --cursor ${escapeTerminalText(data.nextCursor)}
|
|
93159
93300
|
`);
|
|
93160
93301
|
return 0;
|
|
93161
93302
|
}
|
|
@@ -93341,12 +93482,12 @@ async function tokenComments(args, ctx) {
|
|
|
93341
93482
|
]);
|
|
93342
93483
|
if (item.text !== null)
|
|
93343
93484
|
ctx.deps.stdout.write(`
|
|
93344
|
-
${
|
|
93485
|
+
${escapeTerminalText(item.text)}
|
|
93345
93486
|
`);
|
|
93346
93487
|
}
|
|
93347
93488
|
if (data.nextCursor)
|
|
93348
93489
|
ctx.deps.stdout.write(`
|
|
93349
|
-
More: --cursor ${data.nextCursor}
|
|
93490
|
+
More: --cursor ${escapeTerminalText(data.nextCursor)}
|
|
93350
93491
|
`);
|
|
93351
93492
|
return 0;
|
|
93352
93493
|
}
|
|
@@ -101467,7 +101608,7 @@ async function read2(ctx, argv, command) {
|
|
|
101467
101608
|
for (const warning of result2.warnings)
|
|
101468
101609
|
notice(ctx.deps, "stderr", "note", `${safeText(warning)}
|
|
101469
101610
|
`.trim());
|
|
101470
|
-
const out = (line) => ctx.deps.stdout.write(`${line}
|
|
101611
|
+
const out = (line) => ctx.deps.stdout.write(`${terminalLines(line)}
|
|
101471
101612
|
`);
|
|
101472
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.`);
|
|
101473
101614
|
if ("market" in result2) {
|
|
@@ -102885,7 +103026,7 @@ async function runPaidRead(ctx, input) {
|
|
|
102885
103026
|
ctx.deps.stdout.write(`${JSON.stringify({ ok: true, data, ...paid.payment ? { payment: paid.payment } : {}, ...paid.servedFromCredit ? { servedFromCredit: true } : {} })}
|
|
102886
103027
|
`);
|
|
102887
103028
|
} else {
|
|
102888
|
-
ctx.deps.stdout.write(`${JSON.stringify(data, null, 2)}
|
|
103029
|
+
ctx.deps.stdout.write(`${terminalLines(JSON.stringify(data, null, 2))}
|
|
102889
103030
|
`);
|
|
102890
103031
|
}
|
|
102891
103032
|
return 0;
|
|
@@ -104299,6 +104440,9 @@ function financialHeading(text4, style) {
|
|
|
104299
104440
|
function signed(text4, style) {
|
|
104300
104441
|
return /^\+/.test(text4) ? accent(text4, style, "positive") : /^[-−]/.test(text4) ? accent(text4, style, "negative") : text4;
|
|
104301
104442
|
}
|
|
104443
|
+
function signedField(text4) {
|
|
104444
|
+
return /^\+/.test(text4) ? { text: text4, role: "positive" } : /^[-−]/.test(text4) ? { text: text4, role: "negative" } : text4;
|
|
104445
|
+
}
|
|
104302
104446
|
function financialTable(ctx, headers2, rows, columns) {
|
|
104303
104447
|
const style = terminalStyle(ctx.deps, "stdout");
|
|
104304
104448
|
const clean3 = rows.map((row2) => row2.map(terminalText));
|
|
@@ -104315,7 +104459,7 @@ function financialTable(ctx, headers2, rows, columns) {
|
|
|
104315
104459
|
if (style.width >= 60 && table.split(`
|
|
104316
104460
|
`).every((line) => displayWidth(line) <= style.width))
|
|
104317
104461
|
return table;
|
|
104318
|
-
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(`
|
|
104319
104463
|
`)).join(`
|
|
104320
104464
|
|
|
104321
104465
|
`);
|
|
@@ -104329,7 +104473,7 @@ function writeFinancialLines(ctx, rows) {
|
|
|
104329
104473
|
for (const [label = "", value = "", note = ""] of rows) {
|
|
104330
104474
|
const head = ` ${dim(padDisplay(label, labelWidth), style)} ${" ".repeat(valueWidth - displayWidth(value))}${signed(value, style)}`;
|
|
104331
104475
|
if (displayWidth(head) > style.width) {
|
|
104332
|
-
ctx.deps.stdout.write(`${fields([[label,
|
|
104476
|
+
ctx.deps.stdout.write(`${fields([[label, signedField(value)]], style).join(`
|
|
104333
104477
|
`)}
|
|
104334
104478
|
`);
|
|
104335
104479
|
if (note)
|
|
@@ -106319,10 +106463,12 @@ async function quantAsk(args, ctx) {
|
|
|
106319
106463
|
})}
|
|
106320
106464
|
`);
|
|
106321
106465
|
} else {
|
|
106322
|
-
ctx.deps.stdout.write(`${
|
|
106466
|
+
ctx.deps.stdout.write(`${result2.paragraphs.map(escapeTerminalText).join(`
|
|
106467
|
+
|
|
106468
|
+
`)}
|
|
106323
106469
|
`);
|
|
106324
106470
|
if (remaining !== undefined) {
|
|
106325
|
-
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))}` : ""}.
|
|
106326
106472
|
`);
|
|
106327
106473
|
}
|
|
106328
106474
|
}
|
|
@@ -108476,10 +108622,10 @@ function eventLine(event, widths) {
|
|
|
108476
108622
|
const { doing, detail } = action(event);
|
|
108477
108623
|
return [
|
|
108478
108624
|
formatTime(event.at).padEnd(widths.time),
|
|
108479
|
-
event.kind.padEnd(widths.kind),
|
|
108480
|
-
who(event).padEnd(widths.who),
|
|
108481
|
-
doing,
|
|
108482
|
-
detail
|
|
108625
|
+
escapeTerminalText(event.kind).padEnd(widths.kind),
|
|
108626
|
+
escapeTerminalText(who(event)).padEnd(widths.who),
|
|
108627
|
+
escapeTerminalText(doing),
|
|
108628
|
+
escapeTerminalText(detail)
|
|
108483
108629
|
].filter((cell) => cell.trim().length > 0).join(" ").trimEnd();
|
|
108484
108630
|
}
|
|
108485
108631
|
async function tokenEvents(args, ctx) {
|
|
@@ -108560,7 +108706,7 @@ async function tokenEvents(args, ctx) {
|
|
|
108560
108706
|
const widths = {
|
|
108561
108707
|
time: Math.max(...events.map((event) => formatTime(event.at).length)),
|
|
108562
108708
|
kind: Math.max(...events.map((event) => event.kind.length)),
|
|
108563
|
-
who: Math.max(...events.map((event) => who(event).length))
|
|
108709
|
+
who: Math.max(...events.map((event) => escapeTerminalText(who(event)).length))
|
|
108564
108710
|
};
|
|
108565
108711
|
for (const event of events)
|
|
108566
108712
|
out.write(`${eventLine(event, widths)}
|
|
@@ -108758,7 +108904,7 @@ ${retry}
|
|
|
108758
108904
|
["Result", payload.changed ? "edited" : "nothing changed"],
|
|
108759
108905
|
["Version", payload.version === null ? "-" : String(payload.version)],
|
|
108760
108906
|
...payload.changed ? [["Changed", changedOf(body)]] : [],
|
|
108761
|
-
["Description", payload.description ?
|
|
108907
|
+
["Description", payload.description ? escapeTerminalText(payload.description) : "-"],
|
|
108762
108908
|
...linkRows(payload.socials)
|
|
108763
108909
|
]);
|
|
108764
108910
|
return 0;
|
|
@@ -108812,18 +108958,18 @@ async function tokenHistory2(args, ctx) {
|
|
|
108812
108958
|
writeHeading(ctx.deps, "stdout", `version ${item.version}`);
|
|
108813
108959
|
writeFields(ctx.deps, "stdout", [
|
|
108814
108960
|
["At", item.createdAt],
|
|
108815
|
-
["By", item.author?.name ?
|
|
108961
|
+
["By", item.author?.name ? escapeTerminalText(item.author.name) : "Agent"],
|
|
108816
108962
|
["Changed", item.changedFields.join(", ")],
|
|
108817
108963
|
...item.changedFields.map((field) => {
|
|
108818
108964
|
const now = field === "description" ? item.description : item.socials[field.slice("socials.".length)];
|
|
108819
108965
|
const was = field === "description" ? item.prevDescription : item.prevSocials[field.slice("socials.".length)];
|
|
108820
|
-
return [field, `${was ?
|
|
108966
|
+
return [field, `${was ? escapeTerminalText(was) : "-"} -> ${now ? escapeTerminalText(now) : "-"}`];
|
|
108821
108967
|
})
|
|
108822
108968
|
]);
|
|
108823
108969
|
}
|
|
108824
108970
|
if (data.nextCursor)
|
|
108825
108971
|
ctx.deps.stdout.write(`
|
|
108826
|
-
More: --cursor ${data.nextCursor}
|
|
108972
|
+
More: --cursor ${escapeTerminalText(data.nextCursor)}
|
|
108827
108973
|
`);
|
|
108828
108974
|
return 0;
|
|
108829
108975
|
}
|
|
@@ -118722,7 +118868,7 @@ async function runWithConfirm(argv, deps) {
|
|
|
118722
118868
|
async function runCommand(argv, deps) {
|
|
118723
118869
|
const extracted = extractGlobalFlags(argv);
|
|
118724
118870
|
if ("error" in extracted) {
|
|
118725
|
-
deps.stderr.write(`${extracted.error}
|
|
118871
|
+
deps.stderr.write(`${escapeTerminalText(extracted.error)}
|
|
118726
118872
|
`);
|
|
118727
118873
|
return 2;
|
|
118728
118874
|
}
|
|
@@ -118843,7 +118989,7 @@ async function runCommand(argv, deps) {
|
|
|
118843
118989
|
return 1;
|
|
118844
118990
|
}
|
|
118845
118991
|
if (verdict.warning)
|
|
118846
|
-
deps.stderr.write(`${verdict.warning}
|
|
118992
|
+
deps.stderr.write(`${terminalLines(verdict.warning)}
|
|
118847
118993
|
`);
|
|
118848
118994
|
}
|
|
118849
118995
|
if (ROUTED_COMMANDS.has(word3) && routesToCommand(cmd, sub))
|
|
@@ -118874,7 +119020,7 @@ function splitFix(message) {
|
|
|
118874
119020
|
}
|
|
118875
119021
|
function unknownCommand(deps, token2, word3) {
|
|
118876
119022
|
if (token2 !== undefined)
|
|
118877
|
-
deps.stderr.write(`Unknown command: ${token2}
|
|
119023
|
+
deps.stderr.write(`Unknown command: ${escapeTerminalText(token2)}
|
|
118878
119024
|
`);
|
|
118879
119025
|
deps.stderr.write((word3 === undefined ? undefined : renderTopic(word3)) ?? renderTopLevel());
|
|
118880
119026
|
return 1;
|
|
@@ -119090,12 +119236,12 @@ if (isMainModule) {
|
|
|
119090
119236
|
process.exitCode = 1;
|
|
119091
119237
|
if (stream === process.stderr)
|
|
119092
119238
|
return;
|
|
119093
|
-
process.stderr.write(`Unexpected error: ${err?.message ?? String(err)}
|
|
119239
|
+
process.stderr.write(`Unexpected error: ${escapeTerminalText(err?.message ?? String(err))}
|
|
119094
119240
|
`);
|
|
119095
119241
|
});
|
|
119096
119242
|
}
|
|
119097
119243
|
await main().catch((err) => {
|
|
119098
|
-
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))}
|
|
119099
119245
|
`);
|
|
119100
119246
|
process.exitCode = 1;
|
|
119101
119247
|
});
|
package/package.json
CHANGED