@candledottv/cli 0.11.3 → 0.11.4
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/dist/index.js +345 -37
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2254,7 +2254,12 @@ var init_errors = __esm(() => {
|
|
|
2254
2254
|
"SIGN_SIMULATION_FAILED",
|
|
2255
2255
|
"SIGN_LOOKUP_TABLE_UNRESOLVED",
|
|
2256
2256
|
"SIGN_TRANSACTION_UNDECODABLE",
|
|
2257
|
-
"SIGN_BROADCAST_FAILED"
|
|
2257
|
+
"SIGN_BROADCAST_FAILED",
|
|
2258
|
+
"VAULT_LABEL_NOT_FOUND",
|
|
2259
|
+
"VAULT_LABEL_AMBIGUOUS",
|
|
2260
|
+
"VAULT_LABEL_TAKEN",
|
|
2261
|
+
"VAULT_LABEL_UNCHANGED",
|
|
2262
|
+
"VAULT_RENAME_ROLE_REFUSED"
|
|
2258
2263
|
];
|
|
2259
2264
|
VaultError = class VaultError extends Error {
|
|
2260
2265
|
code;
|
|
@@ -14929,6 +14934,7 @@ __export(exports_vault_support, {
|
|
|
14929
14934
|
missingVault: () => missingVault,
|
|
14930
14935
|
findExternalEntry: () => findExternalEntry,
|
|
14931
14936
|
describeRole: () => describeRole,
|
|
14937
|
+
derivationNotice: () => derivationNotice,
|
|
14932
14938
|
confirmLastSix: () => confirmLastSix,
|
|
14933
14939
|
assertVaultHelperIdentities: () => assertVaultHelperIdentities,
|
|
14934
14940
|
assertNotOlderCopy: () => assertNotOlderCopy,
|
|
@@ -15017,6 +15023,13 @@ async function requireVaultRaw(ctx, resolved) {
|
|
|
15017
15023
|
throw missingVault(ctx, resolved);
|
|
15018
15024
|
return raw;
|
|
15019
15025
|
}
|
|
15026
|
+
function derivationNotice(line, purpose) {
|
|
15027
|
+
if (purpose === undefined)
|
|
15028
|
+
return line;
|
|
15029
|
+
return line.endsWith(`
|
|
15030
|
+
`) ? `${line.slice(0, -1)} -- ${purpose}
|
|
15031
|
+
` : `${line} -- ${purpose}`;
|
|
15032
|
+
}
|
|
15020
15033
|
function assertVaultHelperIdentities(deps, envelopes) {
|
|
15021
15034
|
if (deps.releasePolicy.macosHelper.release === "signed") {
|
|
15022
15035
|
for (const envelope of envelopes) {
|
|
@@ -15033,12 +15046,13 @@ async function unlockInteractively(ctx, path, raw, opts = {}) {
|
|
|
15033
15046
|
assertVaultHelperIdentities(deps, file.envelopes);
|
|
15034
15047
|
const facts = await currentPlatformFacts(deps);
|
|
15035
15048
|
const choice = await chooseFactor(ctx, file.envelopes, facts, opts.factor ?? ctx.vaultFactor);
|
|
15036
|
-
const
|
|
15049
|
+
const noticeFor = (purpose) => (line) => deps.stderr.write(derivationNotice(line, purpose));
|
|
15050
|
+
const notice = noticeFor(opts.purpose);
|
|
15037
15051
|
if (choice.kind === "passphrase") {
|
|
15038
15052
|
const typed = await deps.promptSecret(opts.promptText ?? "Vault passphrase (input hidden): ");
|
|
15039
|
-
const openWith = (p, r, candidate) => choice.envelopeId === undefined ? unlockWithPassphrase(p, r, candidate, { notice }) : unlockVault(p, r, { factor: "passphrase", passphrase: candidate, envelopeId: choice.envelopeId }, { notice });
|
|
15040
|
-
const { vault: vault2, passphrase } = await openWithTypedPassphrase(typed, (candidate) => openWith(path, raw, candidate));
|
|
15041
|
-
const open4 = (p, r) => openWith(p, r, passphrase);
|
|
15053
|
+
const openWith = (p, r, candidate, purpose) => choice.envelopeId === undefined ? unlockWithPassphrase(p, r, candidate, { notice: noticeFor(purpose) }) : unlockVault(p, r, { factor: "passphrase", passphrase: candidate, envelopeId: choice.envelopeId }, { notice: noticeFor(purpose) });
|
|
15054
|
+
const { vault: vault2, passphrase } = await openWithTypedPassphrase(typed, (candidate) => openWith(path, raw, candidate, opts.purpose));
|
|
15055
|
+
const open4 = (p, r, purpose) => openWith(p, r, passphrase, purpose);
|
|
15042
15056
|
return {
|
|
15043
15057
|
vault: vault2,
|
|
15044
15058
|
factor: { kind: "passphrase", envelopeId: vault2.envelope.id },
|
|
@@ -35788,7 +35802,7 @@ async function resolveApiKey(deps, profile) {
|
|
|
35788
35802
|
init_render();
|
|
35789
35803
|
|
|
35790
35804
|
// src/version.ts
|
|
35791
|
-
var CLI_VERSION = "0.11.
|
|
35805
|
+
var CLI_VERSION = "0.11.4";
|
|
35792
35806
|
|
|
35793
35807
|
// src/commands/auth.ts
|
|
35794
35808
|
var DEVICE_CODE_PATH = "/api/v1/agent/device/code";
|
|
@@ -36351,7 +36365,11 @@ var HELP = {
|
|
|
36351
36365
|
{ invocation: "status [--unlock]", description: "What the vault holds, and what opens it" },
|
|
36352
36366
|
{
|
|
36353
36367
|
invocation: "new-key --chain solana [--label <name>] [--count <n>] [--labels-from <file>]",
|
|
36354
|
-
description: "Derive the next Solana key, or n of them under one unlock"
|
|
36368
|
+
description: "Derive the next Solana key, or n of them under one unlock; the name must be free"
|
|
36369
|
+
},
|
|
36370
|
+
{
|
|
36371
|
+
invocation: "rename <label|address> <new-label> [--id <entry-id>]",
|
|
36372
|
+
description: "Rename one key. The address, the derivation and the key blob do not change"
|
|
36355
36373
|
},
|
|
36356
36374
|
{ invocation: "phrase show", description: "Show the 24-word recovery phrase (terminal only)" },
|
|
36357
36375
|
{
|
|
@@ -36416,6 +36434,7 @@ var HELP = {
|
|
|
36416
36434
|
examples: [
|
|
36417
36435
|
"candle vault init",
|
|
36418
36436
|
"candle vault new-key --chain solana --label treasury",
|
|
36437
|
+
"candle vault rename key-7 treasury-cold",
|
|
36419
36438
|
"candle vault new-key --chain solana --labels-from ./replacement-names.txt",
|
|
36420
36439
|
"candle vault enroll security-key --label yubikey-a",
|
|
36421
36440
|
"candle vault backup --to /Volumes/BACKUP/vault.enc",
|
|
@@ -41351,12 +41370,9 @@ async function vaultNewKey(args, ctx) {
|
|
|
41351
41370
|
suggestion: "Create a second vault with a fresh root (`candle vault init`) and move the funds across with `candle vault transfer`. There is no flag for this: no fact you could assert would make the old boundary known."
|
|
41352
41371
|
});
|
|
41353
41372
|
}
|
|
41354
|
-
|
|
41355
|
-
|
|
41356
|
-
|
|
41357
|
-
if (clash !== undefined) {
|
|
41358
|
-
return usage(ctx, `A key labelled ${clash} already exists in this vault; every --labels-from name must be new.`);
|
|
41359
|
-
}
|
|
41373
|
+
const clash = labelClash(vault.index, plannedLabels(vault.index.hd, batch, parsed.values["--label"]));
|
|
41374
|
+
if (clash !== undefined) {
|
|
41375
|
+
return usage(ctx, batch.labels !== undefined ? `A key labelled ${clash} already exists in this vault; every --labels-from name must be new.` : parsed.values["--label"] !== undefined ? `A key labelled ${clash} already exists in this vault; choose another --label.` : `A key labelled ${clash} already exists in this vault; pass --label <name> to choose a different name for this key.`);
|
|
41360
41376
|
}
|
|
41361
41377
|
const derived = [];
|
|
41362
41378
|
let last;
|
|
@@ -41452,6 +41468,26 @@ ${derived.length} of ${requested} keys were created and ARE in the vault; the va
|
|
|
41452
41468
|
` + `Re-run for the remaining ${requested - derived.length}${derived.length > 0 ? " (with a --labels-from file holding the names that did not land)" : ""}.
|
|
41453
41469
|
`);
|
|
41454
41470
|
}
|
|
41471
|
+
function plannedLabels(hd, batch, labelFlag) {
|
|
41472
|
+
const labels = [];
|
|
41473
|
+
let counter = hd.nextIndex.solanaVault;
|
|
41474
|
+
for (let made = 0;made < batch.count; made++) {
|
|
41475
|
+
const index = nextAllocatableIndex(counter, hd.exposedIndexes.solanaVault);
|
|
41476
|
+
labels.push(batch.labels?.[made] ?? labelFlag ?? `key-${index}`);
|
|
41477
|
+
counter = index + 1;
|
|
41478
|
+
}
|
|
41479
|
+
return labels;
|
|
41480
|
+
}
|
|
41481
|
+
function labelClash(index, planned) {
|
|
41482
|
+
const taken = new Set(index.entries.map((entry) => entry.label));
|
|
41483
|
+
const seen = new Set;
|
|
41484
|
+
for (const label of planned) {
|
|
41485
|
+
if (taken.has(label) || seen.has(label))
|
|
41486
|
+
return label;
|
|
41487
|
+
seen.add(label);
|
|
41488
|
+
}
|
|
41489
|
+
return;
|
|
41490
|
+
}
|
|
41455
41491
|
function nextAllocatableIndex(counter, exposed) {
|
|
41456
41492
|
let index = counter;
|
|
41457
41493
|
while (exposed.includes(index))
|
|
@@ -42259,6 +42295,9 @@ var walletPageSchema = exports_external.object({
|
|
|
42259
42295
|
isDone: exports_external.boolean(),
|
|
42260
42296
|
continueCursor: exports_external.string().nullable().optional()
|
|
42261
42297
|
});
|
|
42298
|
+
var embeddedSchema = exports_external.object({
|
|
42299
|
+
wallets: exports_external.object({ solana: exports_external.object({ address: exports_external.string() }).passthrough().nullable().optional() }).passthrough().optional()
|
|
42300
|
+
}).passthrough();
|
|
42262
42301
|
var operationSchema = exports_external.object({ job: exports_external.object({ status: exports_external.string() }).passthrough() }).passthrough();
|
|
42263
42302
|
var BASES = {
|
|
42264
42303
|
SOL: { mint: "So11111111111111111111111111111111111111112", decimals: 9 },
|
|
@@ -42313,9 +42352,9 @@ async function request(ctx, key, path, body) {
|
|
|
42313
42352
|
throw new TradingError("INVALID_RESPONSE", "Candle returned an invalid response.");
|
|
42314
42353
|
return result.body;
|
|
42315
42354
|
}
|
|
42316
|
-
async function
|
|
42355
|
+
async function teeWallets(ctx, key, scope) {
|
|
42317
42356
|
let cursor;
|
|
42318
|
-
const
|
|
42357
|
+
const rows = [];
|
|
42319
42358
|
const cursors = new Set;
|
|
42320
42359
|
let appId = "";
|
|
42321
42360
|
for (;; ) {
|
|
@@ -42325,7 +42364,7 @@ async function tradingWallet(ctx, key, name, scope) {
|
|
|
42325
42364
|
appId = response.privyAppId ?? "";
|
|
42326
42365
|
if (!Array.isArray(response.page))
|
|
42327
42366
|
throw new TradingError("INVALID_RESPONSE", "Wallet discovery did not return a page.");
|
|
42328
|
-
|
|
42367
|
+
rows.push(...response.page);
|
|
42329
42368
|
if (response.isDone === true)
|
|
42330
42369
|
break;
|
|
42331
42370
|
cursor = response.continueCursor ?? undefined;
|
|
@@ -42333,9 +42372,18 @@ async function tradingWallet(ctx, key, name, scope) {
|
|
|
42333
42372
|
throw new TradingError("INVALID_RESPONSE", "Wallet discovery did not complete.");
|
|
42334
42373
|
cursors.add(cursor);
|
|
42335
42374
|
}
|
|
42336
|
-
|
|
42337
|
-
|
|
42338
|
-
|
|
42375
|
+
return { rows, appId };
|
|
42376
|
+
}
|
|
42377
|
+
function matchesName(row, name) {
|
|
42378
|
+
return row.id === name || row.address === name || row.label === name;
|
|
42379
|
+
}
|
|
42380
|
+
function describeWallet(row) {
|
|
42381
|
+
return `${row.label ? `${row.label} ` : ""}(${row.id}, ${row.address})`;
|
|
42382
|
+
}
|
|
42383
|
+
function teeWalletList(rows) {
|
|
42384
|
+
return rows.map(describeWallet).join("; ");
|
|
42385
|
+
}
|
|
42386
|
+
async function completeTradingWallet(ctx, row, appId, scope) {
|
|
42339
42387
|
if (!row.active || row.chain !== "solana")
|
|
42340
42388
|
throw new TradingError("TEE_WALLET_INACTIVE", "The payer must be a verified-active Solana TEE wallet.");
|
|
42341
42389
|
if (scope === "launch:write" && row.allowLaunch !== true)
|
|
@@ -42353,6 +42401,37 @@ async function tradingWallet(ctx, key, name, scope) {
|
|
|
42353
42401
|
signer: storedSignerToPem(signer)
|
|
42354
42402
|
};
|
|
42355
42403
|
}
|
|
42404
|
+
async function tradingWallet(ctx, key, name, scope) {
|
|
42405
|
+
const { rows, appId } = await teeWallets(ctx, key, scope);
|
|
42406
|
+
const matches = rows.filter((row) => matchesName(row, name));
|
|
42407
|
+
if (matches.length !== 1) {
|
|
42408
|
+
throw new TradingError("TEE_WALLET_REQUIRED", matches.length > 1 ? `"${name}" matches ${matches.length} TEE wallets on this key: ${teeWalletList(matches)}. Name one by id or address.` : rows.length === 0 ? "This key has no TEE wallets bound to it. Enrol one, or select the profile whose key holds it." : `No TEE wallet on this key is called "${name}". Bound to this key: ${teeWalletList(rows)}.`);
|
|
42409
|
+
}
|
|
42410
|
+
return await completeTradingWallet(ctx, matches[0], appId, scope);
|
|
42411
|
+
}
|
|
42412
|
+
async function tradingPayer(ctx, key, name) {
|
|
42413
|
+
const scope = "swap:write";
|
|
42414
|
+
const { rows, appId } = await teeWallets(ctx, key, scope);
|
|
42415
|
+
const embedded = embeddedSchema.parse(await request(ctx, key, "/api/v1/agent/wallets/embedded")).wallets?.solana?.address;
|
|
42416
|
+
const asEmbedded = () => ({ kind: "embedded", address: embedded });
|
|
42417
|
+
const options = [
|
|
42418
|
+
...rows.map((row) => `TEE ${describeWallet(row)}`),
|
|
42419
|
+
...embedded ? [`embedded (${embedded})`] : []
|
|
42420
|
+
].join("; ");
|
|
42421
|
+
if (name === undefined) {
|
|
42422
|
+
if (rows.length === 1 && !embedded)
|
|
42423
|
+
return { kind: "tee", wallet: await completeTradingWallet(ctx, rows[0], appId, scope) };
|
|
42424
|
+
if (rows.length === 0 && embedded)
|
|
42425
|
+
return asEmbedded();
|
|
42426
|
+
throw new TradingError("PAYER_REQUIRED", options.length === 0 ? "This account has no wallet that can pay for a swap. Enrol a TEE wallet, or create an embedded wallet in the app." : `Name the payer with --wallet. This account can pay from: ${options}.`);
|
|
42427
|
+
}
|
|
42428
|
+
const matches = rows.filter((row) => matchesName(row, name));
|
|
42429
|
+
if (matches.length === 1)
|
|
42430
|
+
return { kind: "tee", wallet: await completeTradingWallet(ctx, matches[0], appId, scope) };
|
|
42431
|
+
if (matches.length === 0 && embedded === name)
|
|
42432
|
+
return asEmbedded();
|
|
42433
|
+
throw new TradingError("TEE_WALLET_REQUIRED", matches.length > 1 ? `"${name}" matches ${matches.length} TEE wallets on this key: ${teeWalletList(matches)}. Name one by id or address.` : options.length === 0 ? `"${name}" is not a wallet this account can pay from, and it has none: enrol a TEE wallet, or create an embedded wallet in the app.` : `"${name}" is not a wallet this account can pay from. It can pay from: ${options}.`);
|
|
42434
|
+
}
|
|
42356
42435
|
function authorizationSignature(wallet, transaction) {
|
|
42357
42436
|
const body = { method: "signTransaction", params: { encoding: "base64", transaction } };
|
|
42358
42437
|
const payload = JSON.stringify({
|
|
@@ -42550,6 +42629,16 @@ async function decimalsFor(ctx, asset, url) {
|
|
|
42550
42629
|
throw new TradingError("INVALID_RESPONSE", "RPC returned invalid mint decimals.");
|
|
42551
42630
|
return decimals;
|
|
42552
42631
|
}
|
|
42632
|
+
async function assertDeferredExecuteSupported(ctx, key, id) {
|
|
42633
|
+
try {
|
|
42634
|
+
await request(ctx, key, "/api/v1/trade/agent/execute", { clientTradeId: id });
|
|
42635
|
+
} catch (error) {
|
|
42636
|
+
if (error instanceof TradingError && error.code === "JOB_NOT_FOUND")
|
|
42637
|
+
return;
|
|
42638
|
+
throw new TradingError("EMBEDDED_PAYER_UNSUPPORTED", "This Candle deployment cannot hold an embedded-wallet trade back for confirmation, so the quote could not be shown before the money moved. Nothing was built. Name a TEE wallet with --wallet, or point at a deployment that has it.");
|
|
42639
|
+
}
|
|
42640
|
+
throw new TradingError("INVALID_RESPONSE", "The execute route answered for a trade that does not exist.");
|
|
42641
|
+
}
|
|
42553
42642
|
async function swap(args, ctx) {
|
|
42554
42643
|
const parsed = parseArgs(args, {
|
|
42555
42644
|
valueFlags: ["--amount", "--percent", "--wallet", "--client-trade-id", "--slippage-bps", "--rpc-url"],
|
|
@@ -42562,8 +42651,8 @@ async function swap(args, ctx) {
|
|
|
42562
42651
|
const flags = parsed.values;
|
|
42563
42652
|
const id = flags["--client-trade-id"] ?? `swap-${randomUUID()}`;
|
|
42564
42653
|
const slippage = Number(flags["--slippage-bps"] ?? "50");
|
|
42565
|
-
if (parsed.positionals.length !== 2 ||
|
|
42566
|
-
writeUsageFailure(ctx.deps, "Usage: candle swap <from> <to> --amount <decimal> | --percent <n> --wallet <tee> [--client-trade-id <id>] [--slippage-bps 50] [--rpc-url <url>] [--yes]", ctx.json);
|
|
42654
|
+
if (parsed.positionals.length !== 2 || Boolean(flags["--amount"]) === Boolean(flags["--percent"]) || !validClientId(id) || !Number.isInteger(slippage) || slippage < 0 || slippage > 1e4) {
|
|
42655
|
+
writeUsageFailure(ctx.deps, "Usage: candle swap <from> <to> --amount <decimal> | --percent <n> [--wallet <tee-or-embedded>] [--client-trade-id <id>] [--slippage-bps 50] [--rpc-url <url>] [--yes]", ctx.json);
|
|
42567
42656
|
return 2;
|
|
42568
42657
|
}
|
|
42569
42658
|
try {
|
|
@@ -42585,7 +42674,10 @@ async function swap(args, ctx) {
|
|
|
42585
42674
|
const prior = await lookupOperation(ctx, key, id, kind);
|
|
42586
42675
|
if (prior)
|
|
42587
42676
|
return printTradingResult(ctx, prior);
|
|
42588
|
-
const
|
|
42677
|
+
const payerWallet = await tradingPayer(ctx, key, flags["--wallet"]);
|
|
42678
|
+
if (payerWallet.kind === "embedded" && kind === "swap")
|
|
42679
|
+
throw new TradingError("PAIR_UNSUPPORTED", "The embedded wallet cannot swap between base assets from this command yet: that rail executes in one call, so there would be nothing to confirm. Trade a token with it, or name a TEE wallet for a base pair.");
|
|
42680
|
+
const wallet = payerWallet.kind === "tee" ? payerWallet.wallet : { address: payerWallet.address };
|
|
42589
42681
|
const decimals = await decimalsFor(ctx, from, flags["--rpc-url"]);
|
|
42590
42682
|
const outDecimals = await decimalsFor(ctx, to, flags["--rpc-url"]);
|
|
42591
42683
|
let amountRaw;
|
|
@@ -42609,11 +42701,13 @@ async function swap(args, ctx) {
|
|
|
42609
42701
|
amountRaw = rawAmount(flags["--amount"], decimals);
|
|
42610
42702
|
if (BigInt(amountRaw) > BigInt(Number.MAX_SAFE_INTEGER))
|
|
42611
42703
|
throw new TradingError("INVALID_AMOUNT", "Amount exceeds the venue's exact integer range.");
|
|
42704
|
+
if (payerWallet.kind === "embedded")
|
|
42705
|
+
await assertDeferredExecuteSupported(ctx, key, id);
|
|
42612
42706
|
if (!await claimOperation(ctx, key, id, kind))
|
|
42613
42707
|
throw new TradingError("OPERATION_ALREADY_STARTED", "This machine already started this id; no write was resent.");
|
|
42614
42708
|
ctx.deps.stderr.write(`Operation: ${id}
|
|
42615
42709
|
`);
|
|
42616
|
-
const payer = { type: "linked", linkedWalletId: wallet.id };
|
|
42710
|
+
const payer = payerWallet.kind === "tee" ? { type: "linked", linkedWalletId: payerWallet.wallet.id } : { type: "main" };
|
|
42617
42711
|
const built = kind === "swap" ? await request(ctx, key, "/api/v1/agent/swap/build", {
|
|
42618
42712
|
clientTradeId: id,
|
|
42619
42713
|
from,
|
|
@@ -42629,7 +42723,8 @@ async function swap(args, ctx) {
|
|
|
42629
42723
|
quoteAsset: (fromBase ?? toBase)?.toLowerCase(),
|
|
42630
42724
|
amountRaw,
|
|
42631
42725
|
maxSlippageBps: slippage,
|
|
42632
|
-
payer
|
|
42726
|
+
payer,
|
|
42727
|
+
...payerWallet.kind === "embedded" ? { deferExecution: true } : {}
|
|
42633
42728
|
});
|
|
42634
42729
|
if (built.job || built.status === "executed")
|
|
42635
42730
|
return printTradingResult(ctx, { ...built, clientTradeId: id, kind });
|
|
@@ -42659,14 +42754,24 @@ async function swap(args, ctx) {
|
|
|
42659
42754
|
};
|
|
42660
42755
|
if (!await confirmQuote(ctx, quote, parsed.booleans.has("--yes")))
|
|
42661
42756
|
return printTradingResult(ctx, { success: true, status: "cancelled", clientTradeId: id, kind, quote });
|
|
42757
|
+
if (!Number.isFinite(data.expiresAt) || data.expiresAt <= ctx.deps.now())
|
|
42758
|
+
throw new TradingError("QUOTE_EXPIRED", "The quote expired before signing. Start a new intention with a new id.");
|
|
42759
|
+
if (payerWallet.kind === "embedded") {
|
|
42760
|
+
const executed = await request(ctx, key, "/api/v1/trade/agent/execute", { clientTradeId: id });
|
|
42761
|
+
return printTradingResult(ctx, {
|
|
42762
|
+
...executed,
|
|
42763
|
+
clientTradeId: id,
|
|
42764
|
+
kind,
|
|
42765
|
+
quote,
|
|
42766
|
+
wallet: safeText(payerWallet.address)
|
|
42767
|
+
});
|
|
42768
|
+
}
|
|
42662
42769
|
const transaction = kind === "swap" ? data.transactionsBase64?.[0] : artifacts.transactionBase64;
|
|
42663
42770
|
if (kind === "swap" && data.transactionsBase64?.length !== 1)
|
|
42664
42771
|
throw new TradingError("INVALID_RESPONSE", "A TEE swap must contain exactly one same-chain transaction.");
|
|
42665
|
-
if (!Number.isFinite(data.expiresAt) || data.expiresAt <= ctx.deps.now())
|
|
42666
|
-
throw new TradingError("QUOTE_EXPIRED", "The quote expired before signing. Start a new intention with a new id.");
|
|
42667
42772
|
if (!transaction)
|
|
42668
42773
|
throw new TradingError("INVALID_RESPONSE", "Missing transaction.");
|
|
42669
|
-
const signed = await relaySign(ctx, key, wallet, transaction);
|
|
42774
|
+
const signed = await relaySign(ctx, key, payerWallet.wallet, transaction);
|
|
42670
42775
|
const result = kind === "swap" ? await request(ctx, key, "/api/v1/agent/swap/submit", {
|
|
42671
42776
|
clientTradeId: id,
|
|
42672
42777
|
swapId: data.swapId,
|
|
@@ -47627,6 +47732,10 @@ init_wallet_keystore();
|
|
|
47627
47732
|
init_esm();
|
|
47628
47733
|
init_args();
|
|
47629
47734
|
init_render();
|
|
47735
|
+
var TEE_PROFILES = new Set(["ember-tee", "ember-hot"]);
|
|
47736
|
+
function isTeeRow(row) {
|
|
47737
|
+
return row.profile !== undefined && TEE_PROFILES.has(row.profile);
|
|
47738
|
+
}
|
|
47630
47739
|
var LINKED_WALLETS_PAGE_LIMIT = 100;
|
|
47631
47740
|
var LINKED_WALLETS_PAGE_CAP = 25;
|
|
47632
47741
|
async function readAllLinkedWallets(args) {
|
|
@@ -47670,6 +47779,9 @@ function incompleteNotice(listing) {
|
|
|
47670
47779
|
var NONE_HINT = `A wallet marked none has no signer on this machine, so a trade from here cannot sign with it.
|
|
47671
47780
|
` + `Import it here (candle wallets import), or run the trade from the machine that imported it.
|
|
47672
47781
|
`;
|
|
47782
|
+
var TEE_HINT = `A wallet marked tee is a TEE trading wallet: pass its id, address or label to candle swap --wallet.
|
|
47783
|
+
` + `This account's embedded wallet, shown above, can pay for a token trade too.
|
|
47784
|
+
`;
|
|
47673
47785
|
var STALE_HINT = `A wallet marked stale is revoked but its signer is still stored here. Run: candle wallets revoke <id>
|
|
47674
47786
|
`;
|
|
47675
47787
|
async function probeSignerStates(rows, store) {
|
|
@@ -47776,20 +47888,24 @@ Linked wallets (${linkedRows.length}):
|
|
|
47776
47888
|
`);
|
|
47777
47889
|
} else {
|
|
47778
47890
|
const cells = linkedRows.map((wallet) => signerCell(signerStates.get(wallet._id), wallet));
|
|
47779
|
-
deps.stdout.write(`${renderTable(["Id", "Wallet", "Address", "Label", "Revoked", "Signer"], linkedRows.map((wallet, index) => [
|
|
47891
|
+
deps.stdout.write(`${renderTable(["Id", "Wallet", "Address", "Label", "Kind", "Revoked", "Signer"], linkedRows.map((wallet, index) => [
|
|
47780
47892
|
wallet._id,
|
|
47781
47893
|
wallet.chain,
|
|
47782
47894
|
wallet.address,
|
|
47783
47895
|
wallet.label ?? "-",
|
|
47896
|
+
isTeeRow(wallet) ? "tee" : "linked",
|
|
47784
47897
|
wallet.revokedAt ? "yes" : "no",
|
|
47785
47898
|
cells[index] ?? "-"
|
|
47786
47899
|
]))}
|
|
47787
47900
|
`);
|
|
47788
47901
|
const anyNone = cells.includes("none");
|
|
47789
47902
|
const anyStale = cells.includes("stale");
|
|
47790
|
-
|
|
47903
|
+
const anyTee = linkedRows.some(isTeeRow);
|
|
47904
|
+
if (anyNone || anyStale || anyTee)
|
|
47791
47905
|
deps.stdout.write(`
|
|
47792
47906
|
`);
|
|
47907
|
+
if (anyTee)
|
|
47908
|
+
deps.stdout.write(TEE_HINT);
|
|
47793
47909
|
if (anyNone)
|
|
47794
47910
|
deps.stdout.write(NONE_HINT);
|
|
47795
47911
|
if (anyStale)
|
|
@@ -50261,7 +50377,8 @@ async function vaultBackup(args, ctx) {
|
|
|
50261
50377
|
}
|
|
50262
50378
|
const opened = await unlockInteractively(ctx, path, raw, {
|
|
50263
50379
|
acceptOlderCopy: parsed.booleans.has("--accept-older-copy"),
|
|
50264
|
-
...verdict.sealed ? { factor: "passphrase" } : {}
|
|
50380
|
+
...verdict.sealed ? { factor: "passphrase" } : {},
|
|
50381
|
+
purpose: "opening the vault"
|
|
50265
50382
|
});
|
|
50266
50383
|
const live = hold(opened.vault);
|
|
50267
50384
|
if (verdict.sealed) {
|
|
@@ -50273,7 +50390,8 @@ async function vaultBackup(args, ctx) {
|
|
|
50273
50390
|
throw copyWriteFailed(destination, error, "copy");
|
|
50274
50391
|
}
|
|
50275
50392
|
}
|
|
50276
|
-
const
|
|
50393
|
+
const written = await stat3(destination);
|
|
50394
|
+
const report = await verifyCopy(ctx, destination, opened.reopen, live, "re-opening the copy to verify it");
|
|
50277
50395
|
const sidecar = sidecarPath(path);
|
|
50278
50396
|
await writeSidecar(sidecar, {
|
|
50279
50397
|
...nextSidecar(await readSidecar(sidecar), live.file),
|
|
@@ -50283,6 +50401,7 @@ async function vaultBackup(args, ctx) {
|
|
|
50283
50401
|
...verdict.sharedDomainAccepted ? { lastBackupSharedDomainAccepted: true } : {}
|
|
50284
50402
|
});
|
|
50285
50403
|
const copyEnvelopes = verdict.sealed ? sealedEnvelopes(live.file.envelopes) : live.file.envelopes;
|
|
50404
|
+
const mode = fileModeOctal(written.mode);
|
|
50286
50405
|
if (ctx.json) {
|
|
50287
50406
|
writeJson(deps, {
|
|
50288
50407
|
ok: true,
|
|
@@ -50294,14 +50413,25 @@ async function vaultBackup(args, ctx) {
|
|
|
50294
50413
|
sharedDomainAccepted: verdict.sharedDomainAccepted,
|
|
50295
50414
|
sharedDomain: verdict.sharedDomain,
|
|
50296
50415
|
verified: true,
|
|
50297
|
-
...reportJson(report, live)
|
|
50416
|
+
...reportJson(report, live),
|
|
50417
|
+
bytesWritten: written.size,
|
|
50418
|
+
mode
|
|
50298
50419
|
});
|
|
50299
50420
|
return 0;
|
|
50300
50421
|
}
|
|
50422
|
+
for (const line of wroteLines(destination, written.size, mode))
|
|
50423
|
+
deps.stdout.write(`${line}
|
|
50424
|
+
`);
|
|
50301
50425
|
writeVerifiedReport(ctx, destination, verdict, report, live);
|
|
50302
50426
|
return 0;
|
|
50303
50427
|
});
|
|
50304
50428
|
}
|
|
50429
|
+
function fileModeOctal(mode) {
|
|
50430
|
+
return (mode & 511).toString(8).padStart(4, "0");
|
|
50431
|
+
}
|
|
50432
|
+
function wroteLines(destination, size, mode) {
|
|
50433
|
+
return [`Wrote ${destination}`, ` size ${formatBytes(size)} (${size} bytes)`, ` mode ${mode}`];
|
|
50434
|
+
}
|
|
50305
50435
|
function resolveBackupDestination(to, deps) {
|
|
50306
50436
|
if (to.trim().toLowerCase() !== ICLOUD_SHORTHAND)
|
|
50307
50437
|
return { path: resolve2(to) };
|
|
@@ -50381,7 +50511,8 @@ async function vaultVerifyBackup(args, ctx) {
|
|
|
50381
50511
|
}
|
|
50382
50512
|
const opened = await unlockInteractively(ctx, path, raw, {
|
|
50383
50513
|
acceptOlderCopy: parsed.booleans.has("--accept-older-copy"),
|
|
50384
|
-
...sealed ? { factor: "passphrase" } : {}
|
|
50514
|
+
...sealed ? { factor: "passphrase" } : {},
|
|
50515
|
+
purpose: "opening the live vault"
|
|
50385
50516
|
});
|
|
50386
50517
|
const live = hold(opened.vault);
|
|
50387
50518
|
const sameEnvelope = parseVaultFile(copyRaw).envelopes.some((envelope) => JSON.stringify(envelope) === JSON.stringify(live.envelope));
|
|
@@ -50390,11 +50521,12 @@ async function vaultVerifyBackup(args, ctx) {
|
|
|
50390
50521
|
const copy = hold((await unlockInteractively(ctx, resolve2(copyPath), copyRaw, {
|
|
50391
50522
|
factor: "passphrase",
|
|
50392
50523
|
acceptOlderCopy: parsed.booleans.has("--accept-older-copy"),
|
|
50393
|
-
promptText: "Passphrase this backup was sealed under (input hidden): "
|
|
50524
|
+
promptText: "Passphrase this backup was sealed under (input hidden): ",
|
|
50525
|
+
purpose: "opening the copy"
|
|
50394
50526
|
})).vault);
|
|
50395
50527
|
report = await verifyVaultIntegrity(copy, { live });
|
|
50396
50528
|
} else {
|
|
50397
|
-
report = await verifyCopy(ctx, resolve2(copyPath), opened.reopen, live);
|
|
50529
|
+
report = await verifyCopy(ctx, resolve2(copyPath), opened.reopen, live, "opening the copy");
|
|
50398
50530
|
}
|
|
50399
50531
|
const sidecar = sidecarPath(path);
|
|
50400
50532
|
await writeSidecar(sidecar, {
|
|
@@ -50409,13 +50541,13 @@ async function vaultVerifyBackup(args, ctx) {
|
|
|
50409
50541
|
return 0;
|
|
50410
50542
|
});
|
|
50411
50543
|
}
|
|
50412
|
-
async function verifyCopy(_ctx, copyPath, reopen, live) {
|
|
50544
|
+
async function verifyCopy(_ctx, copyPath, reopen, live, purpose) {
|
|
50413
50545
|
const raw = await readVaultRaw(copyPath);
|
|
50414
50546
|
if (raw === null)
|
|
50415
50547
|
throw new VaultError("VAULT_MISSING", `No file at ${copyPath}.`, {
|
|
50416
50548
|
suggestion: `The copy this run just wrote is not there. Check the path and the volume: ls -l ${copyPath}`
|
|
50417
50549
|
});
|
|
50418
|
-
const copy = await reopen(copyPath, raw);
|
|
50550
|
+
const copy = await reopen(copyPath, raw, purpose);
|
|
50419
50551
|
try {
|
|
50420
50552
|
return await verifyVaultIntegrity(copy, { live });
|
|
50421
50553
|
} finally {
|
|
@@ -53181,6 +53313,159 @@ async function runTeeImport(ctx, opts) {
|
|
|
53181
53313
|
return submitted.remoteAuthority === "verified-active" ? 0 : 3;
|
|
53182
53314
|
}
|
|
53183
53315
|
|
|
53316
|
+
// src/commands/vault-rename.ts
|
|
53317
|
+
init_args();
|
|
53318
|
+
init_errors();
|
|
53319
|
+
|
|
53320
|
+
// src/vault/labels.ts
|
|
53321
|
+
function entriesWithLabel(index, label) {
|
|
53322
|
+
return index.entries.filter((entry) => entry.label === label);
|
|
53323
|
+
}
|
|
53324
|
+
function duplicateLabels(index) {
|
|
53325
|
+
const byLabel = new Map;
|
|
53326
|
+
for (const entry of index.entries) {
|
|
53327
|
+
const holders = byLabel.get(entry.label);
|
|
53328
|
+
if (holders === undefined)
|
|
53329
|
+
byLabel.set(entry.label, [entry]);
|
|
53330
|
+
else
|
|
53331
|
+
holders.push(entry);
|
|
53332
|
+
}
|
|
53333
|
+
const out = [];
|
|
53334
|
+
for (const [label, entries] of byLabel) {
|
|
53335
|
+
if (entries.length > 1)
|
|
53336
|
+
out.push({ label, entries });
|
|
53337
|
+
}
|
|
53338
|
+
return out;
|
|
53339
|
+
}
|
|
53340
|
+
function hasControlCharacter(label) {
|
|
53341
|
+
for (const char of label) {
|
|
53342
|
+
const code = char.codePointAt(0) ?? 0;
|
|
53343
|
+
if (code < 32 || code === 127)
|
|
53344
|
+
return true;
|
|
53345
|
+
}
|
|
53346
|
+
return false;
|
|
53347
|
+
}
|
|
53348
|
+
function validateLabel(label) {
|
|
53349
|
+
if (label.trim().length === 0)
|
|
53350
|
+
return "A key's label cannot be empty.";
|
|
53351
|
+
if (hasControlCharacter(label)) {
|
|
53352
|
+
return "A key's label cannot contain a newline, a tab or a control character.";
|
|
53353
|
+
}
|
|
53354
|
+
if (label.startsWith("-")) {
|
|
53355
|
+
return `A key's label cannot begin with "-": it would be read as a flag everywhere a label is typed.`;
|
|
53356
|
+
}
|
|
53357
|
+
return;
|
|
53358
|
+
}
|
|
53359
|
+
function resolveRenameTarget(index, old, id) {
|
|
53360
|
+
if (id !== undefined) {
|
|
53361
|
+
const entry = index.entries.find((candidate) => candidate.id === id);
|
|
53362
|
+
return entry === undefined ? { kind: "none" } : { kind: "found", entry, by: "id" };
|
|
53363
|
+
}
|
|
53364
|
+
const byLabel = entriesWithLabel(index, old);
|
|
53365
|
+
if (byLabel.length === 1)
|
|
53366
|
+
return { kind: "found", entry: byLabel[0], by: "label" };
|
|
53367
|
+
if (byLabel.length > 1)
|
|
53368
|
+
return { kind: "ambiguous", by: "label", candidates: byLabel };
|
|
53369
|
+
const byAddress = index.entries.filter((entry) => entry.address === old);
|
|
53370
|
+
if (byAddress.length === 1)
|
|
53371
|
+
return { kind: "found", entry: byAddress[0], by: "address" };
|
|
53372
|
+
if (byAddress.length > 1)
|
|
53373
|
+
return { kind: "ambiguous", by: "address", candidates: byAddress };
|
|
53374
|
+
return { kind: "none" };
|
|
53375
|
+
}
|
|
53376
|
+
|
|
53377
|
+
// src/commands/vault-rename.ts
|
|
53378
|
+
init_store();
|
|
53379
|
+
init_vault_support();
|
|
53380
|
+
var RENAME_USAGE = "Usage: candle vault rename <label|address> <new-label> [--id <entry-id>]";
|
|
53381
|
+
var SAME_STRING_LINE = "The two arguments are the same string; nothing to do.";
|
|
53382
|
+
async function vaultRename(args, ctx) {
|
|
53383
|
+
const parsed = parseArgs(args, {
|
|
53384
|
+
valueFlags: ["--keystore", "--id"],
|
|
53385
|
+
booleanFlags: ["--accept-older-copy"],
|
|
53386
|
+
pathFlags: ["--keystore"]
|
|
53387
|
+
});
|
|
53388
|
+
if ("error" in parsed)
|
|
53389
|
+
return usage(ctx, parsed.error);
|
|
53390
|
+
const [old, next, extra] = parsed.positionals;
|
|
53391
|
+
if (old === undefined || next === undefined || extra !== undefined)
|
|
53392
|
+
return usage(ctx, RENAME_USAGE);
|
|
53393
|
+
const invalid = validateLabel(next);
|
|
53394
|
+
if (invalid !== undefined)
|
|
53395
|
+
return usage(ctx, invalid);
|
|
53396
|
+
if (old === next)
|
|
53397
|
+
return usage(ctx, SAME_STRING_LINE);
|
|
53398
|
+
const id = parsed.values["--id"];
|
|
53399
|
+
if (!refuseEnvPassphrase(ctx))
|
|
53400
|
+
return 1;
|
|
53401
|
+
if (!requireTty(ctx, "vault rename"))
|
|
53402
|
+
return 1;
|
|
53403
|
+
const { deps } = ctx;
|
|
53404
|
+
const resolvedVault = vaultPathFor(ctx, parsed);
|
|
53405
|
+
if ("error" in resolvedVault)
|
|
53406
|
+
return usage(ctx, resolvedVault.error);
|
|
53407
|
+
const path = resolvedVault.path;
|
|
53408
|
+
return runVaultCommand(ctx, async ({ hold }) => {
|
|
53409
|
+
const raw = await requireVaultRaw(ctx, resolvedVault);
|
|
53410
|
+
const opened = await unlockInteractively(ctx, path, raw, {
|
|
53411
|
+
acceptOlderCopy: parsed.booleans.has("--accept-older-copy")
|
|
53412
|
+
});
|
|
53413
|
+
const vault = hold(opened.vault);
|
|
53414
|
+
const entry = resolveTarget(vault.index, old, id);
|
|
53415
|
+
if (entry.role === "tee-wallet") {
|
|
53416
|
+
throw new VaultError("VAULT_RENAME_ROLE_REFUSED", `${entry.label} is a TEE wallet. Its label was sent to Candle when it was enabled and \`candle wallets\` lists that copy, so renaming it here would give one wallet two names and nothing reconciles them.`, {
|
|
53417
|
+
suggestion: "A vault key or an external wallet renames here. For a TEE wallet, nothing in this release changes the name on either side."
|
|
53418
|
+
});
|
|
53419
|
+
}
|
|
53420
|
+
if (entry.label === next) {
|
|
53421
|
+
throw new VaultError("VAULT_LABEL_UNCHANGED", `${next} is already this key's label. Nothing was written.`, {
|
|
53422
|
+
suggestion: "Nothing to rename. `candle vault status --unlock` lists every label."
|
|
53423
|
+
});
|
|
53424
|
+
}
|
|
53425
|
+
if (entriesWithLabel(vault.index, next).length > 0) {
|
|
53426
|
+
throw new VaultError("VAULT_LABEL_TAKEN", `A key labelled ${next} already exists in this vault. Nothing was written.`, {
|
|
53427
|
+
suggestion: "Choose a name no key has, or rename that key first: `candle vault status --unlock` lists them."
|
|
53428
|
+
});
|
|
53429
|
+
}
|
|
53430
|
+
const from = entry.label;
|
|
53431
|
+
await commitVault(vault, {
|
|
53432
|
+
index: {
|
|
53433
|
+
hd: vault.index.hd,
|
|
53434
|
+
entries: vault.index.entries.map((candidate) => candidate.id === entry.id ? { ...candidate, label: next } : candidate)
|
|
53435
|
+
}
|
|
53436
|
+
}, deps);
|
|
53437
|
+
if (ctx.json) {
|
|
53438
|
+
writeJson(deps, { ok: true, id: entry.id, address: entry.address, role: entry.role, from, to: next });
|
|
53439
|
+
return 0;
|
|
53440
|
+
}
|
|
53441
|
+
deps.stdout.write(`Renamed ${from} to ${next}.
|
|
53442
|
+
`);
|
|
53443
|
+
deps.stdout.write(` address ${entry.address}
|
|
53444
|
+
`);
|
|
53445
|
+
deps.stdout.write(` id ${entry.id}
|
|
53446
|
+
`);
|
|
53447
|
+
deps.stdout.write(` role ${entry.role}
|
|
53448
|
+
`);
|
|
53449
|
+
deps.stdout.write(` unchanged address, derivation path, key blob, every envelope
|
|
53450
|
+
`);
|
|
53451
|
+
return 0;
|
|
53452
|
+
});
|
|
53453
|
+
}
|
|
53454
|
+
function resolveTarget(index, old, id) {
|
|
53455
|
+
const match = resolveRenameTarget(index, old, id);
|
|
53456
|
+
if (match.kind === "found")
|
|
53457
|
+
return match.entry;
|
|
53458
|
+
if (match.kind === "none") {
|
|
53459
|
+
throw new VaultError("VAULT_LABEL_NOT_FOUND", id === undefined ? `No key in this vault is called ${old}, and no key has that address or id.` : `No key in this vault has the id ${id}.`, { suggestion: "List them with their labels: `candle vault status --unlock`" });
|
|
53460
|
+
}
|
|
53461
|
+
const candidates = match.candidates.map((entry) => `${entry.address} (${entry.id})`).join(", ");
|
|
53462
|
+
const count = match.candidates.length;
|
|
53463
|
+
const first = match.candidates[0];
|
|
53464
|
+
throw new VaultError("VAULT_LABEL_AMBIGUOUS", match.by === "label" ? `${count} keys in this vault are called ${old}, so this rename would not say which one it meant. Nothing was written.` : `${count} keys in this vault have the address ${old}, so this rename would not say which one it meant. Nothing was written.`, {
|
|
53465
|
+
suggestion: match.by === "label" ? `Name one by address or id. The candidates are ${candidates}.` : `Name one by id. The candidates are ${candidates}. Re-run: \`candle vault rename ${old} <new-label> --id ${first.id}\`.`
|
|
53466
|
+
});
|
|
53467
|
+
}
|
|
53468
|
+
|
|
53184
53469
|
// src/commands/vault-restore.ts
|
|
53185
53470
|
init_args();
|
|
53186
53471
|
import { rm as rm3 } from "node:fs/promises";
|
|
@@ -53884,6 +54169,10 @@ async function vaultStatus(args, ctx) {
|
|
|
53884
54169
|
const vault = hold((await unlockInteractively(ctx, path, raw, { acceptOlderCopy: parsed.booleans.has("--accept-older-copy") })).vault);
|
|
53885
54170
|
unlocked = {
|
|
53886
54171
|
entries: vault.index.entries.map(describeEntry),
|
|
54172
|
+
duplicateLabels: duplicateLabels(vault.index).map((duplicate) => ({
|
|
54173
|
+
label: duplicate.label,
|
|
54174
|
+
entries: duplicate.entries.map((entry) => ({ address: entry.address, id: entry.id }))
|
|
54175
|
+
})),
|
|
53887
54176
|
nextIndex: vault.index.hd.nextIndex,
|
|
53888
54177
|
exposedIndexes: vault.index.hd.exposedIndexes,
|
|
53889
54178
|
rootExported: vault.index.hd.rootExported,
|
|
@@ -54009,6 +54298,14 @@ Keys (${unlocked.entries.length}):
|
|
|
54009
54298
|
`);
|
|
54010
54299
|
}
|
|
54011
54300
|
}
|
|
54301
|
+
if (unlocked.duplicateLabels.length > 0) {
|
|
54302
|
+
deps.stdout.write(`
|
|
54303
|
+
Duplicate labels (${unlocked.duplicateLabels.length}):
|
|
54304
|
+
`);
|
|
54305
|
+
for (const line of duplicateLabelLines(unlocked.duplicateLabels))
|
|
54306
|
+
deps.stdout.write(`${line}
|
|
54307
|
+
`);
|
|
54308
|
+
}
|
|
54012
54309
|
deps.stdout.write(`
|
|
54013
54310
|
Derivation counters (next index per branch):
|
|
54014
54311
|
`);
|
|
@@ -54030,6 +54327,15 @@ The recovery phrase restores derived keys only. It does not restore any key impo
|
|
|
54030
54327
|
return 0;
|
|
54031
54328
|
});
|
|
54032
54329
|
}
|
|
54330
|
+
function duplicateLabelLines(duplicates) {
|
|
54331
|
+
const lines = [];
|
|
54332
|
+
for (const duplicate of duplicates) {
|
|
54333
|
+
const holders = duplicate.entries.map((entry) => `${entry.address} (${entry.id})`).join(", ");
|
|
54334
|
+
lines.push(` ${duplicate.label.padEnd(14)}${duplicate.entries.length} keys: ${holders}`);
|
|
54335
|
+
lines.push(` --from ${duplicate.label} always picks the first; rename one: candle vault rename <address> <new-label>`);
|
|
54336
|
+
}
|
|
54337
|
+
return lines;
|
|
54338
|
+
}
|
|
54033
54339
|
function describeEnvelope(envelope, facts) {
|
|
54034
54340
|
const availability = envelopeAvailability(envelope, facts);
|
|
54035
54341
|
const strength = typeof envelope.strength === "string" ? envelope.strength : undefined;
|
|
@@ -54061,6 +54367,7 @@ function describeEntryInner(entry) {
|
|
|
54061
54367
|
return {
|
|
54062
54368
|
address: entry.address,
|
|
54063
54369
|
label: entry.label,
|
|
54370
|
+
id: entry.id,
|
|
54064
54371
|
role: entry.role,
|
|
54065
54372
|
origin: entry.origin,
|
|
54066
54373
|
derivation: entry.derivation?.path,
|
|
@@ -54588,6 +54895,7 @@ var COMMANDS = {
|
|
|
54588
54895
|
init: vaultInit,
|
|
54589
54896
|
status: vaultStatus,
|
|
54590
54897
|
"new-key": vaultNewKey,
|
|
54898
|
+
rename: vaultRename,
|
|
54591
54899
|
phrase: vaultPhrase,
|
|
54592
54900
|
restore: vaultRestore,
|
|
54593
54901
|
"reconcile-exposure": vaultReconcileExposure,
|
package/package.json
CHANGED