@candledottv/cli 0.8.2 → 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 +177 -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 {
@@ -19175,10 +19212,13 @@ async function executionStatus(cfg, doFetch) {
19175
19212
  ["tier", tier],
19176
19213
  ["limits", limits]
19177
19214
  ].filter(([, r]) => !r.ok);
19215
+ const tierBody = tier.ok ? tier.body : undefined;
19178
19216
  return {
19179
19217
  text: JSON.stringify({
19180
19218
  success: true,
19181
19219
  ready: unreadable.length === 0 ? true : undefined,
19220
+ notice: tierBody?.maxExpired ? tierBody.maxExpiredNotice : undefined,
19221
+ updateAvailable: updateAvailable() ?? undefined,
19182
19222
  unreadable: unreadable.length > 0 ? unreadable.map(([name]) => name) : undefined,
19183
19223
  wallets: wallets2.body,
19184
19224
  tier: tier.body,
@@ -19190,6 +19230,7 @@ async function executionStatus(cfg, doFetch) {
19190
19230
  var SWEEP_BASE_ASSETS;
19191
19231
  var init_orchestrate = __esm(() => {
19192
19232
  init_convert();
19233
+ init_update_notice();
19193
19234
  SWEEP_BASE_ASSETS = {
19194
19235
  solana: ["USDC", "CNDL", "SOL"],
19195
19236
  hood: ["USDG", "ETH"]
@@ -19319,6 +19360,7 @@ function buildRequest(name, args, cfg) {
19319
19360
  async function callAndRelay(name, args, cfg) {
19320
19361
  const { url, init } = buildRequest(name, args, cfg);
19321
19362
  const res = await fetch(url, init);
19363
+ noteVersionHeaders(res);
19322
19364
  const text = await res.text();
19323
19365
  return {
19324
19366
  content: [{ type: "text", text }],
@@ -19446,6 +19488,7 @@ var init_tools = __esm(() => {
19446
19488
  init_zod();
19447
19489
  init_convert();
19448
19490
  init_orchestrate();
19491
+ init_update_notice();
19449
19492
  TOOL_NAMES = [
19450
19493
  "candle_launch_token",
19451
19494
  "candle_get_market",
@@ -19544,9 +19587,6 @@ var init_tools = __esm(() => {
19544
19587
  };
19545
19588
  });
19546
19589
 
19547
- // ../mcp/src/version.ts
19548
- var SERVER_VERSION = "0.6.2";
19549
-
19550
19590
  // ../mcp/src/server.ts
19551
19591
  var exports_server = {};
19552
19592
  __export(exports_server, {
@@ -19675,6 +19715,10 @@ function classifyError(status, raw) {
19675
19715
  }
19676
19716
  return { message: `Request failed with status ${status}` };
19677
19717
  }
19718
+ var latestCliVersionSeen = null;
19719
+ function latestCliVersionFromApi() {
19720
+ return latestCliVersionSeen;
19721
+ }
19678
19722
  async function apiRequest(path, opts) {
19679
19723
  const url = buildUrl(opts.apiUrl, path);
19680
19724
  const insecure = insecureApiUrlFault(opts.apiUrl, opts.env ?? process.env);
@@ -19702,6 +19746,9 @@ async function apiRequest(path, opts) {
19702
19746
  raw: undefined
19703
19747
  };
19704
19748
  }
19749
+ const latestHeader = response.headers?.get?.("x-candle-cli-latest");
19750
+ if (latestHeader)
19751
+ latestCliVersionSeen = latestHeader;
19705
19752
  const text = await response.text();
19706
19753
  const raw = parseBody(text);
19707
19754
  if (response.ok) {
@@ -20258,7 +20305,7 @@ async function resolveApiKey(deps, profile) {
20258
20305
  }
20259
20306
 
20260
20307
  // src/version.ts
20261
- var CLI_VERSION = "0.8.2";
20308
+ var CLI_VERSION = "0.8.4";
20262
20309
 
20263
20310
  // src/commands/auth.ts
20264
20311
  var DEVICE_CODE_PATH = "/api/v1/agent/device/code";
@@ -20600,6 +20647,9 @@ async function authStatus(args, ctx) {
20600
20647
  var RELEASE_BASE_URL = "https://github.com/candledottv/agentic";
20601
20648
  var RELEASE_ISSUER = "https://token.actions.githubusercontent.com";
20602
20649
  var VERSION = /^\d+\.\d+\.\d+$/;
20650
+ function isPlainVersion(value) {
20651
+ return VERSION.test(value);
20652
+ }
20603
20653
  function releaseIdentityUri(version) {
20604
20654
  if (!VERSION.test(version))
20605
20655
  throw new Error(`invalid release version: ${version}`);
@@ -20735,6 +20785,30 @@ async function doctor(args, ctx) {
20735
20785
  passDetail
20736
20786
  }));
20737
20787
  }
20788
+ if (!apiKey) {
20789
+ rows.push({ check: "Plan", state: "SKIP", detail: "no API key to check" });
20790
+ } else {
20791
+ const tierRes = await apiRequest("/api/v1/agent/tier", {
20792
+ auth: "key",
20793
+ credentials: { apiKey },
20794
+ apiUrl,
20795
+ fetch: deps.fetch,
20796
+ env: deps.env
20797
+ });
20798
+ const tierBody = tierRes.ok ? tierRes.body : undefined;
20799
+ if (!tierBody || typeof tierBody.tier !== "string") {
20800
+ rows.push({ check: "Plan", state: "SKIP", detail: "tier not reported" });
20801
+ } else if (tierBody.maxExpired) {
20802
+ rows.push({
20803
+ check: "Plan",
20804
+ state: "WARN",
20805
+ detail: tierBody.maxExpiredNotice ?? `Max expired; this account is now on the ${tierBody.tier} plan`
20806
+ });
20807
+ } else {
20808
+ const fee = typeof tierBody.feeBps === "number" ? ` (${tierBody.feeBps / 100}% trade fee)` : "";
20809
+ rows.push({ check: "Plan", state: "PASS", detail: `${tierBody.tier}${fee}` });
20810
+ }
20811
+ }
20738
20812
  let account;
20739
20813
  if (!apiKey) {
20740
20814
  rows.push({ check: "Launch wallet delegated", state: "SKIP", detail: "no API key to check" });
@@ -21512,6 +21586,58 @@ Console (keys, funding, withdrawal addresses, limits): ${portalDeviceUrl(apiUrl,
21512
21586
  // src/commands/update.ts
21513
21587
  import { createHash as createHash2, randomBytes } from "node:crypto";
21514
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
+
21515
21641
  // src/bun-crypto-shim.ts
21516
21642
  import crypto2, { KeyObject } from "node:crypto";
21517
21643
  var original = crypto2.verify.bind(crypto2);
@@ -21854,12 +21980,19 @@ async function update(args, ctx) {
21854
21980
  }, json);
21855
21981
  return 1;
21856
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}`);
21857
21988
  const download = await fetchAll(deps, base, target.tag, expectedName);
21858
21989
  if (!download.ok) {
21990
+ steps.fail(`downloading ${expectedName}`);
21859
21991
  writeLocalFailure(deps, { code: "UPDATE_UNREACHABLE", message: download.message }, json);
21860
21992
  return 1;
21861
21993
  }
21862
21994
  const { bytes, sums, bundle } = download;
21995
+ steps.done(`downloaded ${expectedName} (${formatBytes(bytes.length)})`);
21863
21996
  const dir = realExec.slice(0, realExec.lastIndexOf("/")) || ".";
21864
21997
  const tmpPath = `${dir}/.candle-update-${target.version}-${randomBytes(6).toString("hex")}`;
21865
21998
  try {
@@ -21868,10 +22001,12 @@ async function update(args, ctx) {
21868
22001
  writeLocalFailure(deps, notWritable(dir, error), json);
21869
22002
  return 1;
21870
22003
  }
22004
+ steps.start("verifying checksum");
21871
22005
  const actual = createHash2("sha256").update(bytes).digest("hex");
21872
22006
  const fromSums = sums.split(`
21873
22007
  `).map((line) => line.trim().split(/\s+/)).find((parts) => parts[1] === expectedName)?.[0];
21874
22008
  if (actual !== asset.sha256 || actual !== fromSums) {
22009
+ steps.fail("verifying checksum");
21875
22010
  await discard(deps, tmpPath);
21876
22011
  writeLocalFailure(deps, {
21877
22012
  code: "UPDATE_VERIFY_FAILED",
@@ -21879,9 +22014,12 @@ async function update(args, ctx) {
21879
22014
  }, json);
21880
22015
  return 1;
21881
22016
  }
22017
+ steps.done("checksum verified");
22018
+ steps.start("verifying signature");
21882
22019
  const verify = deps.verify ?? verifyReleaseAsset;
21883
22020
  const verdict = verify(bytes, bundle, identityUri, RELEASE_ISSUER);
21884
22021
  if (!verdict.ok) {
22022
+ steps.fail("verifying signature");
21885
22023
  await discard(deps, tmpPath);
21886
22024
  writeLocalFailure(deps, {
21887
22025
  code: "UPDATE_VERIFY_FAILED",
@@ -21890,13 +22028,17 @@ async function update(args, ctx) {
21890
22028
  }, json);
21891
22029
  return 1;
21892
22030
  }
22031
+ steps.done("signature verified");
22032
+ steps.start("installing");
21893
22033
  try {
21894
22034
  await deps.rename(tmpPath, realExec);
21895
22035
  } catch (error) {
22036
+ steps.fail("installing");
21896
22037
  await discard(deps, tmpPath);
21897
22038
  writeLocalFailure(deps, notWritable(dir, error), json);
21898
22039
  return 1;
21899
22040
  }
22041
+ steps.done(`installed to ${realExec}`);
21900
22042
  if (json) {
21901
22043
  const payload = { current: CLI_VERSION, latest: target.version, updated: true, path: realExec };
21902
22044
  deps.stdout.write(`${JSON.stringify(payload)}
@@ -28474,6 +28616,30 @@ async function resolveSecretStore(platform = process.platform) {
28474
28616
  return { store: new EncryptedFileSecretStore, backend: "encrypted-file" };
28475
28617
  }
28476
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
+
28477
28643
  // src/index.ts
28478
28644
  function extractGlobalFlags(argv) {
28479
28645
  const rest = [];
@@ -28590,6 +28756,13 @@ function routesToCommand(cmd, sub) {
28590
28756
  }
28591
28757
  var NEVER_GUARDED = new Set(["auth", "profile", "doctor", "verify", "update"]);
28592
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) {
28593
28766
  const extracted = extractGlobalFlags(argv);
28594
28767
  if ("error" in extracted) {
28595
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.2",
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": {