codeam-cli 2.4.32 → 2.4.34

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.32",
1485
+ version: "2.4.34",
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) {
@@ -2293,6 +2383,7 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
2293
2383
  dataSub = null;
2294
2384
  exitSub = null;
2295
2385
  rawModeSet = false;
2386
+ resizeHandler = null;
2296
2387
  /**
2297
2388
  * Factory that returns a working ConPTY strategy or `null` if
2298
2389
  * node-pty can't load. The caller (claude.service.ts) decides
@@ -2304,10 +2395,12 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
2304
2395
  return new _WindowsConPtyStrategy(opts, lib);
2305
2396
  }
2306
2397
  spawn(cmd, cwd, args2 = []) {
2398
+ const cols = process.stdout.columns && process.stdout.columns > 0 ? process.stdout.columns : 120;
2399
+ const rows = process.stdout.rows && process.stdout.rows > 0 ? process.stdout.rows : 30;
2307
2400
  this.pty = this.lib.spawn(cmd, args2, {
2308
2401
  name: "xterm-256color",
2309
- cols: 220,
2310
- rows: 50,
2402
+ cols,
2403
+ rows,
2311
2404
  cwd,
2312
2405
  env: {
2313
2406
  ...process.env,
@@ -2317,11 +2410,17 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
2317
2410
  useConpty: true,
2318
2411
  conptyInheritCursor: false
2319
2412
  });
2413
+ let traceCount = 0;
2320
2414
  this.dataSub = this.pty.onData((data) => {
2321
2415
  process.stdout.write(data);
2322
2416
  this.opts.onData(data);
2417
+ traceCount++;
2418
+ if (traceCount <= 5 || traceCount % 50 === 0) {
2419
+ log.trace("conpty", `onData #${traceCount} ${data.length}B`);
2420
+ }
2323
2421
  });
2324
2422
  this.exitSub = this.pty.onExit(({ exitCode }) => {
2423
+ log.trace("conpty", `claude exited code=${exitCode ?? 0}`);
2325
2424
  this.dispose();
2326
2425
  this.opts.onExit(exitCode ?? 0);
2327
2426
  });
@@ -2334,6 +2433,15 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
2334
2433
  }
2335
2434
  process.stdin.resume();
2336
2435
  process.stdin.on("data", this.stdinHandler);
2436
+ this.resizeHandler = () => {
2437
+ const c2 = process.stdout.columns && process.stdout.columns > 0 ? process.stdout.columns : cols;
2438
+ const r = process.stdout.rows && process.stdout.rows > 0 ? process.stdout.rows : rows;
2439
+ try {
2440
+ this.pty?.resize(c2, r);
2441
+ } catch {
2442
+ }
2443
+ };
2444
+ process.stdout.on("resize", this.resizeHandler);
2337
2445
  }
2338
2446
  write(data) {
2339
2447
  if (!this.pty) return;
@@ -2352,6 +2460,10 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
2352
2460
  this.dataSub = null;
2353
2461
  this.exitSub = null;
2354
2462
  process.stdin.removeListener("data", this.stdinHandler);
2463
+ if (this.resizeHandler) {
2464
+ process.stdout.removeListener("resize", this.resizeHandler);
2465
+ this.resizeHandler = null;
2466
+ }
2355
2467
  if (this.rawModeSet && process.stdin.isTTY) {
2356
2468
  try {
2357
2469
  process.stdin.setRawMode(false);
@@ -2368,8 +2480,8 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
2368
2480
 
2369
2481
  // src/services/claude-installer.ts
2370
2482
  var import_child_process3 = require("child_process");
2371
- var path5 = __toESM(require("path"));
2372
- var os4 = __toESM(require("os"));
2483
+ var path6 = __toESM(require("path"));
2484
+ var os5 = __toESM(require("os"));
2373
2485
 
2374
2486
  // ../../node_modules/@clack/prompts/dist/index.mjs
2375
2487
  var dist_exports = {};
@@ -4283,17 +4395,17 @@ ${c2}
4283
4395
 
4284
4396
  // src/services/claude-installer.ts
4285
4397
  function probeInstallDirs() {
4286
- const home = os4.homedir();
4398
+ const home = os5.homedir();
4287
4399
  if (process.platform === "win32") {
4288
4400
  return [
4289
- path5.join(home, ".claude", "local"),
4290
- path5.join(home, "AppData", "Local", "AnthropicClaude"),
4291
- 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")
4292
4404
  ];
4293
4405
  }
4294
4406
  return [
4295
- path5.join(home, ".local", "bin"),
4296
- path5.join(home, ".claude", "local"),
4407
+ path6.join(home, ".local", "bin"),
4408
+ path6.join(home, ".claude", "local"),
4297
4409
  "/usr/local/bin"
4298
4410
  ];
4299
4411
  }
@@ -4302,7 +4414,7 @@ function isAvailable() {
4302
4414
  }
4303
4415
  function augmentPath() {
4304
4416
  const dirs = probeInstallDirs();
4305
- const sep2 = path5.delimiter;
4417
+ const sep2 = path6.delimiter;
4306
4418
  const current = process.env.PATH ?? "";
4307
4419
  const existing = new Set(current.split(sep2).filter(Boolean));
4308
4420
  const additions = dirs.filter((d3) => !existing.has(d3));
@@ -4360,12 +4472,12 @@ async function ensureClaudeInstalled() {
4360
4472
  }
4361
4473
 
4362
4474
  // src/services/claude-resolver.ts
4363
- var path6 = __toESM(require("path"));
4475
+ var path7 = __toESM(require("path"));
4364
4476
  function buildClaudeLaunch(extraArgs = []) {
4365
4477
  const found = findInPath("claude") ?? findInPath("claude-code");
4366
4478
  if (!found) return null;
4367
4479
  if (process.platform === "win32") {
4368
- const ext = path6.extname(found).toLowerCase();
4480
+ const ext = path7.extname(found).toLowerCase();
4369
4481
  if (ext === ".cmd" || ext === ".bat") {
4370
4482
  return { cmd: "cmd.exe", args: ["/c", found, ...extraArgs] };
4371
4483
  }
@@ -4413,11 +4525,13 @@ var ClaudeService = class {
4413
4525
  }
4414
4526
  }
4415
4527
  if (process.platform === "win32") {
4528
+ log.trace("claude", `spawn (win32) cmd=${launch.cmd} args=${launch.args.join(" ")}`);
4416
4529
  const conpty = WindowsConPtyStrategy.tryCreate(this.strategyOpts);
4417
4530
  if (conpty) {
4418
4531
  try {
4419
4532
  conpty.spawn(launch.cmd, this.opts.cwd, launch.args);
4420
4533
  this.strategy = conpty;
4534
+ log.trace("claude", "ConPTY spawn ok");
4421
4535
  return;
4422
4536
  } catch (err) {
4423
4537
  const msg = err instanceof Error ? err.message : String(err);
@@ -4457,8 +4571,12 @@ var ClaudeService = class {
4457
4571
  * a fresh event-loop tick, after React has flushed the text into input state.
4458
4572
  */
4459
4573
  sendCommand(text) {
4460
- if (!this.strategy) return;
4574
+ if (!this.strategy) {
4575
+ log.trace("claude", "sendCommand dropped (no strategy)");
4576
+ return;
4577
+ }
4461
4578
  const s = this.strategy;
4579
+ log.trace("claude", `sendCommand text=${text.length}B`);
4462
4580
  s.write(text);
4463
4581
  setTimeout(() => s.write("\r"), 50);
4464
4582
  }
@@ -4532,12 +4650,12 @@ var https2 = __toESM(require("https"));
4532
4650
  var http2 = __toESM(require("http"));
4533
4651
 
4534
4652
  // ../../packages/shared/src/protocol/parseChrome.ts
4535
- var SPINNER_RE = /^[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]\s/;
4653
+ var SPINNER_RE = /^(?:[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]|🔴|🟠|🟡|🟢|🔵|🟣|🟤|⚫|⚪|🌀|💭|✨)\s/u;
4536
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;
4537
4655
  var TREE_LINE_RE = /^└\s/;
4538
- var STATUS_LINE_RE = /^\+\s/;
4656
+ var STATUS_LINE_RE = /^(?:\+|[🔴🟠🟡🟢🔵🟣🟤⚫⚪🌀💭✨])\s/u;
4539
4657
  function isChromeLine(line) {
4540
- const t2 = line.trim();
4658
+ const t2 = line.replace(/️/g, "").trim();
4541
4659
  if (!t2) return false;
4542
4660
  if (/^[─━—═─\-]{3,}$/.test(t2)) return true;
4543
4661
  if (SPINNER_RE.test(t2)) return true;
@@ -4562,7 +4680,7 @@ function isChromeLine(line) {
4562
4680
  return false;
4563
4681
  }
4564
4682
  function parseChromeLine(line) {
4565
- const t2 = line.trim();
4683
+ const t2 = line.replace(/️/g, "").trim();
4566
4684
  if (!t2) return null;
4567
4685
  if (/^[─━—═─\-]{3,}$/.test(t2)) return null;
4568
4686
  if (/^[❯>]\s*$/.test(t2)) return null;
@@ -4966,6 +5084,7 @@ var OutputService = class _OutputService {
4966
5084
  this.pollTimer = setInterval(() => this.tick(), _OutputService.POLL_MS);
4967
5085
  }
4968
5086
  newTurn() {
5087
+ log.trace("outputSvc", "newTurn() \u2014 activating output stream");
4969
5088
  this.stopPoll();
4970
5089
  this.rawBuffer = "";
4971
5090
  this.lastSentContent = "";
@@ -5003,9 +5122,11 @@ var OutputService = class _OutputService {
5003
5122
  const printable2 = raw.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, "");
5004
5123
  if (printable2.trim()) {
5005
5124
  this.terminalTurnPending = true;
5125
+ log.trace("outputSvc", `terminal-turn detected (idle, ${raw.length}B)`);
5006
5126
  this.onTerminalTurnDetected?.();
5007
5127
  }
5008
5128
  }
5129
+ log.trace("outputSvc", `push dropped (inactive, ${raw.length}B)`);
5009
5130
  return;
5010
5131
  }
5011
5132
  this.rawBuffer += raw;
@@ -5015,6 +5136,10 @@ var OutputService = class _OutputService {
5015
5136
  this.tryExtractSessionId(printable);
5016
5137
  this.tryDetectRateLimit(printable);
5017
5138
  }
5139
+ log.trace(
5140
+ "outputSvc",
5141
+ `push +${raw.length}B (buf=${this.rawBuffer.length}B printable=${printable.trim().length})`
5142
+ );
5018
5143
  }
5019
5144
  /** Extract Claude conversation ID from output text (e.g., from /cost command or session resume) */
5020
5145
  tryExtractSessionId(text) {
@@ -5057,6 +5182,10 @@ var OutputService = class _OutputService {
5057
5182
  const selector = detectSelector(lines) ?? detectListSelector(lines);
5058
5183
  if (selector) {
5059
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
+ );
5060
5189
  if (idleMs2 >= _OutputService.SELECTOR_IDLE_MS) {
5061
5190
  this.stopPoll();
5062
5191
  this.active = false;
@@ -5067,10 +5196,18 @@ var OutputService = class _OutputService {
5067
5196
  }
5068
5197
  const content = filterChrome(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
5069
5198
  if (!content) {
5199
+ log.trace(
5200
+ "outputSvc",
5201
+ `tick empty content (raw=${this.rawBuffer.length}B lines=${lines.length} elapsed=${elapsed}ms)`
5202
+ );
5070
5203
  if (elapsed >= _OutputService.EMPTY_TIMEOUT_MS) this.finalize();
5071
5204
  return;
5072
5205
  }
5073
5206
  const idleMs = this.lastPushTime > 0 ? now - this.lastPushTime : elapsed;
5207
+ log.trace(
5208
+ "outputSvc",
5209
+ `tick content (raw=${this.rawBuffer.length}B lines=${lines.length} content=${content.length} idleMs=${idleMs})`
5210
+ );
5074
5211
  if (idleMs >= _OutputService.IDLE_MS) {
5075
5212
  this.finalize();
5076
5213
  return;
@@ -5137,15 +5274,33 @@ var OutputService = class _OutputService {
5137
5274
  if (this.pluginAuthToken) {
5138
5275
  headers["X-Plugin-Auth-Token"] = this.pluginAuthToken;
5139
5276
  }
5277
+ log.trace(
5278
+ "outputSvc",
5279
+ `postChunk type=${body.type ?? "(clear)"} done=${body.done === true} bytes=${payload.length}`
5280
+ );
5140
5281
  return new Promise((resolve2) => {
5141
5282
  const attempt = (attemptsLeft) => {
5142
5283
  _transport2.sendOutputChunk(`${API_BASE4}/api/commands/output`, headers, payload).then(({ statusCode, body: resBody }) => {
5284
+ log.trace("outputSvc", `postChunk status=${statusCode}`);
5285
+ if (statusCode === 410 || statusCode === 404 && /SESSION_NOT_FOUND|SESSION_GONE/.test(resBody)) {
5286
+ if (this.active) {
5287
+ process.stderr.write("[codeam] session was deleted/disconnected \u2014 stopping output stream.\n");
5288
+ this.dispose();
5289
+ }
5290
+ resolve2();
5291
+ return;
5292
+ }
5143
5293
  if (statusCode >= 400) {
5144
5294
  process.stderr.write(`[codeam] output API error ${statusCode}: ${resBody}
5145
5295
  `);
5146
5296
  }
5147
5297
  resolve2();
5148
- }).catch(() => {
5298
+ }).catch((err) => {
5299
+ log.trace(
5300
+ "outputSvc",
5301
+ `postChunk error (retries left=${attemptsLeft})`,
5302
+ err
5303
+ );
5149
5304
  if (attemptsLeft > 0) {
5150
5305
  const delay = 200 * (maxRetries - attemptsLeft + 1);
5151
5306
  setTimeout(() => attempt(attemptsLeft - 1), delay);
@@ -5204,33 +5359,12 @@ function _sendOutputChunk(url, headers, payload) {
5204
5359
  }
5205
5360
 
5206
5361
  // src/services/history.service.ts
5207
- var fs4 = __toESM(require("fs"));
5208
- var path7 = __toESM(require("path"));
5209
- var os5 = __toESM(require("os"));
5362
+ var fs5 = __toESM(require("fs"));
5363
+ var path8 = __toESM(require("path"));
5364
+ var os6 = __toESM(require("os"));
5210
5365
  var https3 = __toESM(require("https"));
5211
5366
  var http3 = __toESM(require("http"));
5212
5367
  var import_zod = require("zod");
5213
-
5214
- // src/services/logger.ts
5215
- var LEVELS = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
5216
- function currentLevel() {
5217
- const raw = (process.env.CODEAM_LOG ?? "error").toLowerCase();
5218
- return LEVELS[raw] ?? LEVELS.error;
5219
- }
5220
- function emit(level, tag, msg, err) {
5221
- if (LEVELS[level] > currentLevel()) return;
5222
- const detail = err instanceof Error ? `: ${err.message}` : err !== void 0 ? `: ${String(err)}` : "";
5223
- process.stderr.write(`[codeam:${level}] ${tag} \u2014 ${msg}${detail}
5224
- `);
5225
- }
5226
- var log = {
5227
- error: (tag, msg, err) => emit("error", tag, msg, err),
5228
- warn: (tag, msg, err) => emit("warn", tag, msg, err),
5229
- info: (tag, msg, err) => emit("info", tag, msg, err),
5230
- debug: (tag, msg, err) => emit("debug", tag, msg, err)
5231
- };
5232
-
5233
- // src/services/history.service.ts
5234
5368
  var historyRecordSchema = import_zod.z.object({
5235
5369
  type: import_zod.z.string().optional(),
5236
5370
  timestamp: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).optional(),
@@ -5243,7 +5377,25 @@ var historyRecordSchema = import_zod.z.object({
5243
5377
  }).passthrough();
5244
5378
  var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
5245
5379
  function encodeCwd(cwd) {
5246
- return cwd.replace(/\//g, "-");
5380
+ return cwd.replace(/[\\/:]/g, "-");
5381
+ }
5382
+ function findProjectDir(cwd) {
5383
+ const projectsRoot = path8.join(os6.homedir(), ".claude", "projects");
5384
+ const primary = path8.join(projectsRoot, encodeCwd(cwd));
5385
+ if (fs5.existsSync(primary)) return primary;
5386
+ try {
5387
+ const entries = fs5.readdirSync(projectsRoot, { withFileTypes: true });
5388
+ const wanted = encodeCwd(cwd);
5389
+ for (const e of entries) {
5390
+ if (!e.isDirectory()) continue;
5391
+ const candidate = e.name.replace(/-+/g, "-");
5392
+ if (candidate === wanted.replace(/-+/g, "-")) {
5393
+ return path8.join(projectsRoot, e.name);
5394
+ }
5395
+ }
5396
+ } catch {
5397
+ }
5398
+ return null;
5247
5399
  }
5248
5400
  function extractText(content) {
5249
5401
  if (typeof content === "string") return content;
@@ -5257,7 +5409,7 @@ function parseJsonl(filePath) {
5257
5409
  const messages = [];
5258
5410
  let raw;
5259
5411
  try {
5260
- raw = fs4.readFileSync(filePath, "utf8");
5412
+ raw = fs5.readFileSync(filePath, "utf8");
5261
5413
  } catch (err) {
5262
5414
  if (err.code !== "ENOENT") {
5263
5415
  log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
@@ -5361,7 +5513,7 @@ var HistoryService = class {
5361
5513
  return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
5362
5514
  }
5363
5515
  get projectDir() {
5364
- return path7.join(os5.homedir(), ".claude", "projects", encodeCwd(this.cwd));
5516
+ return findProjectDir(this.cwd) ?? path8.join(os6.homedir(), ".claude", "projects", encodeCwd(this.cwd));
5365
5517
  }
5366
5518
  /** Set the current Claude conversation ID (extracted from /cost command or session start) */
5367
5519
  setCurrentConversationId(id) {
@@ -5373,7 +5525,7 @@ var HistoryService = class {
5373
5525
  /** Return the current message count in the active conversation. */
5374
5526
  getCurrentMessageCount() {
5375
5527
  if (!this.currentConversationId) return 0;
5376
- const filePath = path7.join(this.projectDir, `${this.currentConversationId}.jsonl`);
5528
+ const filePath = path8.join(this.projectDir, `${this.currentConversationId}.jsonl`);
5377
5529
  return parseJsonl(filePath).length;
5378
5530
  }
5379
5531
  /**
@@ -5384,7 +5536,7 @@ var HistoryService = class {
5384
5536
  const deadline = Date.now() + timeoutMs;
5385
5537
  while (Date.now() < deadline) {
5386
5538
  if (!this.currentConversationId) return null;
5387
- const filePath = path7.join(this.projectDir, `${this.currentConversationId}.jsonl`);
5539
+ const filePath = path8.join(this.projectDir, `${this.currentConversationId}.jsonl`);
5388
5540
  const messages = parseJsonl(filePath);
5389
5541
  if (messages.length > previousCount) {
5390
5542
  for (let i = messages.length - 1; i >= previousCount; i--) {
@@ -5399,15 +5551,15 @@ var HistoryService = class {
5399
5551
  detectCurrentConversation() {
5400
5552
  const dir = this.projectDir;
5401
5553
  try {
5402
- const files = fs4.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
5554
+ const files = fs5.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
5403
5555
  try {
5404
- return { name: e.name, mtime: fs4.statSync(path7.join(dir, e.name)).mtimeMs };
5556
+ return { name: e.name, mtime: fs5.statSync(path8.join(dir, e.name)).mtimeMs };
5405
5557
  } catch {
5406
5558
  return { name: e.name, mtime: 0 };
5407
5559
  }
5408
5560
  }).sort((a, b) => b.mtime - a.mtime);
5409
5561
  if (files.length > 0) {
5410
- this.currentConversationId = path7.basename(files[0].name, ".jsonl");
5562
+ this.currentConversationId = path8.basename(files[0].name, ".jsonl");
5411
5563
  }
5412
5564
  } catch {
5413
5565
  }
@@ -5439,13 +5591,13 @@ var HistoryService = class {
5439
5591
  const dir = this.projectDir;
5440
5592
  let entries;
5441
5593
  try {
5442
- entries = fs4.readdirSync(dir, { withFileTypes: true });
5594
+ entries = fs5.readdirSync(dir, { withFileTypes: true });
5443
5595
  } catch {
5444
5596
  return null;
5445
5597
  }
5446
5598
  const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
5447
5599
  try {
5448
- return { name: e.name, mtime: fs4.statSync(path7.join(dir, e.name)).mtimeMs };
5600
+ return { name: e.name, mtime: fs5.statSync(path8.join(dir, e.name)).mtimeMs };
5449
5601
  } catch {
5450
5602
  return { name: e.name, mtime: 0 };
5451
5603
  }
@@ -5453,12 +5605,12 @@ var HistoryService = class {
5453
5605
  if (files.length === 0) return null;
5454
5606
  const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
5455
5607
  if (!files.some((f) => f.name === targetFile)) return null;
5456
- return this.extractUsageFromFile(path7.join(dir, targetFile));
5608
+ return this.extractUsageFromFile(path8.join(dir, targetFile));
5457
5609
  }
5458
5610
  extractUsageFromFile(filePath) {
5459
5611
  let raw;
5460
5612
  try {
5461
- raw = fs4.readFileSync(filePath, "utf8");
5613
+ raw = fs5.readFileSync(filePath, "utf8");
5462
5614
  } catch {
5463
5615
  return null;
5464
5616
  }
@@ -5503,9 +5655,9 @@ var HistoryService = class {
5503
5655
  let totalCost = 0;
5504
5656
  let files;
5505
5657
  try {
5506
- files = fs4.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
5658
+ files = fs5.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
5507
5659
  try {
5508
- return fs4.statSync(path7.join(projectDir, f)).mtimeMs >= monthStartMs;
5660
+ return fs5.statSync(path8.join(projectDir, f)).mtimeMs >= monthStartMs;
5509
5661
  } catch {
5510
5662
  return false;
5511
5663
  }
@@ -5516,7 +5668,7 @@ var HistoryService = class {
5516
5668
  for (const file of files) {
5517
5669
  let raw;
5518
5670
  try {
5519
- raw = fs4.readFileSync(path7.join(projectDir, file), "utf8");
5671
+ raw = fs5.readFileSync(path8.join(projectDir, file), "utf8");
5520
5672
  } catch {
5521
5673
  continue;
5522
5674
  }
@@ -5551,23 +5703,23 @@ var HistoryService = class {
5551
5703
  const dir = this.projectDir;
5552
5704
  let entries;
5553
5705
  try {
5554
- entries = fs4.readdirSync(dir, { withFileTypes: true });
5706
+ entries = fs5.readdirSync(dir, { withFileTypes: true });
5555
5707
  } catch {
5556
5708
  return;
5557
5709
  }
5558
5710
  const sessions2 = [];
5559
5711
  for (const entry of entries) {
5560
5712
  if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
5561
- const id = path7.basename(entry.name, ".jsonl");
5562
- const filePath = path7.join(dir, entry.name);
5713
+ const id = path8.basename(entry.name, ".jsonl");
5714
+ const filePath = path8.join(dir, entry.name);
5563
5715
  let mtime = Date.now();
5564
5716
  try {
5565
- mtime = fs4.statSync(filePath).mtimeMs;
5717
+ mtime = fs5.statSync(filePath).mtimeMs;
5566
5718
  } catch {
5567
5719
  }
5568
5720
  let summary = "";
5569
5721
  try {
5570
- const raw = fs4.readFileSync(filePath, "utf8");
5722
+ const raw = fs5.readFileSync(filePath, "utf8");
5571
5723
  for (const line of raw.split("\n")) {
5572
5724
  if (!line.trim()) continue;
5573
5725
  try {
@@ -5600,7 +5752,7 @@ var HistoryService = class {
5600
5752
  * showing an empty conversation.
5601
5753
  */
5602
5754
  async loadConversation(sessionId) {
5603
- const filePath = path7.join(this.projectDir, `${sessionId}.jsonl`);
5755
+ const filePath = path8.join(this.projectDir, `${sessionId}.jsonl`);
5604
5756
  const messages = parseJsonl(filePath);
5605
5757
  if (messages.length === 0) return;
5606
5758
  const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
@@ -5655,8 +5807,8 @@ function parsePayload(schema, raw) {
5655
5807
  }
5656
5808
 
5657
5809
  // src/services/file-ops.service.ts
5658
- var fs5 = __toESM(require("fs/promises"));
5659
- var path8 = __toESM(require("path"));
5810
+ var fs6 = __toESM(require("fs/promises"));
5811
+ var path9 = __toESM(require("path"));
5660
5812
  var MAX_FILE_BYTES = 5 * 1024 * 1024;
5661
5813
  var MAX_WALK_DEPTH = 6;
5662
5814
  var MAX_VISITED_DIRS = 5e3;
@@ -5691,12 +5843,12 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
5691
5843
  "__pycache__"
5692
5844
  ]);
5693
5845
  function isUnder(parent, candidate) {
5694
- const rel = path8.relative(parent, candidate);
5695
- return rel === "" || !rel.startsWith("..") && !path8.isAbsolute(rel);
5846
+ const rel = path9.relative(parent, candidate);
5847
+ return rel === "" || !rel.startsWith("..") && !path9.isAbsolute(rel);
5696
5848
  }
5697
5849
  async function isExistingFile(absPath) {
5698
5850
  try {
5699
- const stat3 = await fs5.stat(absPath);
5851
+ const stat3 = await fs6.stat(absPath);
5700
5852
  return stat3.isFile();
5701
5853
  } catch {
5702
5854
  return false;
@@ -5709,13 +5861,13 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
5709
5861
  ctx.visited++;
5710
5862
  let entries = [];
5711
5863
  try {
5712
- entries = await fs5.readdir(dir, { withFileTypes: true });
5864
+ entries = await fs6.readdir(dir, { withFileTypes: true });
5713
5865
  } catch {
5714
5866
  return;
5715
5867
  }
5716
5868
  for (const e of entries) {
5717
5869
  if (!e.isFile()) continue;
5718
- const full = path8.join(dir, e.name);
5870
+ const full = path9.join(dir, e.name);
5719
5871
  if (needleVariants.some((needle) => full.endsWith(needle))) {
5720
5872
  ctx.matches.push(full);
5721
5873
  if (ctx.matches.length >= ctx.cap) return;
@@ -5725,21 +5877,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
5725
5877
  if (!e.isDirectory()) continue;
5726
5878
  if (SUBDIR_IGNORE.has(e.name)) continue;
5727
5879
  if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
5728
- await walkForSuffix(path8.join(dir, e.name), needleVariants, depth + 1, ctx);
5880
+ await walkForSuffix(path9.join(dir, e.name), needleVariants, depth + 1, ctx);
5729
5881
  if (ctx.matches.length >= ctx.cap) return;
5730
5882
  }
5731
5883
  }
5732
5884
  async function findFile(rawPath) {
5733
5885
  const cwd = process.cwd();
5734
- if (path8.isAbsolute(rawPath)) {
5735
- const abs = path8.normalize(rawPath);
5886
+ if (path9.isAbsolute(rawPath)) {
5887
+ const abs = path9.normalize(rawPath);
5736
5888
  if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
5737
5889
  }
5738
- const direct = path8.resolve(cwd, rawPath);
5890
+ const direct = path9.resolve(cwd, rawPath);
5739
5891
  if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
5740
- const normalized = path8.normalize(rawPath).replace(/^[./\\]+/, "");
5892
+ const normalized = path9.normalize(rawPath).replace(/^[./\\]+/, "");
5741
5893
  const needles = [
5742
- `${path8.sep}${normalized}`,
5894
+ `${path9.sep}${normalized}`,
5743
5895
  `/${normalized}`
5744
5896
  ].filter((v, i, a) => a.indexOf(v) === i);
5745
5897
  const ctx = { visited: 0, matches: [], cap: 16 };
@@ -5753,7 +5905,7 @@ async function findWriteTarget(rawPath) {
5753
5905
  const found = await findFile(rawPath);
5754
5906
  if (found) return found;
5755
5907
  const cwd = process.cwd();
5756
- const fallback = path8.isAbsolute(rawPath) ? path8.normalize(rawPath) : path8.resolve(cwd, rawPath);
5908
+ const fallback = path9.isAbsolute(rawPath) ? path9.normalize(rawPath) : path9.resolve(cwd, rawPath);
5757
5909
  if (!isUnder(cwd, fallback)) return null;
5758
5910
  return fallback;
5759
5911
  }
@@ -5770,11 +5922,11 @@ async function readProjectFile(rawPath) {
5770
5922
  if (!abs) {
5771
5923
  return { error: `File not found in the project tree: ${rawPath}` };
5772
5924
  }
5773
- const stat3 = await fs5.stat(abs);
5925
+ const stat3 = await fs6.stat(abs);
5774
5926
  if (stat3.size > MAX_FILE_BYTES) {
5775
5927
  return { error: `File too large (${(stat3.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
5776
5928
  }
5777
- const buf = await fs5.readFile(abs);
5929
+ const buf = await fs6.readFile(abs);
5778
5930
  if (looksBinary(buf)) {
5779
5931
  return { error: "Binary file \u2014 refusing to open in a code editor." };
5780
5932
  }
@@ -5793,8 +5945,8 @@ async function writeProjectFile(rawPath, content) {
5793
5945
  if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
5794
5946
  return { error: "Content too large." };
5795
5947
  }
5796
- await fs5.mkdir(path8.dirname(abs), { recursive: true });
5797
- await fs5.writeFile(abs, content, "utf-8");
5948
+ await fs6.mkdir(path9.dirname(abs), { recursive: true });
5949
+ await fs6.writeFile(abs, content, "utf-8");
5798
5950
  return { ok: true };
5799
5951
  } catch (e) {
5800
5952
  const msg = e instanceof Error ? e.message : "Write failed";
@@ -5805,8 +5957,8 @@ async function writeProjectFile(rawPath, content) {
5805
5957
  // src/services/project-ops.service.ts
5806
5958
  var import_child_process4 = require("child_process");
5807
5959
  var import_util = require("util");
5808
- var fs6 = __toESM(require("fs/promises"));
5809
- var path9 = __toESM(require("path"));
5960
+ var fs7 = __toESM(require("fs/promises"));
5961
+ var path10 = __toESM(require("path"));
5810
5962
  var execFileP = (0, import_util.promisify)(import_child_process4.execFile);
5811
5963
  var PROJECT_IGNORE = /* @__PURE__ */ new Set([
5812
5964
  "node_modules",
@@ -5854,7 +6006,7 @@ async function listProjectFiles(opts = {}) {
5854
6006
  }
5855
6007
  let entries = [];
5856
6008
  try {
5857
- entries = await fs6.readdir(dir, { withFileTypes: true });
6009
+ entries = await fs7.readdir(dir, { withFileTypes: true });
5858
6010
  } catch {
5859
6011
  return;
5860
6012
  }
@@ -5864,18 +6016,18 @@ async function listProjectFiles(opts = {}) {
5864
6016
  return;
5865
6017
  }
5866
6018
  if (PROJECT_IGNORE.has(e.name)) continue;
5867
- const full = path9.join(dir, e.name);
6019
+ const full = path10.join(dir, e.name);
5868
6020
  if (e.isDirectory()) {
5869
6021
  if (depth >= 12) continue;
5870
6022
  await walk(full, depth + 1);
5871
6023
  } else if (e.isFile()) {
5872
- const rel = path9.relative(root, full);
6024
+ const rel = path10.relative(root, full);
5873
6025
  if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
5874
6026
  continue;
5875
6027
  }
5876
6028
  let size = 0;
5877
6029
  try {
5878
- const st3 = await fs6.stat(full);
6030
+ const st3 = await fs7.stat(full);
5879
6031
  size = st3.size;
5880
6032
  } catch {
5881
6033
  }
@@ -5977,8 +6129,8 @@ async function gitStatus(cwd) {
5977
6129
  let hasMergeInProgress = false;
5978
6130
  try {
5979
6131
  const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
5980
- const mergeHead = path9.isAbsolute(gitDir) ? path9.join(gitDir, "MERGE_HEAD") : path9.join(root, gitDir, "MERGE_HEAD");
5981
- await fs6.access(mergeHead);
6132
+ const mergeHead = path10.isAbsolute(gitDir) ? path10.join(gitDir, "MERGE_HEAD") : path10.join(root, gitDir, "MERGE_HEAD");
6133
+ await fs7.access(mergeHead);
5982
6134
  hasMergeInProgress = true;
5983
6135
  } catch {
5984
6136
  }
@@ -6055,8 +6207,8 @@ async function gitResolve(file, side, cwd) {
6055
6207
  function saveFilesTemp(files) {
6056
6208
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
6057
6209
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
6058
- const tmpPath = path10.join(os6.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
6059
- fs7.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
6210
+ const tmpPath = path11.join(os7.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
6211
+ fs8.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
6060
6212
  return tmpPath;
6061
6213
  });
6062
6214
  }
@@ -6129,8 +6281,8 @@ try:
6129
6281
  sys.exit((st>>8)&0xFF)
6130
6282
  except Exception:sys.exit(0)
6131
6283
  `;
6132
- const helperPath = path10.join(os6.tmpdir(), "codeam-quota-helper.py");
6133
- fs7.writeFileSync(helperPath, helperScript, { mode: 420 });
6284
+ const helperPath = path11.join(os7.tmpdir(), "codeam-quota-helper.py");
6285
+ fs8.writeFileSync(helperPath, helperScript, { mode: 420 });
6134
6286
  const python = findInPath("python3") ?? findInPath("python");
6135
6287
  if (!python) {
6136
6288
  quotaFetchInProgress = false;
@@ -6162,7 +6314,7 @@ except Exception:sys.exit(0)
6162
6314
  } catch {
6163
6315
  }
6164
6316
  try {
6165
- fs7.unlinkSync(helperPath);
6317
+ fs8.unlinkSync(helperPath);
6166
6318
  } catch {
6167
6319
  }
6168
6320
  quotaFetchInProgress = false;
@@ -6212,7 +6364,7 @@ except Exception:sys.exit(0)
6212
6364
  setTimeout(() => {
6213
6365
  for (const p2 of paths) {
6214
6366
  try {
6215
- fs7.unlinkSync(p2);
6367
+ fs8.unlinkSync(p2);
6216
6368
  } catch {
6217
6369
  }
6218
6370
  }
@@ -6255,6 +6407,25 @@ except Exception:sys.exit(0)
6255
6407
  }
6256
6408
  break;
6257
6409
  }
6410
+ case "session_terminated": {
6411
+ showInfo("Session was deleted from the app \u2014 exiting.");
6412
+ try {
6413
+ claude.kill();
6414
+ } catch {
6415
+ }
6416
+ try {
6417
+ const proc = (0, import_child_process5.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
6418
+ detached: true,
6419
+ stdio: "ignore"
6420
+ });
6421
+ proc.unref();
6422
+ } catch {
6423
+ }
6424
+ outputSvc.dispose();
6425
+ relay.stop();
6426
+ ws.disconnect();
6427
+ process.exit(0);
6428
+ }
6258
6429
  case "shutdown_session": {
6259
6430
  try {
6260
6431
  await relay.sendResult(cmd.id, "success", { ok: true });
@@ -6435,7 +6606,7 @@ except Exception:sys.exit(0)
6435
6606
  setTimeout(() => {
6436
6607
  for (const p2 of paths) {
6437
6608
  try {
6438
- fs7.unlinkSync(p2);
6609
+ fs8.unlinkSync(p2);
6439
6610
  } catch {
6440
6611
  }
6441
6612
  }
@@ -6730,9 +6901,9 @@ async function logout() {
6730
6901
 
6731
6902
  // src/commands/deploy.ts
6732
6903
  var import_child_process10 = require("child_process");
6733
- var fs8 = __toESM(require("fs"));
6734
- var os7 = __toESM(require("os"));
6735
- var path15 = __toESM(require("path"));
6904
+ var fs9 = __toESM(require("fs"));
6905
+ var os8 = __toESM(require("os"));
6906
+ var path16 = __toESM(require("path"));
6736
6907
  var import_util6 = require("util");
6737
6908
  var import_picocolors9 = __toESM(require("picocolors"));
6738
6909
 
@@ -6740,7 +6911,7 @@ var import_picocolors9 = __toESM(require("picocolors"));
6740
6911
  var import_child_process6 = require("child_process");
6741
6912
  var import_util2 = require("util");
6742
6913
  var import_picocolors7 = __toESM(require("picocolors"));
6743
- var path11 = __toESM(require("path"));
6914
+ var path12 = __toESM(require("path"));
6744
6915
  var execFileP2 = (0, import_util2.promisify)(import_child_process6.execFile);
6745
6916
  var MAX_BUFFER = 8 * 1024 * 1024;
6746
6917
  function resetStdinForChild() {
@@ -7229,7 +7400,7 @@ var GitHubCodespacesProvider = class {
7229
7400
  });
7230
7401
  }
7231
7402
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
7232
- const remoteDir = path11.posix.dirname(remotePath);
7403
+ const remoteDir = path12.posix.dirname(remotePath);
7233
7404
  const parts = [
7234
7405
  `mkdir -p ${shellQuote(remoteDir)}`,
7235
7406
  `cat > ${shellQuote(remotePath)}`
@@ -7299,7 +7470,7 @@ function shellQuote(s) {
7299
7470
  // src/services/providers/gitpod.ts
7300
7471
  var import_child_process7 = require("child_process");
7301
7472
  var import_util3 = require("util");
7302
- var path12 = __toESM(require("path"));
7473
+ var path13 = __toESM(require("path"));
7303
7474
  var import_picocolors8 = __toESM(require("picocolors"));
7304
7475
  var execFileP3 = (0, import_util3.promisify)(import_child_process7.execFile);
7305
7476
  var MAX_BUFFER2 = 8 * 1024 * 1024;
@@ -7539,7 +7710,7 @@ var GitpodProvider = class {
7539
7710
  });
7540
7711
  }
7541
7712
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
7542
- const remoteDir = path12.posix.dirname(remotePath);
7713
+ const remoteDir = path13.posix.dirname(remotePath);
7543
7714
  const parts = [
7544
7715
  `mkdir -p ${shellQuote2(remoteDir)}`,
7545
7716
  `cat > ${shellQuote2(remotePath)}`
@@ -7575,7 +7746,7 @@ function shellQuote2(s) {
7575
7746
  // src/services/providers/gitlab-workspaces.ts
7576
7747
  var import_child_process8 = require("child_process");
7577
7748
  var import_util4 = require("util");
7578
- var path13 = __toESM(require("path"));
7749
+ var path14 = __toESM(require("path"));
7579
7750
  var execFileP4 = (0, import_util4.promisify)(import_child_process8.execFile);
7580
7751
  var MAX_BUFFER3 = 8 * 1024 * 1024;
7581
7752
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
@@ -7835,7 +8006,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
7835
8006
  }
7836
8007
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
7837
8008
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
7838
- const remoteDir = path13.posix.dirname(remotePath);
8009
+ const remoteDir = path14.posix.dirname(remotePath);
7839
8010
  const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
7840
8011
  if (options.mode != null) {
7841
8012
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
@@ -7903,7 +8074,7 @@ function shellQuote3(s) {
7903
8074
  // src/services/providers/railway.ts
7904
8075
  var import_child_process9 = require("child_process");
7905
8076
  var import_util5 = require("util");
7906
- var path14 = __toESM(require("path"));
8077
+ var path15 = __toESM(require("path"));
7907
8078
  var execFileP5 = (0, import_util5.promisify)(import_child_process9.execFile);
7908
8079
  var MAX_BUFFER4 = 8 * 1024 * 1024;
7909
8080
  function resetStdinForChild4() {
@@ -8139,7 +8310,7 @@ var RailwayProvider = class {
8139
8310
  if (!projectId || !serviceId) {
8140
8311
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
8141
8312
  }
8142
- const remoteDir = path14.posix.dirname(remotePath);
8313
+ const remoteDir = path15.posix.dirname(remotePath);
8143
8314
  const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
8144
8315
  if (options.mode != null) {
8145
8316
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
@@ -8331,7 +8502,7 @@ async function deploy() {
8331
8502
  process.exit(1);
8332
8503
  }
8333
8504
  }
8334
- const localClaudeDir = path15.join(os7.homedir(), ".claude");
8505
+ const localClaudeDir = path16.join(os8.homedir(), ".claude");
8335
8506
  const localCredsKind = await detectLocalClaudeCredentials(localClaudeDir);
8336
8507
  let bridged = "none";
8337
8508
  if (localCredsKind !== "none") {
@@ -8375,7 +8546,7 @@ async function deploy() {
8375
8546
  process.exit(1);
8376
8547
  }
8377
8548
  claudeStep.stop("\u2713 Claude CLI installed");
8378
- const haveLocalClaude = fs8.existsSync(localClaudeDir) && fs8.statSync(localClaudeDir).isDirectory();
8549
+ const haveLocalClaude = fs9.existsSync(localClaudeDir) && fs9.statSync(localClaudeDir).isDirectory();
8379
8550
  if (haveLocalClaude) {
8380
8551
  const copyStep = fe();
8381
8552
  copyStep.start("Copying local Claude config to workspace\u2026");
@@ -8429,10 +8600,10 @@ async function deploy() {
8429
8600
  }
8430
8601
  }
8431
8602
  if (bridged !== "none") {
8432
- const localClaudeJson = path15.join(os7.homedir(), ".claude.json");
8433
- if (fs8.existsSync(localClaudeJson)) {
8603
+ const localClaudeJson = path16.join(os8.homedir(), ".claude.json");
8604
+ if (fs9.existsSync(localClaudeJson)) {
8434
8605
  try {
8435
- const contents = fs8.readFileSync(localClaudeJson);
8606
+ const contents = fs9.readFileSync(localClaudeJson);
8436
8607
  await provider.uploadFile(
8437
8608
  workspace.id,
8438
8609
  "/home/codespace/.claude.json",
@@ -8622,7 +8793,7 @@ async function runRemoteClaudeLogin(provider, workspaceId) {
8622
8793
  }
8623
8794
  }
8624
8795
  async function detectLocalClaudeCredentials(localClaudeDir) {
8625
- if (fs8.existsSync(path15.join(localClaudeDir, ".credentials.json"))) {
8796
+ if (fs9.existsSync(path16.join(localClaudeDir, ".credentials.json"))) {
8626
8797
  return "flat-file";
8627
8798
  }
8628
8799
  if (process.platform === "darwin") {
@@ -8655,8 +8826,8 @@ async function verifyClaudeAuth(provider, workspaceId) {
8655
8826
  }
8656
8827
  }
8657
8828
  async function bridgeClaudeCredentials(provider, workspaceId, localClaudeDir) {
8658
- const fileBased = path15.join(localClaudeDir, ".credentials.json");
8659
- if (fs8.existsSync(fileBased)) return "flat-file";
8829
+ const fileBased = path16.join(localClaudeDir, ".credentials.json");
8830
+ if (fs9.existsSync(fileBased)) return "flat-file";
8660
8831
  if (process.platform === "darwin") {
8661
8832
  try {
8662
8833
  const { stdout } = await execFileP6(
@@ -8861,10 +9032,172 @@ async function stopWorkspaceFromLocal(target) {
8861
9032
  }
8862
9033
  }
8863
9034
 
9035
+ // src/commands/version.ts
9036
+ var import_picocolors11 = __toESM(require("picocolors"));
9037
+ function version() {
9038
+ const v = true ? "2.4.34" : "unknown";
9039
+ console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
9040
+ }
9041
+
9042
+ // src/commands/help.ts
9043
+ var import_picocolors12 = __toESM(require("picocolors"));
9044
+ function help() {
9045
+ const lines = [
9046
+ "",
9047
+ ` ${import_picocolors12.default.bold(import_picocolors12.default.magenta("codeam-cli"))} ${import_picocolors12.default.dim("\u2014 Claude Code remote control")}`,
9048
+ "",
9049
+ ` ${import_picocolors12.default.bold("Usage")}`,
9050
+ ` ${import_picocolors12.default.cyan("codeam")} ${import_picocolors12.default.dim("[command]")}`,
9051
+ "",
9052
+ ` ${import_picocolors12.default.bold("Commands")}`,
9053
+ ` ${import_picocolors12.default.white("codeam")} ${import_picocolors12.default.dim("start Claude Code with mobile control")}`,
9054
+ ` ${import_picocolors12.default.white("codeam pair")} ${import_picocolors12.default.dim("pair a new mobile device")}`,
9055
+ ` ${import_picocolors12.default.white("codeam sessions")} ${import_picocolors12.default.dim("list paired devices")}`,
9056
+ ` ${import_picocolors12.default.white("codeam status")} ${import_picocolors12.default.dim("show connection info")}`,
9057
+ ` ${import_picocolors12.default.white("codeam logout")} ${import_picocolors12.default.dim("remove all paired sessions")}`,
9058
+ ` ${import_picocolors12.default.white("codeam deploy")} ${import_picocolors12.default.dim("provision a cloud workspace (Codespaces) and pair it")}`,
9059
+ ` ${import_picocolors12.default.white("codeam deploy ls")} ${import_picocolors12.default.dim("list deployed cloud workspaces")}`,
9060
+ ` ${import_picocolors12.default.white("codeam deploy stop")} ${import_picocolors12.default.dim("stop a deployed workspace session")}`,
9061
+ "",
9062
+ ` ${import_picocolors12.default.bold("Flags")}`,
9063
+ ` ${import_picocolors12.default.white("-v, --version")} ${import_picocolors12.default.dim("print the CLI version")}`,
9064
+ ` ${import_picocolors12.default.white("-h, --help")} ${import_picocolors12.default.dim("show this help")}`,
9065
+ "",
9066
+ ` ${import_picocolors12.default.bold("Links")}`,
9067
+ ` ${import_picocolors12.default.dim("Docs:")} ${import_picocolors12.default.green("https://www.codeagent-mobile.com")}`,
9068
+ ` ${import_picocolors12.default.dim("Issues:")} ${import_picocolors12.default.green("https://github.com/edgar-durand/codeagent-mobile-clients/issues")}`,
9069
+ ""
9070
+ ];
9071
+ process.stdout.write(lines.join("\n") + "\n");
9072
+ }
9073
+
9074
+ // src/lib/updateNotifier.ts
9075
+ var fs10 = __toESM(require("fs"));
9076
+ var os9 = __toESM(require("os"));
9077
+ var path17 = __toESM(require("path"));
9078
+ var https4 = __toESM(require("https"));
9079
+ var import_picocolors13 = __toESM(require("picocolors"));
9080
+ var PKG_NAME = "codeam-cli";
9081
+ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
9082
+ var TTL_MS = 24 * 60 * 60 * 1e3;
9083
+ var REQUEST_TIMEOUT_MS = 1500;
9084
+ function cachePath() {
9085
+ const dir = path17.join(os9.homedir(), ".codeam");
9086
+ return path17.join(dir, "update-check.json");
9087
+ }
9088
+ function readCache() {
9089
+ try {
9090
+ const raw = fs10.readFileSync(cachePath(), "utf8");
9091
+ const parsed = JSON.parse(raw);
9092
+ if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
9093
+ return parsed;
9094
+ } catch {
9095
+ return null;
9096
+ }
9097
+ }
9098
+ function writeCache(cache) {
9099
+ try {
9100
+ const file = cachePath();
9101
+ fs10.mkdirSync(path17.dirname(file), { recursive: true });
9102
+ fs10.writeFileSync(file, JSON.stringify(cache));
9103
+ } catch {
9104
+ }
9105
+ }
9106
+ function compareSemver(a, b) {
9107
+ const stripPre = (s) => s.split("-")[0];
9108
+ const aParts = stripPre(a).split(".").map(Number);
9109
+ const bParts = stripPre(b).split(".").map(Number);
9110
+ for (let i = 0; i < 3; i++) {
9111
+ const ai = aParts[i] ?? 0;
9112
+ const bi = bParts[i] ?? 0;
9113
+ if (Number.isNaN(ai) || Number.isNaN(bi)) return 0;
9114
+ if (ai > bi) return 1;
9115
+ if (ai < bi) return -1;
9116
+ }
9117
+ return 0;
9118
+ }
9119
+ function fetchLatest() {
9120
+ return new Promise((resolve2) => {
9121
+ const req = https4.get(
9122
+ REGISTRY_URL,
9123
+ { headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
9124
+ (res) => {
9125
+ if (res.statusCode !== 200) {
9126
+ res.resume();
9127
+ resolve2(null);
9128
+ return;
9129
+ }
9130
+ let buf = "";
9131
+ res.setEncoding("utf8");
9132
+ res.on("data", (chunk) => {
9133
+ buf += chunk;
9134
+ });
9135
+ res.on("end", () => {
9136
+ try {
9137
+ const json = JSON.parse(buf);
9138
+ if (typeof json.version === "string") {
9139
+ resolve2(json.version);
9140
+ } else {
9141
+ resolve2(null);
9142
+ }
9143
+ } catch {
9144
+ resolve2(null);
9145
+ }
9146
+ });
9147
+ }
9148
+ );
9149
+ req.on("timeout", () => {
9150
+ req.destroy();
9151
+ resolve2(null);
9152
+ });
9153
+ req.on("error", () => resolve2(null));
9154
+ });
9155
+ }
9156
+ function notifyIfStale(currentVersion, latest) {
9157
+ if (compareSemver(latest, currentVersion) <= 0) return;
9158
+ const arrow = import_picocolors13.default.dim("\u2192");
9159
+ const cmd = import_picocolors13.default.cyan("npm install -g codeam-cli");
9160
+ const lines = [
9161
+ "",
9162
+ ` ${import_picocolors13.default.yellow("\u25CF")} ${import_picocolors13.default.bold("Update available")} ${import_picocolors13.default.dim(currentVersion)} ${arrow} ${import_picocolors13.default.green(latest)}`,
9163
+ ` Run ${cmd} to upgrade.`,
9164
+ ""
9165
+ ];
9166
+ process.stderr.write(lines.join("\n"));
9167
+ }
9168
+ function checkForUpdates() {
9169
+ if (process.env.NODE_ENV === "test") return;
9170
+ if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
9171
+ if (process.env.CI) return;
9172
+ if (!process.stdout.isTTY) return;
9173
+ const current = true ? "2.4.34" : null;
9174
+ if (!current) return;
9175
+ const cache = readCache();
9176
+ const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
9177
+ if (fresh && cache) {
9178
+ notifyIfStale(current, cache.latest);
9179
+ return;
9180
+ }
9181
+ void fetchLatest().then((latest) => {
9182
+ if (!latest) return;
9183
+ writeCache({ fetchedAt: Date.now(), latest });
9184
+ });
9185
+ }
9186
+
8864
9187
  // src/index.ts
8865
9188
  var [, , command, ...args] = process.argv;
8866
9189
  async function main() {
9190
+ const isMetaCommand = command === "--version" || command === "-v" || command === "version" || command === "--help" || command === "-h" || command === "help";
9191
+ if (!isMetaCommand) checkForUpdates();
8867
9192
  switch (command) {
9193
+ case "--version":
9194
+ case "-v":
9195
+ case "version":
9196
+ return version();
9197
+ case "--help":
9198
+ case "-h":
9199
+ case "help":
9200
+ return help();
8868
9201
  case "pair":
8869
9202
  return pair();
8870
9203
  case "sessions":