codeam-cli 2.76.33 → 2.76.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@ 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.76.33] — 2026-10-14
8
+
9
+ ### Added
10
+
11
+ - **box:** Bake PHP CLI + Composer into the Box image
12
+ - **cli:** Composer pre-flight + PHP guidance for in-app Preview
13
+
7
14
  ## [2.76.32] — 2026-10-14
8
15
 
9
16
  ### Fixed
package/dist/index.js CHANGED
@@ -10636,7 +10636,7 @@ function readAnonId() {
10636
10636
  }
10637
10637
  function superProperties() {
10638
10638
  return {
10639
- cliVersion: true ? "2.76.33" : "0.0.0-dev",
10639
+ cliVersion: true ? "2.76.34" : "0.0.0-dev",
10640
10640
  nodeVersion: process.version,
10641
10641
  platform: process.platform,
10642
10642
  arch: process.arch,
@@ -10885,7 +10885,7 @@ var http = __toESM(require("http"));
10885
10885
  // package.json
10886
10886
  var package_default = {
10887
10887
  name: "codeam-cli",
10888
- version: "2.76.33",
10888
+ version: "2.76.34",
10889
10889
  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.",
10890
10890
  type: "commonjs",
10891
10891
  main: "dist/index.js",
@@ -10973,7 +10973,7 @@ var package_default = {
10973
10973
  zod: "^4.3.6"
10974
10974
  },
10975
10975
  optionalDependencies: {
10976
- "@beads/bd": "1.0.5"
10976
+ "@beads/bd": "1.3.1"
10977
10977
  },
10978
10978
  devDependencies: {
10979
10979
  "@codeam/shared": "*",
@@ -12261,7 +12261,7 @@ var CommandRelayService = class _CommandRelayService {
12261
12261
  // fresh + clear the "CLI update available" banner after a self-update
12262
12262
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
12263
12263
  // pair/reconnect). Older backends ignore the extra field.
12264
- ..."2.76.33" ? { ideVersion: "2.76.33" } : {}
12264
+ ..."2.76.34" ? { ideVersion: "2.76.34" } : {}
12265
12265
  };
12266
12266
  try {
12267
12267
  const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body, this.pollSecretHeader());
@@ -27509,6 +27509,43 @@ var import_child_process21 = require("child_process");
27509
27509
  var fs59 = __toESM(require("fs"));
27510
27510
  var os40 = __toESM(require("os"));
27511
27511
  var path67 = __toESM(require("path"));
27512
+
27513
+ // src/beads/schema-migrate.ts
27514
+ var SCHEMA_SKEW_RE = /Error 1146|table not found|refusing to auto-apply \d+ pending schema migration/i;
27515
+ function isSchemaSkew(text) {
27516
+ return SCHEMA_SKEW_RE.test(text);
27517
+ }
27518
+ var APPLIED_RE = /applying (\d+) pending schema migration/i;
27519
+ var REMOTE_GATE_RE = /remote-backed database/i;
27520
+ async function migrateSchema(bd) {
27521
+ let forced = false;
27522
+ let res = await bd.run(["migrate", "schema"]);
27523
+ if (res.code !== 0 && REMOTE_GATE_RE.test(res.stderr + res.stdout)) {
27524
+ log.info("beads", "schema migrate refused for a remote-backed DB \u2014 migrating with --force");
27525
+ forced = true;
27526
+ res = await bd.run(["migrate", "schema", "--force"]);
27527
+ }
27528
+ const text = `${res.stderr}
27529
+ ${res.stdout}`;
27530
+ if (res.code !== 0) {
27531
+ const detail = (res.stderr || res.stdout).trim().slice(0, 200);
27532
+ log.warn("beads", `bd migrate schema failed (code=${res.code}): ${detail}`);
27533
+ return { status: "failed", detail };
27534
+ }
27535
+ const applied = APPLIED_RE.exec(text);
27536
+ if (applied) {
27537
+ const count = Number(applied[1]);
27538
+ log.info(
27539
+ "beads",
27540
+ `bd migrate schema applied ${count} migration(s)${forced ? " (--force)" : ""}`
27541
+ );
27542
+ return { status: "applied", count, forced };
27543
+ }
27544
+ log.trace("beads", "bd migrate schema: already current");
27545
+ return { status: "current" };
27546
+ }
27547
+
27548
+ // src/beads/bd-adapter.ts
27512
27549
  var BD_PACKAGE = "@beads/bd";
27513
27550
  function resolveBundledBdBinary() {
27514
27551
  return _resolveSeam.resolveBundled();
@@ -27586,6 +27623,8 @@ var BdAdapter = class {
27586
27623
  }
27587
27624
  opts;
27588
27625
  resolved;
27626
+ /** Set when a bd call failed on schema skew; read + cleared by `takeSchemaSkew`. */
27627
+ schemaSkewSeen = false;
27589
27628
  /**
27590
27629
  * Resolve the bd binary lazily, caching the result. Order:
27591
27630
  * bundled @beads/bd → PATH. Returns null when neither is available — the
@@ -27641,8 +27680,20 @@ var BdAdapter = class {
27641
27680
  await _adapterSeam.sleep(750 * attempt);
27642
27681
  res = await _spawnSeam.run(binary, args2, { cwd, env });
27643
27682
  }
27683
+ if (res.code !== 0 && isSchemaSkew(res.stderr + res.stdout)) this.schemaSkewSeen = true;
27644
27684
  return res;
27645
27685
  }
27686
+ /**
27687
+ * Whether a bd call since the last check failed because the project DB is
27688
+ * behind this bd's schema (see `schema-migrate.ts`). `listIssues` /
27689
+ * `statusSummary` swallow failures into []/null, so this is how the watcher
27690
+ * learns the cause. Clears the flag.
27691
+ */
27692
+ takeSchemaSkew() {
27693
+ const seen2 = this.schemaSkewSeen;
27694
+ this.schemaSkewSeen = false;
27695
+ return seen2;
27696
+ }
27646
27697
  /**
27647
27698
  * `bd ready --json` → typed issue array. `bd list --json` shares the shape,
27648
27699
  * so `listIssues` reuses the same parser. Bad JSON / non-zero exit → [].
@@ -28208,7 +28259,8 @@ async function provisionBeads(opts = {}) {
28208
28259
  prefix: null,
28209
28260
  initialized: false,
28210
28261
  exportEnabled: false,
28211
- agentsWired: []
28262
+ agentsWired: [],
28263
+ schemaMigrate: null
28212
28264
  };
28213
28265
  if (!bd.isAvailable()) {
28214
28266
  log.info("beads", "bd binary missing \u2014 running OS installer fallback");
@@ -28290,6 +28342,7 @@ async function provisionBeads(opts = {}) {
28290
28342
  log.warn("beads", "shared dolt sql-server not up \u2014 beads disabled this run");
28291
28343
  return result;
28292
28344
  }
28345
+ result.schemaMigrate = await migrateSchemaSafely(bd);
28293
28346
  try {
28294
28347
  if (!await projectDbReachable(bd)) {
28295
28348
  log.info(
@@ -28305,6 +28358,8 @@ async function provisionBeads(opts = {}) {
28305
28358
  "beads",
28306
28359
  `bd bootstrap failed (code=${boot.code}): ${boot.stderr.slice(0, 200)} \u2014 prefix DB may be absent`
28307
28360
  );
28361
+ } else {
28362
+ result.schemaMigrate = await migrateSchemaSafely(bd);
28308
28363
  }
28309
28364
  }
28310
28365
  if (!await projectDbReachable(bd)) {
@@ -28356,10 +28411,25 @@ async function provisionBeads(opts = {}) {
28356
28411
  result.agentsWired = await setupAgents(bd, opts.agents ?? []);
28357
28412
  log.info(
28358
28413
  "beads",
28359
- `provision done dolt=${result.doltAvailable} server=${result.serverUp} prefix=${result.prefix} initialized=${result.initialized} export=${result.exportEnabled} agentsWired=[${result.agentsWired.join(",")}]`
28414
+ `provision done dolt=${result.doltAvailable} server=${result.serverUp} prefix=${result.prefix} initialized=${result.initialized} schema=${describeSchemaMigrate(result.schemaMigrate)} export=${result.exportEnabled} agentsWired=[${result.agentsWired.join(",")}]`
28360
28415
  );
28361
28416
  return result;
28362
28417
  }
28418
+ async function migrateSchemaSafely(bd) {
28419
+ try {
28420
+ return await migrateSchema(bd);
28421
+ } catch (err) {
28422
+ log.warn("beads", "bd migrate schema threw (non-fatal)", err);
28423
+ return { status: "failed", detail: err.message };
28424
+ }
28425
+ }
28426
+ function describeSchemaMigrate(outcome) {
28427
+ if (!outcome) return "skipped";
28428
+ if (outcome.status === "applied") {
28429
+ return `applied:${outcome.count}${outcome.forced ? ":forced" : ""}`;
28430
+ }
28431
+ return outcome.status;
28432
+ }
28363
28433
  async function projectDbReachable(bd) {
28364
28434
  const ping = await bd.run(["ping"]);
28365
28435
  return ping.code === 0;
@@ -28455,6 +28525,8 @@ var BeadsWatcher = class {
28455
28525
  debounceTimer = null;
28456
28526
  stopped = false;
28457
28527
  lastPushedHash = null;
28528
+ /** One schema-heal attempt per watcher — a failing migrate must not loop. */
28529
+ schemaHealTried = false;
28458
28530
  bd;
28459
28531
  feedPath;
28460
28532
  apiBase;
@@ -28523,10 +28595,15 @@ var BeadsWatcher = class {
28523
28595
  async syncNow() {
28524
28596
  if (this.stopped) return;
28525
28597
  const { projectKey, projectLabel } = deriveProjectIdentity(this.opts.cwd);
28526
- const [issues, summary] = await Promise.all([
28527
- this.bd.listIssues(projectKey),
28528
- this.bd.statusSummary()
28529
- ]);
28598
+ let [issues, summary] = await this.readSnapshot(projectKey);
28599
+ if (!this.schemaHealTried && this.bd.takeSchemaSkew()) {
28600
+ this.schemaHealTried = true;
28601
+ log.info("beads", "snapshot hit schema skew \u2014 running bd migrate schema once");
28602
+ const outcome = await migrateSchema(this.bd);
28603
+ if (outcome.status !== "failed") {
28604
+ [issues, summary] = await this.readSnapshot(projectKey);
28605
+ }
28606
+ }
28530
28607
  const payload = {
28531
28608
  sessionId: this.opts.sessionId,
28532
28609
  pluginId: this.opts.pluginId,
@@ -28565,6 +28642,9 @@ var BeadsWatcher = class {
28565
28642
  log.warn("beads", "ingest POST error", err);
28566
28643
  }
28567
28644
  }
28645
+ readSnapshot(projectKey) {
28646
+ return Promise.all([this.bd.listIssues(projectKey), this.bd.statusSummary()]);
28647
+ }
28568
28648
  headers() {
28569
28649
  return {
28570
28650
  "Content-Type": "application/json",
@@ -51542,7 +51622,7 @@ async function autoUpgradeBeforeCriticalCommand() {
51542
51622
  if (process.env.NODE_ENV === "test") return;
51543
51623
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
51544
51624
  if (process.env.CI) return;
51545
- const current2 = true ? "2.76.33" : null;
51625
+ const current2 = true ? "2.76.34" : null;
51546
51626
  if (!current2) return;
51547
51627
  const cache = readCache();
51548
51628
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -51559,7 +51639,7 @@ function checkForUpdates() {
51559
51639
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
51560
51640
  if (process.env.CI) return;
51561
51641
  if (!process.stdout.isTTY) return;
51562
- const current2 = true ? "2.76.33" : null;
51642
+ const current2 = true ? "2.76.34" : null;
51563
51643
  if (!current2) return;
51564
51644
  const cache = readCache();
51565
51645
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -51735,14 +51815,37 @@ function pidAlive3(pid) {
51735
51815
  return err.code === "EPERM";
51736
51816
  }
51737
51817
  }
51818
+ var CLK_TCK = 100;
51819
+ function processStartMs(pid) {
51820
+ try {
51821
+ const stat3 = fs96.readFileSync(`/proc/${pid}/stat`, "utf8");
51822
+ const ticks = Number(stat3.slice(stat3.lastIndexOf(")") + 2).split(" ")[19]);
51823
+ const btime = /^btime (\d+)$/m.exec(fs96.readFileSync("/proc/stat", "utf8"))?.[1];
51824
+ if (!Number.isFinite(ticks) || !btime) return null;
51825
+ return Number(btime) * 1e3 + ticks * 1e3 / CLK_TCK;
51826
+ } catch {
51827
+ return null;
51828
+ }
51829
+ }
51830
+ var PID_REUSE_SLACK_MS = 2e3;
51738
51831
  function livePids(versionDir) {
51832
+ const dir = path109.join(versionDir, ".pids");
51739
51833
  let names;
51740
51834
  try {
51741
- names = fs96.readdirSync(path109.join(versionDir, ".pids"));
51835
+ names = fs96.readdirSync(dir);
51742
51836
  } catch {
51743
51837
  return [];
51744
51838
  }
51745
- return names.map(Number).filter((pid) => Number.isInteger(pid) && pid > 0 && pidAlive3(pid));
51839
+ return names.map(Number).filter((pid) => {
51840
+ if (!Number.isInteger(pid) || pid <= 0 || !pidAlive3(pid)) return false;
51841
+ const started = processStartMs(pid);
51842
+ if (started === null) return true;
51843
+ try {
51844
+ return started <= fs96.statSync(path109.join(dir, String(pid))).mtimeMs + PID_REUSE_SLACK_MS;
51845
+ } catch {
51846
+ return true;
51847
+ }
51848
+ });
51746
51849
  }
51747
51850
  function registerRunningVersion(root = cliVersionsRoot(), entry = process.argv[1]) {
51748
51851
  const dir = versionDirContaining(root, entry);
@@ -51842,7 +51945,7 @@ var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
51842
51945
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
51843
51946
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
51844
51947
  function currentCliVersion() {
51845
- return true ? "2.76.33" : null;
51948
+ return true ? "2.76.34" : null;
51846
51949
  }
51847
51950
  async function installedVersion(deps) {
51848
51951
  const ls = await deps.run(
@@ -51927,6 +52030,7 @@ async function runSelfUpdateWith(deps) {
51927
52030
  }
51928
52031
  if (compareSemver(latest, current2) <= 0) {
51929
52032
  log.info("host-agent", `self-update: running ${current2}, npm latest ${latest} \u2014 up to date`);
52033
+ if (versionedModeSupported(deps.platform)) collectOldVersions(root);
51930
52034
  return { status: "current" };
51931
52035
  }
51932
52036
  if (!versionedModeSupported(deps.platform)) {
@@ -51943,6 +52047,17 @@ async function runSelfUpdateWith(deps) {
51943
52047
  return { status: "skipped" };
51944
52048
  }
51945
52049
  }
52050
+ function collectOldVersions(root) {
52051
+ const release3 = acquireInstallLock(root);
52052
+ if (!release3) return [];
52053
+ try {
52054
+ const removed = gcVersions(root);
52055
+ if (removed.length) log.info("host-agent", `self-update: gc removed ${removed.join(", ")}`);
52056
+ return removed;
52057
+ } finally {
52058
+ release3();
52059
+ }
52060
+ }
51946
52061
  var VERIFY_TIMEOUT_MS = 3e4;
51947
52062
  async function installVersioned(deps, root, latest, running2) {
51948
52063
  const node = deps.nodePath ?? process.execPath;
@@ -52053,7 +52168,7 @@ function signalHostReady(send = process.send?.bind(process)) {
52053
52168
  }
52054
52169
  function defaultDeps() {
52055
52170
  return {
52056
- ownVersion: true ? "2.76.33" : null,
52171
+ ownVersion: true ? "2.76.34" : null,
52057
52172
  root: cliVersionsRoot(),
52058
52173
  args: process.argv.slice(3),
52059
52174
  execPath: process.execPath,
@@ -54591,7 +54706,7 @@ function checkChokidar() {
54591
54706
  }
54592
54707
  async function doctor(args2 = []) {
54593
54708
  const json = args2.includes("--json");
54594
- const cliVersion = true ? "2.76.33" : "0.0.0-dev";
54709
+ const cliVersion = true ? "2.76.34" : "0.0.0-dev";
54595
54710
  const apiBase2 = resolveApiBaseUrl();
54596
54711
  const diagnosticId = (0, import_node_crypto17.randomUUID)();
54597
54712
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -55127,7 +55242,7 @@ async function mcpWarm(argv = [], opts = {}) {
55127
55242
  // src/commands/version.ts
55128
55243
  var import_picocolors15 = __toESM(require("picocolors"));
55129
55244
  function version2() {
55130
- const v = true ? "2.76.33" : "unknown";
55245
+ const v = true ? "2.76.34" : "unknown";
55131
55246
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
55132
55247
  }
55133
55248
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.76.33",
3
+ "version": "2.76.34",
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",
@@ -88,7 +88,7 @@
88
88
  "zod": "^4.3.6"
89
89
  },
90
90
  "optionalDependencies": {
91
- "@beads/bd": "1.0.5"
91
+ "@beads/bd": "1.3.1"
92
92
  },
93
93
  "devDependencies": {
94
94
  "@codeam/shared": "*",