codeam-cli 2.61.4 → 2.61.7

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 +19 -0
  2. package/dist/index.js +209 -75
  3. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -4,6 +4,25 @@ 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.61.5] — 2026-07-16
8
+
9
+ ### Added
10
+
11
+ - **cli:** ACP chrome-leak canary + detector (stability monitor, part 2)
12
+ - **ci:** Canary seeds onboarding + auths via in-house MiniMax backend
13
+
14
+ ### Fixed
15
+
16
+ - **ci:** Canary auths by subscription (CLAUDE_CODE_OAUTH_TOKEN), not API key
17
+ - **cli:** Stop the changeset reporter from spamming install/SDK files
18
+
19
+ ## [2.61.4] — 2026-07-16
20
+
21
+ ### Fixed
22
+
23
+ - **box:** Add unzip for CodeRabbit install (curl alone insufficient)
24
+ - **cli:** Bump ACP stack to latest + freeze claude binary (broken-chat fix)
25
+
7
26
  ## [2.61.3] — 2026-07-16
8
27
 
9
28
  ### Fixed
package/dist/index.js CHANGED
@@ -1073,11 +1073,11 @@ function quiet(fn) {
1073
1073
  log.debug(TAG, "ignored sync error", err);
1074
1074
  }
1075
1075
  }
1076
- function rmIfExistsQuiet(path75) {
1076
+ function rmIfExistsQuiet(path76) {
1077
1077
  try {
1078
- fs2.rmSync(path75, { force: true });
1078
+ fs2.rmSync(path76, { force: true });
1079
1079
  } catch (err) {
1080
- log.debug(TAG, `rmIfExists failed for ${path75}`, err);
1080
+ log.debug(TAG, `rmIfExists failed for ${path76}`, err);
1081
1081
  }
1082
1082
  }
1083
1083
  function killQuiet(target, signal = "SIGTERM") {
@@ -1261,8 +1261,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
1261
1261
  return decodedFile;
1262
1262
  };
1263
1263
  }
1264
- function normalizeWindowsPath(path75) {
1265
- return path75.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
1264
+ function normalizeWindowsPath(path76) {
1265
+ return path76.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
1266
1266
  }
1267
1267
 
1268
1268
  // ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
@@ -3742,9 +3742,9 @@ async function addSourceContext(frames) {
3742
3742
  LRU_FILE_CONTENTS_CACHE.reduce();
3743
3743
  return frames;
3744
3744
  }
3745
- function getContextLinesFromFile(path75, ranges, output) {
3745
+ function getContextLinesFromFile(path76, ranges, output) {
3746
3746
  return new Promise((resolve8) => {
3747
- const stream = (0, import_node_fs.createReadStream)(path75);
3747
+ const stream = (0, import_node_fs.createReadStream)(path76);
3748
3748
  const lineReaded = (0, import_node_readline.createInterface)({
3749
3749
  input: stream
3750
3750
  });
@@ -3759,7 +3759,7 @@ function getContextLinesFromFile(path75, ranges, output) {
3759
3759
  let rangeStart = range[0];
3760
3760
  let rangeEnd = range[1];
3761
3761
  function onStreamError() {
3762
- LRU_FILE_CONTENTS_FS_READ_FAILED.set(path75, 1);
3762
+ LRU_FILE_CONTENTS_FS_READ_FAILED.set(path76, 1);
3763
3763
  lineReaded.close();
3764
3764
  lineReaded.removeAllListeners();
3765
3765
  destroyStreamAndResolve();
@@ -3820,8 +3820,8 @@ function clearLineContext(frame) {
3820
3820
  delete frame.context_line;
3821
3821
  delete frame.post_context;
3822
3822
  }
3823
- function shouldSkipContextLinesForFile(path75) {
3824
- return path75.startsWith("node:") || path75.endsWith(".min.js") || path75.endsWith(".min.cjs") || path75.endsWith(".min.mjs") || path75.startsWith("data:");
3823
+ function shouldSkipContextLinesForFile(path76) {
3824
+ return path76.startsWith("node:") || path76.endsWith(".min.js") || path76.endsWith(".min.cjs") || path76.endsWith(".min.mjs") || path76.startsWith("data:");
3825
3825
  }
3826
3826
  function shouldSkipContextLinesForFrame(frame) {
3827
3827
  if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
@@ -5975,7 +5975,7 @@ function readAnonId() {
5975
5975
  }
5976
5976
  function superProperties() {
5977
5977
  return {
5978
- cliVersion: true ? "2.61.4" : "0.0.0-dev",
5978
+ cliVersion: true ? "2.61.7" : "0.0.0-dev",
5979
5979
  nodeVersion: process.version,
5980
5980
  platform: process.platform,
5981
5981
  arch: process.arch,
@@ -6156,7 +6156,7 @@ var os4 = __toESM(require("os"));
6156
6156
  // package.json
6157
6157
  var package_default = {
6158
6158
  name: "codeam-cli",
6159
- version: "2.61.4",
6159
+ version: "2.61.7",
6160
6160
  description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
6161
6161
  type: "commonjs",
6162
6162
  main: "dist/index.js",
@@ -6225,7 +6225,7 @@ var package_default = {
6225
6225
  url: "https://github.com/sponsors/edgar-durand"
6226
6226
  },
6227
6227
  engines: {
6228
- node: ">=20.0.0"
6228
+ node: ">=22.0.0"
6229
6229
  },
6230
6230
  dependencies: {
6231
6231
  "@agentclientprotocol/claude-agent-acp": "0.59.0",
@@ -7299,7 +7299,7 @@ var CommandRelayService = class _CommandRelayService {
7299
7299
  // fresh + clear the "CLI update available" banner after a self-update
7300
7300
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
7301
7301
  // pair/reconnect). Older backends ignore the extra field.
7302
- ..."2.61.4" ? { ideVersion: "2.61.4" } : {}
7302
+ ..."2.61.7" ? { ideVersion: "2.61.7" } : {}
7303
7303
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
7304
7304
  }
7305
7305
  /**
@@ -13885,8 +13885,8 @@ function pickLine(obj) {
13885
13885
  function toHunk(raw, groupSeverity) {
13886
13886
  if (!raw || typeof raw !== "object") return null;
13887
13887
  const o = raw;
13888
- const path75 = asString(pick(o, ["file_path", "filePath", "file", "path", "filename", "fileName"])) ?? asString(pick(o, ["location"])?.path);
13889
- if (!path75) return null;
13888
+ const path76 = asString(pick(o, ["file_path", "filePath", "file", "path", "filename", "fileName"])) ?? asString(pick(o, ["location"])?.path);
13889
+ if (!path76) return null;
13890
13890
  const message = asString(
13891
13891
  pick(o, [
13892
13892
  "comment",
@@ -13903,7 +13903,7 @@ function toHunk(raw, groupSeverity) {
13903
13903
  const severity = normSeverity(pick(o, ["severity", "level", "priority", "impact"])) ?? normSeverity(groupSeverity);
13904
13904
  const locObj = pick(o, ["location"]) ?? o;
13905
13905
  return {
13906
- path: path75.trim(),
13906
+ path: path76.trim(),
13907
13907
  line: pickLine(o) ?? pickLine(locObj),
13908
13908
  severity,
13909
13909
  message: (title && message ? `${title}: ${message}` : title || message).trim() || "(no message)"
@@ -13986,10 +13986,10 @@ function parsePlain(stdout) {
13986
13986
  for (const line of stdout.split(/\r?\n/)) {
13987
13987
  const m = line.match(HUNK_LINE_RE);
13988
13988
  if (!m) continue;
13989
- const [, path75, lineNo, sevToken, message] = m;
13990
- if (!path75 || !lineNo || !message) continue;
13989
+ const [, path76, lineNo, sevToken, message] = m;
13990
+ if (!path76 || !lineNo || !message) continue;
13991
13991
  hunks.push({
13992
- path: path75.trim(),
13992
+ path: path76.trim(),
13993
13993
  line: Number(lineNo),
13994
13994
  severity: sevToken ? SEVERITY_MAP[sevToken.toLowerCase()] : void 0,
13995
13995
  message: message.trim().replace(/^[*-]\s+/, "")
@@ -17900,7 +17900,7 @@ async function autoUpgradeBeforeCriticalCommand() {
17900
17900
  if (process.env.NODE_ENV === "test") return;
17901
17901
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
17902
17902
  if (process.env.CI) return;
17903
- const current = true ? "2.61.4" : null;
17903
+ const current = true ? "2.61.7" : null;
17904
17904
  if (!current) return;
17905
17905
  const cache = readCache();
17906
17906
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -17917,7 +17917,7 @@ function checkForUpdates() {
17917
17917
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
17918
17918
  if (process.env.CI) return;
17919
17919
  if (!process.stdout.isTTY) return;
17920
- const current = true ? "2.61.4" : null;
17920
+ const current = true ? "2.61.7" : null;
17921
17921
  if (!current) return;
17922
17922
  const cache = readCache();
17923
17923
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -17937,7 +17937,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
17937
17937
  var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
17938
17938
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
17939
17939
  function currentCliVersion() {
17940
- return true ? "2.61.4" : null;
17940
+ return true ? "2.61.7" : null;
17941
17941
  }
17942
17942
  function runCmd(cmd, args2, timeoutMs) {
17943
17943
  return new Promise((resolve8) => {
@@ -18254,6 +18254,12 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process23.s
18254
18254
  stdio: ["ignore", "pipe", "pipe"],
18255
18255
  detached: false
18256
18256
  });
18257
+ var defaultResumeSpawner = (env, cwd) => (0, import_node_child_process23.spawn)(process.execPath, [process.argv[1]], {
18258
+ cwd,
18259
+ env: { ...process.env, ...env, CODEAM_AUTO_APPROVE: "1" },
18260
+ stdio: ["ignore", "pipe", "pipe"],
18261
+ detached: false
18262
+ });
18257
18263
  var defaultOnIdentityRejected = () => {
18258
18264
  deleteHostIdentity();
18259
18265
  log.warn("host-agent", "host identity rejected by backend \u2014 wiped sealed identity, exiting");
@@ -18268,6 +18274,7 @@ var HostAgentSupervisor = class {
18268
18274
  this.identity = identity;
18269
18275
  this.deps = deps;
18270
18276
  this.spawnChild = deps.spawnChild ?? defaultSpawner;
18277
+ this.resumeSpawner = deps.resumeSpawner ?? defaultResumeSpawner;
18271
18278
  this.resolveAgentAuth = deps.resolveAgentAuth ?? unsealAgentAuth;
18272
18279
  this.setupHeadroom = deps.setupHeadroom ?? setupHeadroomForSelfHosted;
18273
18280
  this.isHeadroomInstalled = deps.isHeadroomInstalled ?? (() => defaultHeadroomRunner.which("headroom"));
@@ -18284,6 +18291,7 @@ var HostAgentSupervisor = class {
18284
18291
  deps;
18285
18292
  children = /* @__PURE__ */ new Map();
18286
18293
  spawnChild;
18294
+ resumeSpawner;
18287
18295
  resolveAgentAuth;
18288
18296
  setupHeadroom;
18289
18297
  /** Probe whether Headroom is already installed (defaults to `which headroom`). */
@@ -18344,6 +18352,7 @@ var HostAgentSupervisor = class {
18344
18352
  void this.beat();
18345
18353
  this.heartbeatTimer = setInterval(() => void this.beat(), HEARTBEAT_INTERVAL_MS);
18346
18354
  this.heartbeatTimer.unref?.();
18355
+ this.resumePersistedSession();
18347
18356
  void reportSessionEvent(
18348
18357
  { hostId: this.identity.hostId, hostToken: this.identity.hostToken },
18349
18358
  { event: "reconcile", activeDeployIds: this.activeSessions().map((s) => s.id) }
@@ -18933,6 +18942,60 @@ var HostAgentSupervisor = class {
18933
18942
  spawnSessionChild(env, cwd, args2 = []) {
18934
18943
  return this.spawnChild({ ...env, ...readHeadroomChildEnv() }, cwd, args2);
18935
18944
  }
18945
+ /**
18946
+ * Auto-resume the user's last active session on supervisor boot (2026-07-16
18947
+ * churn fix). Before this, a restart / self-update killed the session child
18948
+ * and never re-spawned it — the session lost its heartbeat, went "CLI
18949
+ * disconnected", and the user had to reconnect by hand (churn right after an
18950
+ * update). Spawn bare `codeam` via the resume spawner (reconnects the SAME
18951
+ * pluginId via start(), then heartbeats) so the session comes back on its own
18952
+ * in its ORIGINAL ACP shape (self-hosted/codespace = ACP-only; the resume
18953
+ * spawner sets CODEAM_AUTO_APPROVE=1 to keep the local baton/native-TUI OFF).
18954
+ *
18955
+ * Best-effort + guarded: no-op when there's no persisted session, when the
18956
+ * persisted session lacks the reconnect material (pluginId + pollSecret +
18957
+ * agent), or when a fresh deploy already owns a child this boot.
18958
+ */
18959
+ resumePersistedSession() {
18960
+ try {
18961
+ if (this.children.size > 0) return;
18962
+ const session = getActiveSession();
18963
+ if (!session || !session.pluginId || !session.pollSecret || !session.agent) return;
18964
+ const cwd = process.cwd();
18965
+ const proc = this.resumeSpawner({ ...readHeadroomChildEnv() }, cwd);
18966
+ const child = {
18967
+ deployId: session.id,
18968
+ proc,
18969
+ agent: session.agent,
18970
+ startedAt: Date.now()
18971
+ };
18972
+ this.children.set(session.id, child);
18973
+ let tail = "";
18974
+ const appendTail = (buf) => {
18975
+ tail = (tail + buf.toString("utf8")).slice(-2e3);
18976
+ };
18977
+ proc.stdout?.on("data", appendTail);
18978
+ proc.stderr?.on("data", appendTail);
18979
+ proc.once("exit", (code) => {
18980
+ if (this.children.get(session.id)?.proc === proc) this.children.delete(session.id);
18981
+ if (typeof code === "number" && code !== 0) {
18982
+ log.warn(
18983
+ "host-agent",
18984
+ `resumed session ${session.id.slice(0, 8)} exited (${code}): ${tail.trim().slice(-300)}`
18985
+ );
18986
+ }
18987
+ });
18988
+ log.info(
18989
+ "host-agent",
18990
+ `resumed session ${session.id.slice(0, 8)} pluginId=${session.pluginId.slice(0, 12)} (ACP)`
18991
+ );
18992
+ } catch (err) {
18993
+ log.warn(
18994
+ "host-agent",
18995
+ `resume persisted session failed (best-effort): ${err instanceof Error ? err.message : String(err)}`
18996
+ );
18997
+ }
18998
+ }
18936
18999
  /**
18937
19000
  * Run the backend-supplied per-agent CLI install script (e.g.
18938
19001
  * `claude.ai/install.sh`, `npm i -g @openai/codex`). Best-effort + bounded:
@@ -23396,11 +23459,11 @@ function resolveTokenValue(args2) {
23396
23459
  }
23397
23460
  const fileFlag = args2.find((a) => a.startsWith("--token-file="));
23398
23461
  if (fileFlag) {
23399
- const path75 = fileFlag.slice("--token-file=".length);
23462
+ const path76 = fileFlag.slice("--token-file=".length);
23400
23463
  try {
23401
- const content = fs56.readFileSync(path75, "utf8").trim();
23402
- if (content.length === 0) fail(`--token-file ${path75} is empty`);
23403
- rmIfExistsQuiet(path75);
23464
+ const content = fs56.readFileSync(path76, "utf8").trim();
23465
+ if (content.length === 0) fail(`--token-file ${path76} is empty`);
23466
+ rmIfExistsQuiet(path76);
23404
23467
  return content;
23405
23468
  } catch (err) {
23406
23469
  fail(`Could not read --token-file: ${err.message}`);
@@ -30442,8 +30505,67 @@ var import_crypto5 = require("crypto");
30442
30505
 
30443
30506
  // src/services/turn-files/git-changeset.ts
30444
30507
  var import_child_process28 = require("child_process");
30445
- var fs61 = __toESM(require("fs/promises"));
30508
+ var fs62 = __toESM(require("fs/promises"));
30509
+ var path67 = __toESM(require("path"));
30510
+
30511
+ // src/services/turn-files/review-ignore.ts
30512
+ var import_ignore2 = __toESM(require("ignore"));
30513
+ var fs61 = __toESM(require("fs"));
30446
30514
  var path66 = __toESM(require("path"));
30515
+ var CURATED_REVIEW_IGNORE = [
30516
+ // Google Cloud SDK (the incident) — installs a huge python tree.
30517
+ "google-cloud-sdk/",
30518
+ "**/google-cloud-sdk/**",
30519
+ ".config/gcloud/",
30520
+ // Python package installs that land outside a venv (which is already covered).
30521
+ "**/site-packages/**",
30522
+ "**/dist-packages/**",
30523
+ "__pypackages__/",
30524
+ ".local/lib/",
30525
+ "**/.local/lib/**",
30526
+ "pip-wheel-metadata/",
30527
+ // JS package-manager stores/caches not already in isIgnoredFilePath's regex.
30528
+ ".pnpm-store/",
30529
+ "**/.pnpm-store/**",
30530
+ ".yarn/cache/",
30531
+ ".yarn/unplugged/",
30532
+ ".bun/",
30533
+ // Language toolchains dropped into $HOME/a project dir.
30534
+ ".rustup/",
30535
+ ".cargo/registry/",
30536
+ ".cargo/git/",
30537
+ "go/pkg/",
30538
+ ".nvm/",
30539
+ ".rbenv/",
30540
+ ".pyenv/",
30541
+ ".sdkman/",
30542
+ // Cloud / infra CLIs with bundled trees.
30543
+ ".azure/",
30544
+ ".aws/cli/",
30545
+ "awscli/",
30546
+ // CodeAgent's own runtime prefixes (never a user change).
30547
+ ".codeam/",
30548
+ "codeam-node20/"
30549
+ ];
30550
+ function makeReviewIgnore(repoRoot) {
30551
+ const ig = (0, import_ignore2.default)().add(CURATED_REVIEW_IGNORE);
30552
+ try {
30553
+ const custom = fs61.readFileSync(path66.join(repoRoot, ".codeam", "reviewignore"), "utf8");
30554
+ ig.add(custom);
30555
+ } catch {
30556
+ }
30557
+ return (relPath) => {
30558
+ if (!relPath) return false;
30559
+ try {
30560
+ return ig.ignores(relPath);
30561
+ } catch (err) {
30562
+ log.trace("review-ignore", `ignores() threw for "${relPath}"`, err);
30563
+ return false;
30564
+ }
30565
+ };
30566
+ }
30567
+
30568
+ // src/services/turn-files/git-changeset.ts
30447
30569
  async function collectRepoChangeset(opts) {
30448
30570
  const status2 = await runGit3(opts.repoRoot, ["status", "--porcelain=v1", "-z"]);
30449
30571
  if (status2 === null) return null;
@@ -30454,14 +30576,26 @@ async function collectRepoChangeset(opts) {
30454
30576
  "HEAD"
30455
30577
  ]).catch(() => null);
30456
30578
  const numstat = parseNumstat(numstatRaw ?? "");
30579
+ const reviewIgnore = makeReviewIgnore(opts.repoRoot);
30580
+ const rows = parseStatus(status2).filter(
30581
+ (row) => !isIgnoredFilePath(row.filePath) && !reviewIgnore(row.filePath)
30582
+ );
30583
+ const MAX_CHANGESET_FILES = Math.max(1, Number(process.env.CODEAM_MAX_CHANGESET_FILES ?? 500));
30584
+ const truncated = rows.length > MAX_CHANGESET_FILES;
30585
+ const bounded = truncated ? rows.slice(0, MAX_CHANGESET_FILES) : rows;
30586
+ if (truncated) {
30587
+ log.warn(
30588
+ "changeset",
30589
+ `${rows.length} changed files exceeds the ${MAX_CHANGESET_FILES}-file cap \u2014 reporting the first ${MAX_CHANGESET_FILES} only. This usually means a dependency/SDK/build install landed in the session's working directory; add it to .gitignore to remove it from reviews.`
30590
+ );
30591
+ }
30457
30592
  const entries = [];
30458
- for (const row of parseStatus(status2)) {
30459
- if (isIgnoredFilePath(row.filePath)) continue;
30593
+ for (const row of bounded) {
30460
30594
  const numstatEntry = numstat.get(row.filePath);
30461
30595
  let stats;
30462
- if (row.fileStatus === "added" && numstatEntry === void 0) {
30596
+ if (!truncated && row.fileStatus === "added" && numstatEntry === void 0) {
30463
30597
  const lineCount = await readUntrackedLineCount(
30464
- path66.join(opts.repoRoot, row.filePath)
30598
+ path67.join(opts.repoRoot, row.filePath)
30465
30599
  );
30466
30600
  stats = { added: lineCount, removed: 0 };
30467
30601
  } else {
@@ -30492,7 +30626,7 @@ function readUntrackedLineCount(absPath) {
30492
30626
  }
30493
30627
  async function defaultReadUntrackedLineCount(absPath) {
30494
30628
  try {
30495
- const content = await fs61.readFile(absPath, "utf8");
30629
+ const content = await fs62.readFile(absPath, "utf8");
30496
30630
  let count = 0;
30497
30631
  let pos = -1;
30498
30632
  while ((pos = content.indexOf("\n", pos + 1)) !== -1) {
@@ -30584,7 +30718,7 @@ function defaultRunGit(cwd, args2) {
30584
30718
  });
30585
30719
  }
30586
30720
  async function discoverRepos(workingDir, maxDepth = 4) {
30587
- const fs66 = await import("fs/promises");
30721
+ const fs67 = await import("fs/promises");
30588
30722
  const out2 = [];
30589
30723
  await walk(workingDir, 0);
30590
30724
  return out2;
@@ -30592,7 +30726,7 @@ async function discoverRepos(workingDir, maxDepth = 4) {
30592
30726
  if (depth > maxDepth) return;
30593
30727
  let entries = [];
30594
30728
  try {
30595
- const dirents = await fs66.readdir(dir, { withFileTypes: true });
30729
+ const dirents = await fs67.readdir(dir, { withFileTypes: true });
30596
30730
  entries = dirents.filter((d3) => !d3.name.startsWith(".") || d3.name === ".git").map((d3) => ({ name: d3.name, isDirectory: d3.isDirectory() }));
30597
30731
  } catch {
30598
30732
  return;
@@ -30603,8 +30737,8 @@ async function discoverRepos(workingDir, maxDepth = 4) {
30603
30737
  if (hasGit) {
30604
30738
  out2.push({
30605
30739
  repoRoot: dir,
30606
- repoPath: path66.relative(workingDir, dir),
30607
- repoName: path66.basename(dir)
30740
+ repoPath: path67.relative(workingDir, dir),
30741
+ repoName: path67.basename(dir)
30608
30742
  });
30609
30743
  return;
30610
30744
  }
@@ -30612,14 +30746,14 @@ async function discoverRepos(workingDir, maxDepth = 4) {
30612
30746
  if (!entry.isDirectory) continue;
30613
30747
  if (entry.name === "node_modules") continue;
30614
30748
  if (entry.name === "dist" || entry.name === "build") continue;
30615
- await walk(path66.join(dir, entry.name), depth + 1);
30749
+ await walk(path67.join(dir, entry.name), depth + 1);
30616
30750
  }
30617
30751
  }
30618
30752
  }
30619
30753
 
30620
30754
  // src/services/turn-files/files-outbox.ts
30621
- var fs62 = __toESM(require("fs/promises"));
30622
- var path67 = __toESM(require("path"));
30755
+ var fs63 = __toESM(require("fs/promises"));
30756
+ var path68 = __toESM(require("path"));
30623
30757
  var import_os11 = require("os");
30624
30758
  var HOME_OUTBOX_DIR = ".codeam/outbox";
30625
30759
  var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
@@ -30652,16 +30786,16 @@ var FilesOutbox = class {
30652
30786
  backoffIndex = 0;
30653
30787
  stopped = false;
30654
30788
  constructor(opts) {
30655
- const base = opts.baseDir ?? path67.join(homeDir(), HOME_OUTBOX_DIR);
30656
- this.filePath = path67.join(base, `${opts.sessionId}.jsonl`);
30789
+ const base = opts.baseDir ?? path68.join(homeDir(), HOME_OUTBOX_DIR);
30790
+ this.filePath = path68.join(base, `${opts.sessionId}.jsonl`);
30657
30791
  this.post = opts.post;
30658
30792
  this.autoSchedule = opts.autoSchedule !== false;
30659
30793
  }
30660
30794
  /** Persist the entry to disk and trigger a flush. Returns once the
30661
30795
  * line is durable on disk (not once the POST succeeds). */
30662
30796
  async enqueue(entry) {
30663
- await fs62.mkdir(path67.dirname(this.filePath), { recursive: true });
30664
- await fs62.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
30797
+ await fs63.mkdir(path68.dirname(this.filePath), { recursive: true });
30798
+ await fs63.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
30665
30799
  this.backoffIndex = 0;
30666
30800
  if (this.autoSchedule) this.scheduleFlush(0);
30667
30801
  }
@@ -30752,7 +30886,7 @@ var FilesOutbox = class {
30752
30886
  async readAll() {
30753
30887
  let raw = "";
30754
30888
  try {
30755
- raw = await fs62.readFile(this.filePath, "utf8");
30889
+ raw = await fs63.readFile(this.filePath, "utf8");
30756
30890
  } catch {
30757
30891
  return [];
30758
30892
  }
@@ -30776,12 +30910,12 @@ var FilesOutbox = class {
30776
30910
  async rewrite(entries) {
30777
30911
  const tmpPath = `${this.filePath}.${process.pid}.tmp`;
30778
30912
  if (entries.length === 0) {
30779
- await fs62.unlink(this.filePath).catch(() => void 0);
30913
+ await fs63.unlink(this.filePath).catch(() => void 0);
30780
30914
  return;
30781
30915
  }
30782
30916
  const payload = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
30783
- await fs62.writeFile(tmpPath, payload, "utf8");
30784
- await fs62.rename(tmpPath, this.filePath);
30917
+ await fs63.writeFile(tmpPath, payload, "utf8");
30918
+ await fs63.rename(tmpPath, this.filePath);
30785
30919
  }
30786
30920
  };
30787
30921
  function applyJitter(ms) {
@@ -33942,7 +34076,7 @@ var AcpDriver = class {
33942
34076
  };
33943
34077
 
33944
34078
  // src/baton/transcript-mirror.ts
33945
- var fs63 = __toESM(require("fs"));
34079
+ var fs64 = __toESM(require("fs"));
33946
34080
  var TranscriptMirror = class {
33947
34081
  constructor(deps) {
33948
34082
  this.deps = deps;
@@ -34009,7 +34143,7 @@ var TranscriptMirror = class {
34009
34143
  }
34010
34144
  };
34011
34145
  function defaultWatch(file, onChange) {
34012
- const w3 = fs63.watch(file, { persistent: false }, () => onChange());
34146
+ const w3 = fs64.watch(file, { persistent: false }, () => onChange());
34013
34147
  return () => w3.close();
34014
34148
  }
34015
34149
 
@@ -34271,16 +34405,16 @@ function toEpochMs(ts) {
34271
34405
  }
34272
34406
 
34273
34407
  // src/agents/claude/onboarding.ts
34274
- var fs64 = __toESM(require("fs"));
34408
+ var fs65 = __toESM(require("fs"));
34275
34409
  var os50 = __toESM(require("os"));
34276
- var path68 = __toESM(require("path"));
34410
+ var path69 = __toESM(require("path"));
34277
34411
  var ONBOARDING_VERSION_SENTINEL = "9999.0.0";
34278
34412
  function ensureClaudeOnboarded(cwd) {
34279
34413
  try {
34280
- const file = path68.join(os50.homedir(), ".claude.json");
34414
+ const file = path69.join(os50.homedir(), ".claude.json");
34281
34415
  let config = {};
34282
34416
  try {
34283
- config = JSON.parse(fs64.readFileSync(file, "utf8"));
34417
+ config = JSON.parse(fs65.readFileSync(file, "utf8"));
34284
34418
  } catch {
34285
34419
  }
34286
34420
  let changed = false;
@@ -34305,8 +34439,8 @@ function ensureClaudeOnboarded(cwd) {
34305
34439
  }
34306
34440
  }
34307
34441
  if (!changed) return;
34308
- fs64.mkdirSync(path68.dirname(file), { recursive: true });
34309
- fs64.writeFileSync(file, JSON.stringify(config, null, 2));
34442
+ fs65.mkdirSync(path69.dirname(file), { recursive: true });
34443
+ fs65.writeFileSync(file, JSON.stringify(config, null, 2));
34310
34444
  log.info(
34311
34445
  "claude",
34312
34446
  `pre-completed Claude onboarding${cwd ? ` + trusted workspace ${cwd}` : ""}`
@@ -34992,7 +35126,7 @@ var import_picocolors11 = __toESM(require("picocolors"));
34992
35126
  var import_child_process29 = require("child_process");
34993
35127
  var import_util4 = require("util");
34994
35128
  var import_picocolors9 = __toESM(require("picocolors"));
34995
- var path69 = __toESM(require("path"));
35129
+ var path70 = __toESM(require("path"));
34996
35130
  var execFileP6 = (0, import_util4.promisify)(import_child_process29.execFile);
34997
35131
  var MAX_BUFFER = 8 * 1024 * 1024;
34998
35132
  function resetStdinForChild() {
@@ -35481,7 +35615,7 @@ var GitHubCodespacesProvider = class {
35481
35615
  });
35482
35616
  }
35483
35617
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
35484
- const remoteDir = path69.posix.dirname(remotePath);
35618
+ const remoteDir = path70.posix.dirname(remotePath);
35485
35619
  const parts = [
35486
35620
  `mkdir -p ${shellQuote(remoteDir)}`,
35487
35621
  `cat > ${shellQuote(remotePath)}`
@@ -35551,7 +35685,7 @@ function shellQuote(s) {
35551
35685
  // src/services/providers/gitpod.ts
35552
35686
  var import_child_process30 = require("child_process");
35553
35687
  var import_util5 = require("util");
35554
- var path70 = __toESM(require("path"));
35688
+ var path71 = __toESM(require("path"));
35555
35689
  var import_picocolors10 = __toESM(require("picocolors"));
35556
35690
  var execFileP7 = (0, import_util5.promisify)(import_child_process30.execFile);
35557
35691
  var MAX_BUFFER2 = 8 * 1024 * 1024;
@@ -35791,7 +35925,7 @@ var GitpodProvider = class {
35791
35925
  });
35792
35926
  }
35793
35927
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
35794
- const remoteDir = path70.posix.dirname(remotePath);
35928
+ const remoteDir = path71.posix.dirname(remotePath);
35795
35929
  const parts = [
35796
35930
  `mkdir -p ${shellQuote2(remoteDir)}`,
35797
35931
  `cat > ${shellQuote2(remotePath)}`
@@ -35827,7 +35961,7 @@ function shellQuote2(s) {
35827
35961
  // src/services/providers/gitlab-workspaces.ts
35828
35962
  var import_child_process31 = require("child_process");
35829
35963
  var import_util6 = require("util");
35830
- var path71 = __toESM(require("path"));
35964
+ var path72 = __toESM(require("path"));
35831
35965
  var execFileP8 = (0, import_util6.promisify)(import_child_process31.execFile);
35832
35966
  var MAX_BUFFER3 = 8 * 1024 * 1024;
35833
35967
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
@@ -36087,7 +36221,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
36087
36221
  }
36088
36222
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
36089
36223
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
36090
- const remoteDir = path71.posix.dirname(remotePath);
36224
+ const remoteDir = path72.posix.dirname(remotePath);
36091
36225
  const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
36092
36226
  if (options.mode != null) {
36093
36227
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
@@ -36155,7 +36289,7 @@ function shellQuote3(s) {
36155
36289
  // src/services/providers/railway.ts
36156
36290
  var import_child_process32 = require("child_process");
36157
36291
  var import_util7 = require("util");
36158
- var path72 = __toESM(require("path"));
36292
+ var path73 = __toESM(require("path"));
36159
36293
  var execFileP9 = (0, import_util7.promisify)(import_child_process32.execFile);
36160
36294
  var MAX_BUFFER4 = 8 * 1024 * 1024;
36161
36295
  function resetStdinForChild4() {
@@ -36391,7 +36525,7 @@ var RailwayProvider = class {
36391
36525
  if (!projectId || !serviceId) {
36392
36526
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
36393
36527
  }
36394
- const remoteDir = path72.posix.dirname(remotePath);
36528
+ const remoteDir = path73.posix.dirname(remotePath);
36395
36529
  const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
36396
36530
  if (options.mode != null) {
36397
36531
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
@@ -37037,8 +37171,8 @@ async function invite() {
37037
37171
  var import_node_dns = require("dns");
37038
37172
  var import_node_util5 = require("util");
37039
37173
  var import_node_crypto12 = require("crypto");
37040
- var fs65 = __toESM(require("fs"));
37041
- var path73 = __toESM(require("path"));
37174
+ var fs66 = __toESM(require("fs"));
37175
+ var path74 = __toESM(require("path"));
37042
37176
  var import_picocolors14 = __toESM(require("picocolors"));
37043
37177
  var dnsResolveP = (0, import_node_util5.promisify)(import_node_dns.resolve);
37044
37178
  async function checkDns(apiBase2) {
@@ -37094,13 +37228,13 @@ async function checkHealth(apiBase2) {
37094
37228
  }
37095
37229
  }
37096
37230
  function checkConfigDir() {
37097
- const dir = path73.join(require("os").homedir(), ".codeam");
37231
+ const dir = path74.join(require("os").homedir(), ".codeam");
37098
37232
  try {
37099
- fs65.mkdirSync(dir, { recursive: true, mode: 448 });
37100
- const probe = path73.join(dir, ".doctor-probe");
37101
- fs65.writeFileSync(probe, "ok", { mode: 384 });
37102
- const read2 = fs65.readFileSync(probe, "utf8");
37103
- fs65.unlinkSync(probe);
37233
+ fs66.mkdirSync(dir, { recursive: true, mode: 448 });
37234
+ const probe = path74.join(dir, ".doctor-probe");
37235
+ fs66.writeFileSync(probe, "ok", { mode: 384 });
37236
+ const read2 = fs66.readFileSync(probe, "utf8");
37237
+ fs66.unlinkSync(probe);
37104
37238
  if (read2 !== "ok") throw new Error("write/read round-trip mismatch");
37105
37239
  return {
37106
37240
  id: "config-dir",
@@ -37164,7 +37298,7 @@ function checkNodePty() {
37164
37298
  detail: "not required on this platform"
37165
37299
  };
37166
37300
  }
37167
- const vendoredPath = path73.join(__dirname, "vendor", "node-pty");
37301
+ const vendoredPath = path74.join(__dirname, "vendor", "node-pty");
37168
37302
  for (const target of [vendoredPath, "node-pty"]) {
37169
37303
  try {
37170
37304
  require(target);
@@ -37206,7 +37340,7 @@ function checkChokidar() {
37206
37340
  }
37207
37341
  async function doctor(args2 = []) {
37208
37342
  const json = args2.includes("--json");
37209
- const cliVersion = true ? "2.61.4" : "0.0.0-dev";
37343
+ const cliVersion = true ? "2.61.7" : "0.0.0-dev";
37210
37344
  const apiBase2 = resolveApiBaseUrl();
37211
37345
  const diagnosticId = (0, import_node_crypto12.randomUUID)();
37212
37346
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -37717,7 +37851,7 @@ async function mcpRun(args2) {
37717
37851
  // src/commands/version.ts
37718
37852
  var import_picocolors15 = __toESM(require("picocolors"));
37719
37853
  function version2() {
37720
- const v = true ? "2.61.4" : "unknown";
37854
+ const v = true ? "2.61.7" : "unknown";
37721
37855
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
37722
37856
  }
37723
37857
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.61.4",
3
+ "version": "2.61.7",
4
4
  "description": "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device — async. The terminal companion for CodeAgent Mobile.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",
@@ -69,7 +69,7 @@
69
69
  "url": "https://github.com/sponsors/edgar-durand"
70
70
  },
71
71
  "engines": {
72
- "node": ">=20.0.0"
72
+ "node": ">=22.0.0"
73
73
  },
74
74
  "dependencies": {
75
75
  "@agentclientprotocol/claude-agent-acp": "0.59.0",