codeam-cli 2.4.33 → 2.4.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.
package/dist/index.js CHANGED
@@ -519,9 +519,9 @@ var require_windowsPtyAgent = __commonJS({
519
519
  "use strict";
520
520
  Object.defineProperty(exports2, "__esModule", { value: true });
521
521
  exports2.argsToCommandLine = exports2.WindowsPtyAgent = void 0;
522
- var fs9 = require("fs");
523
- var os8 = require("os");
524
- var path16 = require("path");
522
+ var fs11 = require("fs");
523
+ var os10 = require("os");
524
+ var path18 = require("path");
525
525
  var child_process_1 = require("child_process");
526
526
  var net_1 = require("net");
527
527
  var windowsConoutConnection_1 = require_windowsConoutConnection();
@@ -557,7 +557,7 @@ var require_windowsPtyAgent = __commonJS({
557
557
  }
558
558
  }
559
559
  this._ptyNative = this._useConpty ? conptyNative : winptyNative;
560
- cwd = path16.resolve(cwd);
560
+ cwd = path18.resolve(cwd);
561
561
  var commandLine = argsToCommandLine(file, args2);
562
562
  var term;
563
563
  if (this._useConpty) {
@@ -578,7 +578,7 @@ var require_windowsPtyAgent = __commonJS({
578
578
  this._outSocket.on("connect", function() {
579
579
  _this._outSocket.emit("ready_datapipe");
580
580
  });
581
- var inSocketFD = fs9.openSync(term.conin, "w");
581
+ var inSocketFD = fs11.openSync(term.conin, "w");
582
582
  this._inSocket = new net_1.Socket({
583
583
  fd: inSocketFD,
584
584
  readable: false,
@@ -679,7 +679,7 @@ var require_windowsPtyAgent = __commonJS({
679
679
  WindowsPtyAgent2.prototype._getConsoleProcessList = function() {
680
680
  var _this = this;
681
681
  return new Promise(function(resolve2) {
682
- var agent = child_process_1.fork(path16.join(__dirname, "conpty_console_list_agent"), [_this._innerPid.toString()]);
682
+ var agent = child_process_1.fork(path18.join(__dirname, "conpty_console_list_agent"), [_this._innerPid.toString()]);
683
683
  agent.on("message", function(message) {
684
684
  clearTimeout(timeout);
685
685
  resolve2(message.consoleProcessList);
@@ -702,7 +702,7 @@ var require_windowsPtyAgent = __commonJS({
702
702
  configurable: true
703
703
  });
704
704
  WindowsPtyAgent2.prototype._getWindowsBuildNumber = function() {
705
- var osVersion = /(\d+)\.(\d+)\.(\d+)/g.exec(os8.release());
705
+ var osVersion = /(\d+)\.(\d+)\.(\d+)/g.exec(os10.release());
706
706
  var buildNumber = 0;
707
707
  if (osVersion && osVersion.length === 4) {
708
708
  buildNumber = parseInt(osVersion[3]);
@@ -1012,15 +1012,15 @@ var require_unixTerminal = __commonJS({
1012
1012
  })();
1013
1013
  Object.defineProperty(exports2, "__esModule", { value: true });
1014
1014
  exports2.UnixTerminal = void 0;
1015
- var fs9 = require("fs");
1016
- var path16 = require("path");
1015
+ var fs11 = require("fs");
1016
+ var path18 = require("path");
1017
1017
  var tty = require("tty");
1018
1018
  var terminal_1 = require_terminal();
1019
1019
  var utils_1 = require_utils();
1020
1020
  var native = utils_1.loadNativeModule("pty");
1021
1021
  var pty = native.module;
1022
1022
  var helperPath = native.dir + "/spawn-helper";
1023
- helperPath = path16.resolve(__dirname, helperPath);
1023
+ helperPath = path18.resolve(__dirname, helperPath);
1024
1024
  helperPath = helperPath.replace("app.asar", "app.asar.unpacked");
1025
1025
  helperPath = helperPath.replace("node_modules.asar", "node_modules.asar.unpacked");
1026
1026
  var DEFAULT_FILE = "sh";
@@ -1275,7 +1275,7 @@ var require_unixTerminal = __commonJS({
1275
1275
  return;
1276
1276
  }
1277
1277
  var task = this._writeQueue[0];
1278
- fs9.write(this._fd, task.buffer, task.offset, function(err, written) {
1278
+ fs11.write(this._fd, task.buffer, task.offset, function(err, written) {
1279
1279
  if (err) {
1280
1280
  if ("code" in err && err.code === "EAGAIN") {
1281
1281
  _this._writeImmediate = setImmediate(function() {
@@ -1390,9 +1390,9 @@ var require_src = __commonJS({
1390
1390
  });
1391
1391
 
1392
1392
  // src/commands/start.ts
1393
- var fs7 = __toESM(require("fs"));
1394
- var os6 = __toESM(require("os"));
1395
- var path10 = __toESM(require("path"));
1393
+ var fs8 = __toESM(require("fs"));
1394
+ var os7 = __toESM(require("os"));
1395
+ var path11 = __toESM(require("path"));
1396
1396
  var import_crypto = require("crypto");
1397
1397
  var import_child_process5 = require("child_process");
1398
1398
  var import_picocolors2 = __toESM(require("picocolors"));
@@ -1482,8 +1482,9 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
1482
1482
  // package.json
1483
1483
  var package_default = {
1484
1484
  name: "codeam-cli",
1485
- version: "2.4.33",
1485
+ version: "2.4.35",
1486
1486
  description: "Remote control Claude Code (and other AI coding agents) from your mobile phone. Pair your device, send prompts, stream responses in real-time, and approve commands \u2014 from anywhere.",
1487
+ type: "commonjs",
1487
1488
  main: "dist/index.js",
1488
1489
  bin: {
1489
1490
  codeam: "dist/index.js"
@@ -1611,6 +1612,60 @@ function computePollDelay({ baseMs, failures }) {
1611
1612
  return Math.round(jitter);
1612
1613
  }
1613
1614
 
1615
+ // src/services/logger.ts
1616
+ var fs2 = __toESM(require("fs"));
1617
+ var os2 = __toESM(require("os"));
1618
+ var path2 = __toESM(require("path"));
1619
+ var LEVELS = { silent: 0, error: 1, warn: 2, info: 3, debug: 4, trace: 5 };
1620
+ function currentLevel() {
1621
+ if (process.env.CODEAM_DEBUG === "1") return LEVELS.trace;
1622
+ const raw = (process.env.CODEAM_LOG ?? "error").toLowerCase();
1623
+ return LEVELS[raw] ?? LEVELS.error;
1624
+ }
1625
+ var fileEnabled = process.env.CODEAM_DEBUG === "1" || process.env.CODEAM_LOG === "debug" || process.env.CODEAM_LOG === "trace";
1626
+ var debugFilePath = path2.join(os2.homedir(), ".codeam", "debug.log");
1627
+ var fileInitialized = false;
1628
+ function appendToFile(line) {
1629
+ if (!fileEnabled) return;
1630
+ try {
1631
+ if (!fileInitialized) {
1632
+ fs2.mkdirSync(path2.dirname(debugFilePath), { recursive: true });
1633
+ fs2.writeFileSync(
1634
+ debugFilePath,
1635
+ `=== codeam debug log \u2014 pid ${process.pid} \u2014 ${(/* @__PURE__ */ new Date()).toISOString()} ===
1636
+ platform=${process.platform} node=${process.version} cwd=${process.cwd()}
1637
+
1638
+ `
1639
+ );
1640
+ fileInitialized = true;
1641
+ }
1642
+ fs2.appendFileSync(debugFilePath, line);
1643
+ } catch {
1644
+ }
1645
+ }
1646
+ function emit(level, tag, msg, err) {
1647
+ if (LEVELS[level] > currentLevel()) return;
1648
+ const detail = err instanceof Error ? `: ${err.message}` : err !== void 0 ? `: ${String(err)}` : "";
1649
+ const line = `[codeam:${level}] ${tag} \u2014 ${msg}${detail}
1650
+ `;
1651
+ process.stderr.write(line);
1652
+ if (LEVELS[level] >= LEVELS.debug) {
1653
+ appendToFile(`${(/* @__PURE__ */ new Date()).toISOString()} ${line}`);
1654
+ }
1655
+ }
1656
+ var log = {
1657
+ error: (tag, msg, err) => emit("error", tag, msg, err),
1658
+ warn: (tag, msg, err) => emit("warn", tag, msg, err),
1659
+ info: (tag, msg, err) => emit("info", tag, msg, err),
1660
+ debug: (tag, msg, err) => emit("debug", tag, msg, err),
1661
+ /**
1662
+ * Verbose pipeline breadcrumb. Only fires when CODEAM_LOG=trace or
1663
+ * CODEAM_DEBUG=1, so call sites can be liberal — they have zero
1664
+ * cost in normal runs.
1665
+ */
1666
+ trace: (tag, msg, err) => emit("trace", tag, msg, err)
1667
+ };
1668
+
1614
1669
  // src/services/websocket.service.ts
1615
1670
  var API_BASE = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
1616
1671
  var WS_URL = API_BASE.replace("https://", "wss://").replace("http://", "ws://") + "/api/ws";
@@ -1640,6 +1695,7 @@ var WebSocketService = class {
1640
1695
  try {
1641
1696
  this.client = new import_ws.default(WS_URL);
1642
1697
  this.client.on("open", () => {
1698
+ log.trace("ws", `connected to ${WS_URL}`);
1643
1699
  this._connected = true;
1644
1700
  this.reconnectAttempts = 0;
1645
1701
  this.client.send(JSON.stringify({
@@ -1653,12 +1709,18 @@ var WebSocketService = class {
1653
1709
  this.client.on("message", (raw) => {
1654
1710
  try {
1655
1711
  const msg = JSON.parse(raw.toString());
1656
- if (msg.type === "pong" || msg.type === "auth_success" || msg.type === "auth_error") return;
1712
+ if (msg.type === "pong" || msg.type === "auth_success" || msg.type === "auth_error") {
1713
+ log.trace("ws", `meta msg type=${msg.type}`);
1714
+ return;
1715
+ }
1716
+ log.trace("ws", `dispatch msg type=${msg.type}`);
1657
1717
  this.handlers.forEach((h) => h.onMessage(msg.type, msg.payload ?? {}));
1658
- } catch {
1718
+ } catch (err) {
1719
+ log.trace("ws", "malformed message", err);
1659
1720
  }
1660
1721
  });
1661
- this.client.on("close", () => {
1722
+ this.client.on("close", (code, reason) => {
1723
+ log.trace("ws", `closed code=${code} reason=${reason?.toString() || "(empty)"}`);
1662
1724
  this._connected = false;
1663
1725
  this.stopHeartbeat();
1664
1726
  this.handlers.forEach((h) => h.onDisconnected());
@@ -1668,9 +1730,11 @@ var WebSocketService = class {
1668
1730
  this.reconnectTimer = setTimeout(() => this.connect(), delay);
1669
1731
  }
1670
1732
  });
1671
- this.client.on("error", () => {
1733
+ this.client.on("error", (err) => {
1734
+ log.trace("ws", "error", err);
1672
1735
  });
1673
- } catch {
1736
+ } catch (err) {
1737
+ log.trace("ws", "sync connect threw", err);
1674
1738
  }
1675
1739
  }
1676
1740
  send(type, payload) {
@@ -1706,7 +1770,7 @@ var WebSocketService = class {
1706
1770
  // src/services/pairing.service.ts
1707
1771
  var https = __toESM(require("https"));
1708
1772
  var http = __toESM(require("http"));
1709
- var os2 = __toESM(require("os"));
1773
+ var os3 = __toESM(require("os"));
1710
1774
  var API_BASE2 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
1711
1775
  async function requestCode(pluginId) {
1712
1776
  try {
@@ -1716,7 +1780,7 @@ async function requestCode(pluginId) {
1716
1780
  pluginId,
1717
1781
  ideName: "Terminal (codeam-cli)",
1718
1782
  ideVersion: package_default.version,
1719
- hostname: os2.hostname(),
1783
+ hostname: os3.hostname(),
1720
1784
  runtime,
1721
1785
  ...codespaceName ? { codespaceName } : {}
1722
1786
  });
@@ -1880,7 +1944,10 @@ var CommandRelayService = class {
1880
1944
  _running = false;
1881
1945
  pollTimer = null;
1882
1946
  heartbeatTimer = null;
1947
+ agentsTimer = null;
1883
1948
  consecutiveFailures = 0;
1949
+ /** True once `/api/plugin/agents` has accepted at least one report. */
1950
+ agentsRegistered = false;
1884
1951
  start() {
1885
1952
  if (this.pollTimer) {
1886
1953
  clearTimeout(this.pollTimer);
@@ -1890,11 +1957,19 @@ var CommandRelayService = class {
1890
1957
  clearInterval(this.heartbeatTimer);
1891
1958
  this.heartbeatTimer = null;
1892
1959
  }
1960
+ if (this.agentsTimer) {
1961
+ clearInterval(this.agentsTimer);
1962
+ this.agentsTimer = null;
1963
+ }
1893
1964
  this._running = true;
1965
+ this.agentsRegistered = false;
1894
1966
  this.sendHeartbeat(true);
1895
1967
  this.heartbeatTimer = setInterval(() => this.sendHeartbeat(true), 2e4);
1896
1968
  void this.pollLoop();
1897
1969
  this.reportAgents();
1970
+ this.agentsTimer = setInterval(() => {
1971
+ if (this._running && !this.agentsRegistered) this.reportAgents();
1972
+ }, 5e3);
1898
1973
  }
1899
1974
  stop() {
1900
1975
  if (!this._running) return;
@@ -1907,6 +1982,10 @@ var CommandRelayService = class {
1907
1982
  clearInterval(this.heartbeatTimer);
1908
1983
  this.heartbeatTimer = null;
1909
1984
  }
1985
+ if (this.agentsTimer) {
1986
+ clearInterval(this.agentsTimer);
1987
+ this.agentsTimer = null;
1988
+ }
1910
1989
  this.sendHeartbeat(false).catch(() => {
1911
1990
  });
1912
1991
  }
@@ -1932,32 +2011,43 @@ var CommandRelayService = class {
1932
2011
  const commands = data?.data;
1933
2012
  this.consecutiveFailures = 0;
1934
2013
  if (!Array.isArray(commands)) return;
2014
+ if (commands.length > 0) {
2015
+ log.trace("relay", `poll received ${commands.length} command(s)`);
2016
+ }
1935
2017
  for (const obj of commands) {
1936
2018
  try {
2019
+ log.trace("relay", `dispatch type=${obj.type} id=${obj.id}`);
1937
2020
  await this.onCommand({
1938
2021
  id: obj.id,
1939
2022
  sessionId: obj.sessionId,
1940
2023
  type: obj.type,
1941
2024
  payload: obj.payload ?? {}
1942
2025
  });
1943
- } catch {
2026
+ } catch (err) {
2027
+ log.trace("relay", `command handler threw`, err);
1944
2028
  }
1945
2029
  }
1946
- } catch {
2030
+ } catch (err) {
1947
2031
  this.consecutiveFailures += 1;
2032
+ log.trace(
2033
+ "relay",
2034
+ `poll failed (failures=${this.consecutiveFailures})`,
2035
+ err
2036
+ );
1948
2037
  }
1949
2038
  }
1950
2039
  async sendHeartbeat(online) {
1951
2040
  await _postJson(`${API_BASE3}/api/plugin/heartbeat`, {
1952
2041
  pluginId: this.pluginId,
1953
2042
  online
1954
- }).catch(() => {
1955
- });
2043
+ }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
1956
2044
  }
1957
2045
  reportAgents() {
1958
2046
  _postJson(`${API_BASE3}/api/plugin/agents`, {
1959
2047
  pluginId: this.pluginId,
1960
2048
  agents: [{ id: "claude-code", name: "Claude Code", icon: "\u{1F916}", installed: true }]
2049
+ }).then(() => {
2050
+ this.agentsRegistered = true;
1961
2051
  }).catch(() => {
1962
2052
  });
1963
2053
  }
@@ -1965,24 +2055,24 @@ var CommandRelayService = class {
1965
2055
 
1966
2056
  // src/services/pty/unix.strategy.ts
1967
2057
  var import_child_process = require("child_process");
1968
- var fs3 = __toESM(require("fs"));
1969
- var os3 = __toESM(require("os"));
1970
- var path3 = __toESM(require("path"));
2058
+ var fs4 = __toESM(require("fs"));
2059
+ var os4 = __toESM(require("os"));
2060
+ var path4 = __toESM(require("path"));
1971
2061
 
1972
2062
  // src/services/pty/types.ts
1973
- var fs2 = __toESM(require("fs"));
1974
- var path2 = __toESM(require("path"));
2063
+ var fs3 = __toESM(require("fs"));
2064
+ var path3 = __toESM(require("path"));
1975
2065
  function findInPath(name) {
1976
2066
  const isWin = process.platform === "win32";
1977
- const dirs = (process.env.PATH ?? "").split(path2.delimiter).filter(Boolean);
1978
- const hasExt = path2.extname(name).length > 0;
2067
+ const dirs = (process.env.PATH ?? "").split(path3.delimiter).filter(Boolean);
2068
+ const hasExt = path3.extname(name).length > 0;
1979
2069
  const candidates = isWin && !hasExt ? [`${name}.exe`, `${name}.cmd`, `${name}.bat`, `${name}.ps1`, name] : [name];
1980
- const accessFlag = isWin ? fs2.constants.F_OK : fs2.constants.X_OK;
2070
+ const accessFlag = isWin ? fs3.constants.F_OK : fs3.constants.X_OK;
1981
2071
  for (const dir of dirs) {
1982
2072
  for (const candidate of candidates) {
1983
- const full = path2.join(dir, candidate);
2073
+ const full = path3.join(dir, candidate);
1984
2074
  try {
1985
- fs2.accessSync(full, accessFlag);
2075
+ fs3.accessSync(full, accessFlag);
1986
2076
  return full;
1987
2077
  } catch {
1988
2078
  }
@@ -2066,8 +2156,8 @@ var UnixPtyStrategy = class {
2066
2156
  }
2067
2157
  const cols = process.stdout.columns || 220;
2068
2158
  const rows = process.stdout.rows || 50;
2069
- this.helperPath = path3.join(os3.tmpdir(), "codeam-pty-helper.py");
2070
- fs3.writeFileSync(this.helperPath, PYTHON_PTY_HELPER, { mode: 420 });
2159
+ this.helperPath = path4.join(os4.tmpdir(), "codeam-pty-helper.py");
2160
+ fs4.writeFileSync(this.helperPath, PYTHON_PTY_HELPER, { mode: 420 });
2071
2161
  this.proc = (0, import_child_process.spawn)(python, [this.helperPath, cmd, ...args2], {
2072
2162
  stdio: ["pipe", "pipe", "inherit"],
2073
2163
  cwd,
@@ -2196,7 +2286,7 @@ var UnixPtyStrategy = class {
2196
2286
  removeTempFile() {
2197
2287
  if (this.helperPath) {
2198
2288
  try {
2199
- fs3.unlinkSync(this.helperPath);
2289
+ fs4.unlinkSync(this.helperPath);
2200
2290
  } catch {
2201
2291
  }
2202
2292
  this.helperPath = null;
@@ -2268,9 +2358,9 @@ var WindowsPtyStrategy = class {
2268
2358
  };
2269
2359
 
2270
2360
  // src/services/pty/windows-conpty.strategy.ts
2271
- var path4 = __toESM(require("path"));
2361
+ var path5 = __toESM(require("path"));
2272
2362
  function loadNodePty() {
2273
- const vendoredPath = path4.join(__dirname, "vendor", "node-pty");
2363
+ const vendoredPath = path5.join(__dirname, "vendor", "node-pty");
2274
2364
  try {
2275
2365
  return require(vendoredPath);
2276
2366
  } catch (vendorErr) {
@@ -2320,11 +2410,17 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
2320
2410
  useConpty: true,
2321
2411
  conptyInheritCursor: false
2322
2412
  });
2413
+ let traceCount = 0;
2323
2414
  this.dataSub = this.pty.onData((data) => {
2324
2415
  process.stdout.write(data);
2325
2416
  this.opts.onData(data);
2417
+ traceCount++;
2418
+ if (traceCount <= 5 || traceCount % 50 === 0) {
2419
+ log.trace("conpty", `onData #${traceCount} ${data.length}B`);
2420
+ }
2326
2421
  });
2327
2422
  this.exitSub = this.pty.onExit(({ exitCode }) => {
2423
+ log.trace("conpty", `claude exited code=${exitCode ?? 0}`);
2328
2424
  this.dispose();
2329
2425
  this.opts.onExit(exitCode ?? 0);
2330
2426
  });
@@ -2384,8 +2480,8 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
2384
2480
 
2385
2481
  // src/services/claude-installer.ts
2386
2482
  var import_child_process3 = require("child_process");
2387
- var path5 = __toESM(require("path"));
2388
- var os4 = __toESM(require("os"));
2483
+ var path6 = __toESM(require("path"));
2484
+ var os5 = __toESM(require("os"));
2389
2485
 
2390
2486
  // ../../node_modules/@clack/prompts/dist/index.mjs
2391
2487
  var dist_exports = {};
@@ -4299,17 +4395,17 @@ ${c2}
4299
4395
 
4300
4396
  // src/services/claude-installer.ts
4301
4397
  function probeInstallDirs() {
4302
- const home = os4.homedir();
4398
+ const home = os5.homedir();
4303
4399
  if (process.platform === "win32") {
4304
4400
  return [
4305
- path5.join(home, ".claude", "local"),
4306
- path5.join(home, "AppData", "Local", "AnthropicClaude"),
4307
- path5.join(home, "AppData", "Local", "Programs", "AnthropicClaude")
4401
+ path6.join(home, ".claude", "local"),
4402
+ path6.join(home, "AppData", "Local", "AnthropicClaude"),
4403
+ path6.join(home, "AppData", "Local", "Programs", "AnthropicClaude")
4308
4404
  ];
4309
4405
  }
4310
4406
  return [
4311
- path5.join(home, ".local", "bin"),
4312
- path5.join(home, ".claude", "local"),
4407
+ path6.join(home, ".local", "bin"),
4408
+ path6.join(home, ".claude", "local"),
4313
4409
  "/usr/local/bin"
4314
4410
  ];
4315
4411
  }
@@ -4318,7 +4414,7 @@ function isAvailable() {
4318
4414
  }
4319
4415
  function augmentPath() {
4320
4416
  const dirs = probeInstallDirs();
4321
- const sep2 = path5.delimiter;
4417
+ const sep2 = path6.delimiter;
4322
4418
  const current = process.env.PATH ?? "";
4323
4419
  const existing = new Set(current.split(sep2).filter(Boolean));
4324
4420
  const additions = dirs.filter((d3) => !existing.has(d3));
@@ -4376,12 +4472,12 @@ async function ensureClaudeInstalled() {
4376
4472
  }
4377
4473
 
4378
4474
  // src/services/claude-resolver.ts
4379
- var path6 = __toESM(require("path"));
4475
+ var path7 = __toESM(require("path"));
4380
4476
  function buildClaudeLaunch(extraArgs = []) {
4381
4477
  const found = findInPath("claude") ?? findInPath("claude-code");
4382
4478
  if (!found) return null;
4383
4479
  if (process.platform === "win32") {
4384
- const ext = path6.extname(found).toLowerCase();
4480
+ const ext = path7.extname(found).toLowerCase();
4385
4481
  if (ext === ".cmd" || ext === ".bat") {
4386
4482
  return { cmd: "cmd.exe", args: ["/c", found, ...extraArgs] };
4387
4483
  }
@@ -4429,11 +4525,13 @@ var ClaudeService = class {
4429
4525
  }
4430
4526
  }
4431
4527
  if (process.platform === "win32") {
4528
+ log.trace("claude", `spawn (win32) cmd=${launch.cmd} args=${launch.args.join(" ")}`);
4432
4529
  const conpty = WindowsConPtyStrategy.tryCreate(this.strategyOpts);
4433
4530
  if (conpty) {
4434
4531
  try {
4435
4532
  conpty.spawn(launch.cmd, this.opts.cwd, launch.args);
4436
4533
  this.strategy = conpty;
4534
+ log.trace("claude", "ConPTY spawn ok");
4437
4535
  return;
4438
4536
  } catch (err) {
4439
4537
  const msg = err instanceof Error ? err.message : String(err);
@@ -4473,8 +4571,12 @@ var ClaudeService = class {
4473
4571
  * a fresh event-loop tick, after React has flushed the text into input state.
4474
4572
  */
4475
4573
  sendCommand(text) {
4476
- if (!this.strategy) return;
4574
+ if (!this.strategy) {
4575
+ log.trace("claude", "sendCommand dropped (no strategy)");
4576
+ return;
4577
+ }
4477
4578
  const s = this.strategy;
4579
+ log.trace("claude", `sendCommand text=${text.length}B`);
4478
4580
  s.write(text);
4479
4581
  setTimeout(() => s.write("\r"), 50);
4480
4582
  }
@@ -4548,12 +4650,12 @@ var https2 = __toESM(require("https"));
4548
4650
  var http2 = __toESM(require("http"));
4549
4651
 
4550
4652
  // ../../packages/shared/src/protocol/parseChrome.ts
4551
- var SPINNER_RE = /^[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]\s/;
4653
+ var SPINNER_RE = /^(?:[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]|🔴|🟠|🟡|🟢|🔵|🟣|🟤|⚫|⚪|🌀|💭|✨)\s/u;
4552
4654
  var BULLET_TOOL_RE = /^•\s+(?:Read(?:ing)?|Edit(?:ing)?|Writ(?:e|ing)|Bash|Runn(?:ing)?|Search(?:ing)?|Glob(?:bing)?|Grep(?:ping)?|Creat(?:e|ing)|Execut(?:e|ing)|Task|Agent|NotebookEdit)\b/i;
4553
4655
  var TREE_LINE_RE = /^└\s/;
4554
- var STATUS_LINE_RE = /^\+\s/;
4656
+ var STATUS_LINE_RE = /^(?:\+|[🔴🟠🟡🟢🔵🟣🟤⚫⚪🌀💭✨])\s/u;
4555
4657
  function isChromeLine(line) {
4556
- const t2 = line.trim();
4658
+ const t2 = line.replace(/️/g, "").trim();
4557
4659
  if (!t2) return false;
4558
4660
  if (/^[─━—═─\-]{3,}$/.test(t2)) return true;
4559
4661
  if (SPINNER_RE.test(t2)) return true;
@@ -4578,7 +4680,7 @@ function isChromeLine(line) {
4578
4680
  return false;
4579
4681
  }
4580
4682
  function parseChromeLine(line) {
4581
- const t2 = line.trim();
4683
+ const t2 = line.replace(/️/g, "").trim();
4582
4684
  if (!t2) return null;
4583
4685
  if (/^[─━—═─\-]{3,}$/.test(t2)) return null;
4584
4686
  if (/^[❯>]\s*$/.test(t2)) return null;
@@ -4982,6 +5084,7 @@ var OutputService = class _OutputService {
4982
5084
  this.pollTimer = setInterval(() => this.tick(), _OutputService.POLL_MS);
4983
5085
  }
4984
5086
  newTurn() {
5087
+ log.trace("outputSvc", "newTurn() \u2014 activating output stream");
4985
5088
  this.stopPoll();
4986
5089
  this.rawBuffer = "";
4987
5090
  this.lastSentContent = "";
@@ -5019,9 +5122,11 @@ var OutputService = class _OutputService {
5019
5122
  const printable2 = raw.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, "");
5020
5123
  if (printable2.trim()) {
5021
5124
  this.terminalTurnPending = true;
5125
+ log.trace("outputSvc", `terminal-turn detected (idle, ${raw.length}B)`);
5022
5126
  this.onTerminalTurnDetected?.();
5023
5127
  }
5024
5128
  }
5129
+ log.trace("outputSvc", `push dropped (inactive, ${raw.length}B)`);
5025
5130
  return;
5026
5131
  }
5027
5132
  this.rawBuffer += raw;
@@ -5031,6 +5136,10 @@ var OutputService = class _OutputService {
5031
5136
  this.tryExtractSessionId(printable);
5032
5137
  this.tryDetectRateLimit(printable);
5033
5138
  }
5139
+ log.trace(
5140
+ "outputSvc",
5141
+ `push +${raw.length}B (buf=${this.rawBuffer.length}B printable=${printable.trim().length})`
5142
+ );
5034
5143
  }
5035
5144
  /** Extract Claude conversation ID from output text (e.g., from /cost command or session resume) */
5036
5145
  tryExtractSessionId(text) {
@@ -5073,6 +5182,10 @@ var OutputService = class _OutputService {
5073
5182
  const selector = detectSelector(lines) ?? detectListSelector(lines);
5074
5183
  if (selector) {
5075
5184
  const idleMs2 = this.lastPushTime > 0 ? now - this.lastPushTime : elapsed;
5185
+ log.trace(
5186
+ "outputSvc",
5187
+ `tick selector found (idleMs=${idleMs2}, options=${selector.options.length})`
5188
+ );
5076
5189
  if (idleMs2 >= _OutputService.SELECTOR_IDLE_MS) {
5077
5190
  this.stopPoll();
5078
5191
  this.active = false;
@@ -5083,10 +5196,24 @@ var OutputService = class _OutputService {
5083
5196
  }
5084
5197
  const content = filterChrome(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
5085
5198
  if (!content) {
5199
+ log.trace(
5200
+ "outputSvc",
5201
+ `tick empty content (raw=${this.rawBuffer.length}B lines=${lines.length} elapsed=${elapsed}ms)`
5202
+ );
5203
+ if (lines.length > 0 && this.rawBuffer.length > 200) {
5204
+ const dump = lines.map((l, i) => `${i}: ${JSON.stringify(l)}`).join("\n");
5205
+ const preview = dump.length > 1500 ? dump.slice(0, 1500) + "\u2026(truncated)" : dump;
5206
+ log.trace("outputSvc", `lines dump:
5207
+ ${preview}`);
5208
+ }
5086
5209
  if (elapsed >= _OutputService.EMPTY_TIMEOUT_MS) this.finalize();
5087
5210
  return;
5088
5211
  }
5089
5212
  const idleMs = this.lastPushTime > 0 ? now - this.lastPushTime : elapsed;
5213
+ log.trace(
5214
+ "outputSvc",
5215
+ `tick content (raw=${this.rawBuffer.length}B lines=${lines.length} content=${content.length} idleMs=${idleMs})`
5216
+ );
5090
5217
  if (idleMs >= _OutputService.IDLE_MS) {
5091
5218
  this.finalize();
5092
5219
  return;
@@ -5153,15 +5280,38 @@ var OutputService = class _OutputService {
5153
5280
  if (this.pluginAuthToken) {
5154
5281
  headers["X-Plugin-Auth-Token"] = this.pluginAuthToken;
5155
5282
  }
5283
+ const chunkType = body.type ?? "(clear)";
5284
+ log.trace(
5285
+ "outputSvc",
5286
+ `postChunk type=${chunkType} done=${body.done === true} bytes=${payload.length}`
5287
+ );
5288
+ if (chunkType === "select_prompt" || chunkType === "new_turn" || body.type === "text" && body.done === true) {
5289
+ const preview = payload.length > 2048 ? payload.slice(0, 2048) + "\u2026(truncated)" : payload;
5290
+ log.trace("outputSvc", `payload ${preview}`);
5291
+ }
5156
5292
  return new Promise((resolve2) => {
5157
5293
  const attempt = (attemptsLeft) => {
5158
5294
  _transport2.sendOutputChunk(`${API_BASE4}/api/commands/output`, headers, payload).then(({ statusCode, body: resBody }) => {
5295
+ log.trace("outputSvc", `postChunk status=${statusCode}`);
5296
+ if (statusCode === 410 || statusCode === 404 && /SESSION_NOT_FOUND|SESSION_GONE/.test(resBody)) {
5297
+ if (this.active) {
5298
+ process.stderr.write("[codeam] session was deleted/disconnected \u2014 stopping output stream.\n");
5299
+ this.dispose();
5300
+ }
5301
+ resolve2();
5302
+ return;
5303
+ }
5159
5304
  if (statusCode >= 400) {
5160
5305
  process.stderr.write(`[codeam] output API error ${statusCode}: ${resBody}
5161
5306
  `);
5162
5307
  }
5163
5308
  resolve2();
5164
- }).catch(() => {
5309
+ }).catch((err) => {
5310
+ log.trace(
5311
+ "outputSvc",
5312
+ `postChunk error (retries left=${attemptsLeft})`,
5313
+ err
5314
+ );
5165
5315
  if (attemptsLeft > 0) {
5166
5316
  const delay = 200 * (maxRetries - attemptsLeft + 1);
5167
5317
  setTimeout(() => attempt(attemptsLeft - 1), delay);
@@ -5220,33 +5370,12 @@ function _sendOutputChunk(url, headers, payload) {
5220
5370
  }
5221
5371
 
5222
5372
  // src/services/history.service.ts
5223
- var fs4 = __toESM(require("fs"));
5224
- var path7 = __toESM(require("path"));
5225
- var os5 = __toESM(require("os"));
5373
+ var fs5 = __toESM(require("fs"));
5374
+ var path8 = __toESM(require("path"));
5375
+ var os6 = __toESM(require("os"));
5226
5376
  var https3 = __toESM(require("https"));
5227
5377
  var http3 = __toESM(require("http"));
5228
5378
  var import_zod = require("zod");
5229
-
5230
- // src/services/logger.ts
5231
- var LEVELS = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
5232
- function currentLevel() {
5233
- const raw = (process.env.CODEAM_LOG ?? "error").toLowerCase();
5234
- return LEVELS[raw] ?? LEVELS.error;
5235
- }
5236
- function emit(level, tag, msg, err) {
5237
- if (LEVELS[level] > currentLevel()) return;
5238
- const detail = err instanceof Error ? `: ${err.message}` : err !== void 0 ? `: ${String(err)}` : "";
5239
- process.stderr.write(`[codeam:${level}] ${tag} \u2014 ${msg}${detail}
5240
- `);
5241
- }
5242
- var log = {
5243
- error: (tag, msg, err) => emit("error", tag, msg, err),
5244
- warn: (tag, msg, err) => emit("warn", tag, msg, err),
5245
- info: (tag, msg, err) => emit("info", tag, msg, err),
5246
- debug: (tag, msg, err) => emit("debug", tag, msg, err)
5247
- };
5248
-
5249
- // src/services/history.service.ts
5250
5379
  var historyRecordSchema = import_zod.z.object({
5251
5380
  type: import_zod.z.string().optional(),
5252
5381
  timestamp: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).optional(),
@@ -5259,7 +5388,25 @@ var historyRecordSchema = import_zod.z.object({
5259
5388
  }).passthrough();
5260
5389
  var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
5261
5390
  function encodeCwd(cwd) {
5262
- return cwd.replace(/\//g, "-");
5391
+ return cwd.replace(/[\\/:]/g, "-");
5392
+ }
5393
+ function findProjectDir(cwd) {
5394
+ const projectsRoot = path8.join(os6.homedir(), ".claude", "projects");
5395
+ const primary = path8.join(projectsRoot, encodeCwd(cwd));
5396
+ if (fs5.existsSync(primary)) return primary;
5397
+ try {
5398
+ const entries = fs5.readdirSync(projectsRoot, { withFileTypes: true });
5399
+ const wanted = encodeCwd(cwd);
5400
+ for (const e of entries) {
5401
+ if (!e.isDirectory()) continue;
5402
+ const candidate = e.name.replace(/-+/g, "-");
5403
+ if (candidate === wanted.replace(/-+/g, "-")) {
5404
+ return path8.join(projectsRoot, e.name);
5405
+ }
5406
+ }
5407
+ } catch {
5408
+ }
5409
+ return null;
5263
5410
  }
5264
5411
  function extractText(content) {
5265
5412
  if (typeof content === "string") return content;
@@ -5273,7 +5420,7 @@ function parseJsonl(filePath) {
5273
5420
  const messages = [];
5274
5421
  let raw;
5275
5422
  try {
5276
- raw = fs4.readFileSync(filePath, "utf8");
5423
+ raw = fs5.readFileSync(filePath, "utf8");
5277
5424
  } catch (err) {
5278
5425
  if (err.code !== "ENOENT") {
5279
5426
  log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
@@ -5377,7 +5524,7 @@ var HistoryService = class {
5377
5524
  return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
5378
5525
  }
5379
5526
  get projectDir() {
5380
- return path7.join(os5.homedir(), ".claude", "projects", encodeCwd(this.cwd));
5527
+ return findProjectDir(this.cwd) ?? path8.join(os6.homedir(), ".claude", "projects", encodeCwd(this.cwd));
5381
5528
  }
5382
5529
  /** Set the current Claude conversation ID (extracted from /cost command or session start) */
5383
5530
  setCurrentConversationId(id) {
@@ -5389,7 +5536,7 @@ var HistoryService = class {
5389
5536
  /** Return the current message count in the active conversation. */
5390
5537
  getCurrentMessageCount() {
5391
5538
  if (!this.currentConversationId) return 0;
5392
- const filePath = path7.join(this.projectDir, `${this.currentConversationId}.jsonl`);
5539
+ const filePath = path8.join(this.projectDir, `${this.currentConversationId}.jsonl`);
5393
5540
  return parseJsonl(filePath).length;
5394
5541
  }
5395
5542
  /**
@@ -5400,7 +5547,7 @@ var HistoryService = class {
5400
5547
  const deadline = Date.now() + timeoutMs;
5401
5548
  while (Date.now() < deadline) {
5402
5549
  if (!this.currentConversationId) return null;
5403
- const filePath = path7.join(this.projectDir, `${this.currentConversationId}.jsonl`);
5550
+ const filePath = path8.join(this.projectDir, `${this.currentConversationId}.jsonl`);
5404
5551
  const messages = parseJsonl(filePath);
5405
5552
  if (messages.length > previousCount) {
5406
5553
  for (let i = messages.length - 1; i >= previousCount; i--) {
@@ -5415,15 +5562,15 @@ var HistoryService = class {
5415
5562
  detectCurrentConversation() {
5416
5563
  const dir = this.projectDir;
5417
5564
  try {
5418
- const files = fs4.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
5565
+ const files = fs5.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
5419
5566
  try {
5420
- return { name: e.name, mtime: fs4.statSync(path7.join(dir, e.name)).mtimeMs };
5567
+ return { name: e.name, mtime: fs5.statSync(path8.join(dir, e.name)).mtimeMs };
5421
5568
  } catch {
5422
5569
  return { name: e.name, mtime: 0 };
5423
5570
  }
5424
5571
  }).sort((a, b) => b.mtime - a.mtime);
5425
5572
  if (files.length > 0) {
5426
- this.currentConversationId = path7.basename(files[0].name, ".jsonl");
5573
+ this.currentConversationId = path8.basename(files[0].name, ".jsonl");
5427
5574
  }
5428
5575
  } catch {
5429
5576
  }
@@ -5455,13 +5602,13 @@ var HistoryService = class {
5455
5602
  const dir = this.projectDir;
5456
5603
  let entries;
5457
5604
  try {
5458
- entries = fs4.readdirSync(dir, { withFileTypes: true });
5605
+ entries = fs5.readdirSync(dir, { withFileTypes: true });
5459
5606
  } catch {
5460
5607
  return null;
5461
5608
  }
5462
5609
  const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
5463
5610
  try {
5464
- return { name: e.name, mtime: fs4.statSync(path7.join(dir, e.name)).mtimeMs };
5611
+ return { name: e.name, mtime: fs5.statSync(path8.join(dir, e.name)).mtimeMs };
5465
5612
  } catch {
5466
5613
  return { name: e.name, mtime: 0 };
5467
5614
  }
@@ -5469,12 +5616,12 @@ var HistoryService = class {
5469
5616
  if (files.length === 0) return null;
5470
5617
  const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
5471
5618
  if (!files.some((f) => f.name === targetFile)) return null;
5472
- return this.extractUsageFromFile(path7.join(dir, targetFile));
5619
+ return this.extractUsageFromFile(path8.join(dir, targetFile));
5473
5620
  }
5474
5621
  extractUsageFromFile(filePath) {
5475
5622
  let raw;
5476
5623
  try {
5477
- raw = fs4.readFileSync(filePath, "utf8");
5624
+ raw = fs5.readFileSync(filePath, "utf8");
5478
5625
  } catch {
5479
5626
  return null;
5480
5627
  }
@@ -5519,9 +5666,9 @@ var HistoryService = class {
5519
5666
  let totalCost = 0;
5520
5667
  let files;
5521
5668
  try {
5522
- files = fs4.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
5669
+ files = fs5.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
5523
5670
  try {
5524
- return fs4.statSync(path7.join(projectDir, f)).mtimeMs >= monthStartMs;
5671
+ return fs5.statSync(path8.join(projectDir, f)).mtimeMs >= monthStartMs;
5525
5672
  } catch {
5526
5673
  return false;
5527
5674
  }
@@ -5532,7 +5679,7 @@ var HistoryService = class {
5532
5679
  for (const file of files) {
5533
5680
  let raw;
5534
5681
  try {
5535
- raw = fs4.readFileSync(path7.join(projectDir, file), "utf8");
5682
+ raw = fs5.readFileSync(path8.join(projectDir, file), "utf8");
5536
5683
  } catch {
5537
5684
  continue;
5538
5685
  }
@@ -5567,23 +5714,23 @@ var HistoryService = class {
5567
5714
  const dir = this.projectDir;
5568
5715
  let entries;
5569
5716
  try {
5570
- entries = fs4.readdirSync(dir, { withFileTypes: true });
5717
+ entries = fs5.readdirSync(dir, { withFileTypes: true });
5571
5718
  } catch {
5572
5719
  return;
5573
5720
  }
5574
5721
  const sessions2 = [];
5575
5722
  for (const entry of entries) {
5576
5723
  if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
5577
- const id = path7.basename(entry.name, ".jsonl");
5578
- const filePath = path7.join(dir, entry.name);
5724
+ const id = path8.basename(entry.name, ".jsonl");
5725
+ const filePath = path8.join(dir, entry.name);
5579
5726
  let mtime = Date.now();
5580
5727
  try {
5581
- mtime = fs4.statSync(filePath).mtimeMs;
5728
+ mtime = fs5.statSync(filePath).mtimeMs;
5582
5729
  } catch {
5583
5730
  }
5584
5731
  let summary = "";
5585
5732
  try {
5586
- const raw = fs4.readFileSync(filePath, "utf8");
5733
+ const raw = fs5.readFileSync(filePath, "utf8");
5587
5734
  for (const line of raw.split("\n")) {
5588
5735
  if (!line.trim()) continue;
5589
5736
  try {
@@ -5616,7 +5763,7 @@ var HistoryService = class {
5616
5763
  * showing an empty conversation.
5617
5764
  */
5618
5765
  async loadConversation(sessionId) {
5619
- const filePath = path7.join(this.projectDir, `${sessionId}.jsonl`);
5766
+ const filePath = path8.join(this.projectDir, `${sessionId}.jsonl`);
5620
5767
  const messages = parseJsonl(filePath);
5621
5768
  if (messages.length === 0) return;
5622
5769
  const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
@@ -5671,8 +5818,8 @@ function parsePayload(schema, raw) {
5671
5818
  }
5672
5819
 
5673
5820
  // src/services/file-ops.service.ts
5674
- var fs5 = __toESM(require("fs/promises"));
5675
- var path8 = __toESM(require("path"));
5821
+ var fs6 = __toESM(require("fs/promises"));
5822
+ var path9 = __toESM(require("path"));
5676
5823
  var MAX_FILE_BYTES = 5 * 1024 * 1024;
5677
5824
  var MAX_WALK_DEPTH = 6;
5678
5825
  var MAX_VISITED_DIRS = 5e3;
@@ -5707,12 +5854,12 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
5707
5854
  "__pycache__"
5708
5855
  ]);
5709
5856
  function isUnder(parent, candidate) {
5710
- const rel = path8.relative(parent, candidate);
5711
- return rel === "" || !rel.startsWith("..") && !path8.isAbsolute(rel);
5857
+ const rel = path9.relative(parent, candidate);
5858
+ return rel === "" || !rel.startsWith("..") && !path9.isAbsolute(rel);
5712
5859
  }
5713
5860
  async function isExistingFile(absPath) {
5714
5861
  try {
5715
- const stat3 = await fs5.stat(absPath);
5862
+ const stat3 = await fs6.stat(absPath);
5716
5863
  return stat3.isFile();
5717
5864
  } catch {
5718
5865
  return false;
@@ -5725,13 +5872,13 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
5725
5872
  ctx.visited++;
5726
5873
  let entries = [];
5727
5874
  try {
5728
- entries = await fs5.readdir(dir, { withFileTypes: true });
5875
+ entries = await fs6.readdir(dir, { withFileTypes: true });
5729
5876
  } catch {
5730
5877
  return;
5731
5878
  }
5732
5879
  for (const e of entries) {
5733
5880
  if (!e.isFile()) continue;
5734
- const full = path8.join(dir, e.name);
5881
+ const full = path9.join(dir, e.name);
5735
5882
  if (needleVariants.some((needle) => full.endsWith(needle))) {
5736
5883
  ctx.matches.push(full);
5737
5884
  if (ctx.matches.length >= ctx.cap) return;
@@ -5741,21 +5888,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
5741
5888
  if (!e.isDirectory()) continue;
5742
5889
  if (SUBDIR_IGNORE.has(e.name)) continue;
5743
5890
  if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
5744
- await walkForSuffix(path8.join(dir, e.name), needleVariants, depth + 1, ctx);
5891
+ await walkForSuffix(path9.join(dir, e.name), needleVariants, depth + 1, ctx);
5745
5892
  if (ctx.matches.length >= ctx.cap) return;
5746
5893
  }
5747
5894
  }
5748
5895
  async function findFile(rawPath) {
5749
5896
  const cwd = process.cwd();
5750
- if (path8.isAbsolute(rawPath)) {
5751
- const abs = path8.normalize(rawPath);
5897
+ if (path9.isAbsolute(rawPath)) {
5898
+ const abs = path9.normalize(rawPath);
5752
5899
  if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
5753
5900
  }
5754
- const direct = path8.resolve(cwd, rawPath);
5901
+ const direct = path9.resolve(cwd, rawPath);
5755
5902
  if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
5756
- const normalized = path8.normalize(rawPath).replace(/^[./\\]+/, "");
5903
+ const normalized = path9.normalize(rawPath).replace(/^[./\\]+/, "");
5757
5904
  const needles = [
5758
- `${path8.sep}${normalized}`,
5905
+ `${path9.sep}${normalized}`,
5759
5906
  `/${normalized}`
5760
5907
  ].filter((v, i, a) => a.indexOf(v) === i);
5761
5908
  const ctx = { visited: 0, matches: [], cap: 16 };
@@ -5769,7 +5916,7 @@ async function findWriteTarget(rawPath) {
5769
5916
  const found = await findFile(rawPath);
5770
5917
  if (found) return found;
5771
5918
  const cwd = process.cwd();
5772
- const fallback = path8.isAbsolute(rawPath) ? path8.normalize(rawPath) : path8.resolve(cwd, rawPath);
5919
+ const fallback = path9.isAbsolute(rawPath) ? path9.normalize(rawPath) : path9.resolve(cwd, rawPath);
5773
5920
  if (!isUnder(cwd, fallback)) return null;
5774
5921
  return fallback;
5775
5922
  }
@@ -5786,11 +5933,11 @@ async function readProjectFile(rawPath) {
5786
5933
  if (!abs) {
5787
5934
  return { error: `File not found in the project tree: ${rawPath}` };
5788
5935
  }
5789
- const stat3 = await fs5.stat(abs);
5936
+ const stat3 = await fs6.stat(abs);
5790
5937
  if (stat3.size > MAX_FILE_BYTES) {
5791
5938
  return { error: `File too large (${(stat3.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
5792
5939
  }
5793
- const buf = await fs5.readFile(abs);
5940
+ const buf = await fs6.readFile(abs);
5794
5941
  if (looksBinary(buf)) {
5795
5942
  return { error: "Binary file \u2014 refusing to open in a code editor." };
5796
5943
  }
@@ -5809,8 +5956,8 @@ async function writeProjectFile(rawPath, content) {
5809
5956
  if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
5810
5957
  return { error: "Content too large." };
5811
5958
  }
5812
- await fs5.mkdir(path8.dirname(abs), { recursive: true });
5813
- await fs5.writeFile(abs, content, "utf-8");
5959
+ await fs6.mkdir(path9.dirname(abs), { recursive: true });
5960
+ await fs6.writeFile(abs, content, "utf-8");
5814
5961
  return { ok: true };
5815
5962
  } catch (e) {
5816
5963
  const msg = e instanceof Error ? e.message : "Write failed";
@@ -5821,8 +5968,8 @@ async function writeProjectFile(rawPath, content) {
5821
5968
  // src/services/project-ops.service.ts
5822
5969
  var import_child_process4 = require("child_process");
5823
5970
  var import_util = require("util");
5824
- var fs6 = __toESM(require("fs/promises"));
5825
- var path9 = __toESM(require("path"));
5971
+ var fs7 = __toESM(require("fs/promises"));
5972
+ var path10 = __toESM(require("path"));
5826
5973
  var execFileP = (0, import_util.promisify)(import_child_process4.execFile);
5827
5974
  var PROJECT_IGNORE = /* @__PURE__ */ new Set([
5828
5975
  "node_modules",
@@ -5870,7 +6017,7 @@ async function listProjectFiles(opts = {}) {
5870
6017
  }
5871
6018
  let entries = [];
5872
6019
  try {
5873
- entries = await fs6.readdir(dir, { withFileTypes: true });
6020
+ entries = await fs7.readdir(dir, { withFileTypes: true });
5874
6021
  } catch {
5875
6022
  return;
5876
6023
  }
@@ -5880,18 +6027,18 @@ async function listProjectFiles(opts = {}) {
5880
6027
  return;
5881
6028
  }
5882
6029
  if (PROJECT_IGNORE.has(e.name)) continue;
5883
- const full = path9.join(dir, e.name);
6030
+ const full = path10.join(dir, e.name);
5884
6031
  if (e.isDirectory()) {
5885
6032
  if (depth >= 12) continue;
5886
6033
  await walk(full, depth + 1);
5887
6034
  } else if (e.isFile()) {
5888
- const rel = path9.relative(root, full);
6035
+ const rel = path10.relative(root, full);
5889
6036
  if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
5890
6037
  continue;
5891
6038
  }
5892
6039
  let size = 0;
5893
6040
  try {
5894
- const st3 = await fs6.stat(full);
6041
+ const st3 = await fs7.stat(full);
5895
6042
  size = st3.size;
5896
6043
  } catch {
5897
6044
  }
@@ -5993,8 +6140,8 @@ async function gitStatus(cwd) {
5993
6140
  let hasMergeInProgress = false;
5994
6141
  try {
5995
6142
  const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
5996
- const mergeHead = path9.isAbsolute(gitDir) ? path9.join(gitDir, "MERGE_HEAD") : path9.join(root, gitDir, "MERGE_HEAD");
5997
- await fs6.access(mergeHead);
6143
+ const mergeHead = path10.isAbsolute(gitDir) ? path10.join(gitDir, "MERGE_HEAD") : path10.join(root, gitDir, "MERGE_HEAD");
6144
+ await fs7.access(mergeHead);
5998
6145
  hasMergeInProgress = true;
5999
6146
  } catch {
6000
6147
  }
@@ -6071,8 +6218,8 @@ async function gitResolve(file, side, cwd) {
6071
6218
  function saveFilesTemp(files) {
6072
6219
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
6073
6220
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
6074
- const tmpPath = path10.join(os6.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
6075
- fs7.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
6221
+ const tmpPath = path11.join(os7.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
6222
+ fs8.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
6076
6223
  return tmpPath;
6077
6224
  });
6078
6225
  }
@@ -6145,8 +6292,8 @@ try:
6145
6292
  sys.exit((st>>8)&0xFF)
6146
6293
  except Exception:sys.exit(0)
6147
6294
  `;
6148
- const helperPath = path10.join(os6.tmpdir(), "codeam-quota-helper.py");
6149
- fs7.writeFileSync(helperPath, helperScript, { mode: 420 });
6295
+ const helperPath = path11.join(os7.tmpdir(), "codeam-quota-helper.py");
6296
+ fs8.writeFileSync(helperPath, helperScript, { mode: 420 });
6150
6297
  const python = findInPath("python3") ?? findInPath("python");
6151
6298
  if (!python) {
6152
6299
  quotaFetchInProgress = false;
@@ -6178,7 +6325,7 @@ except Exception:sys.exit(0)
6178
6325
  } catch {
6179
6326
  }
6180
6327
  try {
6181
- fs7.unlinkSync(helperPath);
6328
+ fs8.unlinkSync(helperPath);
6182
6329
  } catch {
6183
6330
  }
6184
6331
  quotaFetchInProgress = false;
@@ -6228,7 +6375,7 @@ except Exception:sys.exit(0)
6228
6375
  setTimeout(() => {
6229
6376
  for (const p2 of paths) {
6230
6377
  try {
6231
- fs7.unlinkSync(p2);
6378
+ fs8.unlinkSync(p2);
6232
6379
  } catch {
6233
6380
  }
6234
6381
  }
@@ -6271,6 +6418,25 @@ except Exception:sys.exit(0)
6271
6418
  }
6272
6419
  break;
6273
6420
  }
6421
+ case "session_terminated": {
6422
+ showInfo("Session was deleted from the app \u2014 exiting.");
6423
+ try {
6424
+ claude.kill();
6425
+ } catch {
6426
+ }
6427
+ try {
6428
+ const proc = (0, import_child_process5.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
6429
+ detached: true,
6430
+ stdio: "ignore"
6431
+ });
6432
+ proc.unref();
6433
+ } catch {
6434
+ }
6435
+ outputSvc.dispose();
6436
+ relay.stop();
6437
+ ws.disconnect();
6438
+ process.exit(0);
6439
+ }
6274
6440
  case "shutdown_session": {
6275
6441
  try {
6276
6442
  await relay.sendResult(cmd.id, "success", { ok: true });
@@ -6451,7 +6617,7 @@ except Exception:sys.exit(0)
6451
6617
  setTimeout(() => {
6452
6618
  for (const p2 of paths) {
6453
6619
  try {
6454
- fs7.unlinkSync(p2);
6620
+ fs8.unlinkSync(p2);
6455
6621
  } catch {
6456
6622
  }
6457
6623
  }
@@ -6746,9 +6912,9 @@ async function logout() {
6746
6912
 
6747
6913
  // src/commands/deploy.ts
6748
6914
  var import_child_process10 = require("child_process");
6749
- var fs8 = __toESM(require("fs"));
6750
- var os7 = __toESM(require("os"));
6751
- var path15 = __toESM(require("path"));
6915
+ var fs9 = __toESM(require("fs"));
6916
+ var os8 = __toESM(require("os"));
6917
+ var path16 = __toESM(require("path"));
6752
6918
  var import_util6 = require("util");
6753
6919
  var import_picocolors9 = __toESM(require("picocolors"));
6754
6920
 
@@ -6756,7 +6922,7 @@ var import_picocolors9 = __toESM(require("picocolors"));
6756
6922
  var import_child_process6 = require("child_process");
6757
6923
  var import_util2 = require("util");
6758
6924
  var import_picocolors7 = __toESM(require("picocolors"));
6759
- var path11 = __toESM(require("path"));
6925
+ var path12 = __toESM(require("path"));
6760
6926
  var execFileP2 = (0, import_util2.promisify)(import_child_process6.execFile);
6761
6927
  var MAX_BUFFER = 8 * 1024 * 1024;
6762
6928
  function resetStdinForChild() {
@@ -7245,7 +7411,7 @@ var GitHubCodespacesProvider = class {
7245
7411
  });
7246
7412
  }
7247
7413
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
7248
- const remoteDir = path11.posix.dirname(remotePath);
7414
+ const remoteDir = path12.posix.dirname(remotePath);
7249
7415
  const parts = [
7250
7416
  `mkdir -p ${shellQuote(remoteDir)}`,
7251
7417
  `cat > ${shellQuote(remotePath)}`
@@ -7315,7 +7481,7 @@ function shellQuote(s) {
7315
7481
  // src/services/providers/gitpod.ts
7316
7482
  var import_child_process7 = require("child_process");
7317
7483
  var import_util3 = require("util");
7318
- var path12 = __toESM(require("path"));
7484
+ var path13 = __toESM(require("path"));
7319
7485
  var import_picocolors8 = __toESM(require("picocolors"));
7320
7486
  var execFileP3 = (0, import_util3.promisify)(import_child_process7.execFile);
7321
7487
  var MAX_BUFFER2 = 8 * 1024 * 1024;
@@ -7555,7 +7721,7 @@ var GitpodProvider = class {
7555
7721
  });
7556
7722
  }
7557
7723
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
7558
- const remoteDir = path12.posix.dirname(remotePath);
7724
+ const remoteDir = path13.posix.dirname(remotePath);
7559
7725
  const parts = [
7560
7726
  `mkdir -p ${shellQuote2(remoteDir)}`,
7561
7727
  `cat > ${shellQuote2(remotePath)}`
@@ -7591,7 +7757,7 @@ function shellQuote2(s) {
7591
7757
  // src/services/providers/gitlab-workspaces.ts
7592
7758
  var import_child_process8 = require("child_process");
7593
7759
  var import_util4 = require("util");
7594
- var path13 = __toESM(require("path"));
7760
+ var path14 = __toESM(require("path"));
7595
7761
  var execFileP4 = (0, import_util4.promisify)(import_child_process8.execFile);
7596
7762
  var MAX_BUFFER3 = 8 * 1024 * 1024;
7597
7763
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
@@ -7851,7 +8017,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
7851
8017
  }
7852
8018
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
7853
8019
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
7854
- const remoteDir = path13.posix.dirname(remotePath);
8020
+ const remoteDir = path14.posix.dirname(remotePath);
7855
8021
  const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
7856
8022
  if (options.mode != null) {
7857
8023
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
@@ -7919,7 +8085,7 @@ function shellQuote3(s) {
7919
8085
  // src/services/providers/railway.ts
7920
8086
  var import_child_process9 = require("child_process");
7921
8087
  var import_util5 = require("util");
7922
- var path14 = __toESM(require("path"));
8088
+ var path15 = __toESM(require("path"));
7923
8089
  var execFileP5 = (0, import_util5.promisify)(import_child_process9.execFile);
7924
8090
  var MAX_BUFFER4 = 8 * 1024 * 1024;
7925
8091
  function resetStdinForChild4() {
@@ -8155,7 +8321,7 @@ var RailwayProvider = class {
8155
8321
  if (!projectId || !serviceId) {
8156
8322
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
8157
8323
  }
8158
- const remoteDir = path14.posix.dirname(remotePath);
8324
+ const remoteDir = path15.posix.dirname(remotePath);
8159
8325
  const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
8160
8326
  if (options.mode != null) {
8161
8327
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
@@ -8347,7 +8513,7 @@ async function deploy() {
8347
8513
  process.exit(1);
8348
8514
  }
8349
8515
  }
8350
- const localClaudeDir = path15.join(os7.homedir(), ".claude");
8516
+ const localClaudeDir = path16.join(os8.homedir(), ".claude");
8351
8517
  const localCredsKind = await detectLocalClaudeCredentials(localClaudeDir);
8352
8518
  let bridged = "none";
8353
8519
  if (localCredsKind !== "none") {
@@ -8391,7 +8557,7 @@ async function deploy() {
8391
8557
  process.exit(1);
8392
8558
  }
8393
8559
  claudeStep.stop("\u2713 Claude CLI installed");
8394
- const haveLocalClaude = fs8.existsSync(localClaudeDir) && fs8.statSync(localClaudeDir).isDirectory();
8560
+ const haveLocalClaude = fs9.existsSync(localClaudeDir) && fs9.statSync(localClaudeDir).isDirectory();
8395
8561
  if (haveLocalClaude) {
8396
8562
  const copyStep = fe();
8397
8563
  copyStep.start("Copying local Claude config to workspace\u2026");
@@ -8445,10 +8611,10 @@ async function deploy() {
8445
8611
  }
8446
8612
  }
8447
8613
  if (bridged !== "none") {
8448
- const localClaudeJson = path15.join(os7.homedir(), ".claude.json");
8449
- if (fs8.existsSync(localClaudeJson)) {
8614
+ const localClaudeJson = path16.join(os8.homedir(), ".claude.json");
8615
+ if (fs9.existsSync(localClaudeJson)) {
8450
8616
  try {
8451
- const contents = fs8.readFileSync(localClaudeJson);
8617
+ const contents = fs9.readFileSync(localClaudeJson);
8452
8618
  await provider.uploadFile(
8453
8619
  workspace.id,
8454
8620
  "/home/codespace/.claude.json",
@@ -8638,7 +8804,7 @@ async function runRemoteClaudeLogin(provider, workspaceId) {
8638
8804
  }
8639
8805
  }
8640
8806
  async function detectLocalClaudeCredentials(localClaudeDir) {
8641
- if (fs8.existsSync(path15.join(localClaudeDir, ".credentials.json"))) {
8807
+ if (fs9.existsSync(path16.join(localClaudeDir, ".credentials.json"))) {
8642
8808
  return "flat-file";
8643
8809
  }
8644
8810
  if (process.platform === "darwin") {
@@ -8671,8 +8837,8 @@ async function verifyClaudeAuth(provider, workspaceId) {
8671
8837
  }
8672
8838
  }
8673
8839
  async function bridgeClaudeCredentials(provider, workspaceId, localClaudeDir) {
8674
- const fileBased = path15.join(localClaudeDir, ".credentials.json");
8675
- if (fs8.existsSync(fileBased)) return "flat-file";
8840
+ const fileBased = path16.join(localClaudeDir, ".credentials.json");
8841
+ if (fs9.existsSync(fileBased)) return "flat-file";
8676
8842
  if (process.platform === "darwin") {
8677
8843
  try {
8678
8844
  const { stdout } = await execFileP6(
@@ -8877,10 +9043,172 @@ async function stopWorkspaceFromLocal(target) {
8877
9043
  }
8878
9044
  }
8879
9045
 
9046
+ // src/commands/version.ts
9047
+ var import_picocolors11 = __toESM(require("picocolors"));
9048
+ function version() {
9049
+ const v = true ? "2.4.35" : "unknown";
9050
+ console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
9051
+ }
9052
+
9053
+ // src/commands/help.ts
9054
+ var import_picocolors12 = __toESM(require("picocolors"));
9055
+ function help() {
9056
+ const lines = [
9057
+ "",
9058
+ ` ${import_picocolors12.default.bold(import_picocolors12.default.magenta("codeam-cli"))} ${import_picocolors12.default.dim("\u2014 Claude Code remote control")}`,
9059
+ "",
9060
+ ` ${import_picocolors12.default.bold("Usage")}`,
9061
+ ` ${import_picocolors12.default.cyan("codeam")} ${import_picocolors12.default.dim("[command]")}`,
9062
+ "",
9063
+ ` ${import_picocolors12.default.bold("Commands")}`,
9064
+ ` ${import_picocolors12.default.white("codeam")} ${import_picocolors12.default.dim("start Claude Code with mobile control")}`,
9065
+ ` ${import_picocolors12.default.white("codeam pair")} ${import_picocolors12.default.dim("pair a new mobile device")}`,
9066
+ ` ${import_picocolors12.default.white("codeam sessions")} ${import_picocolors12.default.dim("list paired devices")}`,
9067
+ ` ${import_picocolors12.default.white("codeam status")} ${import_picocolors12.default.dim("show connection info")}`,
9068
+ ` ${import_picocolors12.default.white("codeam logout")} ${import_picocolors12.default.dim("remove all paired sessions")}`,
9069
+ ` ${import_picocolors12.default.white("codeam deploy")} ${import_picocolors12.default.dim("provision a cloud workspace (Codespaces) and pair it")}`,
9070
+ ` ${import_picocolors12.default.white("codeam deploy ls")} ${import_picocolors12.default.dim("list deployed cloud workspaces")}`,
9071
+ ` ${import_picocolors12.default.white("codeam deploy stop")} ${import_picocolors12.default.dim("stop a deployed workspace session")}`,
9072
+ "",
9073
+ ` ${import_picocolors12.default.bold("Flags")}`,
9074
+ ` ${import_picocolors12.default.white("-v, --version")} ${import_picocolors12.default.dim("print the CLI version")}`,
9075
+ ` ${import_picocolors12.default.white("-h, --help")} ${import_picocolors12.default.dim("show this help")}`,
9076
+ "",
9077
+ ` ${import_picocolors12.default.bold("Links")}`,
9078
+ ` ${import_picocolors12.default.dim("Docs:")} ${import_picocolors12.default.green("https://www.codeagent-mobile.com")}`,
9079
+ ` ${import_picocolors12.default.dim("Issues:")} ${import_picocolors12.default.green("https://github.com/edgar-durand/codeagent-mobile-clients/issues")}`,
9080
+ ""
9081
+ ];
9082
+ process.stdout.write(lines.join("\n") + "\n");
9083
+ }
9084
+
9085
+ // src/lib/updateNotifier.ts
9086
+ var fs10 = __toESM(require("fs"));
9087
+ var os9 = __toESM(require("os"));
9088
+ var path17 = __toESM(require("path"));
9089
+ var https4 = __toESM(require("https"));
9090
+ var import_picocolors13 = __toESM(require("picocolors"));
9091
+ var PKG_NAME = "codeam-cli";
9092
+ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
9093
+ var TTL_MS = 24 * 60 * 60 * 1e3;
9094
+ var REQUEST_TIMEOUT_MS = 1500;
9095
+ function cachePath() {
9096
+ const dir = path17.join(os9.homedir(), ".codeam");
9097
+ return path17.join(dir, "update-check.json");
9098
+ }
9099
+ function readCache() {
9100
+ try {
9101
+ const raw = fs10.readFileSync(cachePath(), "utf8");
9102
+ const parsed = JSON.parse(raw);
9103
+ if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
9104
+ return parsed;
9105
+ } catch {
9106
+ return null;
9107
+ }
9108
+ }
9109
+ function writeCache(cache) {
9110
+ try {
9111
+ const file = cachePath();
9112
+ fs10.mkdirSync(path17.dirname(file), { recursive: true });
9113
+ fs10.writeFileSync(file, JSON.stringify(cache));
9114
+ } catch {
9115
+ }
9116
+ }
9117
+ function compareSemver(a, b) {
9118
+ const stripPre = (s) => s.split("-")[0];
9119
+ const aParts = stripPre(a).split(".").map(Number);
9120
+ const bParts = stripPre(b).split(".").map(Number);
9121
+ for (let i = 0; i < 3; i++) {
9122
+ const ai = aParts[i] ?? 0;
9123
+ const bi = bParts[i] ?? 0;
9124
+ if (Number.isNaN(ai) || Number.isNaN(bi)) return 0;
9125
+ if (ai > bi) return 1;
9126
+ if (ai < bi) return -1;
9127
+ }
9128
+ return 0;
9129
+ }
9130
+ function fetchLatest() {
9131
+ return new Promise((resolve2) => {
9132
+ const req = https4.get(
9133
+ REGISTRY_URL,
9134
+ { headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
9135
+ (res) => {
9136
+ if (res.statusCode !== 200) {
9137
+ res.resume();
9138
+ resolve2(null);
9139
+ return;
9140
+ }
9141
+ let buf = "";
9142
+ res.setEncoding("utf8");
9143
+ res.on("data", (chunk) => {
9144
+ buf += chunk;
9145
+ });
9146
+ res.on("end", () => {
9147
+ try {
9148
+ const json = JSON.parse(buf);
9149
+ if (typeof json.version === "string") {
9150
+ resolve2(json.version);
9151
+ } else {
9152
+ resolve2(null);
9153
+ }
9154
+ } catch {
9155
+ resolve2(null);
9156
+ }
9157
+ });
9158
+ }
9159
+ );
9160
+ req.on("timeout", () => {
9161
+ req.destroy();
9162
+ resolve2(null);
9163
+ });
9164
+ req.on("error", () => resolve2(null));
9165
+ });
9166
+ }
9167
+ function notifyIfStale(currentVersion, latest) {
9168
+ if (compareSemver(latest, currentVersion) <= 0) return;
9169
+ const arrow = import_picocolors13.default.dim("\u2192");
9170
+ const cmd = import_picocolors13.default.cyan("npm install -g codeam-cli");
9171
+ const lines = [
9172
+ "",
9173
+ ` ${import_picocolors13.default.yellow("\u25CF")} ${import_picocolors13.default.bold("Update available")} ${import_picocolors13.default.dim(currentVersion)} ${arrow} ${import_picocolors13.default.green(latest)}`,
9174
+ ` Run ${cmd} to upgrade.`,
9175
+ ""
9176
+ ];
9177
+ process.stderr.write(lines.join("\n"));
9178
+ }
9179
+ function checkForUpdates() {
9180
+ if (process.env.NODE_ENV === "test") return;
9181
+ if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
9182
+ if (process.env.CI) return;
9183
+ if (!process.stdout.isTTY) return;
9184
+ const current = true ? "2.4.35" : null;
9185
+ if (!current) return;
9186
+ const cache = readCache();
9187
+ const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
9188
+ if (fresh && cache) {
9189
+ notifyIfStale(current, cache.latest);
9190
+ return;
9191
+ }
9192
+ void fetchLatest().then((latest) => {
9193
+ if (!latest) return;
9194
+ writeCache({ fetchedAt: Date.now(), latest });
9195
+ });
9196
+ }
9197
+
8880
9198
  // src/index.ts
8881
9199
  var [, , command, ...args] = process.argv;
8882
9200
  async function main() {
9201
+ const isMetaCommand = command === "--version" || command === "-v" || command === "version" || command === "--help" || command === "-h" || command === "help";
9202
+ if (!isMetaCommand) checkForUpdates();
8883
9203
  switch (command) {
9204
+ case "--version":
9205
+ case "-v":
9206
+ case "version":
9207
+ return version();
9208
+ case "--help":
9209
+ case "-h":
9210
+ case "help":
9211
+ return help();
8884
9212
  case "pair":
8885
9213
  return pair();
8886
9214
  case "sessions":