@candledottv/cli 0.11.34 → 0.11.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +40 -72
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -28392,6 +28392,7 @@ __export(exports_vault_support, {
28392
28392
  vaultAlreadyExists: () => vaultAlreadyExists,
28393
28393
  usage: () => usage,
28394
28394
  unlockInteractively: () => unlockInteractively,
28395
+ typedConfirm: () => typedConfirm,
28395
28396
  takeRepeatedFlag: () => takeRepeatedFlag,
28396
28397
  runVaultCommand: () => runVaultCommand,
28397
28398
  requireVaultRaw: () => requireVaultRaw,
@@ -28413,7 +28414,7 @@ __export(exports_vault_support, {
28413
28414
  describeEntry: () => describeEntry,
28414
28415
  derivationNotice: () => derivationNotice,
28415
28416
  confirmSend: () => confirmSend,
28416
- confirmLastSix: () => confirmLastSix,
28417
+ confirmAddress: () => confirmAddress,
28417
28418
  assertVaultHelperIdentities: () => assertVaultHelperIdentities,
28418
28419
  assertNotOlderCopy: () => assertNotOlderCopy,
28419
28420
  askForOwnPassphrase: () => askForOwnPassphrase,
@@ -28947,21 +28948,6 @@ async function assertNotOlderCopy(ctx, path, raw, accept) {
28947
28948
  ctx.deps.stderr.write(`Opening an older copy: generation ${String(generation)} against the ${sidecar.lastGeneration} this machine last saw.${detail}
28948
28949
  `);
28949
28950
  }
28950
- async function confirmLastSix(ctx, address, what) {
28951
- const expected = address.slice(-6);
28952
- const normalize3 = (value) => address.startsWith("0x") ? value.toLowerCase() : value;
28953
- for (let attempt = 0;attempt < 3; attempt++) {
28954
- const typed = (await ctx.deps.promptLine(`Type the last six characters of ${what} (${address}) to confirm: `)).trim();
28955
- if (normalize3(typed) === normalize3(expected))
28956
- return;
28957
- const difference = Array.from({ length: Math.max(expected.length, typed.length) }, (_, i) => i).find((i) => normalize3(typed)[i] !== normalize3(expected)[i]) ?? 0;
28958
- ctx.deps.stderr.write(`Expected ${expected}
28959
- You typed ${typed}
28960
- ${" ".repeat(10 + difference)}^ character ${difference + 1} differs
28961
- `);
28962
- }
28963
- throw new VaultError("DESTINATION_NOT_CONFIRMED", "That is not the last six characters of that address; nothing was done.", { suggestion: "Nothing was signed. Run it again and type the last six characters exactly as shown." });
28964
- }
28965
28951
  function groupAddress(address) {
28966
28952
  const prefix = address.startsWith("0x") ? "0x" : "";
28967
28953
  const body = address.slice(prefix.length);
@@ -28972,16 +28958,26 @@ function describeSend(amount, asset, to) {
28972
28958
  return `send ${amount}${asset !== undefined ? ` ${asset}` : ""} to ${to}`;
28973
28959
  }
28974
28960
  async function confirmSend(ctx, send) {
28975
- const display = ctx.json ? ctx.deps.stderr : ctx.deps.stdout;
28976
- display.write(`to ${groupAddress(send.address)}
28977
- `);
28978
- const typed = await ctx.deps.promptLine(`Type confirm to ${describeSend(send.amount, send.asset, send.address)}: `);
28979
- if (typed.trim().toLowerCase() === "confirm")
28961
+ if (await typedConfirm(ctx, "to", send.address, describeSend(send.amount, send.asset, send.address)))
28980
28962
  return;
28981
28963
  throw new VaultError("DESTINATION_NOT_CONFIRMED", "The send was not confirmed; nothing was done.", {
28982
28964
  suggestion: "Nothing was signed. Run it again and type confirm at the prompt."
28983
28965
  });
28984
28966
  }
28967
+ async function confirmAddress(ctx, label, address, action) {
28968
+ if (await typedConfirm(ctx, label, address, action))
28969
+ return;
28970
+ throw new VaultError("DESTINATION_NOT_CONFIRMED", "That was not confirmed; nothing was done.", {
28971
+ suggestion: "Nothing was signed or written. Run it again and type confirm at the prompt."
28972
+ });
28973
+ }
28974
+ async function typedConfirm(io, label, address, action) {
28975
+ const display = io.json ? io.deps.stderr : io.deps.stdout;
28976
+ display.write(`${label.padEnd(4)} ${groupAddress(address)}
28977
+ `);
28978
+ const typed = await io.deps.promptLine(`Type confirm to ${action}: `);
28979
+ return typed.trim().toLowerCase() === "confirm";
28980
+ }
28985
28981
  function writeVaultFailure(ctx, error) {
28986
28982
  if (isUsageError(error))
28987
28983
  return usage(ctx, error.message);
@@ -29561,7 +29557,7 @@ async function maybeReconcileVaultTee(ctx, resolved, caller = "default") {
29561
29557
  ctx.deps.stdout.write(`This profile acts as account ${cached}.
29562
29558
  `);
29563
29559
  try {
29564
- await confirmLastSix(ctx, cached, "that account");
29560
+ await confirmAddress(ctx, "account", cached, `act as account ${cached}`);
29565
29561
  assertedAccount = cached;
29566
29562
  } catch (error) {
29567
29563
  if (isVaultError(error)) {
@@ -29622,7 +29618,7 @@ async function maybeReconcileVaultTee(ctx, resolved, caller = "default") {
29622
29618
  const adoption = await adoptGrantedRow(ctx, resolved.entry, verdict.row, verdict.account, {
29623
29619
  confirmDestination: async (destination) => {
29624
29620
  try {
29625
- await confirmLastSix(ctx, destination, "the server-reported vault destination");
29621
+ await confirmAddress(ctx, "vault", destination, `accept ${destination} as the sweep vault`);
29626
29622
  return true;
29627
29623
  } catch {
29628
29624
  return false;
@@ -33841,7 +33837,7 @@ async function sweepEvmWallet(input) {
33841
33837
  let stopped;
33842
33838
  const somethingToMove = planned.length > 0 || ethBalance > 0n;
33843
33839
  if (somethingToMove) {
33844
- await confirmLastSix(ctx, destination, "the vault destination");
33840
+ await confirmAddress(ctx, "vault", destination, `sweep everything to ${destination}`);
33845
33841
  const stopRequestedAt = entry.tee?.stopRequestedAt ?? new Date(deps.now()).toISOString();
33846
33842
  vault = hold(await patchTee(ctx, vault, entry.id, (tee) => ({ ...tee, stopRequestedAt })));
33847
33843
  if (!emergency)
@@ -34859,9 +34855,8 @@ function isSolanaAddress(value) {
34859
34855
  return false;
34860
34856
  }
34861
34857
  }
34862
- async function confirmVault(deps, vault) {
34863
- const typed = (await deps.promptSecret(`Type the LAST 6 characters of the vault address (${vault}) to confirm: `)).trim();
34864
- return typed === vault.slice(-6);
34858
+ function confirmVault(deps, json, vault, action) {
34859
+ return typedConfirm({ deps, json }, "vault", vault, action);
34865
34860
  }
34866
34861
  async function teeNew(args, ctx) {
34867
34862
  const { deps, json } = ctx;
@@ -35058,8 +35053,8 @@ async function teeEnable(args, ctx) {
35058
35053
  deps.stdout.write(`compromised agent within those limits. Once enabled a copy of this key stays in the TEE for good.
35059
35054
  `);
35060
35055
  }
35061
- if (!await confirmVault(deps, vault)) {
35062
- writeLocalFailure(deps, { code: "VAULT_NOT_CONFIRMED", message: "The vault confirmation did not match. Nothing was enabled." }, json);
35056
+ if (!await confirmVault(deps, json, vault, `enable this TEE wallet with sweeps to ${vault}`)) {
35057
+ writeLocalFailure(deps, { code: "VAULT_NOT_CONFIRMED", message: "The vault was not confirmed. Nothing was enabled." }, json);
35063
35058
  return 1;
35064
35059
  }
35065
35060
  const flow = await runImportFlow({
@@ -35719,8 +35714,8 @@ Stop the agent from your Candle session if you have not, and re-run candle tee d
35719
35714
  deps.stdout.write(`Sweep ${address} -> vault ${vault} (solana-mainnet)
35720
35715
  `);
35721
35716
  }
35722
- if (!await confirmVault(deps, vault)) {
35723
- writeLocalFailure(deps, { code: "VAULT_NOT_CONFIRMED", message: "The vault confirmation did not match. Nothing was signed." }, json);
35717
+ if (!await confirmVault(deps, json, vault, `sweep everything to ${vault}`)) {
35718
+ writeLocalFailure(deps, { code: "VAULT_NOT_CONFIRMED", message: "The vault was not confirmed. Nothing was signed." }, json);
35724
35719
  return 1;
35725
35720
  }
35726
35721
  const secret = base58.decode(entry.privateKey);
@@ -36436,6 +36431,7 @@ var init_tee = __esm(() => {
36436
36431
  init_tee_evm();
36437
36432
  init_tee_status_evm();
36438
36433
  init_tee_status_wallet();
36434
+ init_vault_support();
36439
36435
  init_wallets();
36440
36436
  StoreChangedError = class StoreChangedError extends Error {
36441
36437
  };
@@ -70287,7 +70283,7 @@ init_render();
70287
70283
  init_secret_store();
70288
70284
 
70289
70285
  // src/version.ts
70290
- var CLI_VERSION = "0.11.34";
70286
+ var CLI_VERSION = "0.11.35";
70291
70287
 
70292
70288
  // src/commands/auth.ts
70293
70289
  init_keys_embedded_wallet();
@@ -71536,7 +71532,7 @@ var HELP = {
71536
71532
  },
71537
71533
  {
71538
71534
  invocation: "export-key <label> --to <new-file>",
71539
- description: "Export one key as plaintext (interactive ceremony)"
71535
+ description: "Export one key as plaintext (interactive ceremony: unlock, then type confirm under the address)"
71540
71536
  }
71541
71537
  ],
71542
71538
  flags: [
@@ -72872,14 +72868,6 @@ function groupAddress2(address) {
72872
72868
  function confirmTyped(typed) {
72873
72869
  return typeof typed === "string" && typed.trim().toLowerCase() === "confirm";
72874
72870
  }
72875
- function lastSixMismatch(address, typed) {
72876
- const expected = address.slice(-6);
72877
- const given = typeof typed === "string" ? typed.trim() : "";
72878
- const fold = (value) => address.startsWith("0x") ? value.toLowerCase() : value;
72879
- if (fold(given) === fold(expected))
72880
- return;
72881
- return Array.from({ length: Math.max(expected.length, given.length) }, (_, i) => i).find((i) => fold(given)[i] !== fold(expected)[i]) ?? 0;
72882
- }
72883
72871
  function rawToDecimal(raw, decimals) {
72884
72872
  const text = raw.toString().padStart(decimals + 1, "0");
72885
72873
  const whole = text.slice(0, text.length - decimals);
@@ -72963,7 +72951,6 @@ var TRANSFER_TOOLS = {
72963
72951
  vault: "core_vault_transfer",
72964
72952
  embedded: "candle_transfer"
72965
72953
  };
72966
- var LAST_SIX_TRIES = 3;
72967
72954
  var OPEN_CARD_CAP = 5;
72968
72955
  var MAX_REASON = 500;
72969
72956
  var MAX_PARAMS_BYTES = 64 * 1024;
@@ -73399,25 +73386,6 @@ class CardBook {
73399
73386
  step: "confirm"
73400
73387
  });
73401
73388
  }
73402
- const differsAt = lastSixMismatch(params.to, confirm?.lastSix);
73403
- if (differsAt !== undefined) {
73404
- card2.lastSixFailures = (card2.lastSixFailures ?? 0) + 1;
73405
- const left = LAST_SIX_TRIES - card2.lastSixFailures;
73406
- if (left <= 0) {
73407
- await this.deps.journal.append({
73408
- type: "approval.refused",
73409
- cardId: card2.cardId,
73410
- viewClientId,
73411
- code: "DESTINATION_NOT_CONFIRMED",
73412
- step: "lastSix",
73413
- withdrawn: true
73414
- });
73415
- this.approving.delete(card2.cardId);
73416
- await this.close(card2.cardId, "withdrawn", { by: "last_six" });
73417
- throw refused("DESTINATION_NOT_CONFIRMED", "That is not the last six characters of the address, three times. The card is withdrawn; nothing was sent.", { step: "lastSix", withdrawn: true });
73418
- }
73419
- throw await refuse3("DESTINATION_NOT_CONFIRMED", `That is not the last six characters of the address. Character ${differsAt + 1} differs. Nothing was sent.`, { step: "lastSix", differsAt, triesLeft: left });
73420
- }
73421
73389
  const engine = this.deps.transfers;
73422
73390
  if (engine === undefined)
73423
73391
  throw refused("NOT_IN_THIS_BUILD", "Transfers are not in this build of the core.");
@@ -73436,7 +73404,7 @@ class CardBook {
73436
73404
  cardHash: card2.cardHash,
73437
73405
  opId: card2.opId,
73438
73406
  factorType: factorType2,
73439
- confirm: { typed: true, lastSix: true },
73407
+ confirm: { typed: true },
73440
73408
  viewClientId
73441
73409
  });
73442
73410
  this.publish(card2);
@@ -94512,7 +94480,7 @@ async function vaultExportKey(args, ctx) {
94512
94480
  ctx.deps.stdout.write(`
94513
94481
  ${warning}
94514
94482
  `);
94515
- await confirmLastSix(ctx, entry.address, "the key being exported");
94483
+ await confirmAddress(ctx, "key", entry.address, `export the key for ${entry.address}`);
94516
94484
  vault = hold(await markEverExported(vault, entry, ctx));
94517
94485
  const secret = await decryptKey(vault, entry.id);
94518
94486
  try {
@@ -97123,7 +97091,7 @@ async function promoteEvmFresh(input, fromLabel) {
97123
97091
  addKeys: [blob]
97124
97092
  }, ctx.deps));
97125
97093
  scanStart = await appendScanStart(ctx, vault.path, address, height);
97126
- await confirmLastSix(ctx, destination.address, "the sweep vault destination");
97094
+ await confirmAddress(ctx, "vault", destination.address, `sweep this wallet home to ${destination.address}`);
97127
97095
  if (toKey !== undefined) {
97128
97096
  ctx.deps.stderr.write(`${[toKeyPlanLine(toKey), ...targetWarnings(toKey.target, "evm")].join(`
97129
97097
  `)}
@@ -97219,7 +97187,7 @@ async function promoteEvmInPlace(input, subjectLabel, sweepTo) {
97219
97187
  ctx.deps.stdout.write(`Authorities: the signer-role read (mint, freeze, upgrade and stake authorities) is Solana's; nothing is read on Hood, so any contract this key controls is not checked.
97220
97188
  `);
97221
97189
  printPromoteSentence(ctx, promoteSentence({ n: 1, form: "U", where: "above" }));
97222
- await confirmLastSix(ctx, first.subject.address, "the address being promoted");
97190
+ await confirmAddress(ctx, "address", first.subject.address, `promote ${first.subject.address}`);
97223
97191
  ctx.deps.stderr.write(`${renderControlledBy(controlledBy, 1)}
97224
97192
  `);
97225
97193
  await confirmPromotion(ctx, 1);
@@ -97495,7 +97463,7 @@ async function promoteFresh(ctx, parsed, fromLabel, toKey) {
97495
97463
  },
97496
97464
  addKeys: [blob]
97497
97465
  }, ctx.deps));
97498
- await confirmLastSix(ctx, destination.address, "the sweep vault destination");
97466
+ await confirmAddress(ctx, "vault", destination.address, `sweep this wallet home to ${destination.address}`);
97499
97467
  if (toKey !== undefined) {
97500
97468
  ctx.deps.stderr.write(`${[toKeyPlanLine(toKey), ...targetWarnings(toKey.target, "solana")].join(`
97501
97469
  `)}
@@ -97604,10 +97572,10 @@ async function promoteInPlace(ctx, parsed, subjectLabel, toKey) {
97604
97572
  if (toKey !== undefined)
97605
97573
  await confirmResumeRebind(ctx, toKey, resumeChain);
97606
97574
  const resumed = await resumePromote(ctx, vault, existing, opened.reopen, path, hold, {
97607
- confirmAccount: (account) => confirmLastSix(ctx, account, "that account"),
97575
+ confirmAccount: (account) => confirmAddress(ctx, "account", account, `act as account ${account}`),
97608
97576
  confirmDestination: async (destination2) => {
97609
97577
  try {
97610
- await confirmLastSix(ctx, destination2, "the server-reported vault destination");
97578
+ await confirmAddress(ctx, "vault", destination2, `accept ${destination2} as the sweep vault`);
97611
97579
  return true;
97612
97580
  } catch {
97613
97581
  return false;
@@ -97673,7 +97641,7 @@ async function promoteInPlace(ctx, parsed, subjectLabel, toKey) {
97673
97641
  ctx.deps.stdout.write(`${authoritiesBlock(host, roles)}
97674
97642
  `);
97675
97643
  printPromoteSentence(ctx, promoteSentence({ n: 1, form: sentenceForm(roles), k: keysWithFindings(roles), where: "above" }));
97676
- await confirmLastSix(ctx, first.subject.address, "the address being promoted");
97644
+ await confirmAddress(ctx, "address", first.subject.address, `promote ${first.subject.address}`);
97677
97645
  ctx.deps.stderr.write(`${renderControlledBy(controlledBy, 1)}
97678
97646
  `);
97679
97647
  await confirmPromotion(ctx, 1);
@@ -99495,14 +99463,14 @@ async function matchAgainstAccount(ctx, apiKey, derived) {
99495
99463
  ctx.deps.stdout.write(`
99496
99464
  This profile acts as account ${read2.account}.
99497
99465
  `);
99498
- const typed = (await ctx.deps.promptLine(`Type the last six characters of that account to confirm before any exposure is recorded: `)).trim();
99499
- if (typed !== read2.account.slice(-6)) {
99466
+ const confirmed = await typedConfirm(ctx, "account", read2.account, `record exposure against account ${read2.account}`);
99467
+ if (!confirmed) {
99500
99468
  return {
99501
99469
  matched: [],
99502
99470
  unmatched: [],
99503
99471
  account: undefined,
99504
99472
  complete: false,
99505
- reason: "that is not the last six characters of the account this profile acts as, so it was not confirmed",
99473
+ reason: "the account this profile acts as was not confirmed",
99506
99474
  refused: "EXPOSURE_ACCOUNT_MISMATCH"
99507
99475
  };
99508
99476
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@candledottv/cli",
3
- "version": "0.11.34",
3
+ "version": "0.11.35",
4
4
  "description": "The Candle CLI: authorize a device from your browser, then manage API keys, wallets, and setup health from the terminal",
5
5
  "type": "module",
6
6
  "bin": {