codeam-cli 2.11.0 → 2.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/index.js +392 -101
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,43 @@ All notable changes to `codeam-cli` are documented here.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [2.11.0] — 2026-05-13
8
+
9
+ ### Added
10
+
11
+ - **shared:** Add agent type primitives
12
+ - **shared:** Add AGENT_REGISTRY with Claude enabled
13
+ - **shared:** Export agents module + bump minor
14
+ - **cli:** Define RuntimeStrategy + DeployStrategy interfaces
15
+ - **cli:** Implement ClaudeRuntimeStrategy
16
+ - **cli:** Implement ClaudeDeployStrategy + extract credential bridge
17
+ - **cli:** Add agent to SavedSession + preferredAgent to CliConfig
18
+ - **cli:** Add parseAgentFlag + promptForAgent helpers
19
+ - **cli:** Pair accepts --agent flag + prompts + remembers preferredAgent
20
+ - **cli:** Pair-auto consumes agent from API response
21
+ - **cli:** Change_model + summarize handlers route through RuntimeStrategy
22
+
23
+ ### Changed
24
+
25
+ - **vsc-plugin:** Port JetBrains agent-strategy pattern
26
+ - **cli:** Extract Claude history parsing to agents/claude/history.ts
27
+ - **cli:** Extract Claude /usage parsing to agents/claude/quota.ts
28
+ - **cli:** Rename ClaudeService → AgentService, add registry factory
29
+ - **cli:** History + quota services delegate to RuntimeStrategy
30
+ - **cli:** Start.ts uses session.agent for runtime factory
31
+ - **cli:** Deploy uses DeployStrategy + switches to pair-auto
32
+ - **cli:** Pass args to pair command
33
+
34
+ ### Documentation
35
+
36
+ - Align CLAUDE.md + bump in-source versions to v2.10.8
37
+ - **cli:** Drop misleading codeam-login note in deploy.ts
38
+
39
+ ### Fixed
40
+
41
+ - **cli:** Reload config after addSession to avoid clobbering activeSessionId
42
+ - **cli:** Revert deploy PM2 wrapper to pair --agent=<id> (Phase 1 lacks user-JWT)
43
+
7
44
  ## [2.10.8] — 2026-05-12
8
45
 
9
46
  ### Fixed
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 fs15 = require("fs");
523
- var os14 = require("os");
524
- var path21 = require("path");
522
+ var fs17 = require("fs");
523
+ var os16 = require("os");
524
+ var path23 = 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 = path21.resolve(cwd);
560
+ cwd = path23.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 = fs15.openSync(term.conin, "w");
581
+ var inSocketFD = fs17.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(path21.join(__dirname, "conpty_console_list_agent"), [_this._innerPid.toString()]);
682
+ var agent = child_process_1.fork(path23.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(os14.release());
705
+ var osVersion = /(\d+)\.(\d+)\.(\d+)/g.exec(os16.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 fs15 = require("fs");
1016
- var path21 = require("path");
1015
+ var fs17 = require("fs");
1016
+ var path23 = 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 = path21.resolve(__dirname, helperPath);
1023
+ helperPath = path23.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
- fs15.write(this._fd, task.buffer, task.offset, function(err, written) {
1278
+ fs17.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() {
@@ -1313,10 +1313,10 @@ var require_lib = __commonJS({
1313
1313
  } else {
1314
1314
  terminalCtor = require_unixTerminal().UnixTerminal;
1315
1315
  }
1316
- function spawn10(file, args2, opt) {
1316
+ function spawn11(file, args2, opt) {
1317
1317
  return new terminalCtor(file, args2, opt);
1318
1318
  }
1319
- exports2.spawn = spawn10;
1319
+ exports2.spawn = spawn11;
1320
1320
  function fork(file, args2, opt) {
1321
1321
  return new terminalCtor(file, args2, opt);
1322
1322
  }
@@ -1738,18 +1738,37 @@ function filterChrome(lines) {
1738
1738
 
1739
1739
  // ../../packages/shared/src/models/pricing.ts
1740
1740
  var MODEL_PRICING = {
1741
+ // ── Anthropic / Claude ────────────────────────────────────
1741
1742
  "claude-sonnet-4": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
1742
1743
  "claude-opus-4": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
1743
1744
  "claude-3-5-sonnet": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
1744
1745
  "claude-3-5-haiku": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
1745
- "claude-3-haiku": { input: 0.25, output: 1.25, cacheRead: 0.03, cacheWrite: 0.3 }
1746
+ "claude-3-haiku": { input: 0.25, output: 1.25, cacheRead: 0.03, cacheWrite: 0.3 },
1747
+ // ── Codex / OpenAI ────────────────────────────────────────
1748
+ // Phase 2 placeholder pricing: 0 across the board until OpenAI publishes
1749
+ // confirmed rates for the GPT-5.x catalog. Sync from
1750
+ // developers.openai.com/pricing when available.
1751
+ "gpt-5.5": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1752
+ "gpt-5.4": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1753
+ "gpt-5.4-mini": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1754
+ "gpt-5.3-codex": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1755
+ "gpt-5.2": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1756
+ "codex-auto-review": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
1746
1757
  };
1747
1758
  var MODEL_CONTEXT_WINDOW = {
1759
+ // ── Anthropic / Claude ────────────────────────────────────
1748
1760
  "claude-opus-4": 1e6,
1749
1761
  "claude-sonnet-4": 1e6,
1750
1762
  "claude-3-5-sonnet": 2e5,
1751
1763
  "claude-3-5-haiku": 2e5,
1752
- "claude-3-haiku": 2e5
1764
+ "claude-3-haiku": 2e5,
1765
+ // ── Codex / OpenAI ────────────────────────────────────────
1766
+ "gpt-5.5": 272e3,
1767
+ "gpt-5.4": 272e3,
1768
+ "gpt-5.4-mini": 272e3,
1769
+ "gpt-5.3-codex": 272e3,
1770
+ "gpt-5.2": 272e3,
1771
+ "codex-auto-review": 272e3
1753
1772
  };
1754
1773
  var DEFAULT_CONTEXT_WINDOW = 2e5;
1755
1774
  function getPricing(model) {
@@ -1780,7 +1799,7 @@ var AGENT_REGISTRY = {
1780
1799
  id: "codex",
1781
1800
  displayName: "Codex CLI",
1782
1801
  binaryName: "codex",
1783
- enabled: false,
1802
+ enabled: true,
1784
1803
  supportedAuthKinds: ["oauth_token", "api_key"],
1785
1804
  preferredAuthKind: "oauth_token"
1786
1805
  },
@@ -1900,7 +1919,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
1900
1919
  // package.json
1901
1920
  var package_default = {
1902
1921
  name: "codeam-cli",
1903
- version: "2.11.0",
1922
+ version: "2.12.1",
1904
1923
  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.",
1905
1924
  type: "commonjs",
1906
1925
  main: "dist/index.js",
@@ -5648,13 +5667,285 @@ var ClaudeDeployStrategy = class {
5648
5667
  }
5649
5668
  };
5650
5669
 
5670
+ // src/agents/codex/runtime.ts
5671
+ var import_node_child_process = require("child_process");
5672
+
5673
+ // src/agents/codex/history.ts
5674
+ var import_node_fs2 = __toESM(require("fs"));
5675
+ var import_node_path2 = __toESM(require("path"));
5676
+ var import_node_os = __toESM(require("os"));
5677
+ function resolveHistoryDir2(_cwd, homeOverride) {
5678
+ const home = homeOverride ?? import_node_os.default.homedir();
5679
+ const sessionsRoot = import_node_path2.default.join(home, ".codex", "sessions");
5680
+ if (!import_node_fs2.default.existsSync(sessionsRoot)) return null;
5681
+ const now = /* @__PURE__ */ new Date();
5682
+ const yyyy = String(now.getUTCFullYear());
5683
+ const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
5684
+ const dd = String(now.getUTCDate()).padStart(2, "0");
5685
+ const todayDir = import_node_path2.default.join(sessionsRoot, yyyy, mm, dd);
5686
+ if (!import_node_fs2.default.existsSync(todayDir)) return null;
5687
+ return todayDir;
5688
+ }
5689
+ function parseLine(line) {
5690
+ try {
5691
+ const parsed = JSON.parse(line);
5692
+ if (parsed && typeof parsed === "object" && typeof parsed.timestamp === "string" && typeof parsed.type === "string") {
5693
+ return parsed;
5694
+ }
5695
+ } catch {
5696
+ }
5697
+ return null;
5698
+ }
5699
+ function extractMessageText(content) {
5700
+ if (!Array.isArray(content)) return "";
5701
+ const parts = [];
5702
+ for (const block of content) {
5703
+ if (!block || typeof block !== "object") continue;
5704
+ if (typeof block.text === "string" && block.text.length > 0) {
5705
+ if (block.type === "input_text" || block.type === "output_text") {
5706
+ parts.push(block.text);
5707
+ }
5708
+ }
5709
+ }
5710
+ return parts.join("\n");
5711
+ }
5712
+ function mapRole(codexRole) {
5713
+ if (codexRole === "user") return "user";
5714
+ if (codexRole === "assistant") return "agent";
5715
+ return "system";
5716
+ }
5717
+ function parseHistoryFile2(filePath) {
5718
+ const raw = import_node_fs2.default.readFileSync(filePath, "utf8");
5719
+ const lines = raw.split("\n").filter(Boolean);
5720
+ for (const line of lines) {
5721
+ const rec = parseLine(line);
5722
+ if (!rec) continue;
5723
+ if (rec.type === "session_meta") {
5724
+ const meta = rec.payload;
5725
+ if (meta && typeof meta.cwd === "string") {
5726
+ let resolvedMeta;
5727
+ let resolvedCurrent;
5728
+ try {
5729
+ resolvedMeta = import_node_fs2.default.realpathSync(meta.cwd);
5730
+ resolvedCurrent = import_node_fs2.default.realpathSync(process.cwd());
5731
+ } catch {
5732
+ resolvedMeta = import_node_path2.default.resolve(meta.cwd);
5733
+ resolvedCurrent = import_node_path2.default.resolve(process.cwd());
5734
+ }
5735
+ if (resolvedMeta !== resolvedCurrent) {
5736
+ return [];
5737
+ }
5738
+ }
5739
+ break;
5740
+ }
5741
+ }
5742
+ const out = [];
5743
+ let idx = 0;
5744
+ for (const line of lines) {
5745
+ const rec = parseLine(line);
5746
+ if (!rec) continue;
5747
+ if (rec.type !== "response_item") continue;
5748
+ const payload = rec.payload;
5749
+ const msg = payload?.Message;
5750
+ if (!msg) continue;
5751
+ const text = extractMessageText(msg.content);
5752
+ if (!text) continue;
5753
+ out.push({
5754
+ id: `rollout:${idx}`,
5755
+ role: mapRole(msg.role),
5756
+ text,
5757
+ timestamp: rec.timestamp
5758
+ });
5759
+ idx++;
5760
+ }
5761
+ return out;
5762
+ }
5763
+ function getCurrentUsage2(historyDir) {
5764
+ if (!import_node_fs2.default.existsSync(historyDir)) return null;
5765
+ const files = import_node_fs2.default.readdirSync(historyDir).filter((f) => f.startsWith("rollout-") && f.endsWith(".jsonl")).map((f) => ({ name: f, full: import_node_path2.default.join(historyDir, f) })).map((e) => ({ ...e, mtime: import_node_fs2.default.statSync(e.full).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
5766
+ if (files.length === 0) return null;
5767
+ const latest = files[0].full;
5768
+ const raw = import_node_fs2.default.readFileSync(latest, "utf8");
5769
+ let lastTokenCount = null;
5770
+ for (const line of raw.split("\n").filter(Boolean)) {
5771
+ const rec = parseLine(line);
5772
+ if (!rec || rec.type !== "event_msg") continue;
5773
+ const payload = rec.payload;
5774
+ const info = payload?.TokenCount?.info;
5775
+ const total2 = info?.total_token_usage?.total_tokens;
5776
+ const window = info?.model_context_window;
5777
+ if (typeof total2 === "number" && typeof window === "number" && window > 0) {
5778
+ lastTokenCount = { total: total2, window };
5779
+ }
5780
+ }
5781
+ if (!lastTokenCount) return null;
5782
+ const used = lastTokenCount.total;
5783
+ const total = lastTokenCount.window;
5784
+ return {
5785
+ used,
5786
+ total,
5787
+ percent: Math.min(100, Math.round(used / total * 100))
5788
+ };
5789
+ }
5790
+
5791
+ // src/agents/codex/runtime.ts
5792
+ var CODEX_CONTEXT_WINDOW = 272e3;
5793
+ var CODEX_MODELS = [
5794
+ { id: "gpt-5.5", label: "GPT-5.5", contextWindow: CODEX_CONTEXT_WINDOW },
5795
+ { id: "gpt-5.4", label: "GPT-5.4", contextWindow: CODEX_CONTEXT_WINDOW },
5796
+ { id: "gpt-5.4-mini", label: "GPT-5.4 Mini", contextWindow: CODEX_CONTEXT_WINDOW },
5797
+ { id: "gpt-5.3-codex", label: "GPT-5.3 Codex", contextWindow: CODEX_CONTEXT_WINDOW },
5798
+ { id: "gpt-5.2", label: "GPT-5.2", contextWindow: CODEX_CONTEXT_WINDOW },
5799
+ { id: "codex-auto-review", label: "Codex Auto Review", contextWindow: CODEX_CONTEXT_WINDOW }
5800
+ ];
5801
+ var CodexRuntimeStrategy = class {
5802
+ id = "codex";
5803
+ meta = getAgent("codex");
5804
+ async prepareLaunch() {
5805
+ const binary = findInPath("codex");
5806
+ if (!binary) {
5807
+ await installCodexViaNpm();
5808
+ const afterInstall = findInPath("codex");
5809
+ if (!afterInstall) {
5810
+ throw new Error(
5811
+ "Could not find 'codex' on PATH after running 'npm install -g @openai/codex'. Install it manually and retry."
5812
+ );
5813
+ }
5814
+ return { cmd: afterInstall, args: [] };
5815
+ }
5816
+ return { cmd: binary, args: [] };
5817
+ }
5818
+ /** `codex resume <SESSION_ID>` — subcommand, not flag. */
5819
+ resumeLaunchArgs(sessionId) {
5820
+ return ["resume", sessionId];
5821
+ }
5822
+ resolveHistoryDir(cwd) {
5823
+ return resolveHistoryDir2(cwd);
5824
+ }
5825
+ parseHistoryFile(filePath) {
5826
+ return parseHistoryFile2(filePath);
5827
+ }
5828
+ getCurrentUsage(historyDir) {
5829
+ return getCurrentUsage2(historyDir);
5830
+ }
5831
+ /**
5832
+ * Codex's quota lives behind the `account/get_account_rate_limits` RPC,
5833
+ * not a TUI slash command. Phase 2 ships with this stubbed to null so the
5834
+ * mobile shows "—" for weekly usage on Codex sessions. A follow-up will
5835
+ * invoke the RPC directly.
5836
+ */
5837
+ async fetchWeeklyUsage() {
5838
+ return null;
5839
+ }
5840
+ async listModels() {
5841
+ return CODEX_MODELS;
5842
+ }
5843
+ changeModelInstruction(modelId) {
5844
+ return { type: "pty", ptyInput: `/model ${modelId}\r` };
5845
+ }
5846
+ /**
5847
+ * Codex has no auto-compact (`auto_compact_token_limit: null` for every
5848
+ * model). Both modes fall through to the manual `/compact` slash command.
5849
+ */
5850
+ summarizeInstruction(_mode) {
5851
+ return { ptyInput: "/compact\r" };
5852
+ }
5853
+ };
5854
+ async function installCodexViaNpm() {
5855
+ return new Promise((resolve2, reject) => {
5856
+ const npm = process.platform === "win32" ? "npm.cmd" : "npm";
5857
+ const proc = (0, import_node_child_process.spawn)(npm, ["install", "-g", "@openai/codex"], {
5858
+ stdio: "inherit"
5859
+ });
5860
+ proc.on("close", (code) => {
5861
+ if (code === 0) resolve2();
5862
+ else reject(new Error(`npm install -g @openai/codex exited ${code}`));
5863
+ });
5864
+ proc.on("error", reject);
5865
+ });
5866
+ }
5867
+
5868
+ // src/agents/codex/credentials.ts
5869
+ var import_node_fs3 = __toESM(require("fs"));
5870
+ var import_node_path3 = __toESM(require("path"));
5871
+ var import_node_os2 = __toESM(require("os"));
5872
+ async function detectLocalCodexCredentials() {
5873
+ const home = process.env.HOME ?? import_node_os2.default.homedir();
5874
+ const authJson = import_node_path3.default.join(home, ".codex", "auth.json");
5875
+ if (import_node_fs3.default.existsSync(authJson)) {
5876
+ return { source: "flat-file", description: "~/.codex/auth.json" };
5877
+ }
5878
+ if (process.env.OPENAI_API_KEY) {
5879
+ return { source: "env-var", description: "OPENAI_API_KEY env var" };
5880
+ }
5881
+ return { source: "none", description: "" };
5882
+ }
5883
+ async function bridgeLocalCodexCredentials(provider, workspaceId) {
5884
+ const home = process.env.HOME ?? import_node_os2.default.homedir();
5885
+ const authJson = import_node_path3.default.join(home, ".codex", "auth.json");
5886
+ if (import_node_fs3.default.existsSync(authJson)) {
5887
+ const contents = import_node_fs3.default.readFileSync(authJson);
5888
+ await provider.exec(workspaceId, "mkdir -p ~/.codex && chmod 700 ~/.codex");
5889
+ await provider.uploadFile(workspaceId, "/home/codespace/.codex/auth.json", contents, { mode: 384 });
5890
+ return { source: "flat-file", description: "~/.codex/auth.json" };
5891
+ }
5892
+ const key = process.env.OPENAI_API_KEY;
5893
+ if (key) {
5894
+ const escaped = key.replace(/'/g, "'\\''");
5895
+ const line = `export OPENAI_API_KEY='${escaped}'`;
5896
+ await provider.exec(workspaceId, `grep -qxF "${line}" ~/.bashrc || echo "${line}" >> ~/.bashrc`);
5897
+ return { source: "env-var", description: "OPENAI_API_KEY env var" };
5898
+ }
5899
+ return { source: "none", description: "" };
5900
+ }
5901
+ async function runRemoteCodexLogin(provider, workspaceId) {
5902
+ await provider.streamCommand(workspaceId, 'bash -lc "codex login"');
5903
+ }
5904
+ async function verifyCodexAuth(provider, workspaceId) {
5905
+ const r = await provider.exec(workspaceId, 'bash -lc "codex --version"');
5906
+ return r.code === 0;
5907
+ }
5908
+
5909
+ // src/agents/codex/deploy.ts
5910
+ var CodexDeployStrategy = class {
5911
+ id = "codex";
5912
+ async detectLocalCredentials() {
5913
+ return detectLocalCodexCredentials();
5914
+ }
5915
+ async bridgeLocalCredentials(provider, workspaceId) {
5916
+ return bridgeLocalCodexCredentials(provider, workspaceId);
5917
+ }
5918
+ async runRemoteLogin(provider, workspaceId) {
5919
+ return runRemoteCodexLogin(provider, workspaceId);
5920
+ }
5921
+ /**
5922
+ * Steps 5–7 of the existing local-deploy flow, adapted for Codex:
5923
+ * 1. Install codex via npm
5924
+ * 2. (skipped — bridge already happened before this method runs, or the
5925
+ * fallback runs via runRemoteLogin)
5926
+ * 3. Verify auth; fallback to remote login if verify fails AND no
5927
+ * credentials were bridged.
5928
+ */
5929
+ async setupOnWorkspace(provider, workspaceId, opts) {
5930
+ const installResult = await provider.exec(workspaceId, "npm install -g @openai/codex");
5931
+ if (installResult.code !== 0) {
5932
+ throw new Error(`codex install failed: ${installResult.stderr.slice(0, 500)}`);
5933
+ }
5934
+ const verified = await verifyCodexAuth(provider, workspaceId);
5935
+ if (!verified && opts.bridged === "none") {
5936
+ await runRemoteCodexLogin(provider, workspaceId);
5937
+ }
5938
+ }
5939
+ };
5940
+
5651
5941
  // src/agents/registry.ts
5652
5942
  var runtimeBuilders = {
5653
- claude: () => new ClaudeRuntimeStrategy()
5654
- // codex and copilot added in Phase 2 / later
5943
+ claude: () => new ClaudeRuntimeStrategy(),
5944
+ codex: () => new CodexRuntimeStrategy()
5655
5945
  };
5656
5946
  var deployBuilders = {
5657
- claude: () => new ClaudeDeployStrategy()
5947
+ claude: () => new ClaudeDeployStrategy(),
5948
+ codex: () => new CodexDeployStrategy()
5658
5949
  };
5659
5950
  function createRuntimeStrategy(agent) {
5660
5951
  if (!AGENT_REGISTRY[agent]?.enabled) {
@@ -6149,9 +6440,9 @@ var OutputService = class _OutputService {
6149
6440
  };
6150
6441
 
6151
6442
  // src/services/history.service.ts
6152
- var fs9 = __toESM(require("fs"));
6153
- var path12 = __toESM(require("path"));
6154
- var os10 = __toESM(require("os"));
6443
+ var fs11 = __toESM(require("fs"));
6444
+ var path14 = __toESM(require("path"));
6445
+ var os12 = __toESM(require("os"));
6155
6446
  var https4 = __toESM(require("https"));
6156
6447
  var http4 = __toESM(require("http"));
6157
6448
  var import_zod = require("zod");
@@ -6178,7 +6469,7 @@ function parseJsonl(filePath) {
6178
6469
  const messages = [];
6179
6470
  let raw;
6180
6471
  try {
6181
- raw = fs9.readFileSync(filePath, "utf8");
6472
+ raw = fs11.readFileSync(filePath, "utf8");
6182
6473
  } catch (err) {
6183
6474
  if (err.code !== "ENOENT") {
6184
6475
  log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
@@ -6312,7 +6603,7 @@ var HistoryService = class _HistoryService {
6312
6603
  return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
6313
6604
  }
6314
6605
  get projectDir() {
6315
- return this.runtime.resolveHistoryDir(this.cwd) ?? path12.join(os10.homedir(), ".claude", "projects", encodeCwd(this.cwd));
6606
+ return this.runtime.resolveHistoryDir(this.cwd) ?? path14.join(os12.homedir(), ".claude", "projects", encodeCwd(this.cwd));
6316
6607
  }
6317
6608
  /** Set the current Claude conversation ID (extracted from /cost command or session start) */
6318
6609
  setCurrentConversationId(id) {
@@ -6324,7 +6615,7 @@ var HistoryService = class _HistoryService {
6324
6615
  /** Return the current message count in the active conversation. */
6325
6616
  getCurrentMessageCount() {
6326
6617
  if (!this.currentConversationId) return 0;
6327
- const filePath = path12.join(this.projectDir, `${this.currentConversationId}.jsonl`);
6618
+ const filePath = path14.join(this.projectDir, `${this.currentConversationId}.jsonl`);
6328
6619
  return parseJsonl(filePath).length;
6329
6620
  }
6330
6621
  /**
@@ -6335,7 +6626,7 @@ var HistoryService = class _HistoryService {
6335
6626
  const deadline = Date.now() + timeoutMs;
6336
6627
  while (Date.now() < deadline) {
6337
6628
  if (!this.currentConversationId) return null;
6338
- const filePath = path12.join(this.projectDir, `${this.currentConversationId}.jsonl`);
6629
+ const filePath = path14.join(this.projectDir, `${this.currentConversationId}.jsonl`);
6339
6630
  const messages = parseJsonl(filePath);
6340
6631
  if (messages.length > previousCount) {
6341
6632
  for (let i = messages.length - 1; i >= previousCount; i--) {
@@ -6361,16 +6652,16 @@ var HistoryService = class _HistoryService {
6361
6652
  const dir = this.projectDir;
6362
6653
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
6363
6654
  try {
6364
- const files = fs9.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
6655
+ const files = fs11.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
6365
6656
  try {
6366
- const stat3 = fs9.statSync(path12.join(dir, e.name));
6657
+ const stat3 = fs11.statSync(path14.join(dir, e.name));
6367
6658
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
6368
6659
  } catch {
6369
6660
  return { name: e.name, mtime: 0, birthtime: 0 };
6370
6661
  }
6371
6662
  }).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
6372
6663
  if (files.length > 0) {
6373
- this.currentConversationId = path12.basename(files[0].name, ".jsonl");
6664
+ this.currentConversationId = path14.basename(files[0].name, ".jsonl");
6374
6665
  }
6375
6666
  } catch {
6376
6667
  }
@@ -6404,13 +6695,13 @@ var HistoryService = class _HistoryService {
6404
6695
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
6405
6696
  let entries;
6406
6697
  try {
6407
- entries = fs9.readdirSync(dir, { withFileTypes: true });
6698
+ entries = fs11.readdirSync(dir, { withFileTypes: true });
6408
6699
  } catch {
6409
6700
  return null;
6410
6701
  }
6411
6702
  const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
6412
6703
  try {
6413
- const stat3 = fs9.statSync(path12.join(dir, e.name));
6704
+ const stat3 = fs11.statSync(path14.join(dir, e.name));
6414
6705
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
6415
6706
  } catch {
6416
6707
  return { name: e.name, mtime: 0, birthtime: 0 };
@@ -6419,12 +6710,12 @@ var HistoryService = class _HistoryService {
6419
6710
  if (files.length === 0) return null;
6420
6711
  const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
6421
6712
  if (!files.some((f) => f.name === targetFile)) return null;
6422
- return this.extractUsageFromFile(path12.join(dir, targetFile));
6713
+ return this.extractUsageFromFile(path14.join(dir, targetFile));
6423
6714
  }
6424
6715
  extractUsageFromFile(filePath) {
6425
6716
  let raw;
6426
6717
  try {
6427
- raw = fs9.readFileSync(filePath, "utf8");
6718
+ raw = fs11.readFileSync(filePath, "utf8");
6428
6719
  } catch {
6429
6720
  return null;
6430
6721
  }
@@ -6469,9 +6760,9 @@ var HistoryService = class _HistoryService {
6469
6760
  let totalCost = 0;
6470
6761
  let files;
6471
6762
  try {
6472
- files = fs9.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
6763
+ files = fs11.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
6473
6764
  try {
6474
- return fs9.statSync(path12.join(projectDir, f)).mtimeMs >= monthStartMs;
6765
+ return fs11.statSync(path14.join(projectDir, f)).mtimeMs >= monthStartMs;
6475
6766
  } catch {
6476
6767
  return false;
6477
6768
  }
@@ -6482,7 +6773,7 @@ var HistoryService = class _HistoryService {
6482
6773
  for (const file of files) {
6483
6774
  let raw;
6484
6775
  try {
6485
- raw = fs9.readFileSync(path12.join(projectDir, file), "utf8");
6776
+ raw = fs11.readFileSync(path14.join(projectDir, file), "utf8");
6486
6777
  } catch {
6487
6778
  continue;
6488
6779
  }
@@ -6517,23 +6808,23 @@ var HistoryService = class _HistoryService {
6517
6808
  const dir = this.projectDir;
6518
6809
  let entries;
6519
6810
  try {
6520
- entries = fs9.readdirSync(dir, { withFileTypes: true });
6811
+ entries = fs11.readdirSync(dir, { withFileTypes: true });
6521
6812
  } catch {
6522
6813
  return;
6523
6814
  }
6524
6815
  const sessions2 = [];
6525
6816
  for (const entry of entries) {
6526
6817
  if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
6527
- const id = path12.basename(entry.name, ".jsonl");
6528
- const filePath = path12.join(dir, entry.name);
6818
+ const id = path14.basename(entry.name, ".jsonl");
6819
+ const filePath = path14.join(dir, entry.name);
6529
6820
  let mtime = Date.now();
6530
6821
  try {
6531
- mtime = fs9.statSync(filePath).mtimeMs;
6822
+ mtime = fs11.statSync(filePath).mtimeMs;
6532
6823
  } catch {
6533
6824
  }
6534
6825
  let summary = "";
6535
6826
  try {
6536
- const raw = fs9.readFileSync(filePath, "utf8");
6827
+ const raw = fs11.readFileSync(filePath, "utf8");
6537
6828
  for (const line of raw.split("\n")) {
6538
6829
  if (!line.trim()) continue;
6539
6830
  try {
@@ -6566,7 +6857,7 @@ var HistoryService = class _HistoryService {
6566
6857
  * showing an empty conversation.
6567
6858
  */
6568
6859
  async loadConversation(sessionId) {
6569
- const filePath = path12.join(this.projectDir, `${sessionId}.jsonl`);
6860
+ const filePath = path14.join(this.projectDir, `${sessionId}.jsonl`);
6570
6861
  const messages = parseJsonl(filePath);
6571
6862
  if (messages.length === 0) return;
6572
6863
  const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
@@ -6609,7 +6900,7 @@ var HistoryService = class _HistoryService {
6609
6900
  if (!this.currentConversationId) return 0;
6610
6901
  }
6611
6902
  const sessionId = this.currentConversationId;
6612
- const filePath = path12.join(this.projectDir, `${sessionId}.jsonl`);
6903
+ const filePath = path14.join(this.projectDir, `${sessionId}.jsonl`);
6613
6904
  const messages = parseJsonl(filePath);
6614
6905
  if (messages.length === 0) return 0;
6615
6906
  const marker = this.lastUploadedUuid.get(sessionId);
@@ -6695,9 +6986,9 @@ function buildKeepAlive(ctx) {
6695
6986
  }
6696
6987
 
6697
6988
  // src/commands/start/handlers.ts
6698
- var fs12 = __toESM(require("fs"));
6699
- var os11 = __toESM(require("os"));
6700
- var path15 = __toESM(require("path"));
6989
+ var fs14 = __toESM(require("fs"));
6990
+ var os13 = __toESM(require("os"));
6991
+ var path17 = __toESM(require("path"));
6701
6992
  var import_crypto = require("crypto");
6702
6993
  var import_child_process8 = require("child_process");
6703
6994
 
@@ -6736,8 +7027,8 @@ function parsePayload(schema, raw) {
6736
7027
  }
6737
7028
 
6738
7029
  // src/services/file-ops.service.ts
6739
- var fs10 = __toESM(require("fs/promises"));
6740
- var path13 = __toESM(require("path"));
7030
+ var fs12 = __toESM(require("fs/promises"));
7031
+ var path15 = __toESM(require("path"));
6741
7032
  var MAX_FILE_BYTES = 5 * 1024 * 1024;
6742
7033
  var MAX_WALK_DEPTH = 6;
6743
7034
  var MAX_VISITED_DIRS = 5e3;
@@ -6772,12 +7063,12 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
6772
7063
  "__pycache__"
6773
7064
  ]);
6774
7065
  function isUnder(parent, candidate) {
6775
- const rel = path13.relative(parent, candidate);
6776
- return rel === "" || !rel.startsWith("..") && !path13.isAbsolute(rel);
7066
+ const rel = path15.relative(parent, candidate);
7067
+ return rel === "" || !rel.startsWith("..") && !path15.isAbsolute(rel);
6777
7068
  }
6778
7069
  async function isExistingFile(absPath) {
6779
7070
  try {
6780
- const stat3 = await fs10.stat(absPath);
7071
+ const stat3 = await fs12.stat(absPath);
6781
7072
  return stat3.isFile();
6782
7073
  } catch {
6783
7074
  return false;
@@ -6790,13 +7081,13 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
6790
7081
  ctx.visited++;
6791
7082
  let entries = [];
6792
7083
  try {
6793
- entries = await fs10.readdir(dir, { withFileTypes: true });
7084
+ entries = await fs12.readdir(dir, { withFileTypes: true });
6794
7085
  } catch {
6795
7086
  return;
6796
7087
  }
6797
7088
  for (const e of entries) {
6798
7089
  if (!e.isFile()) continue;
6799
- const full = path13.join(dir, e.name);
7090
+ const full = path15.join(dir, e.name);
6800
7091
  if (needleVariants.some((needle) => full.endsWith(needle))) {
6801
7092
  ctx.matches.push(full);
6802
7093
  if (ctx.matches.length >= ctx.cap) return;
@@ -6806,21 +7097,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
6806
7097
  if (!e.isDirectory()) continue;
6807
7098
  if (SUBDIR_IGNORE.has(e.name)) continue;
6808
7099
  if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
6809
- await walkForSuffix(path13.join(dir, e.name), needleVariants, depth + 1, ctx);
7100
+ await walkForSuffix(path15.join(dir, e.name), needleVariants, depth + 1, ctx);
6810
7101
  if (ctx.matches.length >= ctx.cap) return;
6811
7102
  }
6812
7103
  }
6813
7104
  async function findFile(rawPath) {
6814
7105
  const cwd = process.cwd();
6815
- if (path13.isAbsolute(rawPath)) {
6816
- const abs = path13.normalize(rawPath);
7106
+ if (path15.isAbsolute(rawPath)) {
7107
+ const abs = path15.normalize(rawPath);
6817
7108
  if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
6818
7109
  }
6819
- const direct = path13.resolve(cwd, rawPath);
7110
+ const direct = path15.resolve(cwd, rawPath);
6820
7111
  if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
6821
- const normalized = path13.normalize(rawPath).replace(/^[./\\]+/, "");
7112
+ const normalized = path15.normalize(rawPath).replace(/^[./\\]+/, "");
6822
7113
  const needles = [
6823
- `${path13.sep}${normalized}`,
7114
+ `${path15.sep}${normalized}`,
6824
7115
  `/${normalized}`
6825
7116
  ].filter((v, i, a) => a.indexOf(v) === i);
6826
7117
  const ctx = { visited: 0, matches: [], cap: 16 };
@@ -6834,7 +7125,7 @@ async function findWriteTarget(rawPath) {
6834
7125
  const found = await findFile(rawPath);
6835
7126
  if (found) return found;
6836
7127
  const cwd = process.cwd();
6837
- const fallback = path13.isAbsolute(rawPath) ? path13.normalize(rawPath) : path13.resolve(cwd, rawPath);
7128
+ const fallback = path15.isAbsolute(rawPath) ? path15.normalize(rawPath) : path15.resolve(cwd, rawPath);
6838
7129
  if (!isUnder(cwd, fallback)) return null;
6839
7130
  return fallback;
6840
7131
  }
@@ -6851,11 +7142,11 @@ async function readProjectFile(rawPath) {
6851
7142
  if (!abs) {
6852
7143
  return { error: `File not found in the project tree: ${rawPath}` };
6853
7144
  }
6854
- const stat3 = await fs10.stat(abs);
7145
+ const stat3 = await fs12.stat(abs);
6855
7146
  if (stat3.size > MAX_FILE_BYTES) {
6856
7147
  return { error: `File too large (${(stat3.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
6857
7148
  }
6858
- const buf = await fs10.readFile(abs);
7149
+ const buf = await fs12.readFile(abs);
6859
7150
  if (looksBinary(buf)) {
6860
7151
  return { error: "Binary file \u2014 refusing to open in a code editor." };
6861
7152
  }
@@ -6874,8 +7165,8 @@ async function writeProjectFile(rawPath, content) {
6874
7165
  if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
6875
7166
  return { error: "Content too large." };
6876
7167
  }
6877
- await fs10.mkdir(path13.dirname(abs), { recursive: true });
6878
- await fs10.writeFile(abs, content, "utf-8");
7168
+ await fs12.mkdir(path15.dirname(abs), { recursive: true });
7169
+ await fs12.writeFile(abs, content, "utf-8");
6879
7170
  return { ok: true };
6880
7171
  } catch (e) {
6881
7172
  const msg = e instanceof Error ? e.message : "Write failed";
@@ -6886,8 +7177,8 @@ async function writeProjectFile(rawPath, content) {
6886
7177
  // src/services/project-ops.service.ts
6887
7178
  var import_child_process7 = require("child_process");
6888
7179
  var import_util2 = require("util");
6889
- var fs11 = __toESM(require("fs/promises"));
6890
- var path14 = __toESM(require("path"));
7180
+ var fs13 = __toESM(require("fs/promises"));
7181
+ var path16 = __toESM(require("path"));
6891
7182
  var execFileP2 = (0, import_util2.promisify)(import_child_process7.execFile);
6892
7183
  var PROJECT_IGNORE = /* @__PURE__ */ new Set([
6893
7184
  "node_modules",
@@ -6935,7 +7226,7 @@ async function listProjectFiles(opts = {}) {
6935
7226
  }
6936
7227
  let entries = [];
6937
7228
  try {
6938
- entries = await fs11.readdir(dir, { withFileTypes: true });
7229
+ entries = await fs13.readdir(dir, { withFileTypes: true });
6939
7230
  } catch {
6940
7231
  return;
6941
7232
  }
@@ -6945,18 +7236,18 @@ async function listProjectFiles(opts = {}) {
6945
7236
  return;
6946
7237
  }
6947
7238
  if (PROJECT_IGNORE.has(e.name)) continue;
6948
- const full = path14.join(dir, e.name);
7239
+ const full = path16.join(dir, e.name);
6949
7240
  if (e.isDirectory()) {
6950
7241
  if (depth >= 12) continue;
6951
7242
  await walk(full, depth + 1);
6952
7243
  } else if (e.isFile()) {
6953
- const rel = path14.relative(root, full);
7244
+ const rel = path16.relative(root, full);
6954
7245
  if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
6955
7246
  continue;
6956
7247
  }
6957
7248
  let size = 0;
6958
7249
  try {
6959
- const st3 = await fs11.stat(full);
7250
+ const st3 = await fs13.stat(full);
6960
7251
  size = st3.size;
6961
7252
  } catch {
6962
7253
  }
@@ -7058,8 +7349,8 @@ async function gitStatus(cwd) {
7058
7349
  let hasMergeInProgress = false;
7059
7350
  try {
7060
7351
  const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
7061
- const mergeHead = path14.isAbsolute(gitDir) ? path14.join(gitDir, "MERGE_HEAD") : path14.join(root, gitDir, "MERGE_HEAD");
7062
- await fs11.access(mergeHead);
7352
+ const mergeHead = path16.isAbsolute(gitDir) ? path16.join(gitDir, "MERGE_HEAD") : path16.join(root, gitDir, "MERGE_HEAD");
7353
+ await fs13.access(mergeHead);
7063
7354
  hasMergeInProgress = true;
7064
7355
  } catch {
7065
7356
  }
@@ -7136,8 +7427,8 @@ async function gitResolve(file, side, cwd) {
7136
7427
  function saveFilesTemp(files) {
7137
7428
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
7138
7429
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
7139
- const tmpPath = path15.join(os11.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
7140
- fs12.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
7430
+ const tmpPath = path17.join(os13.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
7431
+ fs14.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
7141
7432
  return tmpPath;
7142
7433
  });
7143
7434
  }
@@ -7156,7 +7447,7 @@ var startTask = (ctx, _cmd, parsed) => {
7156
7447
  setTimeout(() => {
7157
7448
  for (const p2 of paths) {
7158
7449
  try {
7159
- fs12.unlinkSync(p2);
7450
+ fs14.unlinkSync(p2);
7160
7451
  } catch {
7161
7452
  }
7162
7453
  }
@@ -7615,8 +7906,8 @@ async function pair(args2 = []) {
7615
7906
  }
7616
7907
 
7617
7908
  // src/commands/pair-auto.ts
7618
- var fs13 = __toESM(require("fs"));
7619
- var os12 = __toESM(require("os"));
7909
+ var fs15 = __toESM(require("fs"));
7910
+ var os14 = __toESM(require("os"));
7620
7911
  var import_crypto3 = require("crypto");
7621
7912
  var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
7622
7913
  function fail(msg) {
@@ -7633,12 +7924,12 @@ function readTokenFromArgs(args2) {
7633
7924
  }
7634
7925
  const fileFlag = args2.find((a) => a.startsWith("--token-file="));
7635
7926
  if (fileFlag) {
7636
- const path21 = fileFlag.slice("--token-file=".length);
7927
+ const path23 = fileFlag.slice("--token-file=".length);
7637
7928
  try {
7638
- const content = fs13.readFileSync(path21, "utf8").trim();
7639
- if (content.length === 0) fail(`--token-file ${path21} is empty`);
7929
+ const content = fs15.readFileSync(path23, "utf8").trim();
7930
+ if (content.length === 0) fail(`--token-file ${path23} is empty`);
7640
7931
  try {
7641
- fs13.unlinkSync(path21);
7932
+ fs15.unlinkSync(path23);
7642
7933
  } catch {
7643
7934
  }
7644
7935
  return content;
@@ -7656,7 +7947,7 @@ async function claim(token, pluginId) {
7656
7947
  pluginId,
7657
7948
  ideName: "codeam-cli (codespace)",
7658
7949
  ideVersion: process.env.npm_package_version ?? "unknown",
7659
- hostname: os12.hostname(),
7950
+ hostname: os14.hostname(),
7660
7951
  codespaceName: process.env.CODESPACE_NAME ?? ""
7661
7952
  };
7662
7953
  const res = await fetch(url, {
@@ -7808,7 +8099,7 @@ var import_picocolors9 = __toESM(require("picocolors"));
7808
8099
  var import_child_process9 = require("child_process");
7809
8100
  var import_util3 = require("util");
7810
8101
  var import_picocolors7 = __toESM(require("picocolors"));
7811
- var path16 = __toESM(require("path"));
8102
+ var path18 = __toESM(require("path"));
7812
8103
  var execFileP3 = (0, import_util3.promisify)(import_child_process9.execFile);
7813
8104
  var MAX_BUFFER = 8 * 1024 * 1024;
7814
8105
  function resetStdinForChild() {
@@ -8297,7 +8588,7 @@ var GitHubCodespacesProvider = class {
8297
8588
  });
8298
8589
  }
8299
8590
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
8300
- const remoteDir = path16.posix.dirname(remotePath);
8591
+ const remoteDir = path18.posix.dirname(remotePath);
8301
8592
  const parts = [
8302
8593
  `mkdir -p ${shellQuote(remoteDir)}`,
8303
8594
  `cat > ${shellQuote(remotePath)}`
@@ -8367,7 +8658,7 @@ function shellQuote(s) {
8367
8658
  // src/services/providers/gitpod.ts
8368
8659
  var import_child_process10 = require("child_process");
8369
8660
  var import_util4 = require("util");
8370
- var path17 = __toESM(require("path"));
8661
+ var path19 = __toESM(require("path"));
8371
8662
  var import_picocolors8 = __toESM(require("picocolors"));
8372
8663
  var execFileP4 = (0, import_util4.promisify)(import_child_process10.execFile);
8373
8664
  var MAX_BUFFER2 = 8 * 1024 * 1024;
@@ -8607,7 +8898,7 @@ var GitpodProvider = class {
8607
8898
  });
8608
8899
  }
8609
8900
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
8610
- const remoteDir = path17.posix.dirname(remotePath);
8901
+ const remoteDir = path19.posix.dirname(remotePath);
8611
8902
  const parts = [
8612
8903
  `mkdir -p ${shellQuote2(remoteDir)}`,
8613
8904
  `cat > ${shellQuote2(remotePath)}`
@@ -8643,7 +8934,7 @@ function shellQuote2(s) {
8643
8934
  // src/services/providers/gitlab-workspaces.ts
8644
8935
  var import_child_process11 = require("child_process");
8645
8936
  var import_util5 = require("util");
8646
- var path18 = __toESM(require("path"));
8937
+ var path20 = __toESM(require("path"));
8647
8938
  var execFileP5 = (0, import_util5.promisify)(import_child_process11.execFile);
8648
8939
  var MAX_BUFFER3 = 8 * 1024 * 1024;
8649
8940
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
@@ -8903,7 +9194,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
8903
9194
  }
8904
9195
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
8905
9196
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
8906
- const remoteDir = path18.posix.dirname(remotePath);
9197
+ const remoteDir = path20.posix.dirname(remotePath);
8907
9198
  const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
8908
9199
  if (options.mode != null) {
8909
9200
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
@@ -8971,7 +9262,7 @@ function shellQuote3(s) {
8971
9262
  // src/services/providers/railway.ts
8972
9263
  var import_child_process12 = require("child_process");
8973
9264
  var import_util6 = require("util");
8974
- var path19 = __toESM(require("path"));
9265
+ var path21 = __toESM(require("path"));
8975
9266
  var execFileP6 = (0, import_util6.promisify)(import_child_process12.execFile);
8976
9267
  var MAX_BUFFER4 = 8 * 1024 * 1024;
8977
9268
  function resetStdinForChild4() {
@@ -9207,7 +9498,7 @@ var RailwayProvider = class {
9207
9498
  if (!projectId || !serviceId) {
9208
9499
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
9209
9500
  }
9210
- const remoteDir = path19.posix.dirname(remotePath);
9501
+ const remoteDir = path21.posix.dirname(remotePath);
9211
9502
  const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
9212
9503
  if (options.mode != null) {
9213
9504
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
@@ -9749,7 +10040,7 @@ async function stopWorkspaceFromLocal(target) {
9749
10040
  // src/commands/version.ts
9750
10041
  var import_picocolors11 = __toESM(require("picocolors"));
9751
10042
  function version() {
9752
- const v = true ? "2.11.0" : "unknown";
10043
+ const v = true ? "2.12.1" : "unknown";
9753
10044
  console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
9754
10045
  }
9755
10046
 
@@ -9786,9 +10077,9 @@ function help() {
9786
10077
  }
9787
10078
 
9788
10079
  // src/lib/updateNotifier.ts
9789
- var fs14 = __toESM(require("fs"));
9790
- var os13 = __toESM(require("os"));
9791
- var path20 = __toESM(require("path"));
10080
+ var fs16 = __toESM(require("fs"));
10081
+ var os15 = __toESM(require("os"));
10082
+ var path22 = __toESM(require("path"));
9792
10083
  var https5 = __toESM(require("https"));
9793
10084
  var import_picocolors13 = __toESM(require("picocolors"));
9794
10085
  var PKG_NAME = "codeam-cli";
@@ -9796,12 +10087,12 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
9796
10087
  var TTL_MS = 24 * 60 * 60 * 1e3;
9797
10088
  var REQUEST_TIMEOUT_MS = 1500;
9798
10089
  function cachePath() {
9799
- const dir = path20.join(os13.homedir(), ".codeam");
9800
- return path20.join(dir, "update-check.json");
10090
+ const dir = path22.join(os15.homedir(), ".codeam");
10091
+ return path22.join(dir, "update-check.json");
9801
10092
  }
9802
10093
  function readCache() {
9803
10094
  try {
9804
- const raw = fs14.readFileSync(cachePath(), "utf8");
10095
+ const raw = fs16.readFileSync(cachePath(), "utf8");
9805
10096
  const parsed = JSON.parse(raw);
9806
10097
  if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
9807
10098
  return parsed;
@@ -9812,8 +10103,8 @@ function readCache() {
9812
10103
  function writeCache(cache) {
9813
10104
  try {
9814
10105
  const file = cachePath();
9815
- fs14.mkdirSync(path20.dirname(file), { recursive: true });
9816
- fs14.writeFileSync(file, JSON.stringify(cache));
10106
+ fs16.mkdirSync(path22.dirname(file), { recursive: true });
10107
+ fs16.writeFileSync(file, JSON.stringify(cache));
9817
10108
  } catch {
9818
10109
  }
9819
10110
  }
@@ -9884,7 +10175,7 @@ function checkForUpdates() {
9884
10175
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
9885
10176
  if (process.env.CI) return;
9886
10177
  if (!process.stdout.isTTY) return;
9887
- const current = true ? "2.11.0" : null;
10178
+ const current = true ? "2.12.1" : null;
9888
10179
  if (!current) return;
9889
10180
  const cache = readCache();
9890
10181
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.11.0",
3
+ "version": "2.12.1",
4
4
  "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 — from anywhere.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",