codex-token-tracker 0.1.0 → 0.1.1

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 CHANGED
@@ -20,6 +20,8 @@ npm install -g codex-token-tracker
20
20
  npx codex-token-tracker
21
21
  ```
22
22
 
23
+ > **npm 11+ / pnpm 10+ block Electron's install script by default.** That's fine: the first `codex-tracker` run downloads the Electron runtime itself (~100 MB, once). To do it during install instead: `npm install -g codex-token-tracker --allow-scripts=electron`.
24
+
23
25
  Node.js 20+ is required. Electron is an *optional* dependency: if its binary cannot be downloaded (locked-down servers, WSL without a desktop), the package still installs and runs in agent mode.
24
26
 
25
27
  ## Quick start
package/dist/cli.js CHANGED
@@ -1101,11 +1101,11 @@ var init_src = __esm({
1101
1101
 
1102
1102
  // src/core/config.ts
1103
1103
  function configDir() {
1104
- return process.env.CODEX_TRACKER_HOME || import_node_path.default.join(import_node_os.default.homedir(), ".codex-tracker");
1104
+ return process.env.CODEX_TRACKER_HOME || import_node_path2.default.join(import_node_os2.default.homedir(), ".codex-tracker");
1105
1105
  }
1106
1106
  function readJson(file, fallback) {
1107
1107
  try {
1108
- const raw = import_node_fs.default.readFileSync(file, "utf8");
1108
+ const raw = import_node_fs2.default.readFileSync(file, "utf8");
1109
1109
  const parsed = JSON.parse(raw);
1110
1110
  return parsed && typeof parsed === "object" ? parsed : fallback;
1111
1111
  } catch {
@@ -1113,13 +1113,13 @@ function readJson(file, fallback) {
1113
1113
  }
1114
1114
  }
1115
1115
  function writeJsonAtomic(file, value) {
1116
- import_node_fs.default.mkdirSync(import_node_path.default.dirname(file), { recursive: true });
1116
+ import_node_fs2.default.mkdirSync(import_node_path2.default.dirname(file), { recursive: true });
1117
1117
  const tmp = `${file}.${process.pid}.tmp`;
1118
- import_node_fs.default.writeFileSync(tmp, JSON.stringify(value, null, 2), { mode: 384 });
1119
- import_node_fs.default.renameSync(tmp, file);
1118
+ import_node_fs2.default.writeFileSync(tmp, JSON.stringify(value, null, 2), { mode: 384 });
1119
+ import_node_fs2.default.renameSync(tmp, file);
1120
1120
  }
1121
1121
  function configPath() {
1122
- return import_node_path.default.join(configDir(), "config.json");
1122
+ return import_node_path2.default.join(configDir(), "config.json");
1123
1123
  }
1124
1124
  function parseBool(raw) {
1125
1125
  return ["1", "true", "yes", "on"].includes(raw.trim().toLowerCase());
@@ -1224,7 +1224,7 @@ function coerceConfigValue(key, raw) {
1224
1224
  }
1225
1225
  }
1226
1226
  function statePath() {
1227
- return import_node_path.default.join(configDir(), "state.json");
1227
+ return import_node_path2.default.join(configDir(), "state.json");
1228
1228
  }
1229
1229
  function loadState() {
1230
1230
  const s = readJson(statePath(), {});
@@ -1241,7 +1241,7 @@ function clearState() {
1241
1241
  saveState({ pushedBuckets: {}, pushedSessions: {}, lastUploadAt: null });
1242
1242
  }
1243
1243
  function pricingPath() {
1244
- return import_node_path.default.join(configDir(), "pricing.json");
1244
+ return import_node_path2.default.join(configDir(), "pricing.json");
1245
1245
  }
1246
1246
  function loadPricingOverrides() {
1247
1247
  const raw = readJson(pricingPath(), {});
@@ -1259,13 +1259,13 @@ function loadPricingOverrides() {
1259
1259
  }
1260
1260
  return Object.keys(out).length ? out : void 0;
1261
1261
  }
1262
- var import_node_fs, import_node_os, import_node_path, DEFAULT_DASHBOARD_URL, SOURCE_FORMATS, DEFAULT_SOURCES, SOURCE_IDS, DEFAULT_CONFIG, EDITABLE_KEYS, AGENT_NAME_RE;
1262
+ var import_node_fs2, import_node_os2, import_node_path2, DEFAULT_DASHBOARD_URL, SOURCE_FORMATS, DEFAULT_SOURCES, SOURCE_IDS, DEFAULT_CONFIG, EDITABLE_KEYS, AGENT_NAME_RE;
1263
1263
  var init_config = __esm({
1264
1264
  "src/core/config.ts"() {
1265
1265
  "use strict";
1266
- import_node_fs = __toESM(require("node:fs"));
1267
- import_node_os = __toESM(require("node:os"));
1268
- import_node_path = __toESM(require("node:path"));
1266
+ import_node_fs2 = __toESM(require("node:fs"));
1267
+ import_node_os2 = __toESM(require("node:os"));
1268
+ import_node_path2 = __toESM(require("node:path"));
1269
1269
  DEFAULT_DASHBOARD_URL = "https://codex.chenli.dev";
1270
1270
  SOURCE_FORMATS = ["codex", "pi", "generic", "opencode", "cline"];
1271
1271
  DEFAULT_SOURCES = { codex: true, pi: true, hermes: true, opencode: true, cline: true, roo: true, kilo: true };
@@ -1404,6 +1404,8 @@ Options:
1404
1404
  cliUnknownCommand: "Unknown command: {command}",
1405
1405
  cliNoDisplay: "No display detected \u2014 starting agent mode (run `codex-tracker menubar` on a desktop).",
1406
1406
  cliNoElectron: "Electron is not installed (optional dependency) \u2014 starting agent mode.",
1407
+ cliDownloadingElectron: "Electron runtime not found \u2014 downloading it now (about 100 MB, one time)\u2026",
1408
+ cliElectronDownloadFailed: "Electron download failed. Retry with `npm install -g codex-token-tracker --allow-scripts=electron` or `npm rebuild -g electron`; the headless `codex-tracker agent` works without it.",
1407
1409
  cliStartingMenubar: "Starting menu bar app\u2026",
1408
1410
  cliLoginStart: "Connecting this device to {dashboard}",
1409
1411
  cliLoginCode: "Your code: {code}",
@@ -1538,6 +1540,8 @@ var init_zh = __esm({
1538
1540
  cliUnknownCommand: "\u672A\u77E5\u547D\u4EE4\uFF1A{command}",
1539
1541
  cliNoDisplay: "\u672A\u68C0\u6D4B\u5230\u663E\u793A\u73AF\u5883 \u2014 \u4EE5 agent \u6A21\u5F0F\u542F\u52A8\uFF08\u684C\u9762\u73AF\u5883\u8BF7\u8FD0\u884C `codex-tracker menubar`\uFF09\u3002",
1540
1542
  cliNoElectron: "\u672A\u5B89\u88C5 Electron\uFF08\u53EF\u9009\u4F9D\u8D56\uFF09\u2014 \u4EE5 agent \u6A21\u5F0F\u542F\u52A8\u3002",
1543
+ cliDownloadingElectron: "\u672A\u627E\u5230 Electron \u8FD0\u884C\u65F6\uFF0C\u6B63\u5728\u4E0B\u8F7D\uFF08\u7EA6 100 MB\uFF0C\u4EC5\u9700\u4E00\u6B21\uFF09\u2026",
1544
+ cliElectronDownloadFailed: "Electron \u4E0B\u8F7D\u5931\u8D25\u3002\u53EF\u91CD\u8BD5 `npm install -g codex-token-tracker --allow-scripts=electron` \u6216 `npm rebuild -g electron`\uFF1B\u65E0\u754C\u9762\u7684 `codex-tracker agent` \u65E0\u9700 Electron\u3002",
1541
1545
  cliStartingMenubar: "\u6B63\u5728\u542F\u52A8\u83DC\u5355\u680F\u5E94\u7528\u2026",
1542
1546
  cliLoginStart: "\u6B63\u5728\u5C06\u672C\u8BBE\u5907\u8FDE\u63A5\u5230 {dashboard}",
1543
1547
  cliLoginCode: "\u4F60\u7684\u4EE3\u7801\uFF1A{code}",
@@ -1646,7 +1650,7 @@ function isWSL() {
1646
1650
  if (process.platform !== "linux") return wslCache = false;
1647
1651
  if (process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP) return wslCache = true;
1648
1652
  try {
1649
- const v2 = import_node_fs2.default.readFileSync("/proc/version", "utf8").toLowerCase();
1653
+ const v2 = import_node_fs3.default.readFileSync("/proc/version", "utf8").toLowerCase();
1650
1654
  return wslCache = v2.includes("microsoft") || v2.includes("wsl");
1651
1655
  } catch {
1652
1656
  return wslCache = false;
@@ -1662,7 +1666,7 @@ function platformLabel(kind = platformKind()) {
1662
1666
  }
1663
1667
  function hostname() {
1664
1668
  try {
1665
- return import_node_os2.default.hostname().replace(/\.local$/, "");
1669
+ return import_node_os3.default.hostname().replace(/\.local$/, "");
1666
1670
  } catch {
1667
1671
  return "unknown-host";
1668
1672
  }
@@ -1680,7 +1684,7 @@ function hasDisplay() {
1680
1684
  function run(cmd, args) {
1681
1685
  return new Promise((resolve) => {
1682
1686
  try {
1683
- const child = (0, import_node_child_process.spawn)(cmd, args, { stdio: "ignore", detached: true, windowsHide: true });
1687
+ const child = (0, import_node_child_process2.spawn)(cmd, args, { stdio: "ignore", detached: true, windowsHide: true });
1684
1688
  child.on("error", () => resolve(false));
1685
1689
  child.on("spawn", () => {
1686
1690
  child.unref();
@@ -1713,13 +1717,13 @@ function systemLocale() {
1713
1717
  return "en";
1714
1718
  }
1715
1719
  }
1716
- var import_node_fs2, import_node_os2, import_node_child_process, wslCache;
1720
+ var import_node_fs3, import_node_os3, import_node_child_process2, wslCache;
1717
1721
  var init_platform = __esm({
1718
1722
  "src/core/platform.ts"() {
1719
1723
  "use strict";
1720
- import_node_fs2 = __toESM(require("node:fs"));
1721
- import_node_os2 = __toESM(require("node:os"));
1722
- import_node_child_process = require("node:child_process");
1724
+ import_node_fs3 = __toESM(require("node:fs"));
1725
+ import_node_os3 = __toESM(require("node:os"));
1726
+ import_node_child_process2 = require("node:child_process");
1723
1727
  init_i18n();
1724
1728
  wslCache = null;
1725
1729
  }
@@ -3190,12 +3194,12 @@ function createApi(pathParts = []) {
3190
3194
  `API path is expected to be of the form \`api.moduleName.functionName\`. Found: \`${found}\``
3191
3195
  );
3192
3196
  }
3193
- const path11 = pathParts.slice(0, -1).join("/");
3197
+ const path12 = pathParts.slice(0, -1).join("/");
3194
3198
  const exportName = pathParts[pathParts.length - 1];
3195
3199
  if (exportName === "default") {
3196
- return path11;
3200
+ return path12;
3197
3201
  } else {
3198
- return path11 + ":" + exportName;
3202
+ return path12 + ":" + exportName;
3199
3203
  }
3200
3204
  } else if (prop === Symbol.toStringTag) {
3201
3205
  return "FunctionReference";
@@ -3490,16 +3494,16 @@ var init_simple_client_node = __esm({
3490
3494
  });
3491
3495
  require_node_gyp_build = __commonJS({
3492
3496
  "../node_modules/.pnpm/node-gyp-build@4.8.4/node_modules/node-gyp-build/node-gyp-build.js"(exports2, module2) {
3493
- var fs8 = __require("fs");
3494
- var path11 = __require("path");
3495
- var os7 = __require("os");
3497
+ var fs10 = __require("fs");
3498
+ var path12 = __require("path");
3499
+ var os8 = __require("os");
3496
3500
  var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
3497
3501
  var vars2 = process.config && process.config.variables || {};
3498
3502
  var prebuildsOnly = !!process.env.PREBUILDS_ONLY;
3499
3503
  var abi = process.versions.modules;
3500
3504
  var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
3501
- var arch = process.env.npm_config_arch || os7.arch();
3502
- var platform = process.env.npm_config_platform || os7.platform();
3505
+ var arch = process.env.npm_config_arch || os8.arch();
3506
+ var platform = process.env.npm_config_platform || os8.platform();
3503
3507
  var libc = process.env.LIBC || (isAlpine(platform) ? "musl" : "glibc");
3504
3508
  var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars2.arm_version) || "";
3505
3509
  var uv = (process.versions.uv || "").split(".")[0];
@@ -3508,21 +3512,21 @@ var init_simple_client_node = __esm({
3508
3512
  return runtimeRequire(load.resolve(dir));
3509
3513
  }
3510
3514
  load.resolve = load.path = function(dir) {
3511
- dir = path11.resolve(dir || ".");
3515
+ dir = path12.resolve(dir || ".");
3512
3516
  try {
3513
- var name = runtimeRequire(path11.join(dir, "package.json")).name.toUpperCase().replace(/-/g, "_");
3517
+ var name = runtimeRequire(path12.join(dir, "package.json")).name.toUpperCase().replace(/-/g, "_");
3514
3518
  if (process.env[name + "_PREBUILD"]) dir = process.env[name + "_PREBUILD"];
3515
3519
  } catch (err) {
3516
3520
  }
3517
3521
  if (!prebuildsOnly) {
3518
- var release = getFirst(path11.join(dir, "build/Release"), matchBuild);
3522
+ var release = getFirst(path12.join(dir, "build/Release"), matchBuild);
3519
3523
  if (release) return release;
3520
- var debug = getFirst(path11.join(dir, "build/Debug"), matchBuild);
3524
+ var debug = getFirst(path12.join(dir, "build/Debug"), matchBuild);
3521
3525
  if (debug) return debug;
3522
3526
  }
3523
3527
  var prebuild = resolve(dir);
3524
3528
  if (prebuild) return prebuild;
3525
- var nearby = resolve(path11.dirname(process.execPath));
3529
+ var nearby = resolve(path12.dirname(process.execPath));
3526
3530
  if (nearby) return nearby;
3527
3531
  var target = [
3528
3532
  "platform=" + platform,
@@ -3539,26 +3543,26 @@ var init_simple_client_node = __esm({
3539
3543
  ].filter(Boolean).join(" ");
3540
3544
  throw new Error("No native build was found for " + target + "\n loaded from: " + dir + "\n");
3541
3545
  function resolve(dir2) {
3542
- var tuples = readdirSync(path11.join(dir2, "prebuilds")).map(parseTuple);
3546
+ var tuples = readdirSync(path12.join(dir2, "prebuilds")).map(parseTuple);
3543
3547
  var tuple = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0];
3544
3548
  if (!tuple) return;
3545
- var prebuilds = path11.join(dir2, "prebuilds", tuple.name);
3549
+ var prebuilds = path12.join(dir2, "prebuilds", tuple.name);
3546
3550
  var parsed = readdirSync(prebuilds).map(parseTags);
3547
3551
  var candidates = parsed.filter(matchTags(runtime, abi));
3548
3552
  var winner = candidates.sort(compareTags(runtime))[0];
3549
- if (winner) return path11.join(prebuilds, winner.file);
3553
+ if (winner) return path12.join(prebuilds, winner.file);
3550
3554
  }
3551
3555
  };
3552
3556
  function readdirSync(dir) {
3553
3557
  try {
3554
- return fs8.readdirSync(dir);
3558
+ return fs10.readdirSync(dir);
3555
3559
  } catch (err) {
3556
3560
  return [];
3557
3561
  }
3558
3562
  }
3559
3563
  function getFirst(dir, filter) {
3560
3564
  var files = readdirSync(dir).filter(filter);
3561
- return files[0] && path11.join(dir, files[0]);
3565
+ return files[0] && path12.join(dir, files[0]);
3562
3566
  }
3563
3567
  function matchBuild(name) {
3564
3568
  return /\.node$/.test(name);
@@ -3645,7 +3649,7 @@ var init_simple_client_node = __esm({
3645
3649
  return typeof window !== "undefined" && window.process && window.process.type === "renderer";
3646
3650
  }
3647
3651
  function isAlpine(platform2) {
3648
- return platform2 === "linux" && fs8.existsSync("/etc/alpine-release");
3652
+ return platform2 === "linux" && fs10.existsSync("/etc/alpine-release");
3649
3653
  }
3650
3654
  load.parseTags = parseTags;
3651
3655
  load.matchTags = matchTags;
@@ -8719,8 +8723,158 @@ var init_auth = __esm({
8719
8723
  });
8720
8724
 
8721
8725
  // src/cli.ts
8722
- var import_node_path10 = __toESM(require("node:path"));
8723
- var import_node_child_process3 = require("node:child_process");
8726
+ var import_node_path11 = __toESM(require("node:path"));
8727
+ var import_node_fs9 = __toESM(require("node:fs"));
8728
+ var import_node_child_process4 = require("node:child_process");
8729
+
8730
+ // src/core/electron-install.ts
8731
+ var import_node_fs = __toESM(require("node:fs"));
8732
+ var import_node_os = __toESM(require("node:os"));
8733
+ var import_node_path = __toESM(require("node:path"));
8734
+ var import_node_child_process = require("node:child_process");
8735
+ function archName() {
8736
+ switch (process.arch) {
8737
+ case "x64":
8738
+ return "x64";
8739
+ case "arm64":
8740
+ return "arm64";
8741
+ case "ia32":
8742
+ return "ia32";
8743
+ case "arm":
8744
+ return "armv7l";
8745
+ default:
8746
+ return process.arch;
8747
+ }
8748
+ }
8749
+ function platformExecutablePath() {
8750
+ switch (process.platform) {
8751
+ case "darwin":
8752
+ return "Electron.app/Contents/MacOS/Electron";
8753
+ case "win32":
8754
+ return "electron.exe";
8755
+ default:
8756
+ return "electron";
8757
+ }
8758
+ }
8759
+ function electronCacheRoots() {
8760
+ const roots = [];
8761
+ if (process.env.electron_config_cache) roots.push(process.env.electron_config_cache);
8762
+ const home = import_node_os.default.homedir();
8763
+ if (process.platform === "darwin") roots.push(import_node_path.default.join(home, "Library", "Caches", "electron"));
8764
+ else if (process.platform === "win32") {
8765
+ if (process.env.LOCALAPPDATA) roots.push(import_node_path.default.join(process.env.LOCALAPPDATA, "electron", "Cache"));
8766
+ } else roots.push(import_node_path.default.join(process.env.XDG_CACHE_HOME ?? import_node_path.default.join(home, ".cache"), "electron"));
8767
+ return roots.filter((r) => import_node_fs.default.existsSync(r));
8768
+ }
8769
+ function findInDirs(dirs, filename, depth = 3) {
8770
+ for (const dir of dirs) {
8771
+ const found = walk(dir, filename, depth);
8772
+ if (found) return found;
8773
+ }
8774
+ return null;
8775
+ }
8776
+ function walk(dir, filename, depth) {
8777
+ let entries;
8778
+ try {
8779
+ entries = import_node_fs.default.readdirSync(dir, { withFileTypes: true });
8780
+ } catch {
8781
+ return null;
8782
+ }
8783
+ for (const e of entries) {
8784
+ const p = import_node_path.default.join(dir, e.name);
8785
+ if (e.isFile() && e.name === filename && import_node_fs.default.statSync(p).size > 1e6) return p;
8786
+ }
8787
+ if (depth <= 0) return null;
8788
+ for (const e of entries) {
8789
+ if (e.isDirectory()) {
8790
+ const r = walk(import_node_path.default.join(dir, e.name), filename, depth - 1);
8791
+ if (r) return r;
8792
+ }
8793
+ }
8794
+ return null;
8795
+ }
8796
+ function downloadUrl(version2, filename) {
8797
+ const mirror = process.env.ELECTRON_MIRROR || "https://github.com/electron/electron/releases/download/v";
8798
+ const dir = process.env.ELECTRON_CUSTOM_DIR ? process.env.ELECTRON_CUSTOM_DIR.replace("{{ version }}", version2) : version2;
8799
+ const base = mirror.endsWith("/") || mirror.endsWith("v") ? mirror : mirror + "/";
8800
+ return `${base}${dir}/${filename}`;
8801
+ }
8802
+ async function download(url, dest, log2) {
8803
+ const res = await fetch(url, { redirect: "follow" });
8804
+ if (!res.ok || !res.body) throw new Error(`HTTP ${res.status} for ${url}`);
8805
+ const total = Number(res.headers.get("content-length") || 0);
8806
+ import_node_fs.default.mkdirSync(import_node_path.default.dirname(dest), { recursive: true });
8807
+ const tmp = dest + ".part";
8808
+ const out = import_node_fs.default.createWriteStream(tmp);
8809
+ let received = 0;
8810
+ let lastPct = -10;
8811
+ const reader = res.body.getReader();
8812
+ try {
8813
+ for (; ; ) {
8814
+ const { done, value } = await reader.read();
8815
+ if (done) break;
8816
+ received += value.byteLength;
8817
+ if (!out.write(value)) await new Promise((r) => out.once("drain", () => r()));
8818
+ if (total) {
8819
+ const pct = Math.floor(received / total * 100);
8820
+ if (pct >= lastPct + 10) {
8821
+ lastPct = pct;
8822
+ log2.info(` ${pct}% (${(received / 1048576).toFixed(0)} / ${(total / 1048576).toFixed(0)} MB)`);
8823
+ }
8824
+ }
8825
+ }
8826
+ } finally {
8827
+ await new Promise((resolve) => out.end(() => resolve()));
8828
+ }
8829
+ import_node_fs.default.renameSync(tmp, dest);
8830
+ }
8831
+ function extractZip(zip, dest) {
8832
+ import_node_fs.default.rmSync(dest, { recursive: true, force: true });
8833
+ import_node_fs.default.mkdirSync(dest, { recursive: true });
8834
+ const attempts = process.platform === "darwin" ? [["ditto", ["-x", "-k", zip, dest]], ["unzip", ["-q", "-o", zip, "-d", dest]]] : process.platform === "win32" ? [
8835
+ ["tar", ["-xf", zip, "-C", dest]],
8836
+ ["powershell", ["-NoProfile", "-Command", `Expand-Archive -LiteralPath '${zip}' -DestinationPath '${dest}' -Force`]]
8837
+ ] : [["unzip", ["-q", "-o", zip, "-d", dest]], ["bsdtar", ["-xf", zip, "-C", dest]], ["tar", ["-xf", zip, "-C", dest]]];
8838
+ for (const [cmd, args] of attempts) {
8839
+ const r = (0, import_node_child_process.spawnSync)(cmd, args, { stdio: "ignore", windowsHide: true });
8840
+ if (r.status === 0 && import_node_fs.default.existsSync(import_node_path.default.join(dest, platformExecutablePath()))) return true;
8841
+ }
8842
+ return false;
8843
+ }
8844
+ async function installElectronBinary(pkgDir, log2) {
8845
+ let version2;
8846
+ try {
8847
+ version2 = JSON.parse(import_node_fs.default.readFileSync(import_node_path.default.join(pkgDir, "package.json"), "utf8")).version;
8848
+ } catch {
8849
+ return null;
8850
+ }
8851
+ const platform = process.platform === "darwin" && process.env.npm_config_platform === "mas" ? "mas" : process.platform;
8852
+ const filename = `electron-v${version2}-${platform}-${archName()}.zip`;
8853
+ const dist = import_node_path.default.join(pkgDir, "dist");
8854
+ const exe = import_node_path.default.join(dist, platformExecutablePath());
8855
+ let zip = findInDirs(electronCacheRoots(), filename);
8856
+ if (zip) log2.info(` using cached ${import_node_path.default.basename(zip)}`);
8857
+ else {
8858
+ const cacheRoot = electronCacheRoots()[0] ?? import_node_path.default.join(import_node_os.default.tmpdir(), "codex-tracker-electron");
8859
+ zip = import_node_path.default.join(cacheRoot, "codex-token-tracker", filename);
8860
+ const url = downloadUrl(version2, filename);
8861
+ log2.info(` ${url}`);
8862
+ try {
8863
+ await download(url, zip, log2);
8864
+ } catch (err) {
8865
+ log2.error(` download failed: ${err.message}`);
8866
+ return null;
8867
+ }
8868
+ }
8869
+ if (!extractZip(zip, dist)) {
8870
+ log2.error(" extraction failed (no ditto/unzip/tar available?)");
8871
+ return null;
8872
+ }
8873
+ import_node_fs.default.writeFileSync(import_node_path.default.join(pkgDir, "path.txt"), platformExecutablePath());
8874
+ return import_node_fs.default.existsSync(exe) ? exe : null;
8875
+ }
8876
+
8877
+ // src/cli.ts
8724
8878
  init_src();
8725
8879
  init_config();
8726
8880
 
@@ -8730,42 +8884,42 @@ init_src();
8730
8884
  init_config();
8731
8885
 
8732
8886
  // src/core/store.ts
8733
- var import_node_fs6 = __toESM(require("node:fs"));
8887
+ var import_node_fs7 = __toESM(require("node:fs"));
8734
8888
 
8735
8889
  // src/core/sources/index.ts
8736
- var import_node_fs5 = __toESM(require("node:fs"));
8737
- var import_node_os6 = __toESM(require("node:os"));
8738
- var import_node_path8 = __toESM(require("node:path"));
8739
- var import_node_child_process2 = require("node:child_process");
8890
+ var import_node_fs6 = __toESM(require("node:fs"));
8891
+ var import_node_os7 = __toESM(require("node:os"));
8892
+ var import_node_path9 = __toESM(require("node:path"));
8893
+ var import_node_child_process3 = require("node:child_process");
8740
8894
  init_platform();
8741
8895
  init_config();
8742
8896
 
8743
8897
  // src/core/sources/codex.ts
8744
- var import_node_os3 = __toESM(require("node:os"));
8745
- var import_node_path3 = __toESM(require("node:path"));
8898
+ var import_node_os4 = __toESM(require("node:os"));
8899
+ var import_node_path4 = __toESM(require("node:path"));
8746
8900
  init_src();
8747
8901
 
8748
8902
  // src/core/sources/util.ts
8749
- var import_node_fs3 = __toESM(require("node:fs"));
8750
- var import_node_path2 = __toESM(require("node:path"));
8903
+ var import_node_fs4 = __toESM(require("node:fs"));
8904
+ var import_node_path3 = __toESM(require("node:path"));
8751
8905
  var TWO_DAYS = 2 * 864e5;
8752
8906
  function isDir(p) {
8753
8907
  try {
8754
- return import_node_fs3.default.statSync(p).isDirectory();
8908
+ return import_node_fs4.default.statSync(p).isDirectory();
8755
8909
  } catch {
8756
8910
  return false;
8757
8911
  }
8758
8912
  }
8759
8913
  function isFile(p) {
8760
8914
  try {
8761
- return import_node_fs3.default.statSync(p).isFile();
8915
+ return import_node_fs4.default.statSync(p).isFile();
8762
8916
  } catch {
8763
8917
  return false;
8764
8918
  }
8765
8919
  }
8766
8920
  function listDirs(p) {
8767
8921
  try {
8768
- return import_node_fs3.default.readdirSync(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
8922
+ return import_node_fs4.default.readdirSync(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
8769
8923
  } catch {
8770
8924
  return [];
8771
8925
  }
@@ -8775,8 +8929,8 @@ function recentSubdirs(root, maxAgeMs = TWO_DAYS) {
8775
8929
  const now = Date.now();
8776
8930
  for (const name of listDirs(root)) {
8777
8931
  try {
8778
- const st = import_node_fs3.default.statSync(import_node_path2.default.join(root, name));
8779
- if (now - st.mtimeMs < maxAgeMs) out.push(import_node_path2.default.join(root, name));
8932
+ const st = import_node_fs4.default.statSync(import_node_path3.default.join(root, name));
8933
+ if (now - st.mtimeMs < maxAgeMs) out.push(import_node_path3.default.join(root, name));
8780
8934
  } catch {
8781
8935
  }
8782
8936
  }
@@ -8784,7 +8938,7 @@ function recentSubdirs(root, maxAgeMs = TWO_DAYS) {
8784
8938
  }
8785
8939
  function readJsonFile(p) {
8786
8940
  try {
8787
- return JSON.parse(import_node_fs3.default.readFileSync(p, "utf8"));
8941
+ return JSON.parse(import_node_fs4.default.readFileSync(p, "utf8"));
8788
8942
  } catch {
8789
8943
  return null;
8790
8944
  }
@@ -8793,7 +8947,7 @@ function makeRoot(dir, source, agent, format, kind, origin, exts, maxDepth = 6,
8793
8947
  return { dir, source, agent, format, kind, origin, exts, maxDepth, text };
8794
8948
  }
8795
8949
  function basenameNoExt(p) {
8796
- return import_node_path2.default.basename(p).replace(/\.[^.]+$/, "");
8950
+ return import_node_path3.default.basename(p).replace(/\.[^.]+$/, "");
8797
8951
  }
8798
8952
  function projectNameOf(cwd) {
8799
8953
  if (!cwd) return null;
@@ -8830,11 +8984,11 @@ function mergeSessions(parts) {
8830
8984
 
8831
8985
  // src/core/sources/codex.ts
8832
8986
  function codexHome() {
8833
- return process.env.CODEX_HOME || import_node_path3.default.join(import_node_os3.default.homedir(), ".codex");
8987
+ return process.env.CODEX_HOME || import_node_path4.default.join(import_node_os4.default.homedir(), ".codex");
8834
8988
  }
8835
8989
  function underHome(h, envVar, ...rel) {
8836
- if (envVar && h.home === import_node_os3.default.homedir()) return envVar;
8837
- return import_node_path3.default.join(h.home, ...rel);
8990
+ if (envVar && h.home === import_node_os4.default.homedir()) return envVar;
8991
+ return import_node_path4.default.join(h.home, ...rel);
8838
8992
  }
8839
8993
  function dateDirs(root) {
8840
8994
  const dirs = [];
@@ -8842,10 +8996,10 @@ function dateDirs(root) {
8842
8996
  for (const offset of [0, -1]) {
8843
8997
  const d = /* @__PURE__ */ new Date();
8844
8998
  d.setDate(d.getDate() + offset);
8845
- dirs.push(import_node_path3.default.join(root, String(d.getFullYear()), pad(d.getMonth() + 1), pad(d.getDate())));
8999
+ dirs.push(import_node_path4.default.join(root, String(d.getFullYear()), pad(d.getMonth() + 1), pad(d.getDate())));
8846
9000
  }
8847
9001
  const u = /* @__PURE__ */ new Date();
8848
- dirs.push(import_node_path3.default.join(root, String(u.getUTCFullYear()), pad(u.getUTCMonth() + 1), pad(u.getUTCDate())));
9002
+ dirs.push(import_node_path4.default.join(root, String(u.getUTCFullYear()), pad(u.getUTCMonth() + 1), pad(u.getUTCDate())));
8849
9003
  return [...new Set(dirs)];
8850
9004
  }
8851
9005
  var codexSource = {
@@ -8856,8 +9010,8 @@ var codexSource = {
8856
9010
  const roots = [];
8857
9011
  for (const h of ctx.homes) {
8858
9012
  const home = underHome(h, process.env.CODEX_HOME, ".codex");
8859
- const sessions = import_node_path3.default.join(home, "sessions");
8860
- const archived = import_node_path3.default.join(home, "archived_sessions");
9013
+ const sessions = import_node_path4.default.join(home, "sessions");
9014
+ const archived = import_node_path4.default.join(home, "archived_sessions");
8861
9015
  if (isDir(sessions)) roots.push(makeRoot(sessions, "codex", "codex", "codex", "sessions", h.origin, [".jsonl"]));
8862
9016
  if (isDir(archived)) roots.push(makeRoot(archived, "codex", "codex", "codex", "archived", h.origin, [".jsonl"], 1));
8863
9017
  }
@@ -8878,12 +9032,12 @@ var codexSource = {
8878
9032
  };
8879
9033
 
8880
9034
  // src/core/sources/pi.ts
8881
- var import_node_os4 = __toESM(require("node:os"));
8882
- var import_node_path4 = __toESM(require("node:path"));
9035
+ var import_node_os5 = __toESM(require("node:os"));
9036
+ var import_node_path5 = __toESM(require("node:path"));
8883
9037
  init_src();
8884
9038
  function underHome2(h, envVar, ...rel) {
8885
- if (envVar && h.home === import_node_os4.default.homedir()) return envVar;
8886
- return import_node_path4.default.join(h.home, ...rel);
9039
+ if (envVar && h.home === import_node_os5.default.homedir()) return envVar;
9040
+ return import_node_path5.default.join(h.home, ...rel);
8887
9041
  }
8888
9042
  var piSource = {
8889
9043
  id: "pi",
@@ -8892,7 +9046,7 @@ var piSource = {
8892
9046
  discover(ctx) {
8893
9047
  const roots = [];
8894
9048
  for (const h of ctx.homes) {
8895
- const dir = import_node_path4.default.join(underHome2(h, process.env.PI_CODING_AGENT_DIR, ".pi", "agent"), "sessions");
9049
+ const dir = import_node_path5.default.join(underHome2(h, process.env.PI_CODING_AGENT_DIR, ".pi", "agent"), "sessions");
8896
9050
  if (isDir(dir)) roots.push(makeRoot(dir, "pi", "pi", "pi", "flat", h.origin, [".jsonl"], 3));
8897
9051
  }
8898
9052
  return roots;
@@ -8925,12 +9079,12 @@ var genericSource = {
8925
9079
  };
8926
9080
 
8927
9081
  // src/core/sources/hermes.ts
8928
- var import_node_os5 = __toESM(require("node:os"));
8929
- var import_node_path5 = __toESM(require("node:path"));
9082
+ var import_node_os6 = __toESM(require("node:os"));
9083
+ var import_node_path6 = __toESM(require("node:path"));
8930
9084
  init_src();
8931
9085
  function underHome3(h, envVar, ...rel) {
8932
- if (envVar && h.home === import_node_os5.default.homedir()) return envVar;
8933
- return import_node_path5.default.join(h.home, ...rel);
9086
+ if (envVar && h.home === import_node_os6.default.homedir()) return envVar;
9087
+ return import_node_path6.default.join(h.home, ...rel);
8934
9088
  }
8935
9089
  var INPUT_COLS = ["input_tokens", "prompt_tokens", "tokens_in", "input"];
8936
9090
  var OUTPUT_COLS = ["output_tokens", "completion_tokens", "tokens_out", "output"];
@@ -9056,9 +9210,9 @@ var hermesSource = {
9056
9210
  const roots = [];
9057
9211
  for (const h of ctx.homes) {
9058
9212
  const home = underHome3(h, process.env.HERMES_HOME, ".hermes");
9059
- const sessions = import_node_path5.default.join(home, "sessions");
9213
+ const sessions = import_node_path6.default.join(home, "sessions");
9060
9214
  if (isDir(sessions)) roots.push(makeRoot(sessions, "hermes", "hermes", "generic", "flat", h.origin, [".jsonl", ".json"], 4));
9061
- if (isFile(import_node_path5.default.join(home, "state.db"))) roots.push(makeRoot(home, "hermes", "hermes", "generic", "flat", h.origin, ["state.db"], 0, false));
9215
+ if (isFile(import_node_path6.default.join(home, "state.db"))) roots.push(makeRoot(home, "hermes", "hermes", "generic", "flat", h.origin, ["state.db"], 0, false));
9062
9216
  }
9063
9217
  return roots;
9064
9218
  },
@@ -9086,17 +9240,17 @@ var hermesSource = {
9086
9240
  };
9087
9241
 
9088
9242
  // src/core/sources/opencode.ts
9089
- var import_node_fs4 = __toESM(require("node:fs"));
9090
- var import_node_path6 = __toESM(require("node:path"));
9243
+ var import_node_fs5 = __toESM(require("node:fs"));
9244
+ var import_node_path7 = __toESM(require("node:path"));
9091
9245
  init_src();
9092
9246
  function opencodeDataDirs(h, env) {
9093
9247
  if (h.layout === "win32") {
9094
- const local = h.origin === "local" && env.LOCALAPPDATA ? env.LOCALAPPDATA : import_node_path6.default.join(h.home, "AppData", "Local");
9095
- const roaming = h.origin === "local" && env.APPDATA ? env.APPDATA : import_node_path6.default.join(h.home, "AppData", "Roaming");
9096
- return [import_node_path6.default.join(local, "opencode"), import_node_path6.default.join(roaming, "opencode")];
9248
+ const local = h.origin === "local" && env.LOCALAPPDATA ? env.LOCALAPPDATA : import_node_path7.default.join(h.home, "AppData", "Local");
9249
+ const roaming = h.origin === "local" && env.APPDATA ? env.APPDATA : import_node_path7.default.join(h.home, "AppData", "Roaming");
9250
+ return [import_node_path7.default.join(local, "opencode"), import_node_path7.default.join(roaming, "opencode")];
9097
9251
  }
9098
- const xdg = h.origin === "local" && env.XDG_DATA_HOME ? env.XDG_DATA_HOME : import_node_path6.default.join(h.home, ".local", "share");
9099
- return [import_node_path6.default.join(xdg, "opencode")];
9252
+ const xdg = h.origin === "local" && env.XDG_DATA_HOME ? env.XDG_DATA_HOME : import_node_path7.default.join(h.home, ".local", "share");
9253
+ return [import_node_path7.default.join(xdg, "opencode")];
9100
9254
  }
9101
9255
  var sessionCache = /* @__PURE__ */ new Map();
9102
9256
  var authCache = /* @__PURE__ */ new Map();
@@ -9105,13 +9259,13 @@ function findSessionInfo(storage, sessionID) {
9105
9259
  const cached = sessionCache.get(key);
9106
9260
  if (cached && (cached.info || Date.now() - cached.at < 6e4)) return cached.info;
9107
9261
  let info = null;
9108
- const sessionDir = import_node_path6.default.join(storage, "session");
9109
- const direct = import_node_path6.default.join(sessionDir, "info", `${sessionID}.json`);
9110
- if (import_node_fs4.default.existsSync(direct)) info = readJsonFile(direct);
9262
+ const sessionDir = import_node_path7.default.join(storage, "session");
9263
+ const direct = import_node_path7.default.join(sessionDir, "info", `${sessionID}.json`);
9264
+ if (import_node_fs5.default.existsSync(direct)) info = readJsonFile(direct);
9111
9265
  if (!info) {
9112
9266
  for (const project of listDirs(sessionDir)) {
9113
- const p = import_node_path6.default.join(sessionDir, project, `${sessionID}.json`);
9114
- if (import_node_fs4.default.existsSync(p)) {
9267
+ const p = import_node_path7.default.join(sessionDir, project, `${sessionID}.json`);
9268
+ if (import_node_fs5.default.existsSync(p)) {
9115
9269
  info = readJsonFile(p);
9116
9270
  break;
9117
9271
  }
@@ -9121,10 +9275,10 @@ function findSessionInfo(storage, sessionID) {
9121
9275
  return info;
9122
9276
  }
9123
9277
  function openaiIsOAuth(dataDir) {
9124
- const file = import_node_path6.default.join(dataDir, "auth.json");
9278
+ const file = import_node_path7.default.join(dataDir, "auth.json");
9125
9279
  let mtime = 0;
9126
9280
  try {
9127
- mtime = import_node_fs4.default.statSync(file).mtimeMs;
9281
+ mtime = import_node_fs5.default.statSync(file).mtimeMs;
9128
9282
  } catch {
9129
9283
  return false;
9130
9284
  }
@@ -9136,9 +9290,9 @@ function openaiIsOAuth(dataDir) {
9136
9290
  return oauth;
9137
9291
  }
9138
9292
  function storageOf(file) {
9139
- const parts = file.split(import_node_path6.default.sep);
9293
+ const parts = file.split(import_node_path7.default.sep);
9140
9294
  const idx = parts.lastIndexOf("storage");
9141
- return idx >= 0 ? parts.slice(0, idx + 1).join(import_node_path6.default.sep) : null;
9295
+ return idx >= 0 ? parts.slice(0, idx + 1).join(import_node_path7.default.sep) : null;
9142
9296
  }
9143
9297
  var opencodeSource = {
9144
9298
  id: "opencode",
@@ -9149,7 +9303,7 @@ var opencodeSource = {
9149
9303
  const roots = [];
9150
9304
  for (const h of ctx.homes) {
9151
9305
  for (const data of opencodeDataDirs(h, ctx.env)) {
9152
- const storage = import_node_path6.default.join(data, "storage");
9306
+ const storage = import_node_path7.default.join(data, "storage");
9153
9307
  if (isDir(storage)) roots.push(makeRoot(storage, "opencode", "opencode", "opencode", "flat", h.origin, [".json"], 5));
9154
9308
  }
9155
9309
  }
@@ -9157,7 +9311,7 @@ var opencodeSource = {
9157
9311
  },
9158
9312
  hotDirs(root) {
9159
9313
  const out = [root.dir];
9160
- for (const base of [import_node_path6.default.join(root.dir, "message"), import_node_path6.default.join(root.dir, "session", "message")]) out.push(...recentSubdirs(base));
9314
+ for (const base of [import_node_path7.default.join(root.dir, "message"), import_node_path7.default.join(root.dir, "session", "message")]) out.push(...recentSubdirs(base));
9161
9315
  return out;
9162
9316
  },
9163
9317
  watchRecursively: () => true,
@@ -9171,7 +9325,7 @@ var opencodeSource = {
9171
9325
  if (!m || m.role !== "assistant" || !m.tokens || typeof m.sessionID !== "string") return null;
9172
9326
  const provider = typeof m.providerID === "string" ? m.providerID : null;
9173
9327
  const storage = storageOf(file.path);
9174
- const dataDir = storage ? import_node_path6.default.dirname(storage) : null;
9328
+ const dataDir = storage ? import_node_path7.default.dirname(storage) : null;
9175
9329
  const codexAuth = isCodexAuthProvider(provider) || provider === "openai" && !!dataDir && openaiIsOAuth(dataDir);
9176
9330
  if (!opts.includeAllProviders && !codexAuth) return null;
9177
9331
  const t2 = m.tokens;
@@ -9214,22 +9368,22 @@ var opencodeSource = {
9214
9368
  };
9215
9369
 
9216
9370
  // src/core/sources/cline.ts
9217
- var import_node_path7 = __toESM(require("node:path"));
9371
+ var import_node_path8 = __toESM(require("node:path"));
9218
9372
  init_src();
9219
9373
  var APPS = ["Code", "Code - Insiders", "Cursor", "Windsurf", "VSCodium", "Trae"];
9220
9374
  function globalStorageDirs(h, env) {
9221
9375
  const out = [];
9222
- if (h.layout === "darwin") for (const app of APPS) out.push(import_node_path7.default.join(h.home, "Library", "Application Support", app, "User", "globalStorage"));
9376
+ if (h.layout === "darwin") for (const app of APPS) out.push(import_node_path8.default.join(h.home, "Library", "Application Support", app, "User", "globalStorage"));
9223
9377
  else if (h.layout === "win32") {
9224
- const roaming = h.origin === "local" && env.APPDATA ? env.APPDATA : import_node_path7.default.join(h.home, "AppData", "Roaming");
9225
- for (const app of APPS) out.push(import_node_path7.default.join(roaming, app, "User", "globalStorage"));
9378
+ const roaming = h.origin === "local" && env.APPDATA ? env.APPDATA : import_node_path8.default.join(h.home, "AppData", "Roaming");
9379
+ for (const app of APPS) out.push(import_node_path8.default.join(roaming, app, "User", "globalStorage"));
9226
9380
  } else {
9227
- const cfg = h.origin === "local" && env.XDG_CONFIG_HOME ? env.XDG_CONFIG_HOME : import_node_path7.default.join(h.home, ".config");
9228
- for (const app of APPS) out.push(import_node_path7.default.join(cfg, app, "User", "globalStorage"));
9381
+ const cfg = h.origin === "local" && env.XDG_CONFIG_HOME ? env.XDG_CONFIG_HOME : import_node_path8.default.join(h.home, ".config");
9382
+ for (const app of APPS) out.push(import_node_path8.default.join(cfg, app, "User", "globalStorage"));
9229
9383
  }
9230
9384
  if (h.layout !== "win32") {
9231
9385
  for (const server of [".vscode-server", ".vscode-server-insiders", ".cursor-server", ".windsurf-server"]) {
9232
- out.push(import_node_path7.default.join(h.home, server, "data", "User", "globalStorage"));
9386
+ out.push(import_node_path8.default.join(h.home, server, "data", "User", "globalStorage"));
9233
9387
  }
9234
9388
  }
9235
9389
  return out;
@@ -9252,7 +9406,7 @@ function clineFamily(id, label, extensionId) {
9252
9406
  const roots = [];
9253
9407
  for (const h of ctx.homes) {
9254
9408
  for (const gs of globalStorageDirs(h, ctx.env)) {
9255
- const tasks = import_node_path7.default.join(gs, extensionId, "tasks");
9409
+ const tasks = import_node_path8.default.join(gs, extensionId, "tasks");
9256
9410
  if (isDir(tasks)) roots.push(makeRoot(tasks, id, id, "cline", "flat", h.origin, ["ui_messages.json"], 2));
9257
9411
  }
9258
9412
  }
@@ -9268,8 +9422,8 @@ function clineFamily(id, label, extensionId) {
9268
9422
  return null;
9269
9423
  }
9270
9424
  if (!Array.isArray(messages)) return null;
9271
- const taskDir = import_node_path7.default.dirname(file.path);
9272
- const meta = readJsonFile(import_node_path7.default.join(taskDir, "task_metadata.json")) ?? {};
9425
+ const taskDir = import_node_path8.default.dirname(file.path);
9426
+ const meta = readJsonFile(import_node_path8.default.join(taskDir, "task_metadata.json")) ?? {};
9273
9427
  const usageList = Array.isArray(meta.model_usage) ? meta.model_usage : [];
9274
9428
  const agent = file.root.agent;
9275
9429
  const events = [];
@@ -9321,7 +9475,7 @@ function clineFamily(id, label, extensionId) {
9321
9475
  }
9322
9476
  const cwd = meta.cwd ?? meta.cwd_on_task_initialization ?? meta.workspace ?? null;
9323
9477
  return {
9324
- sessionId: import_node_path7.default.basename(taskDir),
9478
+ sessionId: import_node_path8.default.basename(taskDir),
9325
9479
  agent,
9326
9480
  provider,
9327
9481
  startedAt: startedAt ?? lastActivityAt,
@@ -9364,7 +9518,7 @@ function sourceFor(root) {
9364
9518
  function wslDistros() {
9365
9519
  if (process.platform !== "win32") return [];
9366
9520
  try {
9367
- const raw = (0, import_node_child_process2.execFileSync)("wsl.exe", ["-l", "-q"], { timeout: 5e3, windowsHide: true });
9521
+ const raw = (0, import_node_child_process3.execFileSync)("wsl.exe", ["-l", "-q"], { timeout: 5e3, windowsHide: true });
9368
9522
  return raw.toString("utf16le").split(String.fromCharCode(0)).join("").split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
9369
9523
  } catch {
9370
9524
  return [];
@@ -9374,22 +9528,22 @@ var SKIP_WINDOWS_USERS = /* @__PURE__ */ new Set(["public", "default", "default
9374
9528
  function userHomes() {
9375
9529
  const kind = platformKind();
9376
9530
  const localLayout = kind === "darwin" ? "darwin" : kind === "win32" ? "win32" : "linux";
9377
- const out = [{ home: import_node_os6.default.homedir(), origin: "local", layout: localLayout }];
9531
+ const out = [{ home: import_node_os7.default.homedir(), origin: "local", layout: localLayout }];
9378
9532
  if (kind === "win32") {
9379
9533
  for (const distro of wslDistros()) {
9380
9534
  for (const prefix of [`\\\\wsl$\\${distro}`, `\\\\wsl.localhost\\${distro}`]) {
9381
- const home = import_node_path8.default.join(prefix, "home");
9535
+ const home = import_node_path9.default.join(prefix, "home");
9382
9536
  const users = listDirs(home);
9383
9537
  if (!users.length) continue;
9384
- for (const u of users) out.push({ home: import_node_path8.default.join(home, u), origin: "wsl", layout: "linux" });
9385
- out.push({ home: import_node_path8.default.join(prefix, "root"), origin: "wsl", layout: "linux" });
9538
+ for (const u of users) out.push({ home: import_node_path9.default.join(home, u), origin: "wsl", layout: "linux" });
9539
+ out.push({ home: import_node_path9.default.join(prefix, "root"), origin: "wsl", layout: "linux" });
9386
9540
  break;
9387
9541
  }
9388
9542
  }
9389
9543
  } else if (kind === "wsl") {
9390
9544
  for (const u of listDirs("/mnt/c/Users")) {
9391
9545
  if (SKIP_WINDOWS_USERS.has(u.toLowerCase())) continue;
9392
- out.push({ home: import_node_path8.default.join("/mnt/c/Users", u), origin: "windows", layout: "win32" });
9546
+ out.push({ home: import_node_path9.default.join("/mnt/c/Users", u), origin: "windows", layout: "win32" });
9393
9547
  }
9394
9548
  }
9395
9549
  return out;
@@ -9400,7 +9554,7 @@ function discoverSessionRoots(opts = {}) {
9400
9554
  const roots = [];
9401
9555
  const seen = /* @__PURE__ */ new Set();
9402
9556
  const add = (r) => {
9403
- const key = `${import_node_path8.default.resolve(r.dir)}|${r.exts.join(",")}`;
9557
+ const key = `${import_node_path9.default.resolve(r.dir)}|${r.exts.join(",")}`;
9404
9558
  if (seen.has(key)) return;
9405
9559
  seen.add(key);
9406
9560
  roots.push(r);
@@ -9415,8 +9569,8 @@ function discoverSessionRoots(opts = {}) {
9415
9569
  for (const entry of opts.extraSessionDirs ?? []) {
9416
9570
  const e = normalizeExtraDir(entry);
9417
9571
  if (!e) continue;
9418
- const dir = e.path.replace(/^~(?=$|\/|\\)/, import_node_os6.default.homedir());
9419
- if (!import_node_fs5.default.existsSync(dir)) continue;
9572
+ const dir = e.path.replace(/^~(?=$|\/|\\)/, import_node_os7.default.homedir());
9573
+ if (!import_node_fs6.default.existsSync(dir)) continue;
9420
9574
  const def = byFormat[e.format ?? "generic"] ?? genericSource;
9421
9575
  add(def.extraRoot(dir, e.agent));
9422
9576
  }
@@ -9424,28 +9578,28 @@ function discoverSessionRoots(opts = {}) {
9424
9578
  }
9425
9579
  function walkFiles(root) {
9426
9580
  const out = [];
9427
- const walk = (dir, depth) => {
9581
+ const walk2 = (dir, depth) => {
9428
9582
  let entries;
9429
9583
  try {
9430
- entries = import_node_fs5.default.readdirSync(dir, { withFileTypes: true });
9584
+ entries = import_node_fs6.default.readdirSync(dir, { withFileTypes: true });
9431
9585
  } catch {
9432
9586
  return;
9433
9587
  }
9434
9588
  for (const e of entries) {
9435
- const p = import_node_path8.default.join(dir, e.name);
9589
+ const p = import_node_path9.default.join(dir, e.name);
9436
9590
  if (e.isDirectory()) {
9437
- if (depth < root.maxDepth && !e.name.startsWith(".")) walk(p, depth + 1);
9591
+ if (depth < root.maxDepth && !e.name.startsWith(".")) walk2(p, depth + 1);
9438
9592
  } else if (e.isFile() && root.exts.some((x) => e.name.endsWith(x))) {
9439
9593
  out.push(p);
9440
9594
  }
9441
9595
  }
9442
9596
  };
9443
- walk(root.dir, 0);
9597
+ walk2(root.dir, 0);
9444
9598
  return out;
9445
9599
  }
9446
9600
  function listFiles(dir, exts) {
9447
9601
  try {
9448
- return import_node_fs5.default.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && exts.some((x) => e.name.endsWith(x))).map((e) => import_node_path8.default.join(dir, e.name));
9602
+ return import_node_fs6.default.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && exts.some((x) => e.name.endsWith(x))).map((e) => import_node_path9.default.join(dir, e.name));
9449
9603
  } catch {
9450
9604
  return [];
9451
9605
  }
@@ -9509,7 +9663,7 @@ var SessionStore = class {
9509
9663
  async syncFile(p, root) {
9510
9664
  let st;
9511
9665
  try {
9512
- st = await import_node_fs6.default.promises.stat(p);
9666
+ st = await import_node_fs7.default.promises.stat(p);
9513
9667
  } catch {
9514
9668
  if (this.files.delete(p)) return true;
9515
9669
  return false;
@@ -9519,7 +9673,7 @@ var SessionStore = class {
9519
9673
  let session = null;
9520
9674
  if (st.size <= MAX_FILE_BYTES) {
9521
9675
  try {
9522
- const text = root.text ? await import_node_fs6.default.promises.readFile(p, "utf8") : "";
9676
+ const text = root.text ? await import_node_fs7.default.promises.readFile(p, "utf8") : "";
9523
9677
  session = sourceFor(root).parse({ path: p, text, root }, { includeAllProviders: this.getOptions().trackAllProviders });
9524
9678
  } catch {
9525
9679
  session = null;
@@ -9604,8 +9758,8 @@ var SessionStore = class {
9604
9758
  for (const [dir, recursive] of wanted) {
9605
9759
  if (this.watchers.has(dir)) continue;
9606
9760
  try {
9607
- if (!import_node_fs6.default.existsSync(dir)) continue;
9608
- const w = import_node_fs6.default.watch(dir, { persistent: false, recursive }, () => this.scheduleChange());
9761
+ if (!import_node_fs7.default.existsSync(dir)) continue;
9762
+ const w = import_node_fs7.default.watch(dir, { persistent: false, recursive }, () => this.scheduleChange());
9609
9763
  w.on("error", () => {
9610
9764
  w.close();
9611
9765
  this.watchers.delete(dir);
@@ -9798,14 +9952,14 @@ init_auth();
9798
9952
  init_platform();
9799
9953
 
9800
9954
  // src/core/usage-api.ts
9801
- var import_node_fs7 = __toESM(require("node:fs"));
9802
- var import_node_path9 = __toESM(require("node:path"));
9955
+ var import_node_fs8 = __toESM(require("node:fs"));
9956
+ var import_node_path10 = __toESM(require("node:path"));
9803
9957
  init_src();
9804
9958
  function readCodexAuth() {
9805
- const file = import_node_path9.default.join(codexHome(), "auth.json");
9959
+ const file = import_node_path10.default.join(codexHome(), "auth.json");
9806
9960
  let raw;
9807
9961
  try {
9808
- raw = import_node_fs7.default.readFileSync(file, "utf8");
9962
+ raw = import_node_fs8.default.readFileSync(file, "utf8");
9809
9963
  } catch {
9810
9964
  return { error: `Codex login not found (${file})` };
9811
9965
  }
@@ -9857,7 +10011,7 @@ async function fetchLiveRateLimits(appVersion, timeoutMs = 1e4) {
9857
10011
  init_i18n();
9858
10012
 
9859
10013
  // src/version.ts
9860
- var APP_VERSION = true ? "0.1.0" : "0.0.0-dev";
10014
+ var APP_VERSION = true ? "0.1.1" : "0.0.0-dev";
9861
10015
 
9862
10016
  // src/core/engine.ts
9863
10017
  var SHALLOW_MS = 3e3;
@@ -10203,23 +10357,60 @@ function applyDashboardFlag(flags) {
10203
10357
  function electronBinary() {
10204
10358
  try {
10205
10359
  const p = require("electron");
10206
- return typeof p === "string" && p ? p : null;
10360
+ return typeof p === "string" && p && import_node_fs9.default.existsSync(p) ? p : null;
10361
+ } catch {
10362
+ return null;
10363
+ }
10364
+ }
10365
+ function electronPackageDir() {
10366
+ try {
10367
+ return import_node_path11.default.dirname(require.resolve("electron/package.json"));
10207
10368
  } catch {
10208
10369
  return null;
10209
10370
  }
10210
10371
  }
10211
- function startMenubar(background) {
10372
+ async function ensureElectron() {
10373
+ const existing = electronBinary();
10374
+ if (existing) return existing;
10375
+ const dir = electronPackageDir();
10376
+ if (!dir) return null;
10377
+ const t2 = makeT(lang());
10378
+ console.log(t2("cliDownloadingElectron"));
10379
+ const log2 = { info: (m) => console.log(m), error: (m) => console.error(m) };
10380
+ let bin = null;
10381
+ try {
10382
+ bin = await installElectronBinary(dir, log2);
10383
+ } catch (err) {
10384
+ log2.error(` ${err.message}`);
10385
+ }
10386
+ if (!bin) {
10387
+ const installer = import_node_path11.default.join(dir, "install.js");
10388
+ if (import_node_fs9.default.existsSync(installer)) {
10389
+ const env = { ...process.env };
10390
+ delete env.ELECTRON_SKIP_BINARY_DOWNLOAD;
10391
+ (0, import_node_child_process4.spawnSync)(process.execPath, [installer], { stdio: "inherit", cwd: dir, env });
10392
+ }
10393
+ }
10394
+ try {
10395
+ delete require.cache[require.resolve("electron")];
10396
+ } catch {
10397
+ }
10398
+ const found = electronBinary();
10399
+ if (!found) console.error(t2("cliElectronDownloadFailed"));
10400
+ return found;
10401
+ }
10402
+ async function startMenubar(background) {
10212
10403
  const t2 = makeT(lang());
10213
- const bin = electronBinary();
10404
+ const bin = await ensureElectron();
10214
10405
  if (!bin) {
10215
10406
  console.error(t2("cliNoElectron"));
10216
10407
  return 2;
10217
10408
  }
10218
10409
  const env = { ...process.env, CODEX_TRACKER_HOME: configDir() };
10219
10410
  delete env.ELECTRON_RUN_AS_NODE;
10220
- const mainPath = import_node_path10.default.join(__dirname, "main.js");
10411
+ const mainPath = import_node_path11.default.join(__dirname, "main.js");
10221
10412
  console.log(t2("cliStartingMenubar"));
10222
- const child = (0, import_node_child_process3.spawn)(bin, [mainPath], {
10413
+ const child = (0, import_node_child_process4.spawn)(bin, [mainPath], {
10223
10414
  stdio: background ? "ignore" : "inherit",
10224
10415
  detached: background,
10225
10416
  env,
@@ -10479,7 +10670,7 @@ async function main() {
10479
10670
  console.log(t2("cliNoDisplay"));
10480
10671
  return runAgent(args.flags);
10481
10672
  }
10482
- if (!electronBinary()) {
10673
+ if (!await ensureElectron()) {
10483
10674
  console.log(t2("cliNoElectron"));
10484
10675
  return runAgent(args.flags);
10485
10676
  }
package/dist/main.js CHANGED
@@ -1239,6 +1239,8 @@ Options:
1239
1239
  cliUnknownCommand: "Unknown command: {command}",
1240
1240
  cliNoDisplay: "No display detected \u2014 starting agent mode (run `codex-tracker menubar` on a desktop).",
1241
1241
  cliNoElectron: "Electron is not installed (optional dependency) \u2014 starting agent mode.",
1242
+ cliDownloadingElectron: "Electron runtime not found \u2014 downloading it now (about 100 MB, one time)\u2026",
1243
+ cliElectronDownloadFailed: "Electron download failed. Retry with `npm install -g codex-token-tracker --allow-scripts=electron` or `npm rebuild -g electron`; the headless `codex-tracker agent` works without it.",
1242
1244
  cliStartingMenubar: "Starting menu bar app\u2026",
1243
1245
  cliLoginStart: "Connecting this device to {dashboard}",
1244
1246
  cliLoginCode: "Your code: {code}",
@@ -1367,6 +1369,8 @@ var zh = {
1367
1369
  cliUnknownCommand: "\u672A\u77E5\u547D\u4EE4\uFF1A{command}",
1368
1370
  cliNoDisplay: "\u672A\u68C0\u6D4B\u5230\u663E\u793A\u73AF\u5883 \u2014 \u4EE5 agent \u6A21\u5F0F\u542F\u52A8\uFF08\u684C\u9762\u73AF\u5883\u8BF7\u8FD0\u884C `codex-tracker menubar`\uFF09\u3002",
1369
1371
  cliNoElectron: "\u672A\u5B89\u88C5 Electron\uFF08\u53EF\u9009\u4F9D\u8D56\uFF09\u2014 \u4EE5 agent \u6A21\u5F0F\u542F\u52A8\u3002",
1372
+ cliDownloadingElectron: "\u672A\u627E\u5230 Electron \u8FD0\u884C\u65F6\uFF0C\u6B63\u5728\u4E0B\u8F7D\uFF08\u7EA6 100 MB\uFF0C\u4EC5\u9700\u4E00\u6B21\uFF09\u2026",
1373
+ cliElectronDownloadFailed: "Electron \u4E0B\u8F7D\u5931\u8D25\u3002\u53EF\u91CD\u8BD5 `npm install -g codex-token-tracker --allow-scripts=electron` \u6216 `npm rebuild -g electron`\uFF1B\u65E0\u754C\u9762\u7684 `codex-tracker agent` \u65E0\u9700 Electron\u3002",
1370
1374
  cliStartingMenubar: "\u6B63\u5728\u542F\u52A8\u83DC\u5355\u680F\u5E94\u7528\u2026",
1371
1375
  cliLoginStart: "\u6B63\u5728\u5C06\u672C\u8BBE\u5907\u8FDE\u63A5\u5230 {dashboard}",
1372
1376
  cliLoginCode: "\u4F60\u7684\u4EE3\u7801\uFF1A{code}",
@@ -8964,7 +8968,7 @@ async function fetchLiveRateLimits(appVersion, timeoutMs = 1e4) {
8964
8968
  }
8965
8969
 
8966
8970
  // src/version.ts
8967
- var APP_VERSION = true ? "0.1.0" : "0.0.0-dev";
8971
+ var APP_VERSION = true ? "0.1.1" : "0.0.0-dev";
8968
8972
  var APP_NAME = "Codex Tracker";
8969
8973
 
8970
8974
  // src/core/engine.ts
@@ -12959,6 +12959,8 @@ Options:
12959
12959
  cliUnknownCommand: "Unknown command: {command}",
12960
12960
  cliNoDisplay: "No display detected \u2014 starting agent mode (run `codex-tracker menubar` on a desktop).",
12961
12961
  cliNoElectron: "Electron is not installed (optional dependency) \u2014 starting agent mode.",
12962
+ cliDownloadingElectron: "Electron runtime not found \u2014 downloading it now (about 100 MB, one time)\u2026",
12963
+ cliElectronDownloadFailed: "Electron download failed. Retry with `npm install -g codex-token-tracker --allow-scripts=electron` or `npm rebuild -g electron`; the headless `codex-tracker agent` works without it.",
12962
12964
  cliStartingMenubar: "Starting menu bar app\u2026",
12963
12965
  cliLoginStart: "Connecting this device to {dashboard}",
12964
12966
  cliLoginCode: "Your code: {code}",
@@ -13087,6 +13089,8 @@ Options:
13087
13089
  cliUnknownCommand: "\u672A\u77E5\u547D\u4EE4\uFF1A{command}",
13088
13090
  cliNoDisplay: "\u672A\u68C0\u6D4B\u5230\u663E\u793A\u73AF\u5883 \u2014 \u4EE5 agent \u6A21\u5F0F\u542F\u52A8\uFF08\u684C\u9762\u73AF\u5883\u8BF7\u8FD0\u884C `codex-tracker menubar`\uFF09\u3002",
13089
13091
  cliNoElectron: "\u672A\u5B89\u88C5 Electron\uFF08\u53EF\u9009\u4F9D\u8D56\uFF09\u2014 \u4EE5 agent \u6A21\u5F0F\u542F\u52A8\u3002",
13092
+ cliDownloadingElectron: "\u672A\u627E\u5230 Electron \u8FD0\u884C\u65F6\uFF0C\u6B63\u5728\u4E0B\u8F7D\uFF08\u7EA6 100 MB\uFF0C\u4EC5\u9700\u4E00\u6B21\uFF09\u2026",
13093
+ cliElectronDownloadFailed: "Electron \u4E0B\u8F7D\u5931\u8D25\u3002\u53EF\u91CD\u8BD5 `npm install -g codex-token-tracker --allow-scripts=electron` \u6216 `npm rebuild -g electron`\uFF1B\u65E0\u754C\u9762\u7684 `codex-tracker agent` \u65E0\u9700 Electron\u3002",
13090
13094
  cliStartingMenubar: "\u6B63\u5728\u542F\u52A8\u83DC\u5355\u680F\u5E94\u7528\u2026",
13091
13095
  cliLoginStart: "\u6B63\u5728\u5C06\u672C\u8BBE\u5907\u8FDE\u63A5\u5230 {dashboard}",
13092
13096
  cliLoginCode: "\u4F60\u7684\u4EE3\u7801\uFF1A{code}",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-token-tracker",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Menu bar / system tray app and headless agent that tracks OpenAI Codex token usage, cache hits, model mix and API-equivalent cost — and syncs it to your team dashboard.",
5
5
  "license": "MIT",
6
6
  "author": "Chen Li",