codeam-cli 2.76.31 → 2.76.33

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 +102 -38
  3. package/package.json +1 -1
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.76.32] — 2026-10-14
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Don't resume a session that was deleted from the app while the host slept (codeagent-xwoy)
12
+
13
+ ## [2.76.31] — 2026-10-14
14
+
15
+ ### Fixed
16
+
17
+ - **cli:** Cap the chat-pipe text frame and never loop on a 413
18
+ - **cli:** Make a python static-site preview ready instead of timing out
19
+ - **cli:** Let codex one-shots run outside a git repo
20
+ - **cli:** A failed agent preview hands the agent the dev server's output
21
+
22
+ ### Tests
23
+
24
+ - **cli:** Skip the POSIX-only preview subdir spawn test on Windows
25
+
7
26
  ## [2.76.30] — 2026-10-14
8
27
 
9
28
  ### CI
package/dist/index.js CHANGED
@@ -3428,14 +3428,23 @@ Rules:
3428
3428
  otherwise "python3".
3429
3429
  - Prefer binding to 0.0.0.0 \u2014 most frameworks default to localhost which the tunnel cannot reach.
3430
3430
  - For Expo: framework="Expo", command="npx", args=["expo","start","--tunnel"], port=8081, notes="Scan QR with Expo Go".
3431
+ - For PHP (composer.json / index.php): use PHP's built-in server bound to 0.0.0.0 with the
3432
+ web root as docroot \u2014 command="php", args=["-S","0.0.0.0:8080","-t","public"] (Yii3,
3433
+ Symfony, Slim, Mezzio; use the real docroot, e.g. "web" for Yii2, "." for plain PHP),
3434
+ port=8080, ready_pattern="Development Server". Laravel: command="php",
3435
+ args=["artisan","serve","--host=0.0.0.0","--port=8000"], port=8000,
3436
+ ready_pattern="Server running". Do NOT use "composer serve", "symfony serve" or a
3437
+ "./yii serve" wrapper \u2014 call php directly.
3431
3438
  - If no dev server applies (CLI library, lambda, batch script): {"framework":"unsupported","notes":"<reason>"}.
3432
3439
 
3433
3440
  CRITICAL \u2014 setup_commands:
3434
3441
  - DO NOT include an install command (npm install, pnpm install, yarn install,
3435
- yarn, bun install) in setup_commands. A lockfile-aware pre-flight installer
3436
- runs BEFORE setup_commands and picks the correct package manager from the
3437
- lockfile present (pnpm-lock.yaml -> pnpm, yarn.lock -> yarn, bun.lockb -> bun,
3438
- else npm). Emitting an install here either duplicates that work or, worse,
3442
+ yarn, bun install, composer install) in setup_commands. Pre-flight installers
3443
+ run BEFORE setup_commands: composer install whenever composer.json exists and
3444
+ vendor/ is missing, and a lockfile-aware Node installer that picks the
3445
+ correct package manager from the lockfile present (pnpm-lock.yaml -> pnpm,
3446
+ yarn.lock -> yarn, bun.lockb -> bun, else npm). Emitting an install here
3447
+ either duplicates that work or, worse,
3439
3448
  uses the WRONG package manager on top of node_modules just populated by the
3440
3449
  pre-flight, which crashes (e.g. npm errors with "Cannot read properties of
3441
3450
  null (reading 'matches')" when run over pnpm's .pnpm/ layout).
@@ -10627,7 +10636,7 @@ function readAnonId() {
10627
10636
  }
10628
10637
  function superProperties() {
10629
10638
  return {
10630
- cliVersion: true ? "2.76.31" : "0.0.0-dev",
10639
+ cliVersion: true ? "2.76.33" : "0.0.0-dev",
10631
10640
  nodeVersion: process.version,
10632
10641
  platform: process.platform,
10633
10642
  arch: process.arch,
@@ -10876,7 +10885,7 @@ var http = __toESM(require("http"));
10876
10885
  // package.json
10877
10886
  var package_default = {
10878
10887
  name: "codeam-cli",
10879
- version: "2.76.31",
10888
+ version: "2.76.33",
10880
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.",
10881
10890
  type: "commonjs",
10882
10891
  main: "dist/index.js",
@@ -11038,7 +11047,7 @@ async function requestCode(pluginId, pluginSecretHash) {
11038
11047
  return { ok: false, reason: "network" };
11039
11048
  }
11040
11049
  }
11041
- async function fetchCurrentPluginAuthToken(sessionId, pluginId, pollSecret, onUserId) {
11050
+ async function fetchCurrentPluginAuthToken(sessionId, pluginId, pollSecret, onUserId, onSessionGone) {
11042
11051
  try {
11043
11052
  const result = await _transport.postJson(
11044
11053
  `${API_BASE}/api/pairing/reconnect`,
@@ -11070,10 +11079,21 @@ async function fetchCurrentPluginAuthToken(sessionId, pluginId, pollSecret, onUs
11070
11079
  }
11071
11080
  const token = data.pluginAuthToken;
11072
11081
  return typeof token === "string" && token.length > 0 ? token : null;
11073
- } catch {
11082
+ } catch (err) {
11083
+ if (onSessionGone && isSessionNotFound(err)) onSessionGone();
11074
11084
  return null;
11075
11085
  }
11076
11086
  }
11087
+ function isSessionNotFound(err) {
11088
+ const e = err;
11089
+ if (!e || e.statusCode !== 404 || typeof e.body !== "string") return false;
11090
+ try {
11091
+ const parsed = JSON.parse(e.body);
11092
+ return parsed?.code === "SESSION_NOT_FOUND" || parsed?.error?.code === "SESSION_NOT_FOUND";
11093
+ } catch {
11094
+ return false;
11095
+ }
11096
+ }
11077
11097
  var _transport = {
11078
11098
  postJson: _postJson,
11079
11099
  getJson: _getJson,
@@ -12241,7 +12261,7 @@ var CommandRelayService = class _CommandRelayService {
12241
12261
  // fresh + clear the "CLI update available" banner after a self-update
12242
12262
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
12243
12263
  // pair/reconnect). Older backends ignore the extra field.
12244
- ..."2.76.31" ? { ideVersion: "2.76.31" } : {}
12264
+ ..."2.76.33" ? { ideVersion: "2.76.33" } : {}
12245
12265
  };
12246
12266
  try {
12247
12267
  const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body, this.pollSecretHeader());
@@ -24646,6 +24666,24 @@ function isJsInstallCommand(cmd, args2) {
24646
24666
  const verb = args2[0];
24647
24667
  return verb === "install" || verb === "i" || verb === "ci";
24648
24668
  }
24669
+ function detectMissingPhpDeps(cwd) {
24670
+ if (!import_fs5.default.existsSync(import_path8.default.join(cwd, "composer.json"))) return null;
24671
+ if (import_fs5.default.existsSync(import_path8.default.join(cwd, "vendor"))) return null;
24672
+ return {
24673
+ cmd: "composer",
24674
+ args: [
24675
+ "install",
24676
+ "--no-interaction",
24677
+ "--no-progress",
24678
+ "--prefer-dist",
24679
+ "--ignore-platform-req=ext-*"
24680
+ ]
24681
+ };
24682
+ }
24683
+ function isComposerInstallCommand(cmd, args2) {
24684
+ if (cmd !== "composer") return false;
24685
+ return args2[0] === "install" || args2[0] === "i";
24686
+ }
24649
24687
 
24650
24688
  // src/services/preview/script-candidates.ts
24651
24689
  var fs47 = __toESM(require("fs"));
@@ -27108,6 +27146,29 @@ function announceReady(ctx, dev, tun) {
27108
27146
  port: detection.port
27109
27147
  });
27110
27148
  }
27149
+ async function runPreflightInstall(ctx, install, why) {
27150
+ const { detection, cwd, emit: emit2, emitProgress } = ctx;
27151
+ const label = `${install.cmd} ${install.args.join(" ")}`;
27152
+ emitProgress("SETUP_RUN", `${label} (pre-flight \u2014 ${why})`);
27153
+ const result = await runSetupCommand(install.cmd, install.args, cwd, detection.env, {
27154
+ timeoutMs: INSTALL_TIMEOUT_MS2
27155
+ });
27156
+ if (result.status === "timeout") {
27157
+ emit2(USER_EVENTS.PREVIEW_ERROR, {
27158
+ stage: "ready_timeout",
27159
+ message: `Dependency install (${label}) didn't finish within ${Math.round(INSTALL_TIMEOUT_MS2 / 1e3)}s and was stopped. Run it manually in this project, then try the preview again.`
27160
+ });
27161
+ return false;
27162
+ }
27163
+ if (result.status === "failed") {
27164
+ emit2(USER_EVENTS.PREVIEW_ERROR, {
27165
+ stage: "spawn",
27166
+ message: `Dependency install failed (${label}, exit ${result.code}). Run it manually in this project and try again.`
27167
+ });
27168
+ return false;
27169
+ }
27170
+ return true;
27171
+ }
27111
27172
  async function provisionDeps(ctx) {
27112
27173
  const { detection, cwd, emit: emit2, emitProgress } = ctx;
27113
27174
  if (ctx.projectEnvAuth?.pluginAuthToken) {
@@ -27144,32 +27205,21 @@ async function provisionDeps(ctx) {
27144
27205
  return false;
27145
27206
  }
27146
27207
  }
27147
- emitProgress(
27148
- "SETUP_RUN",
27149
- `${missingDeps.cmd} ${missingDeps.args.join(" ")} (pre-flight \u2014 node_modules missing)`
27150
- );
27151
- const result = await runSetupCommand(
27152
- missingDeps.cmd,
27153
- missingDeps.args,
27154
- cwd,
27155
- detection.env,
27156
- { timeoutMs: INSTALL_TIMEOUT_MS2 }
27157
- );
27158
- if (result.status === "timeout") {
27159
- emit2(USER_EVENTS.PREVIEW_ERROR, {
27160
- stage: "ready_timeout",
27161
- message: `Dependency install (${missingDeps.cmd} ${missingDeps.args.join(" ")}) didn't finish within ${Math.round(INSTALL_TIMEOUT_MS2 / 1e3)}s and was stopped. Run it manually in this project, then try the preview again.`
27162
- });
27163
- return false;
27164
- }
27165
- if (result.status === "failed") {
27208
+ if (!await runPreflightInstall(ctx, missingDeps, "node_modules missing")) return false;
27209
+ preflightRan = true;
27210
+ }
27211
+ const missingPhpDeps = detectMissingPhpDeps(cwd);
27212
+ let composerPreflightRan = false;
27213
+ if (missingPhpDeps) {
27214
+ if (!await (0, import_which3.default)("composer", { nothrow: true })) {
27166
27215
  emit2(USER_EVENTS.PREVIEW_ERROR, {
27167
27216
  stage: "spawn",
27168
- message: `Dependency install failed (${missingDeps.cmd} ${missingDeps.args.join(" ")}, exit ${result.code}). Run it manually in this project and try again.`
27217
+ message: "This is a PHP project (composer.json) but Composer isn't installed in this environment, so its dependencies (vendor/) can't be installed. Install PHP and Composer, then try the preview again."
27169
27218
  });
27170
27219
  return false;
27171
27220
  }
27172
- preflightRan = true;
27221
+ if (!await runPreflightInstall(ctx, missingPhpDeps, "vendor/ missing")) return false;
27222
+ composerPreflightRan = true;
27173
27223
  }
27174
27224
  const normalizedSetup = (detection.setup_commands ?? []).map((entry) => {
27175
27225
  if (typeof entry === "string") {
@@ -27182,10 +27232,12 @@ async function provisionDeps(ctx) {
27182
27232
  args: Array.isArray(o.args) ? o.args.filter((a) => typeof a === "string") : []
27183
27233
  };
27184
27234
  }).filter((s) => s.cmd.length > 0);
27185
- const agentSetupCommands = preflightRan ? normalizedSetup.filter((s) => !isJsInstallCommand(s.cmd, s.args)) : normalizedSetup;
27235
+ const agentSetupCommands = normalizedSetup.filter(
27236
+ (s) => !(preflightRan && isJsInstallCommand(s.cmd, s.args)) && !(composerPreflightRan && isComposerInstallCommand(s.cmd, s.args))
27237
+ );
27186
27238
  for (const setup of agentSetupCommands) {
27187
27239
  emitProgress("SETUP_RUN", `${setup.cmd} ${setup.args.join(" ")}`);
27188
- const timeoutMs = isJsInstallCommand(setup.cmd, setup.args) ? INSTALL_TIMEOUT_MS2 : SETUP_TIMEOUT_MS;
27240
+ const timeoutMs = isJsInstallCommand(setup.cmd, setup.args) || isComposerInstallCommand(setup.cmd, setup.args) ? INSTALL_TIMEOUT_MS2 : SETUP_TIMEOUT_MS;
27189
27241
  const result = await runSetupCommand(setup.cmd, setup.args, cwd, detection.env, {
27190
27242
  timeoutMs
27191
27243
  });
@@ -47853,6 +47905,7 @@ async function start(requestedAgent, presetSession) {
47853
47905
  const cwd = process.cwd();
47854
47906
  let reconnectUserId;
47855
47907
  let reconnectPlan;
47908
+ let sessionGone = false;
47856
47909
  const refreshed = await fetchCurrentPluginAuthToken(
47857
47910
  session.id,
47858
47911
  pluginId,
@@ -47860,8 +47913,19 @@ async function start(requestedAgent, presetSession) {
47860
47913
  (userId, plan) => {
47861
47914
  reconnectUserId = userId;
47862
47915
  reconnectPlan = plan;
47916
+ },
47917
+ () => {
47918
+ sessionGone = true;
47863
47919
  }
47864
47920
  );
47921
+ if (sessionGone) {
47922
+ showInfo("This session was deleted from the app \u2014 not resuming it.");
47923
+ try {
47924
+ removeSession(session.id);
47925
+ } catch {
47926
+ }
47927
+ process.exit(0);
47928
+ }
47865
47929
  if (refreshed && refreshed !== session.pluginAuthToken) {
47866
47930
  addSession({ ...session, pluginAuthToken: refreshed });
47867
47931
  session.pluginAuthToken = refreshed;
@@ -51478,7 +51542,7 @@ async function autoUpgradeBeforeCriticalCommand() {
51478
51542
  if (process.env.NODE_ENV === "test") return;
51479
51543
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
51480
51544
  if (process.env.CI) return;
51481
- const current2 = true ? "2.76.31" : null;
51545
+ const current2 = true ? "2.76.33" : null;
51482
51546
  if (!current2) return;
51483
51547
  const cache = readCache();
51484
51548
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -51495,7 +51559,7 @@ function checkForUpdates() {
51495
51559
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
51496
51560
  if (process.env.CI) return;
51497
51561
  if (!process.stdout.isTTY) return;
51498
- const current2 = true ? "2.76.31" : null;
51562
+ const current2 = true ? "2.76.33" : null;
51499
51563
  if (!current2) return;
51500
51564
  const cache = readCache();
51501
51565
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -51778,7 +51842,7 @@ var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
51778
51842
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
51779
51843
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
51780
51844
  function currentCliVersion() {
51781
- return true ? "2.76.31" : null;
51845
+ return true ? "2.76.33" : null;
51782
51846
  }
51783
51847
  async function installedVersion(deps) {
51784
51848
  const ls = await deps.run(
@@ -51989,7 +52053,7 @@ function signalHostReady(send = process.send?.bind(process)) {
51989
52053
  }
51990
52054
  function defaultDeps() {
51991
52055
  return {
51992
- ownVersion: true ? "2.76.31" : null,
52056
+ ownVersion: true ? "2.76.33" : null,
51993
52057
  root: cliVersionsRoot(),
51994
52058
  args: process.argv.slice(3),
51995
52059
  execPath: process.execPath,
@@ -54527,7 +54591,7 @@ function checkChokidar() {
54527
54591
  }
54528
54592
  async function doctor(args2 = []) {
54529
54593
  const json = args2.includes("--json");
54530
- const cliVersion = true ? "2.76.31" : "0.0.0-dev";
54594
+ const cliVersion = true ? "2.76.33" : "0.0.0-dev";
54531
54595
  const apiBase2 = resolveApiBaseUrl();
54532
54596
  const diagnosticId = (0, import_node_crypto17.randomUUID)();
54533
54597
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -55063,7 +55127,7 @@ async function mcpWarm(argv = [], opts = {}) {
55063
55127
  // src/commands/version.ts
55064
55128
  var import_picocolors15 = __toESM(require("picocolors"));
55065
55129
  function version2() {
55066
- const v = true ? "2.76.31" : "unknown";
55130
+ const v = true ? "2.76.33" : "unknown";
55067
55131
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
55068
55132
  }
55069
55133
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.76.31",
3
+ "version": "2.76.33",
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",