@candledottv/cli 0.13.16 → 0.13.18
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 +28 -13
- package/dist/index.js +543 -173
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -532,7 +532,7 @@ var init_terminal_theme = __esm(() => {
|
|
|
532
532
|
});
|
|
533
533
|
|
|
534
534
|
// src/version.ts
|
|
535
|
-
var CLI_VERSION = "0.13.
|
|
535
|
+
var CLI_VERSION = "0.13.18";
|
|
536
536
|
|
|
537
537
|
// src/presentation.ts
|
|
538
538
|
function terminalStyle(deps, stream, machine = false) {
|
|
@@ -11163,6 +11163,25 @@ var init_solana_alt = __esm(() => {
|
|
|
11163
11163
|
];
|
|
11164
11164
|
});
|
|
11165
11165
|
|
|
11166
|
+
// src/account.ts
|
|
11167
|
+
async function fetchAccount(deps, apiUrl, apiKey) {
|
|
11168
|
+
const identity = await apiRequest("/api/v1/agent/wallets/embedded", {
|
|
11169
|
+
auth: "key",
|
|
11170
|
+
credentials: { apiKey },
|
|
11171
|
+
apiUrl,
|
|
11172
|
+
fetch: deps.fetch,
|
|
11173
|
+
env: deps.env
|
|
11174
|
+
});
|
|
11175
|
+
const body = identity.ok ? identity.body : undefined;
|
|
11176
|
+
const account = body?.account;
|
|
11177
|
+
if (account)
|
|
11178
|
+
return { account, ...body?.username ? { username: body.username } : {} };
|
|
11179
|
+
return { failure: identity.ok ? "no account in the response" : identity.message };
|
|
11180
|
+
}
|
|
11181
|
+
var init_account = __esm(() => {
|
|
11182
|
+
init_client();
|
|
11183
|
+
});
|
|
11184
|
+
|
|
11166
11185
|
// src/profiles.ts
|
|
11167
11186
|
function assertOrdinaryProfile(config, profile) {
|
|
11168
11187
|
if (profile !== undefined && config.profiles?.[profile]?.apiKeyItem !== undefined) {
|
|
@@ -11297,7 +11316,7 @@ function credentialEnvOverrides(env) {
|
|
|
11297
11316
|
}
|
|
11298
11317
|
function identityLine(profile, account, apiUrl, overrides, username, options = {}) {
|
|
11299
11318
|
const address = account === undefined ? undefined : shortId(account, options.full);
|
|
11300
|
-
const shown = overrides?.length ? `unknown (${overrides.join(", ")} override)` : username && address ? `${username} (${address})` : address ?? "unknown";
|
|
11319
|
+
const shown = overrides?.length ? address ? `${address} (${overrides.join(", ")} override)` : `unknown (${overrides.join(", ")} override)` : username && address ? `${username} (${address})` : address ?? "unknown";
|
|
11301
11320
|
const name = profile ?? "none";
|
|
11302
11321
|
const host = apiUrl.replace(/^https:\/\//i, "").replace(/\/+$/, "");
|
|
11303
11322
|
const sep = options.style ? symbols(options.style).note : "·";
|
|
@@ -11308,7 +11327,20 @@ async function printIdentity(ctx) {
|
|
|
11308
11327
|
return;
|
|
11309
11328
|
const config = await ctx.deps.readConfig();
|
|
11310
11329
|
const fields2 = effectiveProfileFields(config, ctx.profile);
|
|
11311
|
-
const
|
|
11330
|
+
const overrides = credentialEnvOverrides(ctx.deps.env);
|
|
11331
|
+
let account = fields2.account;
|
|
11332
|
+
let username = fields2.username;
|
|
11333
|
+
if (overrides.length > 0) {
|
|
11334
|
+
account = undefined;
|
|
11335
|
+
username = undefined;
|
|
11336
|
+
const key = ctx.deps.env.CANDLE_API_KEY?.trim();
|
|
11337
|
+
if (key) {
|
|
11338
|
+
const lookup = await fetchAccount(ctx.deps, ctx.apiUrl, key);
|
|
11339
|
+
if (lookup.account)
|
|
11340
|
+
account = lookup.account;
|
|
11341
|
+
}
|
|
11342
|
+
}
|
|
11343
|
+
const line = identityLine(ctx.profile, account, ctx.apiUrl, overrides.length > 0 ? overrides : undefined, username, identityStyle(ctx.deps, "stdout"));
|
|
11312
11344
|
ctx.deps.stdout.write(`${line}
|
|
11313
11345
|
|
|
11314
11346
|
`);
|
|
@@ -11352,6 +11384,7 @@ function profileTable(config, now) {
|
|
|
11352
11384
|
}
|
|
11353
11385
|
var ProfileFleetOnlyError, PRE_PROFILE_FIELDS, API_KEY_PREFIXES, API_KEY_RANDOM_LENGTH = 43, INVALID_RPC_HOST = "invalid (fix with profile set)";
|
|
11354
11386
|
var init_profiles = __esm(() => {
|
|
11387
|
+
init_account();
|
|
11355
11388
|
init_presentation();
|
|
11356
11389
|
init_render();
|
|
11357
11390
|
ProfileFleetOnlyError = class ProfileFleetOnlyError extends Error {
|
|
@@ -12603,9 +12636,9 @@ function kekReuseKey(passphrase, kdf) {
|
|
|
12603
12636
|
const inputs = JSON.stringify([passphrase, kdf.salt, kdf.m, kdf.t, kdf.p, kdf.version]);
|
|
12604
12637
|
return b64u(sha2562(new TextEncoder().encode(inputs)));
|
|
12605
12638
|
}
|
|
12606
|
-
async function derivePassphraseKek(passphrase, kdf, notice2) {
|
|
12639
|
+
async function derivePassphraseKek(passphrase, kdf, notice2, derivationLabel = "Deriving the vault key") {
|
|
12607
12640
|
assertKdfInBounds(kdf);
|
|
12608
|
-
notice2?.(
|
|
12641
|
+
notice2?.(`${derivationLabel} (Argon2id, ${Math.round(kdf.m / 1024)} MiB)
|
|
12609
12642
|
`);
|
|
12610
12643
|
const reuseKey = testKekReuse ? kekReuseKey(passphrase, kdf) : null;
|
|
12611
12644
|
const reused = reuseKey ? testKekReuse?.get(reuseKey) : undefined;
|
|
@@ -13990,7 +14023,7 @@ function closeVault(vault) {
|
|
|
13990
14023
|
async function unlockVault(path, raw, request, opts = {}) {
|
|
13991
14024
|
const file = parseVaultFile(raw);
|
|
13992
14025
|
const envelope = pickEnvelope(file, request);
|
|
13993
|
-
const dek = await unwrapDek(file, envelope, request, opts.notice);
|
|
14026
|
+
const dek = await unwrapDek(file, envelope, request, opts.notice, opts.derivationLabel);
|
|
13994
14027
|
if (dek.length !== DEK_BYTES) {
|
|
13995
14028
|
wipe(dek);
|
|
13996
14029
|
throw new VaultError("VAULT_UNLOCK_FAILED", "The unwrapped key is the wrong length; this file is corrupt.");
|
|
@@ -14030,9 +14063,9 @@ async function openWithDek(path, raw, file, envelope, dek) {
|
|
|
14030
14063
|
throw error;
|
|
14031
14064
|
}
|
|
14032
14065
|
}
|
|
14033
|
-
async function unwrapDek(file, envelope, request, notice2) {
|
|
14066
|
+
async function unwrapDek(file, envelope, request, notice2, derivationLabel) {
|
|
14034
14067
|
if (request.factor === "passphrase") {
|
|
14035
|
-
const kek = await derivePassphraseKek(request.passphrase, passphraseKdf(envelope), notice2);
|
|
14068
|
+
const kek = await derivePassphraseKek(request.passphrase, passphraseKdf(envelope), notice2, derivationLabel);
|
|
14036
14069
|
return withSecret(kek, async (kekBytes) => {
|
|
14037
14070
|
const kekKey2 = await importAesKey(kekBytes);
|
|
14038
14071
|
return open(kekKey2, envelope.wrap, envelopeAad(file, envelope), {
|
|
@@ -28989,13 +29022,13 @@ __export(exports_vault_support, {
|
|
|
28989
29022
|
runVaultCommand: () => runVaultCommand,
|
|
28990
29023
|
requireVaultRaw: () => requireVaultRaw,
|
|
28991
29024
|
requireTty: () => requireTty,
|
|
28992
|
-
requirePromptStreams: () => requirePromptStreams,
|
|
28993
29025
|
refusePromptsWithoutTerminal: () => refusePromptsWithoutTerminal,
|
|
28994
29026
|
refuseEnvPassphrase: () => refuseEnvPassphrase,
|
|
28995
29027
|
openWithTypedPassphrase: () => openWithTypedPassphrase,
|
|
28996
29028
|
nonDefaultVaultFooter: () => nonDefaultVaultFooter,
|
|
28997
29029
|
missingVault: () => missingVault,
|
|
28998
29030
|
menuSafeText: () => menuSafeText,
|
|
29031
|
+
isNoTerminalRefusal: () => isNoTerminalRefusal,
|
|
28999
29032
|
groupAddress: () => groupAddress,
|
|
29000
29033
|
findExternalEntry: () => findExternalEntry,
|
|
29001
29034
|
factorMenu: () => factorMenu,
|
|
@@ -29039,22 +29072,23 @@ function requireTty(ctx, what) {
|
|
|
29039
29072
|
}));
|
|
29040
29073
|
return false;
|
|
29041
29074
|
}
|
|
29042
|
-
function requirePromptStreams(ctx, what) {
|
|
29043
|
-
if (ctx.deps.isTTY.stdin && ctx.deps.isTTY.stderr)
|
|
29044
|
-
return true;
|
|
29045
|
-
writeVaultFailure(ctx, new VaultError("VAULT_UNLOCK_FAILED", `${what} needs a terminal for the passphrase prompt: standard input and standard error must both be a terminal. Standard output may be redirected; that is where the listing goes.`));
|
|
29046
|
-
return false;
|
|
29047
|
-
}
|
|
29048
29075
|
function refusePromptsWithoutTerminal(ctx, what) {
|
|
29049
29076
|
if (ctx.deps.isTTY.stdin && ctx.deps.isTTY.stderr)
|
|
29050
29077
|
return ctx;
|
|
29051
29078
|
const refuse2 = async () => {
|
|
29052
|
-
throw new VaultError("VAULT_UNLOCK_FAILED", `${what} needs a terminal here: this unlock reads a vault passphrase, a security key PIN or a choice from a prompt, and standard input and standard error must both be a terminal for that. Standard output may be redirected.`, {
|
|
29079
|
+
throw tagNoTerminal(new VaultError("VAULT_UNLOCK_FAILED", `${what} needs a terminal here: this unlock reads a vault passphrase, a security key PIN or a choice from a prompt, and standard input and standard error must both be a terminal for that. Standard output may be redirected.`, {
|
|
29053
29080
|
suggestion: "Run it from a terminal, or pass --factor touch-id or --factor passkey to unlock with a factor that reads no prompt. No environment variable or flag supplies a passphrase or PIN."
|
|
29054
|
-
});
|
|
29081
|
+
}));
|
|
29055
29082
|
};
|
|
29056
29083
|
return { ...ctx, deps: { ...ctx.deps, promptSecret: refuse2, promptLine: refuse2 } };
|
|
29057
29084
|
}
|
|
29085
|
+
function tagNoTerminal(error) {
|
|
29086
|
+
noTerminalRefusals.add(error);
|
|
29087
|
+
return error;
|
|
29088
|
+
}
|
|
29089
|
+
function isNoTerminalRefusal(error) {
|
|
29090
|
+
return error instanceof VaultError && noTerminalRefusals.has(error);
|
|
29091
|
+
}
|
|
29058
29092
|
function vaultPathFor(ctx, parsed) {
|
|
29059
29093
|
const flag = parsed.values["--keystore"];
|
|
29060
29094
|
if (flag !== undefined)
|
|
@@ -29674,7 +29708,7 @@ function takeRepeatedFlag(args, flag) {
|
|
|
29674
29708
|
}
|
|
29675
29709
|
return { values, rest };
|
|
29676
29710
|
}
|
|
29677
|
-
var PASSPHRASE_ONLY_PREFIX = "Passphrase only: ";
|
|
29711
|
+
var noTerminalRefusals, PASSPHRASE_ONLY_PREFIX = "Passphrase only: ";
|
|
29678
29712
|
var init_vault_support = __esm(() => {
|
|
29679
29713
|
init_args();
|
|
29680
29714
|
init_presentation();
|
|
@@ -29690,6 +29724,7 @@ var init_vault_support = __esm(() => {
|
|
|
29690
29724
|
init_platform();
|
|
29691
29725
|
init_sidecar();
|
|
29692
29726
|
init_store();
|
|
29727
|
+
noTerminalRefusals = new WeakSet;
|
|
29693
29728
|
});
|
|
29694
29729
|
|
|
29695
29730
|
// src/vault/ed25519.ts
|
|
@@ -38135,6 +38170,7 @@ async function keysList(args, ctx) {
|
|
|
38135
38170
|
async function keysCreate(args, ctx) {
|
|
38136
38171
|
const { deps, apiUrl, json } = ctx;
|
|
38137
38172
|
const parsed = parseArgs(args, {
|
|
38173
|
+
booleanFlags: ["--no-store"],
|
|
38138
38174
|
valueFlags: [
|
|
38139
38175
|
"--scopes",
|
|
38140
38176
|
"--access",
|
|
@@ -38239,11 +38275,12 @@ async function keysCreate(args, ctx) {
|
|
|
38239
38275
|
return 1;
|
|
38240
38276
|
}
|
|
38241
38277
|
const body = result.body;
|
|
38278
|
+
const noStore = parsed.booleans.has("--no-store");
|
|
38242
38279
|
const apiKeyRef = ctx.profile ? profileSecretRef(ctx.profile, "apiKey") : SECRET_REFS.apiKey;
|
|
38243
38280
|
let stored = false;
|
|
38244
38281
|
let storeError;
|
|
38245
38282
|
try {
|
|
38246
|
-
if (!await deps.store.get(apiKeyRef)) {
|
|
38283
|
+
if (!noStore && !await deps.store.get(apiKeyRef)) {
|
|
38247
38284
|
await deps.store.set(apiKeyRef, body.key);
|
|
38248
38285
|
if (ctx.profile) {
|
|
38249
38286
|
await deps.updateProfile(ctx.profile, { keyPrefix: body.keyPrefix, scopes: body.scopes });
|
|
@@ -38277,7 +38314,7 @@ Or mint it that way: candle keys create --access withdraw --embedded-wallet allo
|
|
|
38277
38314
|
...body.embeddedWallet === "allowed" || body.embeddedWallet === "denied" ? [["Embedded wallet", embeddedWalletSummary(body.embeddedWallet)]] : [],
|
|
38278
38315
|
[
|
|
38279
38316
|
"Stored",
|
|
38280
|
-
storeError !== undefined ? "no (store failed)" : stored ? `${deps.backend}, as this CLI's working key` : "no (the CLI already manages a different working key)"
|
|
38317
|
+
storeError !== undefined ? "no (store failed)" : stored ? `${deps.backend}, as this CLI's working key` : noStore ? "no (--no-store)" : "no (the CLI already manages a different working key)"
|
|
38281
38318
|
]
|
|
38282
38319
|
], style).join(`
|
|
38283
38320
|
`)}
|
|
@@ -38564,7 +38601,7 @@ var init_token_clusters = __esm(() => {
|
|
|
38564
38601
|
});
|
|
38565
38602
|
|
|
38566
38603
|
// ../mcp/src/version.ts
|
|
38567
|
-
var SERVER_VERSION = "0.10.
|
|
38604
|
+
var SERVER_VERSION = "0.10.30";
|
|
38568
38605
|
|
|
38569
38606
|
// ../mcp/src/update-notice.ts
|
|
38570
38607
|
function newer(a, b) {
|
|
@@ -40422,6 +40459,12 @@ async function executeTrade(args, cfg, doFetch) {
|
|
|
40422
40459
|
let amountRaw;
|
|
40423
40460
|
let resolved = {};
|
|
40424
40461
|
let paper = isPaperFlag(args.paper);
|
|
40462
|
+
const deferExecution = isPaperFlag(args.deferExecution);
|
|
40463
|
+
if (deferExecution && paper) {
|
|
40464
|
+
return errText("deferExecution cannot be combined with paper; a paper trade records a quote and never executes", {
|
|
40465
|
+
clientTradeId
|
|
40466
|
+
});
|
|
40467
|
+
}
|
|
40425
40468
|
try {
|
|
40426
40469
|
if (args.side === "buy") {
|
|
40427
40470
|
const amount = args.amount;
|
|
@@ -40530,6 +40573,9 @@ async function executeTrade(args, cfg, doFetch) {
|
|
|
40530
40573
|
} catch (err) {
|
|
40531
40574
|
return errText(err instanceof Error ? err.message : String(err), { clientTradeId });
|
|
40532
40575
|
}
|
|
40576
|
+
if (deferExecution && paper) {
|
|
40577
|
+
return errText("deferExecution needs a live holding, and this sell resolved to the paper book (the wallet holds none of the mint); a paper trade never executes", { clientTradeId });
|
|
40578
|
+
}
|
|
40533
40579
|
const posted = await postJson(`${base(cfg)}/api/v1/trade/agent/build`, apiKey, {
|
|
40534
40580
|
clientTradeId,
|
|
40535
40581
|
mint: args.mint,
|
|
@@ -40539,7 +40585,8 @@ async function executeTrade(args, cfg, doFetch) {
|
|
|
40539
40585
|
payer: { type: "main" },
|
|
40540
40586
|
...args.quoteAsset !== undefined ? { quoteAsset: args.quoteAsset } : {},
|
|
40541
40587
|
...args.maxSlippageBps !== undefined ? { maxSlippageBps: args.maxSlippageBps } : {},
|
|
40542
|
-
...paper ? { paper: true } : {}
|
|
40588
|
+
...paper ? { paper: true } : {},
|
|
40589
|
+
...deferExecution ? { deferExecution: true } : {}
|
|
40543
40590
|
}, doFetch);
|
|
40544
40591
|
if ("thrown" in posted)
|
|
40545
40592
|
return transportError("clientTradeId", clientTradeId, posted.thrown);
|
|
@@ -40791,32 +40838,38 @@ async function executionStatus(cfg, doFetch) {
|
|
|
40791
40838
|
}
|
|
40792
40839
|
return { ok: res.ok, status: res.status, body };
|
|
40793
40840
|
};
|
|
40794
|
-
const [wallets2, tier, limits] = await Promise.all([
|
|
40841
|
+
const [wallets2, tier, limits, risk] = await Promise.all([
|
|
40795
40842
|
get("/api/v1/agent/wallets/embedded"),
|
|
40796
40843
|
get("/api/v1/agent/tier"),
|
|
40797
|
-
get("/api/v1/agent/keys/self/limits")
|
|
40844
|
+
get("/api/v1/agent/keys/self/limits"),
|
|
40845
|
+
get("/api/v1/agent/keys/self/risk")
|
|
40798
40846
|
]);
|
|
40799
40847
|
const unreadable2 = [
|
|
40800
40848
|
["wallets", wallets2],
|
|
40801
40849
|
["tier", tier],
|
|
40802
|
-
["limits", limits]
|
|
40850
|
+
["limits", limits],
|
|
40851
|
+
["risk", risk]
|
|
40803
40852
|
].filter(([, r]) => !r.ok);
|
|
40804
40853
|
const tierBody = tier.ok ? tier.body : undefined;
|
|
40854
|
+
const riskBody = risk.ok ? risk.body : undefined;
|
|
40855
|
+
const stopped = riskBody?.stopped === true;
|
|
40805
40856
|
return {
|
|
40806
40857
|
text: JSON.stringify({
|
|
40807
40858
|
success: true,
|
|
40808
|
-
ready: unreadable2.length === 0 ? true : undefined,
|
|
40859
|
+
ready: stopped ? false : unreadable2.length === 0 ? true : undefined,
|
|
40860
|
+
stopped: stopped ? { tripped: riskBody?.tripped ?? null, clearedBy: STOP_CLEARED_BY } : undefined,
|
|
40809
40861
|
notice: tierBody?.maxExpired ? tierBody.maxExpiredNotice : undefined,
|
|
40810
40862
|
updateAvailable: updateAvailable() ?? undefined,
|
|
40811
40863
|
unreadable: unreadable2.length > 0 ? unreadable2.map(([name]) => name) : undefined,
|
|
40812
40864
|
wallets: wallets2.body,
|
|
40813
40865
|
tier: tier.body,
|
|
40814
|
-
limits: limits.body
|
|
40866
|
+
limits: limits.body,
|
|
40867
|
+
risk: risk.body
|
|
40815
40868
|
}, null, 2),
|
|
40816
40869
|
...unreadable2.length > 0 ? { isError: true } : {}
|
|
40817
40870
|
};
|
|
40818
40871
|
}
|
|
40819
|
-
var SOLANA_SEED_CEILINGS, MAIN_PAYER, SWEEP_BASE_ASSETS;
|
|
40872
|
+
var SOLANA_SEED_CEILINGS, MAIN_PAYER, SWEEP_BASE_ASSETS, STOP_CLEARED_BY = "An account owner reviews the profile and resumes it from the agent console; an API key cannot resume it.";
|
|
40820
40873
|
var init_orchestrate = __esm(() => {
|
|
40821
40874
|
init_convert();
|
|
40822
40875
|
init_image();
|
|
@@ -45158,6 +45211,13 @@ function buildRequest(name, args, cfg) {
|
|
|
45158
45211
|
const path = kind === "launch" ? `/api/v1/launch/headless/jobs/${id}` : kind === "swap" ? `/api/v1/agent/swap/by-client-id/${id}` : `/api/v1/trade/agent/jobs/${id}`;
|
|
45159
45212
|
return { url: `${base2}${path}`, init: { method: "GET", headers: jsonHeaders(apiKey) } };
|
|
45160
45213
|
}
|
|
45214
|
+
case "candle_trade_execute": {
|
|
45215
|
+
const { clientTradeId } = args;
|
|
45216
|
+
return {
|
|
45217
|
+
url: `${base2}/api/v1/trade/agent/execute`,
|
|
45218
|
+
init: { method: "POST", headers: jsonHeaders(requireApiKey2(cfg)), body: JSON.stringify({ clientTradeId }) }
|
|
45219
|
+
};
|
|
45220
|
+
}
|
|
45161
45221
|
case "candle_get_staking": {
|
|
45162
45222
|
return { url: `${base2}/api/v1/agent/staking`, init: { method: "GET", headers: jsonHeaders(requireApiKey2(cfg)) } };
|
|
45163
45223
|
}
|
|
@@ -45246,13 +45306,40 @@ function buildRequest(name, args, cfg) {
|
|
|
45246
45306
|
}
|
|
45247
45307
|
case "candle_get_profile_trades": {
|
|
45248
45308
|
const apiKey = requireApiKey2(cfg);
|
|
45249
|
-
const { keyPrefix, limit } = args;
|
|
45250
|
-
const
|
|
45309
|
+
const { keyPrefix, limit, since, until, cursor } = args;
|
|
45310
|
+
const params = new URLSearchParams;
|
|
45311
|
+
if (limit !== undefined)
|
|
45312
|
+
params.set("limit", String(limit));
|
|
45313
|
+
if (since !== undefined)
|
|
45314
|
+
params.set("since", since);
|
|
45315
|
+
if (until !== undefined)
|
|
45316
|
+
params.set("until", until);
|
|
45317
|
+
if (cursor !== undefined)
|
|
45318
|
+
params.set("cursor", cursor);
|
|
45251
45319
|
return {
|
|
45252
|
-
url: `${base2}/api/v1/agent/keys/${encodeURIComponent(keyPrefix)}/trades${
|
|
45320
|
+
url: `${base2}/api/v1/agent/keys/${encodeURIComponent(keyPrefix)}/trades${params.size ? `?${params}` : ""}`,
|
|
45253
45321
|
init: { method: "GET", headers: jsonHeaders(apiKey) }
|
|
45254
45322
|
};
|
|
45255
45323
|
}
|
|
45324
|
+
case "candle_get_paper_inventory": {
|
|
45325
|
+
return {
|
|
45326
|
+
url: `${base2}/api/v1/trade/agent/paper/inventory`,
|
|
45327
|
+
init: { method: "GET", headers: jsonHeaders(requireApiKey2(cfg)) }
|
|
45328
|
+
};
|
|
45329
|
+
}
|
|
45330
|
+
case "candle_get_profile_paper": {
|
|
45331
|
+
const { keyPrefix } = args;
|
|
45332
|
+
return {
|
|
45333
|
+
url: `${base2}/api/v1/agent/keys/${encodeURIComponent(keyPrefix)}/paper`,
|
|
45334
|
+
init: { method: "GET", headers: jsonHeaders(requireApiKey2(cfg)) }
|
|
45335
|
+
};
|
|
45336
|
+
}
|
|
45337
|
+
case "candle_get_account_pnl": {
|
|
45338
|
+
return {
|
|
45339
|
+
url: `${base2}/api/v1/agent/keys/pnl`,
|
|
45340
|
+
init: { method: "GET", headers: jsonHeaders(requireApiKey2(cfg)) }
|
|
45341
|
+
};
|
|
45342
|
+
}
|
|
45256
45343
|
case "candle_transfer": {
|
|
45257
45344
|
const apiKey = requireApiKey2(cfg);
|
|
45258
45345
|
return {
|
|
@@ -45697,9 +45784,24 @@ function registerToolSubset(server, options) {
|
|
|
45697
45784
|
}, async (args) => callAndRelay("candle_get_profile_pnl", args, getConfig(), doFetch));
|
|
45698
45785
|
register("candle_get_profile_trades", {
|
|
45699
45786
|
title: "Read an agent profile's trade history",
|
|
45700
|
-
description: "Orders, actual fills, fees, timestamps and transaction hashes for this profile. Includes FAILED " + "trades, with an errorCode saying why each did not go through, so this answers 'what happened to " + "my order' as well as 'what did I trade'. Reads only; moves nothing.",
|
|
45787
|
+
description: "Orders, actual fills, fees, timestamps and transaction hashes for this profile. Page by time with " + "`since`, `until` and `cursor` (continue with the response's `nextCursor`). Includes FAILED " + "trades, with an errorCode saying why each did not go through, so this answers 'what happened to " + "my order' as well as 'what did I trade'. The profile's swaps are listed among the trades by " + "time, each with kind 'swap', rail ('swap' for candle_swap, 'build' for the CLI's build and " + "submit), fromAsset, toAsset and amountRaw; a candle_swap made without a clientSwapId is not " + "recorded, so it is not listed. Reads only; moves nothing.",
|
|
45701
45788
|
inputSchema: profileTradesShape
|
|
45702
45789
|
}, async (args) => callAndRelay("candle_get_profile_trades", args, getConfig(), doFetch));
|
|
45790
|
+
register("candle_get_paper_inventory", {
|
|
45791
|
+
title: "Read this key's paper inventory",
|
|
45792
|
+
description: "This key's PAPER book in raw units: the positions that paper buys have credited and a paper " + "percent sell sizes itself from. Derived from admitted quotes; no money moved and the live " + "wallet is untouched. Never mix it with live holdings (candle_get_portfolio). Fails retryably " + "when the book cannot be read completely. Needs swap:write on the key. Reads only.",
|
|
45793
|
+
inputSchema: noArgsShape
|
|
45794
|
+
}, async (args) => callAndRelay("candle_get_paper_inventory", args, getConfig(), doFetch));
|
|
45795
|
+
register("candle_get_profile_paper", {
|
|
45796
|
+
title: "Read an agent profile's paper P&L",
|
|
45797
|
+
description: "One profile's PAPER results: the same accounting as candle_get_profile_pnl, from trades that " + "never happened. `pnl` is the paper P&L and `trades` its 50 most recent paper trades. Carries " + '`basis: "quote"`: the figures come from quotes at admission and are optimistic by the ' + "slippage and fees real execution pays. Never add them to live P&L. Reads only.",
|
|
45798
|
+
inputSchema: profilePnlShape
|
|
45799
|
+
}, async (args) => callAndRelay("candle_get_profile_paper", args, getConfig(), doFetch));
|
|
45800
|
+
register("candle_get_account_pnl", {
|
|
45801
|
+
title: "Read P&L across all of an account's agent keys",
|
|
45802
|
+
description: "The account's P&L split by agent profile in one read, as `candle pnl` without `--profile` " + "does: one row per profile with its ledger figures and, where it has a board entry, `board` " + "(all-time public total with creator-fee claims credited to the launching agent, so a profile's " + "board total can differ from its per-key ledger P&L). A wallet shared by several keys books a " + "round to its bound key, else the key whose buy opened it. Use candle_get_profile_pnl for one " + "profile's positions. Reads only.",
|
|
45803
|
+
inputSchema: noArgsShape
|
|
45804
|
+
}, async (args) => callAndRelay("candle_get_account_pnl", args, getConfig(), doFetch));
|
|
45703
45805
|
register("candle_get_portfolio", {
|
|
45704
45806
|
title: "Read what the account holds, on Solana and Hood",
|
|
45705
45807
|
description: "Balances and prices for the wallets Candle already knows on this account, on both chains: the " + "embedded wallet and every TEE wallet. Solana wallets are in `embedded` and `tee` (SOL as raw " + "`lamports`, tokens as raw `amountRaw` with `decimals`). Hood wallets are in `hood.embedded` and " + "`hood.tee` (ETH as raw `wei`, ERC-20s including USDG), never in the top-level arrays. Every " + "wallet and holding carries `chain`. Prices are in `prices`: a Solana mint under its own " + "address, Hood ETH under `hood:native`, and a Hood token under `hood:<contract lowercased>`, so " + "lowercase the address before looking it up. An unpriced entry is `priceUsd: null`, never zero; " + "a Hood one says why in `unpricedReason` (no-market-row, unusable-price, stale-mark, " + "source-unavailable), and `hood.unpriced` / `hood.unpricedByReason` count them. A Hood token " + "mark older than six hours is not used, so many Hood tokens read unpriced. A wallet whose read " + "failed has null balances, never zero, and is listed in `unavailable` (Solana) or " + "`hood.unavailable`; check `complete` before quoting a total. `walletsComplete` is false only " + "when the wallet list itself was cut off, and is absent on a server that predates it: then " + "`complete: false` may be a cut-off list or a failed read. Vault and external wallets are " + "not included: Candle does not know their addresses (the Candle CLI's `candle portfolio` reads " + "them over your own RPC). Needs a key with the account:read scope; without it the answer is " + "SCOPE_MISSING. A server that predates Hood in the portfolio omits `hood` and `chain`. Reads " + "only; moves nothing." + (options.localHyperliquid ? " This local server adds `hyperliquid`: each Hood TEE wallet's Hyperliquid account (account value, " + "unrealized P&L, positions, risk level at the default thresholds) read live from Hyperliquid, with a " + "subtotal. It is not in the ledger and is never part of any total. `read: false` with a reason, or " + "entries in `unread`, means those accounts are unknown, not empty." : ""),
|
|
@@ -45721,7 +45823,7 @@ function registerToolSubset(server, options) {
|
|
|
45721
45823
|
});
|
|
45722
45824
|
register("candle_execution_status", {
|
|
45723
45825
|
title: "Can this key execute right now",
|
|
45724
|
-
description: "One call before trading: the embedded wallets to spend from, the tier that decides what " + "may be traded,
|
|
45826
|
+
description: "One call before trading: the embedded wallets to spend from, the tier that decides what " + "may be traded, this key's own spend limits, and its loss limits and stop state (risk: " + "lossLimits, state, stopped, tripped). A stopped key reports ready: false and a top-level " + "stopped naming the bound that broke; every trade answers RISK_LIMIT_REACHED until an " + "account owner resumes it, so stop trading and say why rather than retrying. Any scope " + "can read its own. Reads only; moves nothing. Call it when " + "a run starts, or after an authorization error, rather than inferring readiness from a " + "failed trade. If a read could not be completed the tool says which one and does NOT " + "claim the account is unready: an unreachable endpoint and a missing tier are different " + "problems with different fixes.",
|
|
45725
45827
|
inputSchema: {}
|
|
45726
45828
|
}, async () => {
|
|
45727
45829
|
const result = await executionStatus(getConfig(), doFetch);
|
|
@@ -45780,6 +45882,13 @@ function registerToolSubset(server, options) {
|
|
|
45780
45882
|
const result = await executeTrade(args, getConfig(), doFetch);
|
|
45781
45883
|
return { content: [{ type: "text", text: result.text }], ...result.isError ? { isError: true } : {} };
|
|
45782
45884
|
});
|
|
45885
|
+
register("candle_trade_execute", {
|
|
45886
|
+
title: "Execute a deferred trade",
|
|
45887
|
+
description: "Execute a trade you built with candle_trade and deferExecution: true. MOVES REAL FUNDS: Candle signs " + "the plan it kept back from the build with the account's own delegation and broadcasts it. Takes the " + "clientTradeId and nothing else, so nothing you send can change what gets signed; to change the trade, " + `build a new one.
|
|
45888
|
+
|
|
45889
|
+
` + "Exactly one execution per build. Calling again never broadcasts a second time: it returns the stored " + "result once the first has confirmed, and a 409 IDEMPOTENCY_CONFLICT while the outcome is still " + "unknown (read candle_get_operation with the clientTradeId, kind trade, before doing anything else). " + "More than 30 minutes after the build it is 409 QUOTE_EXPIRED: build again for a fresh quote. A " + "trade built without deferExecution has already executed and is refused here.",
|
|
45890
|
+
inputSchema: tradeExecuteShape
|
|
45891
|
+
}, async (args) => callAndRelay("candle_trade_execute", args, getConfig(), doFetch));
|
|
45783
45892
|
register("candle_atomic_launch_dry_run", {
|
|
45784
45893
|
title: "Dry-run an atomic Solana launch",
|
|
45785
45894
|
description: "Check a Solana launch bundled with first buys and price each buy, without building anything. Runs the " + "same checks as the atomic build, in the same order (payers, Believer check, fee and spend limits, the " + "key's transaction cap), then returns the launch preview (curveTerms) and one fill per first buy: the " + "tokens it would receive and the floor the build would sign it at. Reads only; moves nothing, reserves " + "nothing, and the clientLaunchId stays unused. It does not check balances, the daily cap, or the " + "transaction's size, so the real launch can still refuse. Needs launch:write, swap:write and Pro or Max.",
|
|
@@ -45988,7 +46097,7 @@ async function registerEligibleProfileTool(server, env = process.env, doFetch =
|
|
|
45988
46097
|
}
|
|
45989
46098
|
return eligible;
|
|
45990
46099
|
}
|
|
45991
|
-
var VAMP_CANDIDATES_DESCRIPTION, localProfileTools, STAKING_DISCLOSURE = "No instant unstake. Read pool.withdrawalTimelockSeconds for the wait. Solana rewards accrue to the holding wallet. sCNDL in any eligible account wallet counts toward Pro and earns the account's Hood staking reward, except a wallet another account also claims, wallets past the 200-wallet cap, and an account with no EVM wallet. CNDL in a pending withdrawal counts toward nothing for Pro.", stakingWriteShape, TOOL_NAMES, LAB_TOOL_NAMES, PROFILE_EDIT_TOOL_NAMES, isProfileEditTool = (name) => PROFILE_EDIT_TOOL_NAMES.includes(name), OPERATION_NOT_FOUND_RULE, getMarketShape, priceHistoryShape, TOKEN_EVENTS_LIMIT_MESSAGE = "limit must be a whole number from 1 to 1000", tokenEventsLimit, TOKEN_EVENTS_KIND_NOTE = "kept back from the request and applied to the returned page by this server, so a filtered page can be shorter than limit while older events of other kinds remain: page with nextCursor as before until it is null", tokenCardShape, tokenEventsShape, tokenForensicsShape, FEED_LIMIT_MESSAGE = "limit must be a whole number from 1 to 200", feedLimit, getFeedShape, reportActivityShape, getAgentProfileShape, listAgentsShape, createPostShape, deletePostShape, listAgentPostsShape, getAgentPositionsShape, getOperationShape, vampCandidatesShape, getFeesShape, atomicPayerShape, atomicLaunchDryRunShape, claimFeesShape, resolveTokenShape, profileWalletsShape, profilePnlShape, profileTradesShape, setProfileWalletsShape, swapShape, tradeShape, tokenImageUrl, tokenBannerUrl, localImageShape, localLaunchTokenShape, localLaunchAndSeedShape, IMAGE_PATH_DISCLOSURE, transferShape, sweepShape, PROFILE_PNL_HYPERLIQUID;
|
|
46100
|
+
var VAMP_CANDIDATES_DESCRIPTION, localProfileTools, STAKING_DISCLOSURE = "No instant unstake. Read pool.withdrawalTimelockSeconds for the wait. Solana rewards accrue to the holding wallet. sCNDL in any eligible account wallet counts toward Pro and earns the account's Hood staking reward, except a wallet another account also claims, wallets past the 200-wallet cap, and an account with no EVM wallet. CNDL in a pending withdrawal counts toward nothing for Pro.", stakingWriteShape, TOOL_NAMES, LAB_TOOL_NAMES, PROFILE_EDIT_TOOL_NAMES, isProfileEditTool = (name) => PROFILE_EDIT_TOOL_NAMES.includes(name), OPERATION_NOT_FOUND_RULE, getMarketShape, priceHistoryShape, TOKEN_EVENTS_LIMIT_MESSAGE = "limit must be a whole number from 1 to 1000", tokenEventsLimit, TOKEN_EVENTS_KIND_NOTE = "kept back from the request and applied to the returned page by this server, so a filtered page can be shorter than limit while older events of other kinds remain: page with nextCursor as before until it is null", tokenCardShape, tokenEventsShape, tokenForensicsShape, FEED_LIMIT_MESSAGE = "limit must be a whole number from 1 to 200", feedLimit, getFeedShape, reportActivityShape, getAgentProfileShape, listAgentsShape, createPostShape, deletePostShape, listAgentPostsShape, getAgentPositionsShape, getOperationShape, vampCandidatesShape, getFeesShape, atomicPayerShape, atomicLaunchDryRunShape, claimFeesShape, resolveTokenShape, profileWalletsShape, profilePnlShape, profileTradesShape, noArgsShape, setProfileWalletsShape, swapShape, tradeShape, tradeExecuteShape, tokenImageUrl, tokenBannerUrl, localImageShape, localLaunchTokenShape, localLaunchAndSeedShape, IMAGE_PATH_DISCLOSURE, transferShape, sweepShape, PROFILE_PNL_HYPERLIQUID;
|
|
45992
46101
|
var init_tools = __esm(() => {
|
|
45993
46102
|
init_zod();
|
|
45994
46103
|
init_client2();
|
|
@@ -46053,6 +46162,7 @@ var init_tools = __esm(() => {
|
|
|
46053
46162
|
"candle_get_agent_positions",
|
|
46054
46163
|
"candle_get_plans",
|
|
46055
46164
|
"candle_trade",
|
|
46165
|
+
"candle_trade_execute",
|
|
46056
46166
|
"candle_launch_and_seed",
|
|
46057
46167
|
"candle_atomic_launch_dry_run",
|
|
46058
46168
|
"candle_swap",
|
|
@@ -46063,6 +46173,9 @@ var init_tools = __esm(() => {
|
|
|
46063
46173
|
"candle_set_profile_wallets",
|
|
46064
46174
|
"candle_get_profile_pnl",
|
|
46065
46175
|
"candle_get_profile_trades",
|
|
46176
|
+
"candle_get_paper_inventory",
|
|
46177
|
+
"candle_get_profile_paper",
|
|
46178
|
+
"candle_get_account_pnl",
|
|
46066
46179
|
"candle_get_portfolio",
|
|
46067
46180
|
"candle_resolve_token",
|
|
46068
46181
|
"candle_execution_status",
|
|
@@ -46245,8 +46358,12 @@ var init_tools = __esm(() => {
|
|
|
46245
46358
|
};
|
|
46246
46359
|
profileTradesShape = {
|
|
46247
46360
|
keyPrefix: exports_external.string().describe("The profile's API key prefix"),
|
|
46248
|
-
limit: exports_external.number().optional().describe("How many of the most recent trades to return. Default 200, max 1000.")
|
|
46361
|
+
limit: exports_external.number().optional().describe("How many of the most recent trades to return. Default 200, max 1000."),
|
|
46362
|
+
since: exports_external.string().optional().describe("Inclusive lower bound on the trade's createdAt: an ISO 8601 time or epoch milliseconds."),
|
|
46363
|
+
until: exports_external.string().optional().describe("Exclusive upper bound on createdAt, in the same forms as `since`."),
|
|
46364
|
+
cursor: exports_external.string().optional().describe("The `nextCursor` of the previous page, to continue the same window.")
|
|
46249
46365
|
};
|
|
46366
|
+
noArgsShape = {};
|
|
46250
46367
|
setProfileWalletsShape = {
|
|
46251
46368
|
keyPrefix: exports_external.string().describe("The profile's API key prefix"),
|
|
46252
46369
|
walletIds: exports_external.array(exports_external.string()).describe("The linked-wallet ids the profile may spend from -- ids, not addresses. This REPLACES the " + "whole set: any wallet omitted loses access. An empty array assigns none.")
|
|
@@ -46267,7 +46384,11 @@ var init_tools = __esm(() => {
|
|
|
46267
46384
|
quoteAsset: exports_external.string().optional().describe('What the wallet spends on a buy or receives on a sell: "sol", "usdc" or "cndl" on Solana, ' + '"eth" or "usdg" on Hood. Echoing the `quoteAsset` a quote (POST /api/v1/trade/agent/quote) returned is safe. On Solana it applies only ' + "to an arbitrary mint Candle never launched (a buy or sell works on any plan) and is ignored for a Candle token, " + "whose quote comes from the token itself. On Hood it is the settlement asset of a DEX " + "trade; a USDG buy adds an approval transaction an ETH buy does not. It is not the route: " + "the cheapest path to the asset is chosen separately. Defaults to sol / ETH settlement."),
|
|
46268
46385
|
maxSlippageBps: exports_external.number().int().min(0).max(5000).optional().describe("Slippage bound in bps, 0-5000 (50%). Server defaults to 100 (1%)"),
|
|
46269
46386
|
clientTradeId: exports_external.string().optional().describe("Idempotency key. Auto-generated when omitted and echoed in the result. Retrying with the " + "SAME id is safe (idempotent replay); a new id is a SECOND trade."),
|
|
46270
|
-
paper: exports_external.preprocess((value) => value === "true" || value === 1 || value === "1" ? true : value === "false" || value === 0 || value === "0" ? false : value, exports_external.boolean().optional().describe("Rehearse instead of trading. The request passes every admission rule a live trade passes " + "-- the same planner, spend gate, key cap and loss limits -- and records the quote, but " + "nothing is ever broadcast and no funds move. Use it to check that a strategy is admitted " + "before risking anything on it. A paper fill is optimistic by construction: it books the " + "quoted price, so the gap between a paper arm and a live one IS the execution cost. " + "A sell of a mint this key already paper-bought also closes that paper book when the " + "live wallet is empty, even if this flag is omitted."))
|
|
46387
|
+
paper: exports_external.preprocess((value) => value === "true" || value === 1 || value === "1" ? true : value === "false" || value === 0 || value === "0" ? false : value, exports_external.boolean().optional().describe("Rehearse instead of trading. The request passes every admission rule a live trade passes " + "-- the same planner, spend gate, key cap and loss limits -- and records the quote, but " + "nothing is ever broadcast and no funds move. Use it to check that a strategy is admitted " + "before risking anything on it. A paper fill is optimistic by construction: it books the " + "quoted price, so the gap between a paper arm and a live one IS the execution cost. " + "A sell of a mint this key already paper-bought also closes that paper book when the " + "live wallet is empty, even if this flag is omitted.")),
|
|
46388
|
+
deferExecution: exports_external.preprocess((value) => value === "true" || value === 1 || value === "1" ? true : value === "false" || value === 0 || value === "0" ? false : value, exports_external.boolean().optional().describe("Quote first, spend later. The trade is built and the result carries the quote with " + 'status "built"; NOTHING is signed or spent until candle_trade_execute is called with the same ' + "clientTradeId. The build holds for 30 minutes; executing after that is QUOTE_EXPIRED and you " + "build again for a fresh quote. Rebuilding with the same clientTradeId re-quotes it, until it has " + "been sent to candle_trade_execute. There is no cancel: let an unwanted build expire. Cannot be " + "combined with `paper`."))
|
|
46389
|
+
};
|
|
46390
|
+
tradeExecuteShape = {
|
|
46391
|
+
clientTradeId: exports_external.string().min(1).describe("The clientTradeId of a candle_trade built with deferExecution: true. Nothing else is accepted.")
|
|
46271
46392
|
};
|
|
46272
46393
|
tokenImageUrl = launchSchemaField(launchTokenShape, "imageUrl");
|
|
46273
46394
|
tokenBannerUrl = launchSchemaField(launchTokenShape, "bannerUrl");
|
|
@@ -74092,29 +74213,11 @@ ${USAGE3}` : USAGE3, ctx.json);
|
|
|
74092
74213
|
}
|
|
74093
74214
|
|
|
74094
74215
|
// src/commands/auth.ts
|
|
74095
|
-
|
|
74096
|
-
import { join as join9 } from "node:path";
|
|
74097
|
-
|
|
74098
|
-
// src/account.ts
|
|
74099
|
-
init_client();
|
|
74100
|
-
async function fetchAccount(deps, apiUrl, apiKey) {
|
|
74101
|
-
const identity = await apiRequest("/api/v1/agent/wallets/embedded", {
|
|
74102
|
-
auth: "key",
|
|
74103
|
-
credentials: { apiKey },
|
|
74104
|
-
apiUrl,
|
|
74105
|
-
fetch: deps.fetch,
|
|
74106
|
-
env: deps.env
|
|
74107
|
-
});
|
|
74108
|
-
const body = identity.ok ? identity.body : undefined;
|
|
74109
|
-
const account = body?.account;
|
|
74110
|
-
if (account)
|
|
74111
|
-
return { account, ...body?.username ? { username: body.username } : {} };
|
|
74112
|
-
return { failure: identity.ok ? "no account in the response" : identity.message };
|
|
74113
|
-
}
|
|
74114
|
-
|
|
74115
|
-
// src/commands/auth.ts
|
|
74216
|
+
init_account();
|
|
74116
74217
|
init_agent_key_access();
|
|
74117
74218
|
init_args();
|
|
74219
|
+
import { homedir as homedir3 } from "node:os";
|
|
74220
|
+
import { join as join9 } from "node:path";
|
|
74118
74221
|
|
|
74119
74222
|
// src/checks.ts
|
|
74120
74223
|
init_client();
|
|
@@ -76510,9 +76613,9 @@ var HELP = {
|
|
|
76510
76613
|
group: "Account",
|
|
76511
76614
|
intro: "Holdings, prices and value of every wallet",
|
|
76512
76615
|
summary: "Every wallet's holdings, prices and value on Solana and Hood (vault read over your own RPCs)",
|
|
76513
|
-
description: "Vault, TEE and embedded wallets on Solana and Hood, each token with its chain, amount, price and value, then subtotals per group and per chain and one total. TEE and embedded balances, on both chains, come from Candle. Solana vault and external wallets are read over your own Solana RPC, and Candle is sent only the mints they hold, for prices. EVM vault keys are read over your own EVM RPC for ETH and USDG only (the CLI cannot list other tokens), which must be Hood's (chain id 4663); any other chain is refused. Their prices come from Candle by asset (ETH, USDG), never by address. Unpriced tokens are shown as unpriced with the reason (no-market-row, stale-mark, unusable-price, source-unavailable) and left out of the total. A wallet that could not be read in full is marked not read, the footer names its chain, and the exit is 3. Where Candle serves LP, each Solana TEE wallet's DAMM v2 positions follow the tokens (share of the pool plus unclaimed fees, valued by Candle at the same marks) and count in the total; a position whose pool could not be read is shown as not read and the total says it is partial. A bridge moves value between the chain subtotals; it is not a trade. With a filter, only the wallets whose label or address contains it are read and shown, the subtotals and total are for those wallets, and the first line says how many matched. When Candle listed only part of the account's wallets, the result stays partial even if every wallet shown was read, and no match says so. After the totals, a Hyperliquid section (live from Hyperliquid, not in the ledger, and never in the total) lists each EVM TEE wallet's Hyperliquid account value, unrealized P&L, positions and risk at the default thresholds, read from Hyperliquid's public API on this machine; a failed read there is not read and the exit is 3. --chain solana and --no-perps read nothing from Hyperliquid.",
|
|
76616
|
+
description: "Vault, TEE and embedded wallets on Solana and Hood, each token with its chain, amount, price and value, then subtotals per group and per chain and one total. TEE and embedded balances, on both chains, come from Candle. Solana vault and external wallets are read over your own Solana RPC, and Candle is sent only the mints they hold, for prices. EVM vault keys are read over your own EVM RPC for ETH and USDG only (the CLI cannot list other tokens), which must be Hood's (chain id 4663); any other chain is refused. Their prices come from Candle by asset (ETH, USDG), never by address. Unpriced tokens are shown as unpriced with the reason (no-market-row, stale-mark, unusable-price, source-unavailable) and left out of the total. A wallet that could not be read in full is marked not read, the footer names its chain, and the exit is 3. Where Candle serves LP, each Solana TEE wallet's DAMM v2 positions follow the tokens (share of the pool plus unclaimed fees, valued by Candle at the same marks) and count in the total; a position whose pool could not be read is shown as not read and the total says it is partial. A bridge moves value between the chain subtotals; it is not a trade. With a filter, only the wallets whose label or address contains it are read and shown, the subtotals and total are for those wallets, and the first line says how many matched. When Candle listed only part of the account's wallets, the result stays partial even if every wallet shown was read, and no match says so. After the totals, a Hyperliquid section (live from Hyperliquid, not in the ledger, and never in the total) lists each EVM TEE wallet's Hyperliquid account value, unrealized P&L, positions and risk at the default thresholds, read from Hyperliquid's public API on this machine; a failed read there is not read and the exit is 3. --chain solana and --no-perps read nothing from Hyperliquid. The vault is read only for its addresses, so the command needs no terminal: without one, a factor that asks nothing (--factor touch-id or passkey) still unlocks it, and otherwise the vault row says not read and the other groups are shown, but the result is partial: the total says so and the exit is 3. --no-vault leaves the vault out on purpose, and then the result can be complete (exit 0).",
|
|
76514
76617
|
usage: [
|
|
76515
|
-
"candle portfolio [<filter>] [--chain solana|hood] [--rpc-url <url>] [--evm-rpc-url <url>] [--no-perps] [--json]"
|
|
76618
|
+
"candle portfolio [<filter>] [--chain solana|hood] [--rpc-url <url>] [--evm-rpc-url <url>] [--no-perps] [--no-vault] [--json]"
|
|
76516
76619
|
],
|
|
76517
76620
|
rows: [
|
|
76518
76621
|
{
|
|
@@ -76534,6 +76637,10 @@ var HELP = {
|
|
|
76534
76637
|
invocation: "--no-perps",
|
|
76535
76638
|
description: "Skip the Hyperliquid section: send no EVM TEE address to Hyperliquid's public API"
|
|
76536
76639
|
},
|
|
76640
|
+
{
|
|
76641
|
+
invocation: "--no-vault",
|
|
76642
|
+
description: "Leave the vault out: no unlock, and no vault address is sent to your RPC"
|
|
76643
|
+
},
|
|
76537
76644
|
KEYSTORE_FLAG
|
|
76538
76645
|
],
|
|
76539
76646
|
examples: [
|
|
@@ -76895,8 +77002,8 @@ var HELP = {
|
|
|
76895
77002
|
description: "List live API keys: name and Read, Write or Withdraw access; --all includes revoked keys; --scopes adds the raw scopes"
|
|
76896
77003
|
},
|
|
76897
77004
|
{
|
|
76898
|
-
invocation: "create [--access read|write|withdraw|trade-perps | --scopes <a,b,c>] [--label <name>] [--expires-in <days>] [--tx-limit <usd> [--reset daily|weekly|monthly|never]] [--embedded-wallet deny|allow]",
|
|
76899
|
-
description: "Create an API key; --access mints one of the levels (write trades and launches; only withdraw can move funds; trade-perps is exactly account:read and perps:write, for Hyperliquid perps setup and orders, and it cannot fund its own Hyperliquid account: a Relay deposit needs swap:write and a withdrawal needs perps:withdraw, each a separate grant through --scopes). A trade-perps key trades from an EVM TEE wallet promoted onto it (candle vault new-key --chain evm, then candle vault promote --from <cold> --to-key <prefix>; candle tee new is Solana only) and a signer on the trading machine (candle tee signer new --key <prefix>). --embedded-wallet defaults to deny; allow lets the key spend the account's embedded wallet and is confirmed at a terminal"
|
|
77005
|
+
invocation: "create [--access read|write|withdraw|trade-perps | --scopes <a,b,c>] [--label <name>] [--expires-in <days>] [--tx-limit <usd> [--reset daily|weekly|monthly|never]] [--embedded-wallet deny|allow] [--no-store]",
|
|
77006
|
+
description: "Create an API key; --no-store prints it without saving it as the profile's working key (default: store only if no working key exists). --access mints one of the levels (write trades and launches; only withdraw can move funds; trade-perps is exactly account:read and perps:write, for Hyperliquid perps setup and orders, and it cannot fund its own Hyperliquid account: a Relay deposit needs swap:write and a withdrawal needs perps:withdraw, each a separate grant through --scopes). A trade-perps key trades from an EVM TEE wallet promoted onto it (candle vault new-key --chain evm, then candle vault promote --from <cold> --to-key <prefix>; candle tee new is Solana only) and a signer on the trading machine (candle tee signer new --key <prefix>). --embedded-wallet defaults to deny; allow lets the key spend the account's embedded wallet and is confirmed at a terminal"
|
|
76900
77007
|
},
|
|
76901
77008
|
{
|
|
76902
77009
|
invocation: "access <prefix|label|self> (--access read|write|withdraw [--yes] | --history)",
|
|
@@ -76910,6 +77017,10 @@ var HELP = {
|
|
|
76910
77017
|
invocation: "limit <prefix|label> (--tx-limit <usd> [--reset daily|weekly|monthly|never] | --clear)",
|
|
76911
77018
|
description: "Set or clear the USD transaction cap on an existing key, with the device token. --reset defaults to daily"
|
|
76912
77019
|
},
|
|
77020
|
+
{
|
|
77021
|
+
invocation: "limits <prefix> <asset>=<raw> [<asset>=<raw> ...]",
|
|
77022
|
+
description: "Set a key's per-asset caps (sol, usdc, cndl, eth, usdg; a whole number of base units per transaction) with the profile's own API key. It can only lower a cap or add one; raising or removing one is refused (LOOSEN_REQUIRES_SESSION) and is the owner's, in the web key manager. The list replaces the key's caps"
|
|
77023
|
+
},
|
|
76913
77024
|
{
|
|
76914
77025
|
invocation: "self embedded-wallet deny",
|
|
76915
77026
|
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"
|
|
@@ -76946,11 +77057,12 @@ var HELP = {
|
|
|
76946
77057
|
"candle keys list",
|
|
76947
77058
|
"candle keys create --access withdraw --label rebalancer",
|
|
76948
77059
|
"candle keys create --access trade-perps --label perps-bot",
|
|
76949
|
-
"candle keys create --scopes trade:write --label agent-one",
|
|
77060
|
+
"candle keys create --scopes trade:write --label agent-one --no-store",
|
|
76950
77061
|
"candle keys access cndl --access withdraw",
|
|
76951
77062
|
"candle keys access self --access read --yes",
|
|
76952
77063
|
"candle keys update agent-one --embedded-wallet deny --yes",
|
|
76953
77064
|
"candle keys self embedded-wallet deny",
|
|
77065
|
+
"candle keys limits Ab3dEf9h sol=1000000000 usdc=50000000",
|
|
76954
77066
|
"candle keys wallets ck_live_ab12",
|
|
76955
77067
|
"candle tee rebind --label-prefix dest- --to-key Ab3dEf9h",
|
|
76956
77068
|
"candle keys signer approve ABCD-EFGH --key tr-2",
|
|
@@ -76981,6 +77093,10 @@ var HELP = {
|
|
|
76981
77093
|
description: "Give agents access to the account's embedded wallets: opens the Agents page's grant step in your browser (no key is created) and, with an API key, waits until it lands"
|
|
76982
77094
|
},
|
|
76983
77095
|
{ invocation: "revoke <wallet-id>", description: "Revoke a linked wallet" },
|
|
77096
|
+
{
|
|
77097
|
+
invocation: "allowlist [--all]",
|
|
77098
|
+
description: "The account's withdrawal allowlist (the destinations candle transfer --to <label> resolves by). Any key reads it; approving an address is the owner's, in the agent console's Wallets tab (an API key cannot approve); an acting key can revoke one, a read-only key cannot. --all adds removed rows"
|
|
77099
|
+
},
|
|
76984
77100
|
{
|
|
76985
77101
|
invocation: "trust <label|address|id|prefix*>...",
|
|
76986
77102
|
description: "Mark linked wallets yours, so agents can move funds into them (owner only; typed confirm)"
|
|
@@ -77014,6 +77130,7 @@ var HELP = {
|
|
|
77014
77130
|
"candle wallet authorize",
|
|
77015
77131
|
"candle wallet import --chain solana --key-file ./signer.json",
|
|
77016
77132
|
"candle wallet revoke wal_123",
|
|
77133
|
+
"candle wallet allowlist",
|
|
77017
77134
|
"candle wallet trust 'tr-*' 'dest-*'",
|
|
77018
77135
|
"candle wallet allow-launch launcher-1",
|
|
77019
77136
|
"candle wallet close-empty --keep EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
@@ -77872,6 +77989,9 @@ var TOOL_CLASSES = {
|
|
|
77872
77989
|
candle_get_profile_wallets: READ,
|
|
77873
77990
|
candle_get_profile_pnl: READ,
|
|
77874
77991
|
candle_get_profile_trades: READ,
|
|
77992
|
+
candle_get_paper_inventory: READ,
|
|
77993
|
+
candle_get_profile_paper: READ,
|
|
77994
|
+
candle_get_account_pnl: READ,
|
|
77875
77995
|
candle_get_portfolio: READ,
|
|
77876
77996
|
candle_resolve_token: READ,
|
|
77877
77997
|
candle_execution_status: READ,
|
|
@@ -77891,6 +78011,7 @@ var TOOL_CLASSES = {
|
|
|
77891
78011
|
candle_perps_risk: READ,
|
|
77892
78012
|
candle_perps_thresholds: READ,
|
|
77893
78013
|
candle_trade: card("trade", "clientTradeId"),
|
|
78014
|
+
candle_trade_execute: card("trade"),
|
|
77894
78015
|
candle_swap: card("swap", "clientSwapId"),
|
|
77895
78016
|
candle_launch_token: card("launch", "clientLaunchId"),
|
|
77896
78017
|
candle_launch_and_seed: card("launch", "clientLaunchId"),
|
|
@@ -77980,7 +78101,8 @@ async function readOnce(executor, target, writeKey) {
|
|
|
77980
78101
|
if (!readsById(target))
|
|
77981
78102
|
return { readable: false };
|
|
77982
78103
|
const kind = target.kind === "launch" ? "launch" : "trade";
|
|
77983
|
-
const
|
|
78104
|
+
const clientId = target.tool === "candle_trade_execute" ? target.params.clientTradeId : target.opId;
|
|
78105
|
+
const outcome = await executor.execute("candle_get_operation", { clientId, kind }, writeKey);
|
|
77984
78106
|
if (outcome.state === "succeeded" || outcome.state === "pending") {
|
|
77985
78107
|
const job = outcome.result?.job ?? outcome.result;
|
|
77986
78108
|
const status = typeof job?.status === "string" ? job.status : "";
|
|
@@ -89471,8 +89593,7 @@ async function externalList(args, ctx) {
|
|
|
89471
89593
|
return usage(ctx, `Unexpected argument: ${parsed.positionals[0]}`);
|
|
89472
89594
|
if (!refuseEnvPassphrase(ctx))
|
|
89473
89595
|
return 1;
|
|
89474
|
-
|
|
89475
|
-
return 1;
|
|
89596
|
+
const promptCtx = refusePromptsWithoutTerminal(ctx, "external list");
|
|
89476
89597
|
const { deps } = ctx;
|
|
89477
89598
|
const resolvedVault = vaultPathFor(ctx, parsed);
|
|
89478
89599
|
if ("error" in resolvedVault)
|
|
@@ -89480,7 +89601,7 @@ async function externalList(args, ctx) {
|
|
|
89480
89601
|
const path = resolvedVault.path;
|
|
89481
89602
|
return runVaultCommand(ctx, async ({ hold }) => {
|
|
89482
89603
|
const raw = await requireVaultRaw(ctx, resolvedVault);
|
|
89483
|
-
const vault = hold((await unlockInteractively(
|
|
89604
|
+
const vault = hold((await unlockInteractively(promptCtx, path, raw, { acceptOlderCopy: parsed.booleans.has("--accept-older-copy") })).vault);
|
|
89484
89605
|
const externals = vault.index.entries.filter((entry) => entry.role === "external");
|
|
89485
89606
|
const rows = externals.map((entry) => ({
|
|
89486
89607
|
address: entry.address,
|
|
@@ -90263,6 +90384,100 @@ ${fields(rows, style).join(`
|
|
|
90263
90384
|
return 0;
|
|
90264
90385
|
}
|
|
90265
90386
|
|
|
90387
|
+
// src/commands/keys-limits.ts
|
|
90388
|
+
init_args();
|
|
90389
|
+
init_client();
|
|
90390
|
+
init_deps();
|
|
90391
|
+
init_presentation();
|
|
90392
|
+
init_profiles();
|
|
90393
|
+
init_key_output();
|
|
90394
|
+
var USAGE8 = "Usage: candle keys limits <prefix> <asset>=<raw-amount> [<asset>=<raw-amount> ...]";
|
|
90395
|
+
var KEY_LIMIT_ASSETS = { sol: 9, usdc: 6, cndl: 6, eth: 18, usdg: 6 };
|
|
90396
|
+
var RAW_AMOUNT = /^[1-9][0-9]*$/;
|
|
90397
|
+
var NO_API_KEY3 = {
|
|
90398
|
+
code: "API_KEY_REQUIRED",
|
|
90399
|
+
message: "candle keys limits changes caps with an API key, and this profile holds none.",
|
|
90400
|
+
suggestion: "Select the profile holding the key with --profile. A device token cannot set per-asset caps."
|
|
90401
|
+
};
|
|
90402
|
+
var LOOSEN_HINT = "This command can only lower a cap. Raising or removing one needs the owner's session: use the web key manager.";
|
|
90403
|
+
async function keysLimits(args, ctx) {
|
|
90404
|
+
const { deps, apiUrl, json } = ctx;
|
|
90405
|
+
const parsed = parseArgs(args, { keyPrefixPositional: true });
|
|
90406
|
+
if ("error" in parsed) {
|
|
90407
|
+
writeScreenUsageFailure(deps, parsed.error, json);
|
|
90408
|
+
return 2;
|
|
90409
|
+
}
|
|
90410
|
+
const target = parsed.positionals[0];
|
|
90411
|
+
const caps = parsed.positionals.slice(1);
|
|
90412
|
+
if (!target || caps.length === 0) {
|
|
90413
|
+
writeScreenUsageFailure(deps, USAGE8, json);
|
|
90414
|
+
return 2;
|
|
90415
|
+
}
|
|
90416
|
+
const limits = [];
|
|
90417
|
+
for (const cap of caps) {
|
|
90418
|
+
const eq = cap.indexOf("=");
|
|
90419
|
+
const asset = (eq < 0 ? cap : cap.slice(0, eq)).toLowerCase();
|
|
90420
|
+
const raw = eq < 0 ? "" : cap.slice(eq + 1);
|
|
90421
|
+
if (!(asset in KEY_LIMIT_ASSETS)) {
|
|
90422
|
+
writeScreenUsageFailure(deps, `The asset must be one of: ${Object.keys(KEY_LIMIT_ASSETS).join(", ")}.`, json);
|
|
90423
|
+
return 2;
|
|
90424
|
+
}
|
|
90425
|
+
if (!RAW_AMOUNT.test(raw)) {
|
|
90426
|
+
writeScreenUsageFailure(deps, `${asset}=<raw> takes a positive whole number of base units (${asset} has ${KEY_LIMIT_ASSETS[asset]} decimals), with no sign, decimal point or leading zero.`, json);
|
|
90427
|
+
return 2;
|
|
90428
|
+
}
|
|
90429
|
+
if (limits.some((limit) => limit.asset === asset)) {
|
|
90430
|
+
writeScreenUsageFailure(deps, `${asset} is named twice.`, json);
|
|
90431
|
+
return 2;
|
|
90432
|
+
}
|
|
90433
|
+
limits.push({ asset, maxPerTxRaw: raw });
|
|
90434
|
+
}
|
|
90435
|
+
await printIdentity(ctx);
|
|
90436
|
+
const apiKey = await resolveApiKey(deps, ctx.profile);
|
|
90437
|
+
if (!apiKey) {
|
|
90438
|
+
writeScreenLocalFailure(deps, NO_API_KEY3, json);
|
|
90439
|
+
return 1;
|
|
90440
|
+
}
|
|
90441
|
+
const result = await apiRequest(`/api/v1/agent/keys/${encodeURIComponent(target)}/limits`, {
|
|
90442
|
+
method: "PUT",
|
|
90443
|
+
auth: "key",
|
|
90444
|
+
credentials: { apiKey },
|
|
90445
|
+
apiUrl,
|
|
90446
|
+
fetch: deps.fetch,
|
|
90447
|
+
env: deps.env,
|
|
90448
|
+
body: { limits }
|
|
90449
|
+
});
|
|
90450
|
+
if (!result.ok) {
|
|
90451
|
+
if (!json && result.code === "LOOSEN_REQUIRES_SESSION")
|
|
90452
|
+
writeScreenLocalFailure(deps, {
|
|
90453
|
+
code: "LOOSEN_REQUIRES_SESSION",
|
|
90454
|
+
message: "That would raise or remove a cap; nothing changed.",
|
|
90455
|
+
suggestion: LOOSEN_HINT
|
|
90456
|
+
}, json);
|
|
90457
|
+
else
|
|
90458
|
+
writeScreenFailure(deps, result, { apiUrl, authType: "key" }, json);
|
|
90459
|
+
return 1;
|
|
90460
|
+
}
|
|
90461
|
+
const stored = result.body.limits ?? limits;
|
|
90462
|
+
if (json) {
|
|
90463
|
+
deps.stdout.write(`${JSON.stringify({ ok: true, keyPrefix: target, limits: stored })}
|
|
90464
|
+
`);
|
|
90465
|
+
return 0;
|
|
90466
|
+
}
|
|
90467
|
+
const style = terminalStyle(deps, "stdout");
|
|
90468
|
+
const rows = [
|
|
90469
|
+
["Key", target],
|
|
90470
|
+
...stored.map((limit) => [limit.asset, `${limit.maxPerTxRaw} base units per transaction`])
|
|
90471
|
+
];
|
|
90472
|
+
deps.stdout.write(`${success("Per-asset caps updated", style)}
|
|
90473
|
+
|
|
90474
|
+
${heading("Caps", style)}
|
|
90475
|
+
${fields(rows, style).join(`
|
|
90476
|
+
`)}
|
|
90477
|
+
`);
|
|
90478
|
+
return 0;
|
|
90479
|
+
}
|
|
90480
|
+
|
|
90266
90481
|
// src/commands/keys-rotate.ts
|
|
90267
90482
|
init_args();
|
|
90268
90483
|
init_client();
|
|
@@ -90292,7 +90507,7 @@ var TOKEN_PAGE_BODY_CAP_BYTES = 32 * 1024;
|
|
|
90292
90507
|
var TOKEN_PAGE_SECTIONS = ["thesis", "tokenomics", "lore"];
|
|
90293
90508
|
var FLAG_REASONS = ["spam", "scam", "abuse", "misleading", "unsafe_link", "other"];
|
|
90294
90509
|
var FLAG_DETAIL_LIMIT = 500;
|
|
90295
|
-
var
|
|
90510
|
+
var USAGE9 = {
|
|
90296
90511
|
about: "Usage: candle token about <address> [--chain solana|hood] [--capabilities]",
|
|
90297
90512
|
claim: "Usage: candle token claim <address> [--chain solana|hood] [--creator [--challenge [--message-out <path>] | --challenge-id <id> --signature <sig>]] [--idempotency-key <key>]",
|
|
90298
90513
|
section: `Usage: candle token section set <address> <thesis|tokenomics|lore> --file <path> --base-revision <n> [--clear] [--chain solana|hood] [--idempotency-key <key>]
|
|
@@ -90552,7 +90767,7 @@ function terminalTextKeepingLines(text3) {
|
|
|
90552
90767
|
`);
|
|
90553
90768
|
}
|
|
90554
90769
|
async function tokenAbout(args, ctx) {
|
|
90555
|
-
const usage4 = usageFor(ctx,
|
|
90770
|
+
const usage4 = usageFor(ctx, USAGE9.about);
|
|
90556
90771
|
const parsed = parseArgs(args, { valueFlags: ["--chain"], booleanFlags: ["--capabilities"] });
|
|
90557
90772
|
if ("error" in parsed)
|
|
90558
90773
|
return usage4(parsed.error);
|
|
@@ -90598,7 +90813,7 @@ async function tokenAbout(args, ctx) {
|
|
|
90598
90813
|
return 0;
|
|
90599
90814
|
}
|
|
90600
90815
|
async function tokenClaim(args, ctx) {
|
|
90601
|
-
const usage4 = usageFor(ctx,
|
|
90816
|
+
const usage4 = usageFor(ctx, USAGE9.claim);
|
|
90602
90817
|
const parsed = parseArgs(args, {
|
|
90603
90818
|
valueFlags: ["--chain", "--challenge-id", "--signature", "--message-out", "--idempotency-key"],
|
|
90604
90819
|
booleanFlags: ["--creator", "--challenge"],
|
|
@@ -90725,7 +90940,7 @@ var SECTION_FLAGS = {
|
|
|
90725
90940
|
restore: { valueFlags: ["--chain", "--version-id", "--base-revision", "--idempotency-key"], booleanFlags: [] }
|
|
90726
90941
|
};
|
|
90727
90942
|
async function tokenSection(args, ctx) {
|
|
90728
|
-
const usage4 = usageFor(ctx,
|
|
90943
|
+
const usage4 = usageFor(ctx, USAGE9.section);
|
|
90729
90944
|
const action = args[0];
|
|
90730
90945
|
if (action !== "set" && action !== "restore")
|
|
90731
90946
|
return usage4();
|
|
@@ -90818,7 +91033,7 @@ async function tokenSection(args, ctx) {
|
|
|
90818
91033
|
return 0;
|
|
90819
91034
|
}
|
|
90820
91035
|
async function tokenHistory(args, ctx) {
|
|
90821
|
-
const usage4 = usageFor(ctx,
|
|
91036
|
+
const usage4 = usageFor(ctx, USAGE9.history);
|
|
90822
91037
|
const parsed = parseArgs(args, { valueFlags: ["--chain", "--limit", "--cursor"] });
|
|
90823
91038
|
if ("error" in parsed)
|
|
90824
91039
|
return usage4(parsed.error);
|
|
@@ -90873,7 +91088,7 @@ More: --cursor ${data.nextCursor}
|
|
|
90873
91088
|
return 0;
|
|
90874
91089
|
}
|
|
90875
91090
|
async function tokenFlag(args, ctx) {
|
|
90876
|
-
const usage4 = usageFor(ctx,
|
|
91091
|
+
const usage4 = usageFor(ctx, USAGE9.flag);
|
|
90877
91092
|
const parsed = parseArgs(args, { valueFlags: ["--chain", "--content", "--reason", "--detail", "--idempotency-key"] });
|
|
90878
91093
|
if ("error" in parsed)
|
|
90879
91094
|
return usage4(parsed.error);
|
|
@@ -90968,7 +91183,7 @@ async function tokenUpdate(args, ctx) {
|
|
|
90968
91183
|
return 0;
|
|
90969
91184
|
}
|
|
90970
91185
|
async function tokenComment(args, ctx) {
|
|
90971
|
-
const usage4 = usageFor(ctx,
|
|
91186
|
+
const usage4 = usageFor(ctx, USAGE9.comment);
|
|
90972
91187
|
const parsed = parseArgs(args, { valueFlags: ["--chain", "--file", "--idempotency-key"], pathFlags: ["--file"] });
|
|
90973
91188
|
if ("error" in parsed)
|
|
90974
91189
|
return usage4(parsed.error);
|
|
@@ -91013,7 +91228,7 @@ async function tokenComment(args, ctx) {
|
|
|
91013
91228
|
return 0;
|
|
91014
91229
|
}
|
|
91015
91230
|
async function tokenComments(args, ctx) {
|
|
91016
|
-
const usage4 = usageFor(ctx,
|
|
91231
|
+
const usage4 = usageFor(ctx, USAGE9.comments);
|
|
91017
91232
|
const parsed = parseArgs(args, { valueFlags: ["--chain", "--limit", "--cursor"] });
|
|
91018
91233
|
if ("error" in parsed)
|
|
91019
91234
|
return usage4(parsed.error);
|
|
@@ -91100,7 +91315,7 @@ async function tokenPost(args, ctx) {
|
|
|
91100
91315
|
}
|
|
91101
91316
|
|
|
91102
91317
|
// src/commands/keys-rotate.ts
|
|
91103
|
-
var
|
|
91318
|
+
var USAGE10 = `Usage: candle keys rotate <prefix|label> [--grace <duration> | --now] [--force] [--yes] [--idempotency-key <key>]
|
|
91104
91319
|
candle keys rotate <prefix|label> --finish`;
|
|
91105
91320
|
var MAX_GRACE_SECONDS = 86400;
|
|
91106
91321
|
var DEFAULT_GRACE_SECONDS = 3600;
|
|
@@ -91127,7 +91342,7 @@ async function keysRotate(args, ctx) {
|
|
|
91127
91342
|
}
|
|
91128
91343
|
const target = parsed.positionals[0];
|
|
91129
91344
|
if (!target || parsed.positionals.length !== 1) {
|
|
91130
|
-
writeScreenUsageFailure(deps,
|
|
91345
|
+
writeScreenUsageFailure(deps, USAGE10, json);
|
|
91131
91346
|
return 2;
|
|
91132
91347
|
}
|
|
91133
91348
|
const finish = parsed.booleans.has("--finish");
|
|
@@ -91380,7 +91595,7 @@ init_presentation();
|
|
|
91380
91595
|
init_profiles();
|
|
91381
91596
|
init_render();
|
|
91382
91597
|
init_key_output();
|
|
91383
|
-
var
|
|
91598
|
+
var NO_API_KEY4 = {
|
|
91384
91599
|
code: "NO_API_KEY",
|
|
91385
91600
|
message: "No API key for this profile.",
|
|
91386
91601
|
suggestion: "Set CANDLE_API_KEY, or run `candle keys create` and store one."
|
|
@@ -91404,7 +91619,7 @@ async function keysWalletsList(args, ctx) {
|
|
|
91404
91619
|
await printIdentity(ctx);
|
|
91405
91620
|
const apiKey = await resolveApiKey(deps, ctx.profile);
|
|
91406
91621
|
if (!apiKey) {
|
|
91407
|
-
writeScreenLocalFailure(deps,
|
|
91622
|
+
writeScreenLocalFailure(deps, NO_API_KEY4, json);
|
|
91408
91623
|
return 1;
|
|
91409
91624
|
}
|
|
91410
91625
|
const result = await apiRequest(`/api/v1/agent/keys/${encodeURIComponent(prefix)}/wallets`, {
|
|
@@ -91470,7 +91685,7 @@ async function keysWalletsSet(args, ctx) {
|
|
|
91470
91685
|
await printIdentity(ctx);
|
|
91471
91686
|
const apiKey = await resolveApiKey(deps, ctx.profile);
|
|
91472
91687
|
if (!apiKey) {
|
|
91473
|
-
writeScreenLocalFailure(deps,
|
|
91688
|
+
writeScreenLocalFailure(deps, NO_API_KEY4, json);
|
|
91474
91689
|
return 1;
|
|
91475
91690
|
}
|
|
91476
91691
|
const result = await apiRequest(`/api/v1/agent/keys/${encodeURIComponent(prefix)}/wallets`, {
|
|
@@ -91525,7 +91740,7 @@ async function keysWalletsScope(args, ctx) {
|
|
|
91525
91740
|
await printIdentity(ctx);
|
|
91526
91741
|
const apiKey = await resolveApiKey(deps, ctx.profile);
|
|
91527
91742
|
if (!apiKey) {
|
|
91528
|
-
writeScreenLocalFailure(deps,
|
|
91743
|
+
writeScreenLocalFailure(deps, NO_API_KEY4, json);
|
|
91529
91744
|
return 1;
|
|
91530
91745
|
}
|
|
91531
91746
|
const result = await apiRequest(`/api/v1/agent/keys/${encodeURIComponent(prefix)}/wallet-scope`, {
|
|
@@ -92047,6 +92262,8 @@ init_solana_lite();
|
|
|
92047
92262
|
var NATIVE_MINT2 = "So11111111111111111111111111111111111111112";
|
|
92048
92263
|
var LAMPORTS_PER_SIGNATURE = 5000n;
|
|
92049
92264
|
var TOKEN_ACCOUNT_RENT_LAMPORTS = 2039280n;
|
|
92265
|
+
var BUYER_TALLY_RENT_LAMPORTS = 1461600n;
|
|
92266
|
+
var REGISTER_BUYER = [144, 92, 137, 46, 209, 220, 27, 164];
|
|
92050
92267
|
var DEFAULT_UNITS_PER_INSTRUCTION = 200000n;
|
|
92051
92268
|
var MAX_COMPUTE_UNITS = 1400000n;
|
|
92052
92269
|
|
|
@@ -92130,7 +92347,7 @@ function readLeg(tx, index, wallet3) {
|
|
|
92130
92347
|
if (program === MEMO_PROGRAM_ID)
|
|
92131
92348
|
continue;
|
|
92132
92349
|
if (program !== undefined)
|
|
92133
|
-
others.push({ program, data: ix.data });
|
|
92350
|
+
others.push({ program, data: ix.data, accounts: accounts.map(key) });
|
|
92134
92351
|
}
|
|
92135
92352
|
if (unitPrice > 0n) {
|
|
92136
92353
|
const units = unitLimit ?? (computed * DEFAULT_UNITS_PER_INSTRUCTION > MAX_COMPUTE_UNITS ? MAX_COMPUTE_UNITS : computed * DEFAULT_UNITS_PER_INSTRUCTION);
|
|
@@ -92138,12 +92355,36 @@ function readLeg(tx, index, wallet3) {
|
|
|
92138
92355
|
}
|
|
92139
92356
|
return { lamports, tokenOut, systemTransfers, others };
|
|
92140
92357
|
}
|
|
92358
|
+
function isRegisterBuyer(data) {
|
|
92359
|
+
return data.length >= 8 && REGISTER_BUYER.every((byte, i) => data[i] === byte);
|
|
92360
|
+
}
|
|
92361
|
+
function readRegisterBuyer(register, swap3, wallet3) {
|
|
92362
|
+
if (register.program !== swap3.program)
|
|
92363
|
+
throw new BundleReadError("leg 1 registers the buyer with another program");
|
|
92364
|
+
if (register.data.length !== 12 || u323(register.data, 8) !== 0n)
|
|
92365
|
+
throw new BundleReadError("leg 1 registers the buyer with a proof");
|
|
92366
|
+
const [buyer, rentPayer, rules, tally] = register.accounts;
|
|
92367
|
+
if (buyer !== wallet3)
|
|
92368
|
+
throw new BundleReadError("leg 1 registers a buyer other than this wallet");
|
|
92369
|
+
if (rules === undefined || tally === undefined || !swap3.accounts.includes(rules) || !swap3.accounts.includes(tally))
|
|
92370
|
+
throw new BundleReadError("leg 1 registers the buyer for another pool than the one it buys");
|
|
92371
|
+
return rentPayer === wallet3 ? BUYER_TALLY_RENT_LAMPORTS : 0n;
|
|
92372
|
+
}
|
|
92141
92373
|
function readLaunchBundle(args) {
|
|
92142
92374
|
const launch = readLeg(decodeLeg(args.legs[0], 0), 0, args.wallet);
|
|
92143
92375
|
const buy = readLeg(decodeLeg(args.legs[1], 1), 1, args.wallet);
|
|
92144
|
-
|
|
92376
|
+
let registerLamports = 0n;
|
|
92377
|
+
const first = buy.others[0];
|
|
92378
|
+
if (first && isRegisterBuyer(first.data)) {
|
|
92379
|
+
if (!args.rules)
|
|
92380
|
+
throw new BundleReadError("leg 1 registers a buyer on a launch without rules");
|
|
92381
|
+
const swap4 = buy.others[1];
|
|
92382
|
+
if (buy.others.length !== 2 || !swap4 || isRegisterBuyer(swap4.data))
|
|
92383
|
+
throw new BundleReadError("leg 1 is not one curve swap");
|
|
92384
|
+
registerLamports = readRegisterBuyer(first, swap4, args.wallet);
|
|
92385
|
+
} else if (buy.others.length !== 1)
|
|
92145
92386
|
throw new BundleReadError("leg 1 is not one curve swap");
|
|
92146
|
-
const swap3 = buy.others[
|
|
92387
|
+
const swap3 = buy.others[buy.others.length - 1];
|
|
92147
92388
|
if (u64(swap3.data, 8) !== args.buyRaw)
|
|
92148
92389
|
throw new BundleReadError("leg 1 does not swap the confirmed --buy");
|
|
92149
92390
|
if (args.minOutRaw !== undefined && u64(swap3.data, 16) !== args.minOutRaw)
|
|
@@ -92163,7 +92404,7 @@ function readLaunchBundle(args) {
|
|
|
92163
92404
|
feeRaw = buy.tokenOut;
|
|
92164
92405
|
quoteRaw = args.buyRaw + buy.tokenOut + launch.tokenOut;
|
|
92165
92406
|
}
|
|
92166
|
-
return { lamports: launch.lamports + buy.lamports, quoteRaw, feeRaw };
|
|
92407
|
+
return { lamports: launch.lamports + buy.lamports + registerLamports, quoteRaw, feeRaw };
|
|
92167
92408
|
}
|
|
92168
92409
|
|
|
92169
92410
|
// src/launch-check.ts
|
|
@@ -92380,7 +92621,7 @@ function poolRequestFields(flags) {
|
|
|
92380
92621
|
|
|
92381
92622
|
// src/commands/launch.ts
|
|
92382
92623
|
init_swap();
|
|
92383
|
-
var
|
|
92624
|
+
var USAGE11 = `Usage: ${LAUNCH_USAGE}`;
|
|
92384
92625
|
var EMBEDDED_WALLET_NAMES = new Set(LAUNCH_DEFINITION.wallet.embeddedNames);
|
|
92385
92626
|
var LAUNCH_ARG_SPEC = {
|
|
92386
92627
|
valueFlags: [
|
|
@@ -92491,7 +92732,7 @@ async function launch(args, ctx) {
|
|
|
92491
92732
|
const dryRun = parsed.booleans.has("--dry-run");
|
|
92492
92733
|
const check = parsed.booleans.has("--check");
|
|
92493
92734
|
if (check && dryRun) {
|
|
92494
|
-
writeUsageFailure(ctx.deps, `--check and --dry-run are two different stops; pass one. ${
|
|
92735
|
+
writeUsageFailure(ctx.deps, `--check and --dry-run are two different stops; pass one. ${USAGE11}`, ctx.json);
|
|
92495
92736
|
return 2;
|
|
92496
92737
|
}
|
|
92497
92738
|
const fieldProblem = launchFieldProblem(flags);
|
|
@@ -92501,11 +92742,11 @@ async function launch(args, ctx) {
|
|
|
92501
92742
|
}
|
|
92502
92743
|
const curveProblem = curveFlagProblem(flags);
|
|
92503
92744
|
if (curveProblem) {
|
|
92504
|
-
writeUsageFailure(ctx.deps, `${curveProblem} ${
|
|
92745
|
+
writeUsageFailure(ctx.deps, `${curveProblem} ${USAGE11}`, ctx.json);
|
|
92505
92746
|
return 2;
|
|
92506
92747
|
}
|
|
92507
92748
|
if (flags["--image"] !== undefined && flags["--image-url"] !== undefined) {
|
|
92508
|
-
writeUsageFailure(ctx.deps, `Pass --image <path> or --image-url <https-url>, not both: a launch has one image. ${
|
|
92749
|
+
writeUsageFailure(ctx.deps, `Pass --image <path> or --image-url <https-url>, not both: a launch has one image. ${USAGE11}`, ctx.json);
|
|
92509
92750
|
return 2;
|
|
92510
92751
|
}
|
|
92511
92752
|
if (flags["--image-url"] !== undefined && !flags["--image-url"].startsWith("https://")) {
|
|
@@ -92518,24 +92759,24 @@ async function launch(args, ctx) {
|
|
|
92518
92759
|
}
|
|
92519
92760
|
const missingRequired = !flags["--name"] || !flags["--symbol"] || flags["--image"] === undefined && flags["--image-url"] === undefined || !flags["--wallet"];
|
|
92520
92761
|
if (parsed.positionals.length || !check && missingRequired || !validClientId(id)) {
|
|
92521
|
-
writeUsageFailure(ctx.deps,
|
|
92762
|
+
writeUsageFailure(ctx.deps, USAGE11, ctx.json);
|
|
92522
92763
|
return 2;
|
|
92523
92764
|
}
|
|
92524
92765
|
try {
|
|
92525
92766
|
poolRequestFields(flags);
|
|
92526
92767
|
} catch (error) {
|
|
92527
|
-
writeUsageFailure(ctx.deps, `${error.message}. ${
|
|
92768
|
+
writeUsageFailure(ctx.deps, `${error.message}. ${USAGE11}`, ctx.json);
|
|
92528
92769
|
return 2;
|
|
92529
92770
|
}
|
|
92530
92771
|
let feeWallet;
|
|
92531
92772
|
try {
|
|
92532
92773
|
feeWallet = feeWalletSelection(flags);
|
|
92533
92774
|
} catch (error) {
|
|
92534
|
-
writeUsageFailure(ctx.deps, `${error.message}. ${
|
|
92775
|
+
writeUsageFailure(ctx.deps, `${error.message}. ${USAGE11}`, ctx.json);
|
|
92535
92776
|
return 2;
|
|
92536
92777
|
}
|
|
92537
92778
|
if (parsed.booleans.has("--require-fee-wallet") && !feeWallet) {
|
|
92538
|
-
writeUsageFailure(ctx.deps, `--require-fee-wallet needs --fee-wallet <address>: pass the wallet this launch's fees earn to. ${
|
|
92779
|
+
writeUsageFailure(ctx.deps, `--require-fee-wallet needs --fee-wallet <address>: pass the wallet this launch's fees earn to. ${USAGE11}`, ctx.json);
|
|
92539
92780
|
return 2;
|
|
92540
92781
|
}
|
|
92541
92782
|
try {
|
|
@@ -92543,11 +92784,11 @@ async function launch(args, ctx) {
|
|
|
92543
92784
|
launchBuyRaw(flags["--buy"], 18);
|
|
92544
92785
|
const hinted = flagChain(flags);
|
|
92545
92786
|
if (hinted === "hood" && feeWalletGiven(flags)) {
|
|
92546
|
-
writeUsageFailure(ctx.deps, `--fee-wallet is Solana only; a Hood launch cannot take it. ${
|
|
92787
|
+
writeUsageFailure(ctx.deps, `--fee-wallet is Solana only; a Hood launch cannot take it. ${USAGE11}`, ctx.json);
|
|
92547
92788
|
return 2;
|
|
92548
92789
|
}
|
|
92549
92790
|
if (!check && hinted === "hood" && flags["--dex-version"] === undefined) {
|
|
92550
|
-
writeUsageFailure(ctx.deps, `A Hood launch needs --dex-version v3|v4. ${
|
|
92791
|
+
writeUsageFailure(ctx.deps, `A Hood launch needs --dex-version v3|v4. ${USAGE11}`, ctx.json);
|
|
92551
92792
|
return 2;
|
|
92552
92793
|
}
|
|
92553
92794
|
let image;
|
|
@@ -92565,7 +92806,7 @@ async function launch(args, ctx) {
|
|
|
92565
92806
|
let rules;
|
|
92566
92807
|
if (!check && launchRulesGiven(flags)) {
|
|
92567
92808
|
if (hinted === "hood") {
|
|
92568
|
-
writeUsageFailure(ctx.deps, `${LAUNCH_RULES_SOLANA_ONLY} ${
|
|
92809
|
+
writeUsageFailure(ctx.deps, `${LAUNCH_RULES_SOLANA_ONLY} ${USAGE11}`, ctx.json);
|
|
92569
92810
|
return 2;
|
|
92570
92811
|
}
|
|
92571
92812
|
try {
|
|
@@ -92972,7 +93213,8 @@ async function atomicLaunch(ctx, key, args) {
|
|
|
92972
93213
|
wallet: wallet3.address,
|
|
92973
93214
|
quote: quoteAsset,
|
|
92974
93215
|
buyRaw: BigInt(buy.raw),
|
|
92975
|
-
...minOut !== undefined ? { minOutRaw: BigInt(minOut) } : {}
|
|
93216
|
+
...minOut !== undefined ? { minOutRaw: BigInt(minOut) } : {},
|
|
93217
|
+
rules: args.rules !== undefined
|
|
92976
93218
|
});
|
|
92977
93219
|
} catch (error) {
|
|
92978
93220
|
if (!(error instanceof BundleReadError))
|
|
@@ -93385,7 +93627,7 @@ init_presentation();
|
|
|
93385
93627
|
init_render();
|
|
93386
93628
|
init_trading();
|
|
93387
93629
|
init_swap();
|
|
93388
|
-
var
|
|
93630
|
+
var USAGE12 = "Usage: candle launch status <mint-or-client-launch-id> [--wait]";
|
|
93389
93631
|
var LAUNCH_WAIT_MS = 10 * 60 * 1000;
|
|
93390
93632
|
var LAUNCH_WAIT_POLL_MS = 1e4;
|
|
93391
93633
|
var LIVE = new Set(["trading", "bonded", "migrated"]);
|
|
@@ -93431,7 +93673,7 @@ async function readStatus(ctx, key, id) {
|
|
|
93431
93673
|
async function launchStatus(args, ctx) {
|
|
93432
93674
|
const parsed = parseArgs(args, { booleanFlags: ["--wait"] });
|
|
93433
93675
|
if ("error" in parsed || parsed.positionals.length !== 1 || !validClientId(parsed.positionals[0] ?? "")) {
|
|
93434
|
-
writeUsageFailure(ctx.deps,
|
|
93676
|
+
writeUsageFailure(ctx.deps, USAGE12, ctx.json);
|
|
93435
93677
|
return 2;
|
|
93436
93678
|
}
|
|
93437
93679
|
const id = parsed.positionals[0];
|
|
@@ -93915,6 +94157,7 @@ var MCP_TOOL_NAMES = [
|
|
|
93915
94157
|
"candle_get_plans",
|
|
93916
94158
|
"candle_report_activity",
|
|
93917
94159
|
"candle_trade",
|
|
94160
|
+
"candle_trade_execute",
|
|
93918
94161
|
"candle_swap",
|
|
93919
94162
|
"candle_transfer",
|
|
93920
94163
|
"candle_sweep",
|
|
@@ -93923,6 +94166,9 @@ var MCP_TOOL_NAMES = [
|
|
|
93923
94166
|
"candle_set_profile_wallets",
|
|
93924
94167
|
"candle_get_profile_pnl",
|
|
93925
94168
|
"candle_get_profile_trades",
|
|
94169
|
+
"candle_get_paper_inventory",
|
|
94170
|
+
"candle_get_profile_paper",
|
|
94171
|
+
"candle_get_account_pnl",
|
|
93926
94172
|
"candle_get_portfolio",
|
|
93927
94173
|
"candle_resolve_token",
|
|
93928
94174
|
"candle_execution_status",
|
|
@@ -94044,7 +94290,8 @@ async function mcp(args, ctx) {
|
|
|
94044
94290
|
}
|
|
94045
94291
|
const identityConfig = await deps.readConfig();
|
|
94046
94292
|
const identityFields = effectiveProfileFields(identityConfig, ctx.profile);
|
|
94047
|
-
|
|
94293
|
+
const identityOverrides = credentialEnvOverrides(deps.env);
|
|
94294
|
+
deps.stderr.write(`${identityLine(ctx.profile, identityOverrides.length > 0 ? undefined : identityFields.account, apiUrl, identityOverrides.length > 0 ? identityOverrides : undefined, identityFields.username, identityStyle(deps, "stderr"))}
|
|
94048
94295
|
|
|
94049
94296
|
`);
|
|
94050
94297
|
if (parsed.booleans.has("--print-config")) {
|
|
@@ -99328,7 +99575,7 @@ init_swap();
|
|
|
99328
99575
|
init_vault_support();
|
|
99329
99576
|
var WITHDRAW_SCOPE = "perps:withdraw";
|
|
99330
99577
|
var ID_RE = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
99331
|
-
var
|
|
99578
|
+
var USAGE13 = "Usage: candle perps withdraw <amount> --to solana|hood [--wallet <evm tee>] [--id <id>] [--keystore <path>] | candle perps withdraw status <id>";
|
|
99332
99579
|
var WITHDRAW_POLL_MS = 10 * 60 * 1000;
|
|
99333
99580
|
var WITHDRAW_POLL_INTERVAL_MS = 5000;
|
|
99334
99581
|
var TERMINAL = new Set(["settled", "refunded", "failed"]);
|
|
@@ -99379,7 +99626,7 @@ async function withdrawStatus(ctx, id) {
|
|
|
99379
99626
|
async function perpsWithdraw(args, ctx) {
|
|
99380
99627
|
if (args[0] === "status") {
|
|
99381
99628
|
if (args.length !== 2 || !ID_RE.test(args[1]))
|
|
99382
|
-
return usage10(ctx,
|
|
99629
|
+
return usage10(ctx, USAGE13);
|
|
99383
99630
|
try {
|
|
99384
99631
|
return await withdrawStatus(ctx, args[1]);
|
|
99385
99632
|
} catch (error) {
|
|
@@ -99398,7 +99645,7 @@ async function perpsWithdraw(args, ctx) {
|
|
|
99398
99645
|
const [amount] = parsed.positionals;
|
|
99399
99646
|
const to = parsed.values["--to"];
|
|
99400
99647
|
if (parsed.positionals.length !== 1 || !amount || to !== "solana" && to !== "hood")
|
|
99401
|
-
return usage10(ctx,
|
|
99648
|
+
return usage10(ctx, USAGE13);
|
|
99402
99649
|
const amountMicros = parseWithdrawAmount(amount);
|
|
99403
99650
|
if (amountMicros === null)
|
|
99404
99651
|
return usage10(ctx, "The amount must be a positive USDC decimal with at most 6 decimals.");
|
|
@@ -101480,12 +101727,12 @@ function planPromotionLine2(table) {
|
|
|
101480
101727
|
init_presentation();
|
|
101481
101728
|
init_render();
|
|
101482
101729
|
init_key_output();
|
|
101483
|
-
var
|
|
101730
|
+
var USAGE14 = "Usage: candle plans";
|
|
101484
101731
|
async function plans(args, ctx) {
|
|
101485
101732
|
const parsed = parseArgs(args, {});
|
|
101486
101733
|
if ("error" in parsed || parsed.positionals.length > 0) {
|
|
101487
101734
|
writeScreenUsageFailure(ctx.deps, "error" in parsed ? `${parsed.error}
|
|
101488
|
-
${
|
|
101735
|
+
${USAGE14}` : USAGE14, ctx.json);
|
|
101489
101736
|
return 2;
|
|
101490
101737
|
}
|
|
101491
101738
|
const result = await apiRequest("/api/v1/agent/plans", {
|
|
@@ -101874,15 +102121,14 @@ async function runPlugin(name2, rawArgs, ctx) {
|
|
|
101874
102121
|
if (split2.wallets.length > 0) {
|
|
101875
102122
|
if (!refuseEnvPassphrase(ctx))
|
|
101876
102123
|
return 1;
|
|
101877
|
-
|
|
101878
|
-
return 1;
|
|
102124
|
+
const promptCtx = refusePromptsWithoutTerminal(ctx, "candle <plugin> --wallet");
|
|
101879
102125
|
const resolvedVault = vaultPathFor(ctx, { values: {}, booleans: new Set, positionals: [] });
|
|
101880
102126
|
if ("error" in resolvedVault)
|
|
101881
102127
|
return usage(ctx, resolvedVault.error);
|
|
101882
102128
|
const vaultPath = resolvedVault.path;
|
|
101883
102129
|
const resolved = await runVaultCommand(ctx, async ({ hold }) => {
|
|
101884
102130
|
const raw = await requireVaultRaw(ctx, resolvedVault);
|
|
101885
|
-
const vault = hold((await unlockInteractively(
|
|
102131
|
+
const vault = hold((await unlockInteractively(promptCtx, vaultPath, raw)).vault);
|
|
101886
102132
|
for (const requested of split2.wallets) {
|
|
101887
102133
|
const entry = findExternalEntry(vault.index, requested);
|
|
101888
102134
|
if (entry === undefined) {
|
|
@@ -102100,7 +102346,7 @@ function writePosition(ctx, p) {
|
|
|
102100
102346
|
// src/commands/pnl.ts
|
|
102101
102347
|
var CHAINS = ["solana", "hood"];
|
|
102102
102348
|
var CHAIN_TITLES = { solana: "Solana", hood: "Hood" };
|
|
102103
|
-
var
|
|
102349
|
+
var NO_API_KEY5 = {
|
|
102104
102350
|
code: "NO_API_KEY",
|
|
102105
102351
|
message: "No API key for this profile.",
|
|
102106
102352
|
suggestion: "Set CANDLE_API_KEY, or run `candle auth login` to store one."
|
|
@@ -102119,7 +102365,7 @@ async function pnl(args, ctx) {
|
|
|
102119
102365
|
}
|
|
102120
102366
|
const apiKey = await resolveApiKey(deps, ctx.profile);
|
|
102121
102367
|
if (!apiKey) {
|
|
102122
|
-
writeLocalFailure(deps,
|
|
102368
|
+
writeLocalFailure(deps, NO_API_KEY5, json);
|
|
102123
102369
|
return 1;
|
|
102124
102370
|
}
|
|
102125
102371
|
const perProfile = ctx.profileFlag !== undefined;
|
|
@@ -102567,19 +102813,25 @@ var UNREAD_LABELS = {
|
|
|
102567
102813
|
usdg: "USDG",
|
|
102568
102814
|
"hood-tokens": "tokens"
|
|
102569
102815
|
};
|
|
102570
|
-
var
|
|
102816
|
+
var NO_API_KEY6 = {
|
|
102571
102817
|
code: "NO_API_KEY",
|
|
102572
102818
|
message: "No API key for this profile.",
|
|
102573
102819
|
suggestion: "Set CANDLE_API_KEY, or run `candle auth login` to store one."
|
|
102574
102820
|
};
|
|
102821
|
+
var NO_TERMINAL_REASON = "not read: no terminal for its unlock";
|
|
102822
|
+
var NO_TERMINAL_NOTE = "The vault was not read: its unlock needs a prompt, and standard input and standard error are not both a terminal. Run it at a terminal, pass --factor touch-id or --factor passkey to unlock without a prompt, or pass --no-vault to leave it out.";
|
|
102575
102823
|
async function portfolio(args, ctx) {
|
|
102576
102824
|
const parsed = parseArgs(args, {
|
|
102577
102825
|
valueFlags: ["--rpc-url", "--evm-rpc-url", "--keystore", "--chain"],
|
|
102578
|
-
booleanFlags: ["--no-perps"],
|
|
102826
|
+
booleanFlags: ["--no-perps", "--no-vault"],
|
|
102579
102827
|
pathFlags: ["--keystore"]
|
|
102580
102828
|
});
|
|
102581
102829
|
if ("error" in parsed)
|
|
102582
102830
|
return usage(ctx, parsed.error);
|
|
102831
|
+
const noVault = parsed.booleans.has("--no-vault");
|
|
102832
|
+
if (noVault && parsed.values["--keystore"] !== undefined) {
|
|
102833
|
+
return usage(ctx, "--keystore names the vault to read and has no effect with --no-vault.");
|
|
102834
|
+
}
|
|
102583
102835
|
if (parsed.positionals.length > 1)
|
|
102584
102836
|
return usage(ctx, `Unexpected argument: ${parsed.positionals[1]}`);
|
|
102585
102837
|
const filter = parsed.positionals[0];
|
|
@@ -102602,7 +102854,7 @@ async function portfolio(args, ctx) {
|
|
|
102602
102854
|
}
|
|
102603
102855
|
const apiKey = await resolveApiKey(deps, ctx.profile);
|
|
102604
102856
|
if (!apiKey) {
|
|
102605
|
-
writeLocalFailure(deps,
|
|
102857
|
+
writeLocalFailure(deps, NO_API_KEY6, ctx.json);
|
|
102606
102858
|
return 1;
|
|
102607
102859
|
}
|
|
102608
102860
|
let solana;
|
|
@@ -102626,16 +102878,30 @@ async function portfolio(args, ctx) {
|
|
|
102626
102878
|
let vaultEntries;
|
|
102627
102879
|
let evmEntries;
|
|
102628
102880
|
let vaultReason;
|
|
102881
|
+
let vaultSkipped = false;
|
|
102629
102882
|
let known = 0;
|
|
102630
|
-
const raw = await readVaultRaw(resolvedVault.path);
|
|
102631
|
-
|
|
102632
|
-
|
|
102633
|
-
} else {
|
|
102883
|
+
const raw = noVault ? null : await readVaultRaw(resolvedVault.path);
|
|
102884
|
+
let vault;
|
|
102885
|
+
if (raw !== null) {
|
|
102634
102886
|
if (!refuseEnvPassphrase(ctx))
|
|
102635
102887
|
return 1;
|
|
102636
|
-
|
|
102637
|
-
|
|
102638
|
-
|
|
102888
|
+
try {
|
|
102889
|
+
const unlockCtx = refusePromptsWithoutTerminal(ctx, "portfolio");
|
|
102890
|
+
vault = hold((await unlockInteractively(unlockCtx, resolvedVault.path, raw)).vault);
|
|
102891
|
+
} catch (error) {
|
|
102892
|
+
if (!isNoTerminalRefusal(error))
|
|
102893
|
+
throw error;
|
|
102894
|
+
}
|
|
102895
|
+
}
|
|
102896
|
+
if (noVault) {
|
|
102897
|
+
vaultReason = "not read (--no-vault)";
|
|
102898
|
+
} else if (raw === null) {
|
|
102899
|
+
vaultReason = `no vault at ${resolvedVault.path}`;
|
|
102900
|
+
} else if (vault === undefined) {
|
|
102901
|
+
vaultReason = NO_TERMINAL_REASON;
|
|
102902
|
+
vaultSkipped = true;
|
|
102903
|
+
notice(deps, "stderr", "note", NO_TERMINAL_NOTE);
|
|
102904
|
+
} else {
|
|
102639
102905
|
vaultEntries = showSolana ? vault.index.entries.filter((entry) => entry.chain === "solana" && (entry.role === "vault" || entry.role === "external")).map((entry) => ({ address: entry.address, label: entry.label, role: entry.role })) : [];
|
|
102640
102906
|
evmEntries = showHood ? vault.index.entries.filter((entry) => entry.chain === "evm" && entry.role === "vault").map((entry) => ({ address: entry.address, label: entry.label })) : [];
|
|
102641
102907
|
known += vaultEntries.length + evmEntries.length;
|
|
@@ -102850,7 +103116,7 @@ async function portfolio(args, ctx) {
|
|
|
102850
103116
|
const listStatus = walletListStatus(answered);
|
|
102851
103117
|
const shownUnread = unavailable6.length > 0 || lpUnread > 0;
|
|
102852
103118
|
const listNote = listStatus === "cut-off" ? "cut-off" : listStatus === "unknown" && !shownUnread ? "unknown" : null;
|
|
102853
|
-
const complete = listNote === null && !shownUnread && !perpsPartial;
|
|
103119
|
+
const complete = listNote === null && !shownUnread && !perpsPartial && !vaultSkipped;
|
|
102854
103120
|
const totalUsd = groups.reduce((sum, g) => sum + g.valueUsd, 0);
|
|
102855
103121
|
const unpriced = groups.reduce((sum, g) => sum + g.unpriced, 0);
|
|
102856
103122
|
const lp = lpSection ? {
|
|
@@ -102927,6 +103193,7 @@ async function portfolio(args, ctx) {
|
|
|
102927
103193
|
byChain,
|
|
102928
103194
|
unavailableByChain,
|
|
102929
103195
|
listNote,
|
|
103196
|
+
vaultSkipped,
|
|
102930
103197
|
lp
|
|
102931
103198
|
});
|
|
102932
103199
|
if (perps)
|
|
@@ -103302,7 +103569,7 @@ ${tableBlock(ctx, "LP positions", ["GROUP", "WALLET", "POSITION", "POOL", "HOLDI
|
|
|
103302
103569
|
notes.push([
|
|
103303
103570
|
"total",
|
|
103304
103571
|
formatUsd(totals.totalUsd),
|
|
103305
|
-
totals.unpriced > 0 ? `${totals.unpriced} unpriced ${totals.unpriced === 1 ? "holding" : "holdings"} not counted` : "every holding priced"
|
|
103572
|
+
(totals.vaultSkipped ? "partial: the vault was not read (no terminal for its unlock). " : "") + (totals.unpriced > 0 ? `${totals.unpriced} unpriced ${totals.unpriced === 1 ? "holding" : "holdings"} not counted` : totals.vaultSkipped ? "other holdings priced" : "every holding priced")
|
|
103306
103573
|
]);
|
|
103307
103574
|
const style = terminalStyle(deps, "stdout");
|
|
103308
103575
|
const width = Math.max(...notes.map(([label]) => displayWidth(label)));
|
|
@@ -103324,6 +103591,8 @@ ${tableBlock(ctx, "LP positions", ["GROUP", "WALLET", "POSITION", "POOL", "HOLDI
|
|
|
103324
103591
|
if (unavailable6 > 0) {
|
|
103325
103592
|
warnings.push(`${unavailable6} ${unavailable6 === 1 ? "wallet" : "wallets"} could not be read in full (${unreadByChain.map(([chain2, n]) => `${n} on ${CHAIN_NAMES[chain2]}`).join(", ")}). What was not read is marked "not read" and is not in the total.`);
|
|
103326
103593
|
}
|
|
103594
|
+
if (totals.vaultSkipped)
|
|
103595
|
+
warnings.push("The vault was not read: no terminal for its unlock. Its holdings are not in the total, so the total is partial (exit 3). Run it in a terminal, or pass --no-vault to leave the vault out on purpose.");
|
|
103327
103596
|
writeNotes2(ctx, warnings);
|
|
103328
103597
|
writeListNote(ctx, totals.listNote);
|
|
103329
103598
|
const later = [];
|
|
@@ -103377,6 +103646,7 @@ ${financialHeading("Hyperliquid (live from Hyperliquid, not in the ledger; not i
|
|
|
103377
103646
|
}
|
|
103378
103647
|
|
|
103379
103648
|
// src/commands/profile.ts
|
|
103649
|
+
init_account();
|
|
103380
103650
|
init_args();
|
|
103381
103651
|
init_client();
|
|
103382
103652
|
init_human_output();
|
|
@@ -103673,7 +103943,7 @@ candle profile use <name>`);
|
|
|
103673
103943
|
init_args();
|
|
103674
103944
|
init_deps();
|
|
103675
103945
|
init_key_output();
|
|
103676
|
-
var
|
|
103946
|
+
var USAGE15 = 'Usage: candle quant ask "<text>" [--timeout <s>] [--json]';
|
|
103677
103947
|
var QUANT_ASK_DEFAULT_TIMEOUT_S = 300;
|
|
103678
103948
|
var QUANT_ASK_MAX_TIMEOUT_S = 3600;
|
|
103679
103949
|
var QUANT_ASK_SEND_TRIES = 3;
|
|
@@ -103842,7 +104112,7 @@ async function quantAsk(args, ctx) {
|
|
|
103842
104112
|
const timeoutS = timeoutRaw === undefined ? QUANT_ASK_DEFAULT_TIMEOUT_S : Number(timeoutRaw);
|
|
103843
104113
|
const text4 = "error" in parsed ? "" : parsed.positionals[0] ?? "";
|
|
103844
104114
|
if ("error" in parsed || parsed.positionals.length !== 1 || !text4.trim() || !Number.isInteger(timeoutS) || timeoutS < 1 || timeoutS > QUANT_ASK_MAX_TIMEOUT_S) {
|
|
103845
|
-
writeScreenUsageFailure(ctx.deps,
|
|
104115
|
+
writeScreenUsageFailure(ctx.deps, USAGE15, ctx.json);
|
|
103846
104116
|
return 2;
|
|
103847
104117
|
}
|
|
103848
104118
|
const apiKey = await resolveApiKey(ctx.deps, ctx.profile);
|
|
@@ -104051,7 +104321,7 @@ init_client();
|
|
|
104051
104321
|
init_human_output();
|
|
104052
104322
|
init_presentation();
|
|
104053
104323
|
init_render();
|
|
104054
|
-
var
|
|
104324
|
+
var USAGE16 = "Usage: candle scan <contract address> [--chain solana|hood] [--json]";
|
|
104055
104325
|
var OTHER_CHAIN = "Candle trades Solana and Hood today; that is not a Solana or Hood contract address.";
|
|
104056
104326
|
var HOOD_ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
104057
104327
|
var SOLANA_ADDRESS = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
@@ -104151,20 +104421,20 @@ async function scan(args, ctx) {
|
|
|
104151
104421
|
}
|
|
104152
104422
|
const named = parsed.values["--chain"];
|
|
104153
104423
|
if (parsed.positionals.length !== 1 || named !== undefined && named !== "solana" && named !== "hood") {
|
|
104154
|
-
writeUsageFailure(ctx.deps,
|
|
104424
|
+
writeUsageFailure(ctx.deps, USAGE16, ctx.json);
|
|
104155
104425
|
return 2;
|
|
104156
104426
|
}
|
|
104157
104427
|
const address = parsed.positionals[0].trim();
|
|
104158
104428
|
const found = chainOfAddress(address);
|
|
104159
104429
|
if (found === null) {
|
|
104160
104430
|
writeUsageFailure(ctx.deps, `${OTHER_CHAIN}
|
|
104161
|
-
${
|
|
104431
|
+
${USAGE16}`, ctx.json);
|
|
104162
104432
|
return 2;
|
|
104163
104433
|
}
|
|
104164
104434
|
if (named !== undefined && named !== found) {
|
|
104165
104435
|
const label = (c) => c === "hood" ? "Hood" : "Solana";
|
|
104166
104436
|
writeUsageFailure(ctx.deps, `That address is a ${label(found)} contract, not a ${label(named)} one. Candle trades Solana and Hood today.
|
|
104167
|
-
${
|
|
104437
|
+
${USAGE16}`, ctx.json);
|
|
104168
104438
|
return 2;
|
|
104169
104439
|
}
|
|
104170
104440
|
const chain2 = found;
|
|
@@ -104507,8 +104777,7 @@ async function sign5(args, ctx) {
|
|
|
104507
104777
|
return usage(ctx, solana.error);
|
|
104508
104778
|
if (!refuseEnvPassphrase(ctx))
|
|
104509
104779
|
return 1;
|
|
104510
|
-
|
|
104511
|
-
return 1;
|
|
104780
|
+
const promptCtx = refusePromptsWithoutTerminal(ctx, "candle sign");
|
|
104512
104781
|
const { deps } = ctx;
|
|
104513
104782
|
const resolvedVault = vaultPathFor(ctx, parsed);
|
|
104514
104783
|
if ("error" in resolvedVault)
|
|
@@ -104538,7 +104807,7 @@ async function sign5(args, ctx) {
|
|
|
104538
104807
|
throw error;
|
|
104539
104808
|
}
|
|
104540
104809
|
const raw = await requireVaultRaw(ctx, resolvedVault);
|
|
104541
|
-
const opened = await unlockInteractively(
|
|
104810
|
+
const opened = await unlockInteractively(promptCtx, path, raw, {
|
|
104542
104811
|
acceptOlderCopy: parsed.booleans.has("--accept-older-copy")
|
|
104543
104812
|
});
|
|
104544
104813
|
const vault = hold(opened.vault);
|
|
@@ -104697,8 +104966,7 @@ async function signMessage2(args, ctx) {
|
|
|
104697
104966
|
const requested = lifted.values[0];
|
|
104698
104967
|
if (!refuseEnvPassphrase(ctx))
|
|
104699
104968
|
return 1;
|
|
104700
|
-
|
|
104701
|
-
return 1;
|
|
104969
|
+
const promptCtx = refusePromptsWithoutTerminal(ctx, "candle sign message");
|
|
104702
104970
|
const { deps } = ctx;
|
|
104703
104971
|
const resolvedVault = vaultPathFor(ctx, parsed);
|
|
104704
104972
|
if ("error" in resolvedVault)
|
|
@@ -104712,7 +104980,7 @@ async function signMessage2(args, ctx) {
|
|
|
104712
104980
|
throw new VaultError("VAULT_UNREADABLE", `The message could not be read: ${error instanceof Error ? error.message : error}.`);
|
|
104713
104981
|
}
|
|
104714
104982
|
const raw = await requireVaultRaw(ctx, resolvedVault);
|
|
104715
|
-
const opened = await unlockInteractively(
|
|
104983
|
+
const opened = await unlockInteractively(promptCtx, path, raw, {
|
|
104716
104984
|
acceptOlderCopy: parsed.booleans.has("--accept-older-copy")
|
|
104717
104985
|
});
|
|
104718
104986
|
const vault = hold(opened.vault);
|
|
@@ -105480,8 +105748,7 @@ async function vaultRename(args, ctx) {
|
|
|
105480
105748
|
const id = parsed.values["--id"];
|
|
105481
105749
|
if (!refuseEnvPassphrase(ctx))
|
|
105482
105750
|
return 1;
|
|
105483
|
-
|
|
105484
|
-
return 1;
|
|
105751
|
+
const promptCtx = refusePromptsWithoutTerminal(ctx, "vault rename");
|
|
105485
105752
|
const { deps } = ctx;
|
|
105486
105753
|
const resolvedVault = vaultPathFor(ctx, parsed);
|
|
105487
105754
|
if ("error" in resolvedVault)
|
|
@@ -105502,7 +105769,7 @@ async function vaultRename(args, ctx) {
|
|
|
105502
105769
|
}
|
|
105503
105770
|
return runVaultCommand(ctx, async ({ hold }) => {
|
|
105504
105771
|
const raw = await requireVaultRaw(ctx, resolvedVault);
|
|
105505
|
-
const opened = await unlockInteractively(
|
|
105772
|
+
const opened = await unlockInteractively(promptCtx, path, raw, {
|
|
105506
105773
|
acceptOlderCopy: parsed.booleans.has("--accept-older-copy")
|
|
105507
105774
|
});
|
|
105508
105775
|
const vault = hold(opened.vault);
|
|
@@ -105773,10 +106040,9 @@ async function teeRename(args, ctx) {
|
|
|
105773
106040
|
if ("error" in vaultPath)
|
|
105774
106041
|
return usage(ctx, vaultPath.error);
|
|
105775
106042
|
const raw = rows.some((row2) => row2.tee) ? await readVaultRaw(vaultPath.path) : null;
|
|
105776
|
-
|
|
105777
|
-
return 1;
|
|
106043
|
+
const promptCtx = refusePromptsWithoutTerminal(ctx, "tee rename");
|
|
105778
106044
|
return runVaultCommand(ctx, async ({ hold }) => {
|
|
105779
|
-
const vault = raw === null ? null : hold((await unlockInteractively(
|
|
106045
|
+
const vault = raw === null ? null : hold((await unlockInteractively(promptCtx, vaultPath.path, raw, {
|
|
105780
106046
|
acceptOlderCopy: parsed.booleans.has("--accept-older-copy")
|
|
105781
106047
|
})).vault);
|
|
105782
106048
|
const local = [];
|
|
@@ -106042,7 +106308,7 @@ init_deps();
|
|
|
106042
106308
|
init_human_output();
|
|
106043
106309
|
init_render();
|
|
106044
106310
|
var KINDS = ["trade", "signal", "callout"];
|
|
106045
|
-
var
|
|
106311
|
+
var USAGE17 = "Usage: candle token events <mint> [--chain solana|hood] [--kind trade|signal|callout] [--since <iso-8601>] [--limit <n>] [--before <cursor>] [--json]";
|
|
106046
106312
|
function readEventsBody(body) {
|
|
106047
106313
|
if (body === null || typeof body !== "object" || !Array.isArray(body.events))
|
|
106048
106314
|
return null;
|
|
@@ -106100,11 +106366,11 @@ async function tokenEvents(args, ctx) {
|
|
|
106100
106366
|
const since = parsed.values["--since"];
|
|
106101
106367
|
const before = parsed.values["--before"];
|
|
106102
106368
|
if (parsed.positionals.length !== 1 || chain2 !== "solana" && chain2 !== "hood") {
|
|
106103
|
-
writeUsageFailure(ctx.deps,
|
|
106369
|
+
writeUsageFailure(ctx.deps, USAGE17, ctx.json);
|
|
106104
106370
|
return 2;
|
|
106105
106371
|
}
|
|
106106
106372
|
if (kind !== undefined && !KINDS.includes(kind)) {
|
|
106107
|
-
writeUsageFailure(ctx.deps,
|
|
106373
|
+
writeUsageFailure(ctx.deps, USAGE17, ctx.json);
|
|
106108
106374
|
return 2;
|
|
106109
106375
|
}
|
|
106110
106376
|
if (limit !== undefined && !/^[1-9][0-9]{0,3}$/.test(limit) || Number(limit) > 1000) {
|
|
@@ -106443,7 +106709,7 @@ init_render();
|
|
|
106443
106709
|
init_solana_verify();
|
|
106444
106710
|
init_trading();
|
|
106445
106711
|
init_swap();
|
|
106446
|
-
var
|
|
106712
|
+
var USAGE18 = "Usage: candle transfer --to <address|wallet name|allowlist label|vault> --asset <SOL|USDC|CNDL|ETH|USDG>|--mint <mint>|--token <0x...> --amount <decimal|max> [--wallet <name>] [--rpc-url <url>] [--yes] [--json]";
|
|
106447
106713
|
var TRANSFER_ASSETS = [...Object.keys(BASES), ...Object.keys(HOOD_BASES)];
|
|
106448
106714
|
var BASE58_ADDRESS = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
106449
106715
|
async function readLinkedWallets(ctx, key) {
|
|
@@ -106588,7 +106854,7 @@ async function transfer(args, ctx) {
|
|
|
106588
106854
|
const flags2 = parsed.values;
|
|
106589
106855
|
const asset = flags2["--asset"]?.toUpperCase();
|
|
106590
106856
|
if (parsed.positionals.length !== 0 || !flags2["--to"] || !flags2["--amount"] || [flags2["--asset"], flags2["--mint"], flags2["--token"]].filter(Boolean).length !== 1 || asset !== undefined && !TRANSFER_ASSETS.includes(asset) || flags2["--mint"] !== undefined && !BASE58_ADDRESS.test(flags2["--mint"]) || flags2["--token"] !== undefined && !checkEvmAddress(flags2["--token"]).ok) {
|
|
106591
|
-
writeUsageFailure(ctx.deps,
|
|
106857
|
+
writeUsageFailure(ctx.deps, USAGE18, ctx.json);
|
|
106592
106858
|
return 2;
|
|
106593
106859
|
}
|
|
106594
106860
|
const to = flags2["--to"];
|
|
@@ -109585,7 +109851,10 @@ async function createVault(request2, clock) {
|
|
|
109585
109851
|
if (raw === null) {
|
|
109586
109852
|
throw new VaultError("VAULT_WRITE_FAILED", `The vault was written to ${request2.path} but could not be read back.`);
|
|
109587
109853
|
}
|
|
109588
|
-
return unlockWithPassphrase(request2.path, raw, request2.passphrase, {
|
|
109854
|
+
return unlockWithPassphrase(request2.path, raw, request2.passphrase, {
|
|
109855
|
+
notice: request2.notice,
|
|
109856
|
+
derivationLabel: request2.verificationLabel
|
|
109857
|
+
});
|
|
109589
109858
|
}
|
|
109590
109859
|
|
|
109591
109860
|
// src/commands/vault-init.ts
|
|
@@ -109659,8 +109928,8 @@ async function runPhraseCeremony(ctx, vault, opts = {}) {
|
|
|
109659
109928
|
const { closeVault: closeVault2 } = await Promise.resolve().then(() => (init_store(), exports_store));
|
|
109660
109929
|
closeVault2(reopened);
|
|
109661
109930
|
}
|
|
109662
|
-
notice(deps, "stdout", "
|
|
109663
|
-
They do NOT restore any key imported from
|
|
109931
|
+
notice(deps, "stdout", "warning", `The 24 words below re-derive every key this vault derives, on any BIP-39 wallet, with no passphrase and no server.
|
|
109932
|
+
They do NOT restore any key you imported from an older wallet store; the vault file plus a factor does that.
|
|
109664
109933
|
Anyone who reads them can move every derived key's funds.
|
|
109665
109934
|
|
|
109666
109935
|
`);
|
|
@@ -109688,6 +109957,9 @@ Anyone who reads them can move every derived key's funds.
|
|
|
109688
109957
|
}
|
|
109689
109958
|
deps.stdout.write(`
|
|
109690
109959
|
`);
|
|
109960
|
+
await deps.promptLine("Press Enter when you have saved the 24 words offline; the screen will clear for the check: ");
|
|
109961
|
+
deps.stdout.write("\x1B[2J\x1B[H");
|
|
109962
|
+
notice(deps, "stdout", "warning", "The phrase is no longer on screen. Clear your terminal's scrollback: the words were rendered there and this CLI cannot remove them.");
|
|
109691
109963
|
const positions = randomPositions(3, words.length);
|
|
109692
109964
|
let confirmed = true;
|
|
109693
109965
|
for (const position of positions) {
|
|
@@ -109696,8 +109968,6 @@ Anyone who reads them can move every derived key's funds.
|
|
|
109696
109968
|
confirmed = false;
|
|
109697
109969
|
}
|
|
109698
109970
|
deps.stdout.write("\x1B[2J\x1B[H");
|
|
109699
|
-
notice(deps, "stdout", "warning", `The phrase is no longer on screen. Clear your terminal's scrollback: the words were rendered there and this CLI cannot remove them.
|
|
109700
|
-
`.trim());
|
|
109701
109971
|
notice(deps, "stdout", "warning", `This vault now records that the recovery phrase was exported; that record never resets.
|
|
109702
109972
|
`.trim());
|
|
109703
109973
|
words.fill("");
|
|
@@ -109768,7 +110038,8 @@ async function vaultInit(args, ctx) {
|
|
|
109768
110038
|
strength: strengthFor(own),
|
|
109769
110039
|
rootEntropy,
|
|
109770
110040
|
label: parsed.values["--label"],
|
|
109771
|
-
notice: (line) => deps.stderr.write(line)
|
|
110041
|
+
notice: (line) => deps.stderr.write(line),
|
|
110042
|
+
verificationLabel: "Verifying the vault key"
|
|
109772
110043
|
}, deps));
|
|
109773
110044
|
try {
|
|
109774
110045
|
if (ctx.json) {
|
|
@@ -109798,12 +110069,13 @@ async function vaultInit(args, ctx) {
|
|
|
109798
110069
|
["Verified", "file re-read, opened with your passphrase, root blob decrypted"],
|
|
109799
110070
|
["Create a key", "candle vault new-key --chain solana"]
|
|
109800
110071
|
]);
|
|
109801
|
-
|
|
110072
|
+
if (deps.platform === "darwin")
|
|
110073
|
+
notice(deps, "stdout", "note", `
|
|
109802
110074
|
${APPLE_ACCOUNT_NOTICE}
|
|
109803
110075
|
`.trim());
|
|
109804
110076
|
if (countRecoverableFactors(vault.file.envelopes) === 1) {
|
|
109805
110077
|
notice(deps, "stdout", "warning", `
|
|
109806
|
-
This vault has exactly one recoverable factor: the passphrase. Lose it and no copy of this file can be opened, and the recovery phrase becomes the only route back.
|
|
110078
|
+
This vault has exactly one recoverable factor: the passphrase. Lose it and no copy of this file can be opened, and the recovery phrase becomes the only route back. To add a security key later, run this separate optional command: candle vault enroll security-key
|
|
109807
110079
|
`.trim());
|
|
109808
110080
|
}
|
|
109809
110081
|
notice(deps, "stdout", "note", `
|
|
@@ -110962,13 +111234,12 @@ async function vaultList(args, ctx) {
|
|
|
110962
111234
|
if (balances && "error" in evmRpc)
|
|
110963
111235
|
return usage(ctx, evmRpc.error);
|
|
110964
111236
|
const evmRpcUrl = "url" in evmRpc ? evmRpc.url : DEFAULT_HOOD_RPC_URL;
|
|
110965
|
-
|
|
110966
|
-
return 1;
|
|
111237
|
+
const promptCtx = refusePromptsWithoutTerminal(ctx, "vault list");
|
|
110967
111238
|
return runVaultCommand(ctx, async ({ hold }) => {
|
|
110968
111239
|
const raw = await readVaultRaw(path);
|
|
110969
111240
|
if (raw === null)
|
|
110970
111241
|
throw missingVault(ctx, resolvedVault);
|
|
110971
|
-
const vault = hold((await unlockInteractively(
|
|
111242
|
+
const vault = hold((await unlockInteractively(promptCtx, path, raw, { acceptOlderCopy: parsed.booleans.has("--accept-older-copy") })).vault);
|
|
110972
111243
|
const all = vault.index.entries;
|
|
110973
111244
|
const filtered = filter === undefined ? all : all.filter((entry) => matches2(entry, filter));
|
|
110974
111245
|
const showRetired = parsed.booleans.has("--all");
|
|
@@ -111358,7 +111629,7 @@ async function promoteEvmFresh(input, fromLabel) {
|
|
|
111358
111629
|
addKeys: [blob]
|
|
111359
111630
|
}, ctx.deps));
|
|
111360
111631
|
scanStart = await appendScanStart(ctx, vault.path, address, height);
|
|
111361
|
-
await confirmAddress(ctx, "vault", destination.address, `
|
|
111632
|
+
await confirmAddress(ctx, "vault", destination.address, `promote a new TEE wallet with pinned vault ${destination.address}`);
|
|
111362
111633
|
if (toKey !== undefined) {
|
|
111363
111634
|
notice(ctx.deps, "stderr", "note", `${[toKeyPlanLine(toKey), ...signerStaysLines(toKey), ...targetWarnings(toKey.target, "evm")].join(`
|
|
111364
111635
|
`)}
|
|
@@ -111738,7 +112009,7 @@ async function promoteFresh(ctx, parsed, fromLabel, toKey) {
|
|
|
111738
112009
|
},
|
|
111739
112010
|
addKeys: [blob]
|
|
111740
112011
|
}, ctx.deps));
|
|
111741
|
-
await confirmAddress(ctx, "vault", destination.address, `
|
|
112012
|
+
await confirmAddress(ctx, "vault", destination.address, `promote a new TEE wallet with pinned vault ${destination.address}`);
|
|
111742
112013
|
if (toKey !== undefined) {
|
|
111743
112014
|
notice(ctx.deps, "stderr", "note", `${[toKeyPlanLine(toKey), ...signerStaysLines(toKey), ...targetWarnings(toKey.target, "solana")].join(`
|
|
111744
112015
|
`)}
|
|
@@ -114249,8 +114520,8 @@ init_store();
|
|
|
114249
114520
|
init_vault_support();
|
|
114250
114521
|
var LIST_POINTER = "One line per key, with an optional SOL balance: candle vault list";
|
|
114251
114522
|
var NO_VERIFIED_BACKUP_NOTE = `No backup of this vault has ever been verified from this machine. If this file is lost, only the 24-word recovery phrase can rebuild it, and it rebuilds derived keys only. Take one now:
|
|
114252
|
-
candle vault backup --to <path
|
|
114253
|
-
on a Mac with iCloud Drive: candle vault backup --to icloud
|
|
114523
|
+
candle vault backup --to <path>`;
|
|
114524
|
+
var ICLOUD_BACKUP_LINE = "on a Mac with iCloud Drive: candle vault backup --to icloud";
|
|
114254
114525
|
async function vaultStatus(args, ctx) {
|
|
114255
114526
|
const parsed = parseArgs(args, {
|
|
114256
114527
|
valueFlags: ["--keystore"],
|
|
@@ -114392,7 +114663,8 @@ async function vaultStatus(args, ctx) {
|
|
|
114392
114663
|
notice(deps, "stdout", "note", "No vault.state.json beside this vault, so an older copy of it cannot be recognized on this machine.");
|
|
114393
114664
|
}
|
|
114394
114665
|
if (sidecar?.lastVerifiedBackupAt === undefined)
|
|
114395
|
-
notice(deps, "stdout", "warning", NO_VERIFIED_BACKUP_NOTE
|
|
114666
|
+
notice(deps, "stdout", "warning", deps.platform === "darwin" ? `${NO_VERIFIED_BACKUP_NOTE}
|
|
114667
|
+
${ICLOUD_BACKUP_LINE}` : NO_VERIFIED_BACKUP_NOTE);
|
|
114396
114668
|
if (legacyPresent) {
|
|
114397
114669
|
notice(deps, "stdout", "warning", `A legacy wallets.enc exists at ${legacy}; this CLI does not read it.
|
|
114398
114670
|
A key it holds that was imported is already a linked wallet, managed with: candle wallets
|
|
@@ -114698,7 +114970,7 @@ init_human_output();
|
|
|
114698
114970
|
init_release();
|
|
114699
114971
|
import { dirname as dirname13, join as join15 } from "node:path";
|
|
114700
114972
|
init_key_output();
|
|
114701
|
-
var
|
|
114973
|
+
var USAGE19 = "Usage: candle verify <file> --bundle <path> [--identity <uri>] [--issuer <url>]";
|
|
114702
114974
|
async function resolveIdentity(deps, bundlePath, flag) {
|
|
114703
114975
|
if (flag)
|
|
114704
114976
|
return { kind: "ok", uri: flag, provenance: "identity from --identity" };
|
|
@@ -114730,19 +115002,19 @@ async function verify(args, ctx) {
|
|
|
114730
115002
|
});
|
|
114731
115003
|
if ("error" in parsed) {
|
|
114732
115004
|
writeScreenUsageFailure(deps, `${parsed.error}
|
|
114733
|
-
${
|
|
115005
|
+
${USAGE19}`, json);
|
|
114734
115006
|
return 2;
|
|
114735
115007
|
}
|
|
114736
115008
|
const file = parsed.positionals[0];
|
|
114737
115009
|
if (parsed.positionals.length !== 1 || file === undefined) {
|
|
114738
115010
|
writeScreenUsageFailure(deps, `verify takes exactly one file.
|
|
114739
|
-
${
|
|
115011
|
+
${USAGE19}`, json);
|
|
114740
115012
|
return 2;
|
|
114741
115013
|
}
|
|
114742
115014
|
const bundlePath = parsed.values["--bundle"];
|
|
114743
115015
|
if (!bundlePath) {
|
|
114744
115016
|
writeScreenUsageFailure(deps, `--bundle is required.
|
|
114745
|
-
${
|
|
115017
|
+
${USAGE19}`, json);
|
|
114746
115018
|
return 2;
|
|
114747
115019
|
}
|
|
114748
115020
|
const resolved = await resolveIdentity(deps, bundlePath, parsed.values["--identity"]);
|
|
@@ -114756,7 +115028,7 @@ ${USAGE18}`, json);
|
|
|
114756
115028
|
}
|
|
114757
115029
|
if (resolved.kind === "absent") {
|
|
114758
115030
|
writeScreenUsageFailure(deps, `--identity is required: there is no latest.json beside ${bundlePath} to take the release version from.
|
|
114759
|
-
${
|
|
115031
|
+
${USAGE19}`, json);
|
|
114760
115032
|
return 2;
|
|
114761
115033
|
}
|
|
114762
115034
|
const identity4 = resolved.uri;
|
|
@@ -115006,6 +115278,101 @@ function walletsDisallowLaunch(args, ctx) {
|
|
|
115006
115278
|
return setAllowLaunch(args, ctx, false);
|
|
115007
115279
|
}
|
|
115008
115280
|
|
|
115281
|
+
// src/commands/wallets-allowlist.ts
|
|
115282
|
+
init_args();
|
|
115283
|
+
init_client();
|
|
115284
|
+
init_deps();
|
|
115285
|
+
init_human_output();
|
|
115286
|
+
init_presentation();
|
|
115287
|
+
init_profiles();
|
|
115288
|
+
init_render();
|
|
115289
|
+
init_key_output();
|
|
115290
|
+
var EMPTY_NOTE = "No approved withdrawal addresses. An agent can move funds only to the account's own wallets and its vault.";
|
|
115291
|
+
var MANAGED_HINT = "Approving an address needs the owner signed in to the agent console's Wallets tab; an API key cannot approve. An acting key can revoke one; a read-only key cannot.";
|
|
115292
|
+
async function walletsAllowlist(args, ctx) {
|
|
115293
|
+
const { deps, apiUrl, json } = ctx;
|
|
115294
|
+
const parsed = parseArgs(args, { booleanFlags: ["--all"] });
|
|
115295
|
+
if ("error" in parsed) {
|
|
115296
|
+
writeScreenUsageFailure(deps, parsed.error, json);
|
|
115297
|
+
return 2;
|
|
115298
|
+
}
|
|
115299
|
+
if (parsed.positionals.length > 0) {
|
|
115300
|
+
writeScreenUsageFailure(deps, `Unexpected argument: ${parsed.positionals[0]}`, json);
|
|
115301
|
+
return 2;
|
|
115302
|
+
}
|
|
115303
|
+
await printIdentity(ctx);
|
|
115304
|
+
const apiKey = await resolveApiKey(deps, ctx.profile);
|
|
115305
|
+
if (!apiKey) {
|
|
115306
|
+
writeScreenLocalFailure(deps, { code: "NO_API_KEY", message: "No API key available.", suggestion: "Run: candle keys create" }, json);
|
|
115307
|
+
return 1;
|
|
115308
|
+
}
|
|
115309
|
+
const query3 = parsed.booleans.has("--all") ? "?includeRevoked=true" : "";
|
|
115310
|
+
const result = await apiRequest(`/api/v1/agent/withdrawal-addresses${query3}`, {
|
|
115311
|
+
method: "GET",
|
|
115312
|
+
auth: "key",
|
|
115313
|
+
credentials: { apiKey },
|
|
115314
|
+
apiUrl,
|
|
115315
|
+
fetch: deps.fetch,
|
|
115316
|
+
env: deps.env
|
|
115317
|
+
});
|
|
115318
|
+
if (!result.ok) {
|
|
115319
|
+
writeScreenFailure(deps, result, { apiUrl, authType: "key" }, json);
|
|
115320
|
+
return 1;
|
|
115321
|
+
}
|
|
115322
|
+
const body = result.body;
|
|
115323
|
+
if (!Array.isArray(body.addresses)) {
|
|
115324
|
+
writeScreenLocalFailure(deps, { code: "INVALID_RESPONSE", message: "The withdrawal allowlist read did not return a list." }, json);
|
|
115325
|
+
return 1;
|
|
115326
|
+
}
|
|
115327
|
+
const rows = body.addresses;
|
|
115328
|
+
if (json) {
|
|
115329
|
+
deps.stdout.write(`${JSON.stringify({ success: true, addresses: rows, count: rows.length })}
|
|
115330
|
+
`);
|
|
115331
|
+
return 0;
|
|
115332
|
+
}
|
|
115333
|
+
const style = terminalStyle(deps, "stdout");
|
|
115334
|
+
deps.stdout.write(`${heading(`Withdrawal allowlist (${rows.length})`, style)}
|
|
115335
|
+
|
|
115336
|
+
`);
|
|
115337
|
+
if (rows.length === 0) {
|
|
115338
|
+
notice(deps, "stdout", "note", EMPTY_NOTE);
|
|
115339
|
+
notice(deps, "stdout", "note", MANAGED_HINT);
|
|
115340
|
+
return 0;
|
|
115341
|
+
}
|
|
115342
|
+
if (style.width < 100) {
|
|
115343
|
+
for (const row2 of rows) {
|
|
115344
|
+
writeFields(deps, "stdout", [
|
|
115345
|
+
["Id", row2.id],
|
|
115346
|
+
["Address", row2.address],
|
|
115347
|
+
["Label", row2.label],
|
|
115348
|
+
["Chain", row2.chain],
|
|
115349
|
+
["Revoked", row2.revokedAt ? "yes" : "no"]
|
|
115350
|
+
]);
|
|
115351
|
+
deps.stdout.write(`
|
|
115352
|
+
`);
|
|
115353
|
+
}
|
|
115354
|
+
} else {
|
|
115355
|
+
deps.stdout.write(`${renderTable(["Id", "Chain", "Address", "Label", "Revoked"], rows.map((row2) => [
|
|
115356
|
+
row2.id,
|
|
115357
|
+
row2.chain,
|
|
115358
|
+
row2.address,
|
|
115359
|
+
row2.label ?? symbols(style).missing,
|
|
115360
|
+
row2.revokedAt ? "yes" : "no"
|
|
115361
|
+
]), {
|
|
115362
|
+
style,
|
|
115363
|
+
width: style.width,
|
|
115364
|
+
indent: 2,
|
|
115365
|
+
rule: false,
|
|
115366
|
+
columns: [{ id: true }, { priority: 3 }, { id: true }, { priority: 2 }, { priority: 1 }]
|
|
115367
|
+
})}
|
|
115368
|
+
`);
|
|
115369
|
+
}
|
|
115370
|
+
deps.stdout.write(`
|
|
115371
|
+
`);
|
|
115372
|
+
notice(deps, "stdout", "note", MANAGED_HINT);
|
|
115373
|
+
return 0;
|
|
115374
|
+
}
|
|
115375
|
+
|
|
115009
115376
|
// src/commands/wallets-authorize.ts
|
|
115010
115377
|
init_args();
|
|
115011
115378
|
init_client();
|
|
@@ -115015,7 +115382,7 @@ init_presentation();
|
|
|
115015
115382
|
init_profiles();
|
|
115016
115383
|
init_render();
|
|
115017
115384
|
init_key_output();
|
|
115018
|
-
var
|
|
115385
|
+
var USAGE20 = "Usage: candle wallets authorize [--no-browser] [--no-wait] [--timeout <seconds>] [--json]";
|
|
115019
115386
|
var AUTHORIZE_WAIT_DEFAULT_SECONDS = 600;
|
|
115020
115387
|
var AUTHORIZE_POLL_MS = 3000;
|
|
115021
115388
|
function authorizeUrl(apiUrl, portalOrigin) {
|
|
@@ -115038,12 +115405,12 @@ async function walletsAuthorize(args, ctx) {
|
|
|
115038
115405
|
const parsed = parseArgs(args, { booleanFlags: ["--no-browser", "--no-wait"], valueFlags: ["--timeout"] });
|
|
115039
115406
|
if ("error" in parsed) {
|
|
115040
115407
|
writeScreenUsageFailure(deps, `${parsed.error}
|
|
115041
|
-
${
|
|
115408
|
+
${USAGE20}`, json);
|
|
115042
115409
|
return 2;
|
|
115043
115410
|
}
|
|
115044
115411
|
if (parsed.positionals.length > 0) {
|
|
115045
115412
|
writeScreenUsageFailure(deps, `Unexpected argument: ${parsed.positionals[0]}
|
|
115046
|
-
${
|
|
115413
|
+
${USAGE20}`, json);
|
|
115047
115414
|
return 2;
|
|
115048
115415
|
}
|
|
115049
115416
|
let timeoutSeconds = AUTHORIZE_WAIT_DEFAULT_SECONDS;
|
|
@@ -115052,7 +115419,7 @@ ${USAGE19}`, json);
|
|
|
115052
115419
|
const n = Number(rawTimeout);
|
|
115053
115420
|
if (!Number.isInteger(n) || n <= 0) {
|
|
115054
115421
|
writeScreenUsageFailure(deps, `--timeout must be a whole number of seconds above 0, got ${rawTimeout}
|
|
115055
|
-
${
|
|
115422
|
+
${USAGE20}`, json);
|
|
115056
115423
|
return 2;
|
|
115057
115424
|
}
|
|
115058
115425
|
timeoutSeconds = n;
|
|
@@ -115159,7 +115526,7 @@ init_solana_lite();
|
|
|
115159
115526
|
init_trading();
|
|
115160
115527
|
init_swap();
|
|
115161
115528
|
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
115162
|
-
var
|
|
115529
|
+
var USAGE21 = "Usage: candle wallet close-empty [--wallet embedded|<tee address|label>] [--keep <mint>]... [--client-trade-id <id>] [--preview] [--yes] [--json]";
|
|
115163
115530
|
var BASE58_ADDRESS2 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
115164
115531
|
var LISTED_ROWS = 50;
|
|
115165
115532
|
var TOKEN_IX_CLOSE_ACCOUNT3 = 9;
|
|
@@ -115434,7 +115801,7 @@ async function closeTee(ctx, key, wallet3, name2, run2) {
|
|
|
115434
115801
|
async function walletsCloseEmpty(args, ctx) {
|
|
115435
115802
|
const lifted = splitKeep(args);
|
|
115436
115803
|
if ("error" in lifted) {
|
|
115437
|
-
writeUsageFailure(ctx.deps, `${lifted.error}. ${
|
|
115804
|
+
writeUsageFailure(ctx.deps, `${lifted.error}. ${USAGE21}`, ctx.json);
|
|
115438
115805
|
return 2;
|
|
115439
115806
|
}
|
|
115440
115807
|
const parsed = parseArgs(lifted.rest, {
|
|
@@ -115448,7 +115815,7 @@ async function walletsCloseEmpty(args, ctx) {
|
|
|
115448
115815
|
const named = parsed.values["--client-trade-id"];
|
|
115449
115816
|
const id = named ?? `close-${randomUUID16()}`;
|
|
115450
115817
|
if (parsed.positionals.length !== 0 || !validClientId(id) || !lifted.keep.every((m) => BASE58_ADDRESS2.test(m))) {
|
|
115451
|
-
writeUsageFailure(ctx.deps,
|
|
115818
|
+
writeUsageFailure(ctx.deps, USAGE21, ctx.json);
|
|
115452
115819
|
return 2;
|
|
115453
115820
|
}
|
|
115454
115821
|
const run2 = {
|
|
@@ -115708,6 +116075,7 @@ init_ownership();
|
|
|
115708
116075
|
init_file_lock();
|
|
115709
116076
|
|
|
115710
116077
|
// src/guard.ts
|
|
116078
|
+
init_account();
|
|
115711
116079
|
init_profiles();
|
|
115712
116080
|
async function verifyProfileAccount(ctx, config) {
|
|
115713
116081
|
const { deps, profile } = ctx;
|
|
@@ -115956,6 +116324,7 @@ var COMMANDS = {
|
|
|
115956
116324
|
access: keysAccess,
|
|
115957
116325
|
update: keysUpdate,
|
|
115958
116326
|
limit: keysLimit,
|
|
116327
|
+
limits: keysLimits,
|
|
115959
116328
|
self: keysSelf,
|
|
115960
116329
|
rotate: keysRotate,
|
|
115961
116330
|
revoke: keysRevoke,
|
|
@@ -115974,7 +116343,8 @@ var COMMANDS = {
|
|
|
115974
116343
|
"allow-launch": walletsAllowLaunch,
|
|
115975
116344
|
"disallow-launch": walletsDisallowLaunch,
|
|
115976
116345
|
"close-empty": walletsCloseEmpty,
|
|
115977
|
-
authorize: walletsAuthorize
|
|
116346
|
+
authorize: walletsAuthorize,
|
|
116347
|
+
allowlist: walletsAllowlist
|
|
115978
116348
|
},
|
|
115979
116349
|
bare: wallets
|
|
115980
116350
|
},
|