codeam-cli 2.12.17 → 2.14.0

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 +22 -0
  2. package/dist/index.js +340 -38
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,28 @@ 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.13.0] — 2026-05-16
8
+
9
+ ### Added
10
+
11
+ - **cli:** Forward CODEAM_VERCEL_BYPASS as x-vercel-protection-bypass
12
+
13
+ ### CI
14
+
15
+ - **deps:** Cap @types/node major in CLI dependabot config
16
+
17
+ ### Chore
18
+
19
+ - **deps:** Bump gradle-wrapper in /apps/jetbrains-plugin (#32)
20
+ - **deps:** Bump org.jetbrains.intellij.platform (#29)
21
+
22
+ ## [2.12.17] — 2026-05-16
23
+
24
+ ### Fixed
25
+
26
+ - **cli:** Submit multi-line composer prompts; document new commands + env vars
27
+ - **vsc-plugin:** Replace placeholder activity bar icon with branded </> mark
28
+
7
29
  ## [2.12.16] — 2026-05-15
8
30
 
9
31
  ### Documentation
package/dist/index.js CHANGED
@@ -1018,7 +1018,7 @@ var require_unixTerminal = __commonJS({
1018
1018
  var terminal_1 = require_terminal();
1019
1019
  var utils_1 = require_utils();
1020
1020
  var native = utils_1.loadNativeModule("pty");
1021
- var pty = native.module;
1021
+ var pty2 = native.module;
1022
1022
  var helperPath = native.dir + "/spawn-helper";
1023
1023
  helperPath = path23.resolve(__dirname, helperPath);
1024
1024
  helperPath = helperPath.replace("app.asar", "app.asar.unpacked");
@@ -1076,7 +1076,7 @@ var require_unixTerminal = __commonJS({
1076
1076
  }
1077
1077
  _this.emit("exit", code, signal);
1078
1078
  };
1079
- var term = pty.fork(file, args2, parsedEnv, cwd, _this._cols, _this._rows, uid, gid, encoding === "utf8", helperPath, onexit);
1079
+ var term = pty2.fork(file, args2, parsedEnv, cwd, _this._cols, _this._rows, uid, gid, encoding === "utf8", helperPath, onexit);
1080
1080
  _this._socket = new tty.ReadStream(term.fd);
1081
1081
  if (encoding !== null) {
1082
1082
  _this._socket.setEncoding(encoding);
@@ -1164,7 +1164,7 @@ var require_unixTerminal = __commonJS({
1164
1164
  var cols = opt.cols || terminal_1.DEFAULT_COLS;
1165
1165
  var rows = opt.rows || terminal_1.DEFAULT_ROWS;
1166
1166
  var encoding = opt.encoding === void 0 ? "utf8" : opt.encoding;
1167
- var term = pty.open(cols, rows);
1167
+ var term = pty2.open(cols, rows);
1168
1168
  self._master = new tty.ReadStream(term.master);
1169
1169
  if (encoding !== null) {
1170
1170
  self._master.setEncoding(encoding);
@@ -1215,10 +1215,10 @@ var require_unixTerminal = __commonJS({
1215
1215
  */
1216
1216
  get: function() {
1217
1217
  if (process.platform === "darwin") {
1218
- var title = pty.process(this._fd);
1218
+ var title = pty2.process(this._fd);
1219
1219
  return title !== "kernel_task" ? title : this._file;
1220
1220
  }
1221
- return pty.process(this._fd, this._pty) || this._file;
1221
+ return pty2.process(this._fd, this._pty) || this._file;
1222
1222
  },
1223
1223
  enumerable: false,
1224
1224
  configurable: true
@@ -1227,7 +1227,7 @@ var require_unixTerminal = __commonJS({
1227
1227
  if (cols <= 0 || rows <= 0 || isNaN(cols) || isNaN(rows) || cols === Infinity || rows === Infinity) {
1228
1228
  throw new Error("resizing must be done using positive cols and rows");
1229
1229
  }
1230
- pty.resize(this._fd, cols, rows);
1230
+ pty2.resize(this._fd, cols, rows);
1231
1231
  this._cols = cols;
1232
1232
  this._rows = rows;
1233
1233
  };
@@ -1313,10 +1313,10 @@ var require_lib = __commonJS({
1313
1313
  } else {
1314
1314
  terminalCtor = require_unixTerminal().UnixTerminal;
1315
1315
  }
1316
- function spawn11(file, args2, opt) {
1316
+ function spawn12(file, args2, opt) {
1317
1317
  return new terminalCtor(file, args2, opt);
1318
1318
  }
1319
- exports2.spawn = spawn11;
1319
+ exports2.spawn = spawn12;
1320
1320
  function fork(file, args2, opt) {
1321
1321
  return new terminalCtor(file, args2, opt);
1322
1322
  }
@@ -1689,7 +1689,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
1689
1689
  // package.json
1690
1690
  var package_default = {
1691
1691
  name: "codeam-cli",
1692
- version: "2.12.17",
1692
+ version: "2.14.0",
1693
1693
  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.",
1694
1694
  type: "commonjs",
1695
1695
  main: "dist/index.js",
@@ -1817,6 +1817,12 @@ var https = __toESM(require("https"));
1817
1817
  var http = __toESM(require("http"));
1818
1818
  var os2 = __toESM(require("os"));
1819
1819
 
1820
+ // src/lib/backend-headers.ts
1821
+ function vercelBypassHeader() {
1822
+ const token = process.env.CODEAM_VERCEL_BYPASS;
1823
+ return token ? { "x-vercel-protection-bypass": token } : {};
1824
+ }
1825
+
1820
1826
  // src/lib/poll-delay.ts
1821
1827
  var MAX_DELAY_MS = 3e4;
1822
1828
  function computePollDelay({ baseMs, failures }) {
@@ -1915,7 +1921,8 @@ async function _postJson(url, body) {
1915
1921
  method: "POST",
1916
1922
  headers: {
1917
1923
  "Content-Type": "application/json",
1918
- "Content-Length": Buffer.byteLength(data)
1924
+ "Content-Length": Buffer.byteLength(data),
1925
+ ...vercelBypassHeader()
1919
1926
  },
1920
1927
  timeout: 1e4
1921
1928
  },
@@ -1957,6 +1964,7 @@ async function _getJson(url) {
1957
1964
  port: u2.port || (u2.protocol === "https:" ? 443 : 80),
1958
1965
  path: u2.pathname + u2.search,
1959
1966
  method: "GET",
1967
+ headers: { ...vercelBypassHeader() },
1960
1968
  timeout: 1e4
1961
1969
  },
1962
1970
  (res) => {
@@ -2111,7 +2119,7 @@ var CommandRelayService = class {
2111
2119
  port: url.port || (url.protocol === "https:" ? 443 : 80),
2112
2120
  path: `${url.pathname}${url.search}`,
2113
2121
  method: "GET",
2114
- headers: { Accept: "text/event-stream", "Cache-Control": "no-cache" },
2122
+ headers: { Accept: "text/event-stream", "Cache-Control": "no-cache", ...vercelBypassHeader() },
2115
2123
  timeout: 35e3
2116
2124
  },
2117
2125
  (res) => {
@@ -6468,7 +6476,8 @@ var ChunkEmitter = class {
6468
6476
  // it can route legacy translations / 426 us when we're
6469
6477
  // too far behind. Bumped to 2.0.0 with the discriminated-
6470
6478
  // chunk + delta-chrome refactor in this release.
6471
- "X-Codeam-Protocol-Version": "2.0.0"
6479
+ "X-Codeam-Protocol-Version": "2.0.0",
6480
+ ...vercelBypassHeader()
6472
6481
  };
6473
6482
  if (opts.pluginAuthToken) {
6474
6483
  this.headers["X-Plugin-Auth-Token"] = opts.pluginAuthToken;
@@ -6780,6 +6789,31 @@ var OutputService = class _OutputService {
6780
6789
  this.dispose();
6781
6790
  }
6782
6791
  }
6792
+ /**
6793
+ * Push a terminal-data chunk for the IDE-integrated terminal
6794
+ * panel. Distinct from chat output: `type: 'terminal_data'`
6795
+ * lets the host filter the SSE stream by terminal session id.
6796
+ * `done: false` because terminal sessions are long-lived — the
6797
+ * `terminal_close` command emits the final chunk separately.
6798
+ */
6799
+ async sendTerminalChunk(terminalSessionId, data) {
6800
+ await this.emitter.send({
6801
+ type: "terminal_data",
6802
+ terminalSessionId,
6803
+ data,
6804
+ done: false
6805
+ });
6806
+ }
6807
+ /** Final chunk for a terminal session — fires when the PTY
6808
+ * exits, so the host can update UI (badge "exit 0", etc). */
6809
+ async sendTerminalExit(terminalSessionId, exitCode) {
6810
+ await this.emitter.send({
6811
+ type: "terminal_exit",
6812
+ terminalSessionId,
6813
+ exitCode,
6814
+ done: true
6815
+ });
6816
+ }
6783
6817
  stopPoll() {
6784
6818
  if (this.pollTimer) {
6785
6819
  clearInterval(this.pollTimer);
@@ -6983,7 +7017,8 @@ function post(endpoint, body) {
6983
7017
  method: "POST",
6984
7018
  headers: {
6985
7019
  "Content-Type": "application/json",
6986
- "Content-Length": Buffer.byteLength(payload)
7020
+ "Content-Length": Buffer.byteLength(payload),
7021
+ ...vercelBypassHeader()
6987
7022
  },
6988
7023
  timeout: 15e3
6989
7024
  },
@@ -7277,7 +7312,7 @@ var HistoryService = class _HistoryService {
7277
7312
  } catch {
7278
7313
  return;
7279
7314
  }
7280
- const sessions2 = [];
7315
+ const sessions3 = [];
7281
7316
  for (const entry of entries) {
7282
7317
  if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
7283
7318
  const id = path14.basename(entry.name, ".jsonl");
@@ -7307,11 +7342,11 @@ var HistoryService = class _HistoryService {
7307
7342
  }
7308
7343
  } catch {
7309
7344
  }
7310
- if (summary) sessions2.push({ id, summary, timestamp: mtime });
7345
+ if (summary) sessions3.push({ id, summary, timestamp: mtime });
7311
7346
  }
7312
- if (sessions2.length === 0) return;
7313
- sessions2.sort((a, b) => b.timestamp - a.timestamp);
7314
- await post("/api/sessions/claude-sessions", { pluginId: this.pluginId, sessions: sessions2 });
7347
+ if (sessions3.length === 0) return;
7348
+ sessions3.sort((a, b) => b.timestamp - a.timestamp);
7349
+ await post("/api/sessions/claude-sessions", { pluginId: this.pluginId, sessions: sessions3 });
7315
7350
  }
7316
7351
  /**
7317
7352
  * Read a specific session's full conversation and POST it to the API in batches.
@@ -7454,7 +7489,7 @@ function buildKeepAlive(ctx) {
7454
7489
  var fs14 = __toESM(require("fs"));
7455
7490
  var os13 = __toESM(require("os"));
7456
7491
  var path17 = __toESM(require("path"));
7457
- var import_crypto = require("crypto");
7492
+ var import_crypto2 = require("crypto");
7458
7493
  var import_child_process8 = require("child_process");
7459
7494
 
7460
7495
  // src/lib/payload.ts
@@ -7484,7 +7519,26 @@ var startCommandSchema = import_zod2.z.object({
7484
7519
  message: import_zod2.z.string().max(8e3).optional(),
7485
7520
  paths: import_zod2.z.array(import_zod2.z.string().max(4096)).optional(),
7486
7521
  side: import_zod2.z.enum(["ours", "theirs"]).optional(),
7487
- limit: import_zod2.z.number().int().min(1).max(500).optional()
7522
+ limit: import_zod2.z.number().int().min(1).max(500).optional(),
7523
+ // search_files options. `query` is the haystack/needle string,
7524
+ // declared above for list_files. The rest mirror VS Code's
7525
+ // search panel toggles + the @codeam/ide-core SearchOptions
7526
+ // contract.
7527
+ caseSensitive: import_zod2.z.boolean().optional(),
7528
+ wholeWord: import_zod2.z.boolean().optional(),
7529
+ regex: import_zod2.z.boolean().optional(),
7530
+ include: import_zod2.z.array(import_zod2.z.string().max(512)).max(64).optional(),
7531
+ exclude: import_zod2.z.array(import_zod2.z.string().max(512)).max(64).optional(),
7532
+ maxResults: import_zod2.z.number().int().min(1).max(500).optional(),
7533
+ // terminal_open / _write / _resize / _close. `sessionId` is the
7534
+ // opaque uuid returned by `terminal_open` and required by every
7535
+ // subsequent op. `data` carries keystrokes (any UTF-8 string).
7536
+ // `cwd` lets the host pin the spawn directory.
7537
+ sessionId: import_zod2.z.string().min(1).max(128).optional(),
7538
+ data: import_zod2.z.string().max(64 * 1024).optional(),
7539
+ cwd: import_zod2.z.string().max(4096).optional(),
7540
+ cols: import_zod2.z.number().int().min(1).max(500).optional(),
7541
+ rows: import_zod2.z.number().int().min(1).max(200).optional()
7488
7542
  });
7489
7543
  function parsePayload(schema, raw) {
7490
7544
  const result = schema.safeParse(raw);
@@ -7842,13 +7896,19 @@ async function gitDiffStaged(file, cwd) {
7842
7896
  return { diff: r.stdout.slice(0, MAX_DIFF_BYTES), truncated };
7843
7897
  }
7844
7898
  async function gitLog(limit = 30, cwd) {
7845
- const sep2 = "";
7846
- const fmt = ["%H", "%h", "%an", "%aI", "%s"].join(sep2);
7899
+ const SEP = "";
7900
+ const fmt = `%H${SEP}%s${SEP}%an${SEP}%ct${SEP}%D`;
7847
7901
  const r = await git(["log", `-n${Math.min(limit, 200)}`, `--pretty=format:${fmt}`], cwd);
7848
7902
  if (r.code !== 0) return { commits: [], error: r.stderr.trim() };
7849
7903
  const commits = r.stdout.split("\n").filter(Boolean).map((line) => {
7850
- const [hash, shortHash, author, date, subject] = line.split(sep2);
7851
- return { hash, shortHash, author, date, subject };
7904
+ const [sha, subject, author, ts, refs] = line.split(SEP);
7905
+ return {
7906
+ sha: sha ?? "",
7907
+ subject: subject ?? "",
7908
+ author: author ?? "",
7909
+ timestamp: parseInt(ts ?? "0", 10) * 1e3,
7910
+ refs: (refs ?? "").split(",").map((s) => s.trim().replace(/^HEAD -> /, "")).filter((s) => s.length > 0)
7911
+ };
7852
7912
  });
7853
7913
  return { commits };
7854
7914
  }
@@ -7887,12 +7947,189 @@ async function gitResolve(file, side, cwd) {
7887
7947
  if (add.code !== 0) return { error: add.stderr.trim() || "git add (resolve) failed" };
7888
7948
  return { ok: true };
7889
7949
  }
7950
+ var MAX_SEARCH_HITS = 500;
7951
+ var MAX_SEARCH_BYTES = 256 * 1024;
7952
+ async function searchFiles(opts) {
7953
+ const cwd = opts.cwd ?? process.cwd();
7954
+ const cap = Math.min(opts.maxResults ?? MAX_SEARCH_HITS, MAX_SEARCH_HITS);
7955
+ if (!opts.query.trim()) return { hits: [], total: 0, truncated: false };
7956
+ const args2 = ["grep", "-n", "--column", "-I"];
7957
+ if (!opts.caseSensitive) args2.push("-i");
7958
+ if (opts.wholeWord) args2.push("-w");
7959
+ if (opts.regex) args2.push("-E");
7960
+ else args2.push("-F");
7961
+ args2.push(opts.query);
7962
+ if (opts.include && opts.include.length > 0) {
7963
+ args2.push("--");
7964
+ for (const p2 of opts.include) args2.push(p2);
7965
+ } else if (opts.exclude && opts.exclude.length > 0) {
7966
+ args2.push("--");
7967
+ args2.push(".");
7968
+ }
7969
+ for (const p2 of opts.exclude ?? []) args2.push(`:!${p2}`);
7970
+ const r = await git(args2, cwd);
7971
+ if (r.code !== 0 && r.code !== 1) {
7972
+ return jsSearchFiles(opts, cwd, cap);
7973
+ }
7974
+ const hits = [];
7975
+ const lines = r.stdout.split("\n");
7976
+ let truncated = false;
7977
+ let byteBudget = MAX_SEARCH_BYTES;
7978
+ for (const line of lines) {
7979
+ if (!line) continue;
7980
+ if (hits.length >= cap) {
7981
+ truncated = true;
7982
+ break;
7983
+ }
7984
+ if (byteBudget <= 0) {
7985
+ truncated = true;
7986
+ break;
7987
+ }
7988
+ byteBudget -= line.length;
7989
+ const m = line.match(/^([^]+?):(\d+):(\d+):(.*)$/);
7990
+ if (!m) continue;
7991
+ const filePath = m[1] ?? "";
7992
+ const lineNo = parseInt(m[2] ?? "0", 10);
7993
+ const col = parseInt(m[3] ?? "1", 10);
7994
+ const text = (m[4] ?? "").slice(0, 400);
7995
+ if (!filePath) continue;
7996
+ hits.push({
7997
+ path: filePath,
7998
+ line: lineNo,
7999
+ column: col,
8000
+ text,
8001
+ matchLength: opts.query.length
8002
+ });
8003
+ }
8004
+ return { hits, total: hits.length, truncated };
8005
+ }
8006
+ async function jsSearchFiles(opts, cwd, cap) {
8007
+ const files = await listProjectFiles({ cwd, cap: 2e3 });
8008
+ const hits = [];
8009
+ const needle = opts.caseSensitive ? opts.query : opts.query.toLowerCase();
8010
+ let truncated = files.truncated;
8011
+ for (const f of files.files) {
8012
+ if (hits.length >= cap) {
8013
+ truncated = true;
8014
+ break;
8015
+ }
8016
+ let content = "";
8017
+ try {
8018
+ content = await fs13.readFile(path16.join(cwd, f.path), "utf8");
8019
+ } catch {
8020
+ continue;
8021
+ }
8022
+ const lines = content.split("\n");
8023
+ for (let i = 0; i < lines.length && hits.length < cap; i++) {
8024
+ const line = lines[i] ?? "";
8025
+ const hay = opts.caseSensitive ? line : line.toLowerCase();
8026
+ const idx = hay.indexOf(needle);
8027
+ if (idx === -1) continue;
8028
+ hits.push({
8029
+ path: f.path,
8030
+ line: i + 1,
8031
+ column: idx + 1,
8032
+ text: line.slice(0, 400),
8033
+ matchLength: opts.query.length
8034
+ });
8035
+ }
8036
+ }
8037
+ return { hits, total: hits.length, truncated };
8038
+ }
8039
+
8040
+ // src/services/terminal-ops.service.ts
8041
+ var import_crypto = require("crypto");
8042
+ var pty = __toESM(require_lib());
8043
+ var MAX_CONCURRENT_SESSIONS = 4;
8044
+ var sessions = /* @__PURE__ */ new Map();
8045
+ var onDataHandler = null;
8046
+ var onExitHandler = null;
8047
+ function registerTerminalHandlers(opts) {
8048
+ onDataHandler = opts.onData;
8049
+ onExitHandler = opts.onExit;
8050
+ }
8051
+ function defaultShell() {
8052
+ if (process.platform === "win32") {
8053
+ return process.env.COMSPEC ?? "powershell.exe";
8054
+ }
8055
+ return process.env.SHELL ?? "/bin/bash";
8056
+ }
8057
+ function openTerminal(opts) {
8058
+ if (sessions.size >= MAX_CONCURRENT_SESSIONS) {
8059
+ return { error: `Too many open terminals (max ${MAX_CONCURRENT_SESSIONS})` };
8060
+ }
8061
+ const shell = opts.shell ?? defaultShell();
8062
+ const cwd = opts.cwd ?? process.cwd();
8063
+ const env = {
8064
+ ...process.env,
8065
+ TERM: "xterm-256color",
8066
+ COLORTERM: "truecolor"
8067
+ };
8068
+ env.FORCE_COLOR = "1";
8069
+ try {
8070
+ const term = pty.spawn(shell, [], {
8071
+ name: "xterm-256color",
8072
+ cols: Math.max(1, Math.min(opts.cols ?? 80, 500)),
8073
+ rows: Math.max(1, Math.min(opts.rows ?? 24, 200)),
8074
+ cwd,
8075
+ env,
8076
+ // Windows-specific: ConPTY is the default on Win 10 1809+
8077
+ // and is what we want. node-pty falls back to winpty
8078
+ // automatically on older builds.
8079
+ useConpty: process.platform === "win32" ? true : void 0
8080
+ });
8081
+ const id = (0, import_crypto.randomUUID)();
8082
+ const dataListener = term.onData((data) => {
8083
+ onDataHandler?.({ sessionId: id, data });
8084
+ });
8085
+ const exitListener = term.onExit(({ exitCode }) => {
8086
+ onExitHandler?.({ sessionId: id, exitCode });
8087
+ sessions.delete(id);
8088
+ });
8089
+ sessions.set(id, { id, pty: term, dataListener, exitListener });
8090
+ return { sessionId: id };
8091
+ } catch (e) {
8092
+ return { error: e instanceof Error ? e.message : "spawn failed" };
8093
+ }
8094
+ }
8095
+ function writeTerminal(sessionId, data) {
8096
+ const s = sessions.get(sessionId);
8097
+ if (!s) return { ok: false, error: "No such session" };
8098
+ try {
8099
+ s.pty.write(data);
8100
+ return { ok: true };
8101
+ } catch (e) {
8102
+ return { ok: false, error: e instanceof Error ? e.message : "write failed" };
8103
+ }
8104
+ }
8105
+ function resizeTerminal(sessionId, cols, rows) {
8106
+ const s = sessions.get(sessionId);
8107
+ if (!s) return { ok: false, error: "No such session" };
8108
+ try {
8109
+ s.pty.resize(Math.max(1, Math.min(cols, 500)), Math.max(1, Math.min(rows, 200)));
8110
+ return { ok: true };
8111
+ } catch (e) {
8112
+ return { ok: false, error: e instanceof Error ? e.message : "resize failed" };
8113
+ }
8114
+ }
8115
+ function closeTerminal(sessionId) {
8116
+ const s = sessions.get(sessionId);
8117
+ if (!s) return { ok: true };
8118
+ try {
8119
+ s.dataListener.dispose();
8120
+ s.exitListener.dispose();
8121
+ s.pty.kill();
8122
+ } catch {
8123
+ }
8124
+ sessions.delete(sessionId);
8125
+ return { ok: true };
8126
+ }
7890
8127
 
7891
8128
  // src/commands/start/handlers.ts
7892
8129
  function saveFilesTemp(files) {
7893
8130
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
7894
8131
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
7895
- const tmpPath = path17.join(os13.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
8132
+ const tmpPath = path17.join(os13.tmpdir(), `codeam-${(0, import_crypto2.randomUUID)()}-${safeName}`);
7896
8133
  fs14.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
7897
8134
  return tmpPath;
7898
8135
  });
@@ -8067,7 +8304,7 @@ var shutdownSession = async (ctx, cmd) => {
8067
8304
  ctx.relay.stop();
8068
8305
  process.exit(0);
8069
8306
  };
8070
- var readFile2 = async (ctx, cmd, parsed) => {
8307
+ var readFile3 = async (ctx, cmd, parsed) => {
8071
8308
  if (!parsed.path) {
8072
8309
  await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path" });
8073
8310
  return;
@@ -8087,6 +8324,58 @@ var listFiles = async (ctx, cmd, parsed) => {
8087
8324
  const result = await listProjectFiles({ query: parsed.query });
8088
8325
  await ctx.relay.sendResult(cmd.id, "completed", result);
8089
8326
  };
8327
+ var terminalOpenH = async (ctx, cmd, parsed) => {
8328
+ const r = openTerminal({
8329
+ cols: typeof parsed.cols === "number" ? parsed.cols : void 0,
8330
+ rows: typeof parsed.rows === "number" ? parsed.rows : void 0,
8331
+ cwd: typeof parsed.cwd === "string" ? parsed.cwd : void 0
8332
+ });
8333
+ if ("error" in r) {
8334
+ await ctx.relay.sendResult(cmd.id, "failed", { error: r.error });
8335
+ return;
8336
+ }
8337
+ await ctx.relay.sendResult(cmd.id, "completed", r);
8338
+ };
8339
+ var terminalWriteH = async (ctx, cmd, parsed) => {
8340
+ if (typeof parsed.sessionId !== "string" || typeof parsed.data !== "string") {
8341
+ await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing sessionId or data" });
8342
+ return;
8343
+ }
8344
+ const r = writeTerminal(parsed.sessionId, parsed.data);
8345
+ await ctx.relay.sendResult(cmd.id, r.ok ? "completed" : "failed", r);
8346
+ };
8347
+ var terminalResizeH = async (ctx, cmd, parsed) => {
8348
+ if (typeof parsed.sessionId !== "string" || typeof parsed.cols !== "number" || typeof parsed.rows !== "number") {
8349
+ await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing sessionId / cols / rows" });
8350
+ return;
8351
+ }
8352
+ const r = resizeTerminal(parsed.sessionId, parsed.cols, parsed.rows);
8353
+ await ctx.relay.sendResult(cmd.id, r.ok ? "completed" : "failed", r);
8354
+ };
8355
+ var terminalCloseH = async (ctx, cmd, parsed) => {
8356
+ if (typeof parsed.sessionId !== "string") {
8357
+ await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing sessionId" });
8358
+ return;
8359
+ }
8360
+ const r = closeTerminal(parsed.sessionId);
8361
+ await ctx.relay.sendResult(cmd.id, "completed", r);
8362
+ };
8363
+ var searchFilesH = async (ctx, cmd, parsed) => {
8364
+ if (!parsed.query || typeof parsed.query !== "string") {
8365
+ await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing query" });
8366
+ return;
8367
+ }
8368
+ const result = await searchFiles({
8369
+ query: parsed.query,
8370
+ caseSensitive: parsed.caseSensitive,
8371
+ wholeWord: parsed.wholeWord,
8372
+ regex: parsed.regex,
8373
+ include: Array.isArray(parsed.include) ? parsed.include : void 0,
8374
+ exclude: Array.isArray(parsed.exclude) ? parsed.exclude : void 0,
8375
+ maxResults: typeof parsed.maxResults === "number" ? parsed.maxResults : void 0
8376
+ });
8377
+ await ctx.relay.sendResult(cmd.id, "completed", result);
8378
+ };
8090
8379
  var gitStatusH = async (ctx, cmd) => {
8091
8380
  const result = await gitStatus();
8092
8381
  await ctx.relay.sendResult(cmd.id, "completed", result);
@@ -8142,9 +8431,14 @@ var handlers = {
8142
8431
  set_keep_alive: setKeepAlive,
8143
8432
  session_terminated: sessionTerminated,
8144
8433
  shutdown_session: shutdownSession,
8145
- read_file: readFile2,
8434
+ read_file: readFile3,
8146
8435
  write_file: writeFile2,
8147
8436
  list_files: listFiles,
8437
+ search_files: searchFilesH,
8438
+ terminal_open: terminalOpenH,
8439
+ terminal_write: terminalWriteH,
8440
+ terminal_resize: terminalResizeH,
8441
+ terminal_close: terminalCloseH,
8148
8442
  git_status: gitStatusH,
8149
8443
  git_diff: gitDiffH,
8150
8444
  git_diff_staged: gitDiffStagedH,
@@ -8246,6 +8540,14 @@ async function start(requestedAgent) {
8246
8540
  await dispatchCommand(ctx, cmd);
8247
8541
  }, runtime.meta);
8248
8542
  ctx.relay = relay;
8543
+ registerTerminalHandlers({
8544
+ onData: ({ sessionId, data }) => {
8545
+ void outputSvc.sendTerminalChunk(sessionId, data);
8546
+ },
8547
+ onExit: ({ sessionId, exitCode }) => {
8548
+ void outputSvc.sendTerminalExit(sessionId, exitCode);
8549
+ }
8550
+ });
8249
8551
  function sigintHandler() {
8250
8552
  claude.kill();
8251
8553
  outputSvc.dispose();
@@ -8264,7 +8566,7 @@ async function start(requestedAgent) {
8264
8566
  }
8265
8567
 
8266
8568
  // src/commands/pair.ts
8267
- var import_crypto2 = require("crypto");
8569
+ var import_crypto3 = require("crypto");
8268
8570
  var import_picocolors3 = __toESM(require("picocolors"));
8269
8571
 
8270
8572
  // src/ui/prompts.ts
@@ -8273,10 +8575,10 @@ async function confirmAction(message) {
8273
8575
  if (q(result)) return false;
8274
8576
  return result;
8275
8577
  }
8276
- async function selectSession(sessions2, activeId) {
8578
+ async function selectSession(sessions3, activeId) {
8277
8579
  const result = await _t({
8278
8580
  message: "Select active session:",
8279
- options: sessions2.map((s) => ({
8581
+ options: sessions3.map((s) => ({
8280
8582
  value: s.id,
8281
8583
  label: `${s.userName} ${s.plan}`,
8282
8584
  hint: s.id === activeId ? "active" : `paired ${new Date(s.pairedAt).toLocaleDateString()}`
@@ -8327,7 +8629,7 @@ async function pair(args2 = []) {
8327
8629
  const flagAgent = parseAgentFlag(args2);
8328
8630
  const agentId = flagAgent ?? await promptForAgent(config.preferredAgent ?? "claude");
8329
8631
  showIntro();
8330
- const pluginId = (0, import_crypto2.randomUUID)();
8632
+ const pluginId = (0, import_crypto3.randomUUID)();
8331
8633
  const spin = dist_exports.spinner();
8332
8634
  spin.start("Requesting pairing code...");
8333
8635
  const result = await requestCode(pluginId);
@@ -8383,7 +8685,7 @@ async function pair(args2 = []) {
8383
8685
  // src/commands/pair-auto.ts
8384
8686
  var fs15 = __toESM(require("fs"));
8385
8687
  var os14 = __toESM(require("os"));
8386
- var import_crypto3 = require("crypto");
8688
+ var import_crypto4 = require("crypto");
8387
8689
  var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
8388
8690
  function fail(msg) {
8389
8691
  console.error(`
@@ -8427,7 +8729,7 @@ async function claim(token, pluginId) {
8427
8729
  };
8428
8730
  const res = await fetch(url, {
8429
8731
  method: "POST",
8430
- headers: { "Content-Type": "application/json" },
8732
+ headers: { "Content-Type": "application/json", ...vercelBypassHeader() },
8431
8733
  body: JSON.stringify(body)
8432
8734
  });
8433
8735
  const json = await res.json();
@@ -8445,7 +8747,7 @@ async function claim(token, pluginId) {
8445
8747
  }
8446
8748
  async function pairAuto(args2) {
8447
8749
  const token = readTokenFromArgs(args2);
8448
- const pluginId = (0, import_crypto3.randomUUID)();
8750
+ const pluginId = (0, import_crypto4.randomUUID)();
8449
8751
  console.log(" Claiming pairing token\u2026");
8450
8752
  const claimed = await claim(token, pluginId);
8451
8753
  if (!isKnownAgentId(claimed.agent)) {
@@ -8470,7 +8772,7 @@ async function pairAuto(args2) {
8470
8772
 
8471
8773
  // src/commands/sessions.ts
8472
8774
  var import_picocolors4 = __toESM(require("picocolors"));
8473
- async function sessions(args2) {
8775
+ async function sessions2(args2) {
8474
8776
  const [sub, id] = args2;
8475
8777
  if (sub === "switch") return switchSession();
8476
8778
  if (sub === "delete") {
@@ -10515,7 +10817,7 @@ async function stopWorkspaceFromLocal(target) {
10515
10817
  // src/commands/version.ts
10516
10818
  var import_picocolors11 = __toESM(require("picocolors"));
10517
10819
  function version() {
10518
- const v = true ? "2.12.17" : "unknown";
10820
+ const v = true ? "2.14.0" : "unknown";
10519
10821
  console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
10520
10822
  }
10521
10823
 
@@ -10654,7 +10956,7 @@ function checkForUpdates() {
10654
10956
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
10655
10957
  if (process.env.CI) return;
10656
10958
  if (!process.stdout.isTTY) return;
10657
- const current = true ? "2.12.17" : null;
10959
+ const current = true ? "2.14.0" : null;
10658
10960
  if (!current) return;
10659
10961
  const cache = readCache();
10660
10962
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -10687,7 +10989,7 @@ async function main() {
10687
10989
  case "pair-auto":
10688
10990
  return pairAuto(args);
10689
10991
  case "sessions":
10690
- return sessions(args);
10992
+ return sessions2(args);
10691
10993
  case "status":
10692
10994
  return status();
10693
10995
  case "logout":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.12.17",
3
+ "version": "2.14.0",
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",