codeam-cli 2.13.0 → 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 +15 -0
  2. package/dist/index.js +325 -33
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@ 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
+
7
22
  ## [2.12.17] — 2026-05-16
8
23
 
9
24
  ### Fixed
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.13.0",
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",
@@ -6789,6 +6789,31 @@ var OutputService = class _OutputService {
6789
6789
  this.dispose();
6790
6790
  }
6791
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
+ }
6792
6817
  stopPoll() {
6793
6818
  if (this.pollTimer) {
6794
6819
  clearInterval(this.pollTimer);
@@ -7287,7 +7312,7 @@ var HistoryService = class _HistoryService {
7287
7312
  } catch {
7288
7313
  return;
7289
7314
  }
7290
- const sessions2 = [];
7315
+ const sessions3 = [];
7291
7316
  for (const entry of entries) {
7292
7317
  if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
7293
7318
  const id = path14.basename(entry.name, ".jsonl");
@@ -7317,11 +7342,11 @@ var HistoryService = class _HistoryService {
7317
7342
  }
7318
7343
  } catch {
7319
7344
  }
7320
- if (summary) sessions2.push({ id, summary, timestamp: mtime });
7345
+ if (summary) sessions3.push({ id, summary, timestamp: mtime });
7321
7346
  }
7322
- if (sessions2.length === 0) return;
7323
- sessions2.sort((a, b) => b.timestamp - a.timestamp);
7324
- 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 });
7325
7350
  }
7326
7351
  /**
7327
7352
  * Read a specific session's full conversation and POST it to the API in batches.
@@ -7464,7 +7489,7 @@ function buildKeepAlive(ctx) {
7464
7489
  var fs14 = __toESM(require("fs"));
7465
7490
  var os13 = __toESM(require("os"));
7466
7491
  var path17 = __toESM(require("path"));
7467
- var import_crypto = require("crypto");
7492
+ var import_crypto2 = require("crypto");
7468
7493
  var import_child_process8 = require("child_process");
7469
7494
 
7470
7495
  // src/lib/payload.ts
@@ -7494,7 +7519,26 @@ var startCommandSchema = import_zod2.z.object({
7494
7519
  message: import_zod2.z.string().max(8e3).optional(),
7495
7520
  paths: import_zod2.z.array(import_zod2.z.string().max(4096)).optional(),
7496
7521
  side: import_zod2.z.enum(["ours", "theirs"]).optional(),
7497
- 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()
7498
7542
  });
7499
7543
  function parsePayload(schema, raw) {
7500
7544
  const result = schema.safeParse(raw);
@@ -7852,13 +7896,19 @@ async function gitDiffStaged(file, cwd) {
7852
7896
  return { diff: r.stdout.slice(0, MAX_DIFF_BYTES), truncated };
7853
7897
  }
7854
7898
  async function gitLog(limit = 30, cwd) {
7855
- const sep2 = "";
7856
- const fmt = ["%H", "%h", "%an", "%aI", "%s"].join(sep2);
7899
+ const SEP = "";
7900
+ const fmt = `%H${SEP}%s${SEP}%an${SEP}%ct${SEP}%D`;
7857
7901
  const r = await git(["log", `-n${Math.min(limit, 200)}`, `--pretty=format:${fmt}`], cwd);
7858
7902
  if (r.code !== 0) return { commits: [], error: r.stderr.trim() };
7859
7903
  const commits = r.stdout.split("\n").filter(Boolean).map((line) => {
7860
- const [hash, shortHash, author, date, subject] = line.split(sep2);
7861
- 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
+ };
7862
7912
  });
7863
7913
  return { commits };
7864
7914
  }
@@ -7897,12 +7947,189 @@ async function gitResolve(file, side, cwd) {
7897
7947
  if (add.code !== 0) return { error: add.stderr.trim() || "git add (resolve) failed" };
7898
7948
  return { ok: true };
7899
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
+ }
7900
8127
 
7901
8128
  // src/commands/start/handlers.ts
7902
8129
  function saveFilesTemp(files) {
7903
8130
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
7904
8131
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
7905
- 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}`);
7906
8133
  fs14.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
7907
8134
  return tmpPath;
7908
8135
  });
@@ -8077,7 +8304,7 @@ var shutdownSession = async (ctx, cmd) => {
8077
8304
  ctx.relay.stop();
8078
8305
  process.exit(0);
8079
8306
  };
8080
- var readFile2 = async (ctx, cmd, parsed) => {
8307
+ var readFile3 = async (ctx, cmd, parsed) => {
8081
8308
  if (!parsed.path) {
8082
8309
  await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path" });
8083
8310
  return;
@@ -8097,6 +8324,58 @@ var listFiles = async (ctx, cmd, parsed) => {
8097
8324
  const result = await listProjectFiles({ query: parsed.query });
8098
8325
  await ctx.relay.sendResult(cmd.id, "completed", result);
8099
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
+ };
8100
8379
  var gitStatusH = async (ctx, cmd) => {
8101
8380
  const result = await gitStatus();
8102
8381
  await ctx.relay.sendResult(cmd.id, "completed", result);
@@ -8152,9 +8431,14 @@ var handlers = {
8152
8431
  set_keep_alive: setKeepAlive,
8153
8432
  session_terminated: sessionTerminated,
8154
8433
  shutdown_session: shutdownSession,
8155
- read_file: readFile2,
8434
+ read_file: readFile3,
8156
8435
  write_file: writeFile2,
8157
8436
  list_files: listFiles,
8437
+ search_files: searchFilesH,
8438
+ terminal_open: terminalOpenH,
8439
+ terminal_write: terminalWriteH,
8440
+ terminal_resize: terminalResizeH,
8441
+ terminal_close: terminalCloseH,
8158
8442
  git_status: gitStatusH,
8159
8443
  git_diff: gitDiffH,
8160
8444
  git_diff_staged: gitDiffStagedH,
@@ -8256,6 +8540,14 @@ async function start(requestedAgent) {
8256
8540
  await dispatchCommand(ctx, cmd);
8257
8541
  }, runtime.meta);
8258
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
+ });
8259
8551
  function sigintHandler() {
8260
8552
  claude.kill();
8261
8553
  outputSvc.dispose();
@@ -8274,7 +8566,7 @@ async function start(requestedAgent) {
8274
8566
  }
8275
8567
 
8276
8568
  // src/commands/pair.ts
8277
- var import_crypto2 = require("crypto");
8569
+ var import_crypto3 = require("crypto");
8278
8570
  var import_picocolors3 = __toESM(require("picocolors"));
8279
8571
 
8280
8572
  // src/ui/prompts.ts
@@ -8283,10 +8575,10 @@ async function confirmAction(message) {
8283
8575
  if (q(result)) return false;
8284
8576
  return result;
8285
8577
  }
8286
- async function selectSession(sessions2, activeId) {
8578
+ async function selectSession(sessions3, activeId) {
8287
8579
  const result = await _t({
8288
8580
  message: "Select active session:",
8289
- options: sessions2.map((s) => ({
8581
+ options: sessions3.map((s) => ({
8290
8582
  value: s.id,
8291
8583
  label: `${s.userName} ${s.plan}`,
8292
8584
  hint: s.id === activeId ? "active" : `paired ${new Date(s.pairedAt).toLocaleDateString()}`
@@ -8337,7 +8629,7 @@ async function pair(args2 = []) {
8337
8629
  const flagAgent = parseAgentFlag(args2);
8338
8630
  const agentId = flagAgent ?? await promptForAgent(config.preferredAgent ?? "claude");
8339
8631
  showIntro();
8340
- const pluginId = (0, import_crypto2.randomUUID)();
8632
+ const pluginId = (0, import_crypto3.randomUUID)();
8341
8633
  const spin = dist_exports.spinner();
8342
8634
  spin.start("Requesting pairing code...");
8343
8635
  const result = await requestCode(pluginId);
@@ -8393,7 +8685,7 @@ async function pair(args2 = []) {
8393
8685
  // src/commands/pair-auto.ts
8394
8686
  var fs15 = __toESM(require("fs"));
8395
8687
  var os14 = __toESM(require("os"));
8396
- var import_crypto3 = require("crypto");
8688
+ var import_crypto4 = require("crypto");
8397
8689
  var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
8398
8690
  function fail(msg) {
8399
8691
  console.error(`
@@ -8455,7 +8747,7 @@ async function claim(token, pluginId) {
8455
8747
  }
8456
8748
  async function pairAuto(args2) {
8457
8749
  const token = readTokenFromArgs(args2);
8458
- const pluginId = (0, import_crypto3.randomUUID)();
8750
+ const pluginId = (0, import_crypto4.randomUUID)();
8459
8751
  console.log(" Claiming pairing token\u2026");
8460
8752
  const claimed = await claim(token, pluginId);
8461
8753
  if (!isKnownAgentId(claimed.agent)) {
@@ -8480,7 +8772,7 @@ async function pairAuto(args2) {
8480
8772
 
8481
8773
  // src/commands/sessions.ts
8482
8774
  var import_picocolors4 = __toESM(require("picocolors"));
8483
- async function sessions(args2) {
8775
+ async function sessions2(args2) {
8484
8776
  const [sub, id] = args2;
8485
8777
  if (sub === "switch") return switchSession();
8486
8778
  if (sub === "delete") {
@@ -10525,7 +10817,7 @@ async function stopWorkspaceFromLocal(target) {
10525
10817
  // src/commands/version.ts
10526
10818
  var import_picocolors11 = __toESM(require("picocolors"));
10527
10819
  function version() {
10528
- const v = true ? "2.13.0" : "unknown";
10820
+ const v = true ? "2.14.0" : "unknown";
10529
10821
  console.log(`${import_picocolors11.default.bold("codeam-cli")} ${import_picocolors11.default.cyan(v)}`);
10530
10822
  }
10531
10823
 
@@ -10664,7 +10956,7 @@ function checkForUpdates() {
10664
10956
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
10665
10957
  if (process.env.CI) return;
10666
10958
  if (!process.stdout.isTTY) return;
10667
- const current = true ? "2.13.0" : null;
10959
+ const current = true ? "2.14.0" : null;
10668
10960
  if (!current) return;
10669
10961
  const cache = readCache();
10670
10962
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -10697,7 +10989,7 @@ async function main() {
10697
10989
  case "pair-auto":
10698
10990
  return pairAuto(args);
10699
10991
  case "sessions":
10700
- return sessions(args);
10992
+ return sessions2(args);
10701
10993
  case "status":
10702
10994
  return status();
10703
10995
  case "logout":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.13.0",
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",