@candledottv/cli 0.8.3 → 0.8.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.
Files changed (2) hide show
  1. package/dist/index.js +151 -4
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -18850,6 +18850,42 @@ var init_convert = __esm(() => {
18850
18850
  DECIMAL_RE = /^\d+(\.\d+)?$/;
18851
18851
  });
18852
18852
 
18853
+ // ../mcp/src/version.ts
18854
+ var SERVER_VERSION = "0.6.4";
18855
+
18856
+ // ../mcp/src/update-notice.ts
18857
+ function newer(a, b) {
18858
+ const [a1 = 0, a2 = 0, a3 = 0] = a.split(".").map(Number);
18859
+ const [b1 = 0, b2 = 0, b3 = 0] = b.split(".").map(Number);
18860
+ return a1 !== b1 ? a1 > b1 : a2 !== b2 ? a2 > b2 : a3 > b3;
18861
+ }
18862
+ function noteVersionHeaders(res) {
18863
+ const headers = res?.headers;
18864
+ const value = typeof headers?.get === "function" ? headers.get("x-candle-mcp-latest") : null;
18865
+ if (!value || !PLAIN_VERSION.test(value))
18866
+ return;
18867
+ if (latestSeen !== null && !newer(value, latestSeen))
18868
+ return;
18869
+ latestSeen = value;
18870
+ if (!warned && newer(value, SERVER_VERSION) && !process.env.CANDLE_NO_UPDATE_NOTIFIER) {
18871
+ warned = true;
18872
+ console.error(`@candledottv/mcp ${value} is available (running ${SERVER_VERSION}). Update: npm install -g @candledottv/mcp@latest`);
18873
+ }
18874
+ }
18875
+ function updateAvailable() {
18876
+ if (latestSeen === null || !newer(latestSeen, SERVER_VERSION))
18877
+ return null;
18878
+ return {
18879
+ current: SERVER_VERSION,
18880
+ latest: latestSeen,
18881
+ command: "npm install -g @candledottv/mcp@latest"
18882
+ };
18883
+ }
18884
+ var PLAIN_VERSION, latestSeen = null, warned = false;
18885
+ var init_update_notice = __esm(() => {
18886
+ PLAIN_VERSION = /^\d+\.\d+\.\d+$/;
18887
+ });
18888
+
18853
18889
  // ../mcp/src/orchestrate.ts
18854
18890
  import { randomUUID } from "node:crypto";
18855
18891
  function requireApiKey(cfg) {
@@ -19156,6 +19192,7 @@ async function executionStatus(cfg, doFetch) {
19156
19192
  const apiKey = requireApiKey(cfg);
19157
19193
  const get = async (path) => {
19158
19194
  const res = await doFetch(`${base(cfg)}${path}`, { method: "GET", headers: headers(apiKey) });
19195
+ noteVersionHeaders(res);
19159
19196
  const text = await res.text();
19160
19197
  let body;
19161
19198
  try {
@@ -19181,6 +19218,7 @@ async function executionStatus(cfg, doFetch) {
19181
19218
  success: true,
19182
19219
  ready: unreadable.length === 0 ? true : undefined,
19183
19220
  notice: tierBody?.maxExpired ? tierBody.maxExpiredNotice : undefined,
19221
+ updateAvailable: updateAvailable() ?? undefined,
19184
19222
  unreadable: unreadable.length > 0 ? unreadable.map(([name]) => name) : undefined,
19185
19223
  wallets: wallets2.body,
19186
19224
  tier: tier.body,
@@ -19192,6 +19230,7 @@ async function executionStatus(cfg, doFetch) {
19192
19230
  var SWEEP_BASE_ASSETS;
19193
19231
  var init_orchestrate = __esm(() => {
19194
19232
  init_convert();
19233
+ init_update_notice();
19195
19234
  SWEEP_BASE_ASSETS = {
19196
19235
  solana: ["USDC", "CNDL", "SOL"],
19197
19236
  hood: ["USDG", "ETH"]
@@ -19321,6 +19360,7 @@ function buildRequest(name, args, cfg) {
19321
19360
  async function callAndRelay(name, args, cfg) {
19322
19361
  const { url, init } = buildRequest(name, args, cfg);
19323
19362
  const res = await fetch(url, init);
19363
+ noteVersionHeaders(res);
19324
19364
  const text = await res.text();
19325
19365
  return {
19326
19366
  content: [{ type: "text", text }],
@@ -19448,6 +19488,7 @@ var init_tools = __esm(() => {
19448
19488
  init_zod();
19449
19489
  init_convert();
19450
19490
  init_orchestrate();
19491
+ init_update_notice();
19451
19492
  TOOL_NAMES = [
19452
19493
  "candle_launch_token",
19453
19494
  "candle_get_market",
@@ -19546,9 +19587,6 @@ var init_tools = __esm(() => {
19546
19587
  };
19547
19588
  });
19548
19589
 
19549
- // ../mcp/src/version.ts
19550
- var SERVER_VERSION = "0.6.3";
19551
-
19552
19590
  // ../mcp/src/server.ts
19553
19591
  var exports_server = {};
19554
19592
  __export(exports_server, {
@@ -19677,6 +19715,10 @@ function classifyError(status, raw) {
19677
19715
  }
19678
19716
  return { message: `Request failed with status ${status}` };
19679
19717
  }
19718
+ var latestCliVersionSeen = null;
19719
+ function latestCliVersionFromApi() {
19720
+ return latestCliVersionSeen;
19721
+ }
19680
19722
  async function apiRequest(path, opts) {
19681
19723
  const url = buildUrl(opts.apiUrl, path);
19682
19724
  const insecure = insecureApiUrlFault(opts.apiUrl, opts.env ?? process.env);
@@ -19704,6 +19746,9 @@ async function apiRequest(path, opts) {
19704
19746
  raw: undefined
19705
19747
  };
19706
19748
  }
19749
+ const latestHeader = response.headers?.get?.("x-candle-cli-latest");
19750
+ if (latestHeader)
19751
+ latestCliVersionSeen = latestHeader;
19707
19752
  const text = await response.text();
19708
19753
  const raw = parseBody(text);
19709
19754
  if (response.ok) {
@@ -20260,7 +20305,7 @@ async function resolveApiKey(deps, profile) {
20260
20305
  }
20261
20306
 
20262
20307
  // src/version.ts
20263
- var CLI_VERSION = "0.8.3";
20308
+ var CLI_VERSION = "0.8.4";
20264
20309
 
20265
20310
  // src/commands/auth.ts
20266
20311
  var DEVICE_CODE_PATH = "/api/v1/agent/device/code";
@@ -20602,6 +20647,9 @@ async function authStatus(args, ctx) {
20602
20647
  var RELEASE_BASE_URL = "https://github.com/candledottv/agentic";
20603
20648
  var RELEASE_ISSUER = "https://token.actions.githubusercontent.com";
20604
20649
  var VERSION = /^\d+\.\d+\.\d+$/;
20650
+ function isPlainVersion(value) {
20651
+ return VERSION.test(value);
20652
+ }
20605
20653
  function releaseIdentityUri(version) {
20606
20654
  if (!VERSION.test(version))
20607
20655
  throw new Error(`invalid release version: ${version}`);
@@ -21538,6 +21586,58 @@ Console (keys, funding, withdrawal addresses, limits): ${portalDeviceUrl(apiUrl,
21538
21586
  // src/commands/update.ts
21539
21587
  import { createHash as createHash2, randomBytes } from "node:crypto";
21540
21588
 
21589
+ // src/progress.ts
21590
+ var FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
21591
+ var INTERVAL_MS = 80;
21592
+ function stepReporter(write, tty) {
21593
+ let timer = null;
21594
+ let frame = 0;
21595
+ let current = null;
21596
+ const clearSpinner = () => {
21597
+ if (timer !== null)
21598
+ clearInterval(timer);
21599
+ timer = null;
21600
+ if (tty && current !== null)
21601
+ write("\r\x1B[2K");
21602
+ current = null;
21603
+ };
21604
+ return {
21605
+ start(label) {
21606
+ clearSpinner();
21607
+ current = label;
21608
+ if (!tty) {
21609
+ write(`${label}...
21610
+ `);
21611
+ return;
21612
+ }
21613
+ write(`${FRAMES[0]} ${label}`);
21614
+ frame = 1;
21615
+ timer = setInterval(() => {
21616
+ write(`\r\x1B[2K${FRAMES[frame % FRAMES.length]} ${current}`);
21617
+ frame++;
21618
+ }, INTERVAL_MS);
21619
+ if (typeof timer === "object" && "unref" in timer)
21620
+ timer.unref();
21621
+ },
21622
+ done(label) {
21623
+ clearSpinner();
21624
+ write(`✓ ${label}
21625
+ `);
21626
+ },
21627
+ fail(label) {
21628
+ clearSpinner();
21629
+ write(`✗ ${label}
21630
+ `);
21631
+ },
21632
+ stop: clearSpinner
21633
+ };
21634
+ }
21635
+ function formatBytes(n) {
21636
+ if (n >= 1024 * 1024)
21637
+ return `${(n / (1024 * 1024)).toFixed(1)} MB`;
21638
+ return `${Math.max(1, Math.round(n / 1024))} KB`;
21639
+ }
21640
+
21541
21641
  // src/bun-crypto-shim.ts
21542
21642
  import crypto2, { KeyObject } from "node:crypto";
21543
21643
  var original = crypto2.verify.bind(crypto2);
@@ -21880,12 +21980,19 @@ async function update(args, ctx) {
21880
21980
  }, json);
21881
21981
  return 1;
21882
21982
  }
21983
+ const steps = json ? stepReporter(() => {}, false) : stepReporter((text) => deps.stderr.write(text), process.stderr.isTTY === true);
21984
+ if (!json)
21985
+ deps.stderr.write(`Updating candle ${CLI_VERSION} -> ${target.version}
21986
+ `);
21987
+ steps.start(`downloading ${expectedName}`);
21883
21988
  const download = await fetchAll(deps, base, target.tag, expectedName);
21884
21989
  if (!download.ok) {
21990
+ steps.fail(`downloading ${expectedName}`);
21885
21991
  writeLocalFailure(deps, { code: "UPDATE_UNREACHABLE", message: download.message }, json);
21886
21992
  return 1;
21887
21993
  }
21888
21994
  const { bytes, sums, bundle } = download;
21995
+ steps.done(`downloaded ${expectedName} (${formatBytes(bytes.length)})`);
21889
21996
  const dir = realExec.slice(0, realExec.lastIndexOf("/")) || ".";
21890
21997
  const tmpPath = `${dir}/.candle-update-${target.version}-${randomBytes(6).toString("hex")}`;
21891
21998
  try {
@@ -21894,10 +22001,12 @@ async function update(args, ctx) {
21894
22001
  writeLocalFailure(deps, notWritable(dir, error), json);
21895
22002
  return 1;
21896
22003
  }
22004
+ steps.start("verifying checksum");
21897
22005
  const actual = createHash2("sha256").update(bytes).digest("hex");
21898
22006
  const fromSums = sums.split(`
21899
22007
  `).map((line) => line.trim().split(/\s+/)).find((parts) => parts[1] === expectedName)?.[0];
21900
22008
  if (actual !== asset.sha256 || actual !== fromSums) {
22009
+ steps.fail("verifying checksum");
21901
22010
  await discard(deps, tmpPath);
21902
22011
  writeLocalFailure(deps, {
21903
22012
  code: "UPDATE_VERIFY_FAILED",
@@ -21905,9 +22014,12 @@ async function update(args, ctx) {
21905
22014
  }, json);
21906
22015
  return 1;
21907
22016
  }
22017
+ steps.done("checksum verified");
22018
+ steps.start("verifying signature");
21908
22019
  const verify = deps.verify ?? verifyReleaseAsset;
21909
22020
  const verdict = verify(bytes, bundle, identityUri, RELEASE_ISSUER);
21910
22021
  if (!verdict.ok) {
22022
+ steps.fail("verifying signature");
21911
22023
  await discard(deps, tmpPath);
21912
22024
  writeLocalFailure(deps, {
21913
22025
  code: "UPDATE_VERIFY_FAILED",
@@ -21916,13 +22028,17 @@ async function update(args, ctx) {
21916
22028
  }, json);
21917
22029
  return 1;
21918
22030
  }
22031
+ steps.done("signature verified");
22032
+ steps.start("installing");
21919
22033
  try {
21920
22034
  await deps.rename(tmpPath, realExec);
21921
22035
  } catch (error) {
22036
+ steps.fail("installing");
21922
22037
  await discard(deps, tmpPath);
21923
22038
  writeLocalFailure(deps, notWritable(dir, error), json);
21924
22039
  return 1;
21925
22040
  }
22041
+ steps.done(`installed to ${realExec}`);
21926
22042
  if (json) {
21927
22043
  const payload = { current: CLI_VERSION, latest: target.version, updated: true, path: realExec };
21928
22044
  deps.stdout.write(`${JSON.stringify(payload)}
@@ -28500,6 +28616,30 @@ async function resolveSecretStore(platform = process.platform) {
28500
28616
  return { store: new EncryptedFileSecretStore, backend: "encrypted-file" };
28501
28617
  }
28502
28618
 
28619
+ // src/update-notice.ts
28620
+ var DAY_MS = 24 * 60 * 60 * 1000;
28621
+ async function maybeWriteUpdateNotice(deps, opts = {}) {
28622
+ try {
28623
+ if (deps.env.CANDLE_NO_UPDATE_NOTIFIER)
28624
+ return;
28625
+ if (opts.command === "update" || opts.command === "doctor")
28626
+ return;
28627
+ const latest = latestCliVersionFromApi();
28628
+ if (!latest || !isPlainVersion(latest) || compareVersions(latest, CLI_VERSION) <= 0)
28629
+ return;
28630
+ const config = await deps.readConfig();
28631
+ const prior = config.updateNotice;
28632
+ if (prior && prior.version === latest && deps.now() - prior.shownAt < DAY_MS)
28633
+ return;
28634
+ const line = `Update available: candle ${CLI_VERSION} -> ${latest}. Run: candle update`;
28635
+ const tty = process.stderr.isTTY === true;
28636
+ deps.stderr.write(tty ? `\x1B[2m${line}\x1B[0m
28637
+ ` : `${line}
28638
+ `);
28639
+ await deps.writeConfig({ updateNotice: { version: latest, shownAt: deps.now() } });
28640
+ } catch {}
28641
+ }
28642
+
28503
28643
  // src/index.ts
28504
28644
  function extractGlobalFlags(argv) {
28505
28645
  const rest = [];
@@ -28616,6 +28756,13 @@ function routesToCommand(cmd, sub) {
28616
28756
  }
28617
28757
  var NEVER_GUARDED = new Set(["auth", "profile", "doctor", "verify", "update"]);
28618
28758
  async function run2(argv, deps) {
28759
+ const code = await runCommand(argv, deps);
28760
+ const extractedForNotice = extractGlobalFlags(argv);
28761
+ const word = "error" in extractedForNotice ? undefined : canonicalCommand(extractedForNotice.rest[0] === "candle" ? extractedForNotice.rest[1] : extractedForNotice.rest[0]);
28762
+ await maybeWriteUpdateNotice(deps, { command: word });
28763
+ return code;
28764
+ }
28765
+ async function runCommand(argv, deps) {
28619
28766
  const extracted = extractGlobalFlags(argv);
28620
28767
  if ("error" in extracted) {
28621
28768
  deps.stderr.write(`${extracted.error}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@candledottv/cli",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
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": {