codeam-cli 2.39.64 → 2.39.66

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,18 @@ 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.39.65] — 2026-06-20
8
+
9
+ ### Added
10
+
11
+ - **self-hosted:** Run sessions in AUTO mode (auto-approve permissions)
12
+
13
+ ## [2.39.64] — 2026-06-20
14
+
15
+ ### Added
16
+
17
+ - **host-agent:** Handle self_hosted_refresh_credentials (in-place re-auth)
18
+
7
19
  ## [2.39.63] — 2026-06-20
8
20
 
9
21
  ### Added
package/dist/index.js CHANGED
@@ -5388,7 +5388,7 @@ function readAnonId() {
5388
5388
  }
5389
5389
  function superProperties() {
5390
5390
  return {
5391
- cliVersion: true ? "2.39.64" : "0.0.0-dev",
5391
+ cliVersion: true ? "2.39.66" : "0.0.0-dev",
5392
5392
  nodeVersion: process.version,
5393
5393
  platform: process.platform,
5394
5394
  arch: process.arch,
@@ -5547,7 +5547,7 @@ var os4 = __toESM(require("os"));
5547
5547
  // package.json
5548
5548
  var package_default = {
5549
5549
  name: "codeam-cli",
5550
- version: "2.39.64",
5550
+ version: "2.39.66",
5551
5551
  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.",
5552
5552
  type: "commonjs",
5553
5553
  main: "dist/index.js",
@@ -14143,29 +14143,32 @@ var activePreviews = /* @__PURE__ */ new Map();
14143
14143
  function registerPreview(sessionId, preview) {
14144
14144
  activePreviews.set(sessionId, preview);
14145
14145
  }
14146
- async function killPreview(sessionId) {
14147
- const preview = activePreviews.get(sessionId);
14148
- if (!preview) return;
14149
- if (preview.tunnel) {
14146
+ function killProcessTree(child, signal = "SIGTERM") {
14147
+ const pid = child.pid;
14148
+ if (pid == null) return;
14149
+ if (process.platform !== "win32") {
14150
14150
  try {
14151
- preview.tunnel.kill("SIGTERM");
14151
+ process.kill(-pid, signal);
14152
+ return;
14152
14153
  } catch {
14153
14154
  }
14154
14155
  }
14155
- await new Promise((r) => setTimeout(r, 100));
14156
14156
  try {
14157
- preview.devServer.kill("SIGTERM");
14157
+ child.kill(signal);
14158
14158
  } catch {
14159
14159
  }
14160
+ }
14161
+ async function killPreview(sessionId) {
14162
+ const preview = activePreviews.get(sessionId);
14163
+ if (!preview) return;
14164
+ if (preview.tunnel) {
14165
+ killProcessTree(preview.tunnel, "SIGTERM");
14166
+ }
14167
+ await new Promise((r) => setTimeout(r, 100));
14168
+ killProcessTree(preview.devServer, "SIGTERM");
14160
14169
  const sigkillTimer = setTimeout(() => {
14161
- try {
14162
- preview.devServer.kill("SIGKILL");
14163
- } catch {
14164
- }
14165
- try {
14166
- preview.tunnel?.kill("SIGKILL");
14167
- } catch {
14168
- }
14170
+ killProcessTree(preview.devServer, "SIGKILL");
14171
+ if (preview.tunnel) killProcessTree(preview.tunnel, "SIGKILL");
14169
14172
  }, 250);
14170
14173
  sigkillTimer.unref?.();
14171
14174
  activePreviews.delete(sessionId);
@@ -16225,17 +16228,42 @@ var previewStartH = (ctx, _cmd, parsed) => {
16225
16228
  });
16226
16229
  return;
16227
16230
  }
16228
- void postPreviewEvent({
16229
- sessionId: ctx.sessionId,
16230
- pluginId: ctx.pluginId,
16231
- pluginAuthToken,
16232
- type: "preview_error",
16233
- payload: {
16234
- stage: "spawn",
16235
- message: `Port ${detection.port} is already in use by another process, so the dev server can't start there. Stop whatever is listening on port ${detection.port} and try the preview again.`
16231
+ if (raceExisting) {
16232
+ log.info(
16233
+ "preview",
16234
+ `reclaiming stale preview holding port ${detection.port} for session=${ctx.sessionId} (exit=${raceExisting.devServer.exitCode})`
16235
+ );
16236
+ await killPreview(ctx.sessionId);
16237
+ const freeDeadline = Date.now() + 4e3;
16238
+ while (await isPortListening(detection.port) && Date.now() < freeDeadline) {
16239
+ await new Promise((r) => setTimeout(r, 200));
16236
16240
  }
16237
- });
16238
- return;
16241
+ if (await isPortListening(detection.port)) {
16242
+ void postPreviewEvent({
16243
+ sessionId: ctx.sessionId,
16244
+ pluginId: ctx.pluginId,
16245
+ pluginAuthToken,
16246
+ type: "preview_error",
16247
+ payload: {
16248
+ stage: "spawn",
16249
+ message: `Port ${detection.port} is still in use after stopping the previous preview. Wait a moment and try again.`
16250
+ }
16251
+ });
16252
+ return;
16253
+ }
16254
+ } else {
16255
+ void postPreviewEvent({
16256
+ sessionId: ctx.sessionId,
16257
+ pluginId: ctx.pluginId,
16258
+ pluginAuthToken,
16259
+ type: "preview_error",
16260
+ payload: {
16261
+ stage: "spawn",
16262
+ message: `Port ${detection.port} is already in use by another process, so the dev server can't start there. Stop whatever is listening on port ${detection.port} and try the preview again.`
16263
+ }
16264
+ });
16265
+ return;
16266
+ }
16239
16267
  }
16240
16268
  const spawnable = normalizeDetectionForSpawn(detection, process.cwd());
16241
16269
  emitProgress(
@@ -16245,7 +16273,14 @@ var previewStartH = (ctx, _cmd, parsed) => {
16245
16273
  const devServer = (0, import_child_process20.spawn)(spawnable.command, spawnable.args, {
16246
16274
  cwd: process.cwd(),
16247
16275
  env: { ...process.env, ...spawnable.env ?? {} },
16248
- stdio: ["ignore", "pipe", "pipe"]
16276
+ stdio: ["ignore", "pipe", "pipe"],
16277
+ // POSIX: lead a new process group so teardown can SIGTERM the whole
16278
+ // tree. Dev servers fork worker children that bind the port; killing
16279
+ // only the direct child orphans them and leaks the port, so the next
16280
+ // preview_start hits EADDRINUSE on a port we already hold. Group-kill
16281
+ // (killProcessTree) reaps the workers too. Windows has no process
16282
+ // groups — leave detached off there (direct kill is the only option).
16283
+ detached: process.platform !== "win32"
16249
16284
  });
16250
16285
  emitProgress("BIND_PORT", String(detection.port));
16251
16286
  emitProgress("WAITING_FOR_READY", detection.ready_pattern);
@@ -16264,6 +16299,7 @@ var previewStartH = (ctx, _cmd, parsed) => {
16264
16299
  portProbe: isNextJs ? () => waitForPortListening(detection.port, { timeoutMs: 1e3, intervalMs: 250 }) : void 0
16265
16300
  });
16266
16301
  if (outcome.kind === "exited") {
16302
+ killProcessTree(devServer, "SIGTERM");
16267
16303
  void postPreviewEvent({
16268
16304
  sessionId: ctx.sessionId,
16269
16305
  pluginId: ctx.pluginId,
@@ -16278,10 +16314,7 @@ var previewStartH = (ctx, _cmd, parsed) => {
16278
16314
  return;
16279
16315
  }
16280
16316
  if (outcome.kind === "timeout") {
16281
- try {
16282
- devServer.kill("SIGTERM");
16283
- } catch {
16284
- }
16317
+ killProcessTree(devServer, "SIGTERM");
16285
16318
  void postPreviewEvent({
16286
16319
  sessionId: ctx.sessionId,
16287
16320
  pluginId: ctx.pluginId,
@@ -16310,10 +16343,7 @@ var previewStartH = (ctx, _cmd, parsed) => {
16310
16343
  }
16311
16344
  }
16312
16345
  if (!expoUrl) {
16313
- try {
16314
- devServer.kill("SIGTERM");
16315
- } catch {
16316
- }
16346
+ killProcessTree(devServer, "SIGTERM");
16317
16347
  void postPreviewEvent({
16318
16348
  sessionId: ctx.sessionId,
16319
16349
  pluginId: ctx.pluginId,
@@ -16329,10 +16359,7 @@ var previewStartH = (ctx, _cmd, parsed) => {
16329
16359
  try {
16330
16360
  bin = await resolveCloudflared();
16331
16361
  } catch (e) {
16332
- try {
16333
- devServer.kill("SIGTERM");
16334
- } catch {
16335
- }
16362
+ killProcessTree(devServer, "SIGTERM");
16336
16363
  void postPreviewEvent({
16337
16364
  sessionId: ctx.sessionId,
16338
16365
  pluginId: ctx.pluginId,
@@ -16426,10 +16453,7 @@ var previewStartH = (ctx, _cmd, parsed) => {
16426
16453
  }
16427
16454
  }
16428
16455
  if (!parsedUrl) {
16429
- try {
16430
- devServer.kill("SIGTERM");
16431
- } catch {
16432
- }
16456
+ killProcessTree(devServer, "SIGTERM");
16433
16457
  void postPreviewEvent({
16434
16458
  sessionId: ctx.sessionId,
16435
16459
  pluginId: ctx.pluginId,
@@ -17367,7 +17391,7 @@ function checkForUpdates() {
17367
17391
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
17368
17392
  if (process.env.CI) return;
17369
17393
  if (!process.stdout.isTTY) return;
17370
- const current = true ? "2.39.64" : null;
17394
+ const current = true ? "2.39.66" : null;
17371
17395
  if (!current) return;
17372
17396
  const cache = readCache();
17373
17397
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -17772,7 +17796,7 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process13.s
17772
17796
  detached: false
17773
17797
  });
17774
17798
  function currentCliVersion() {
17775
- return true ? "2.39.64" : null;
17799
+ return true ? "2.39.66" : null;
17776
17800
  }
17777
17801
  function runCmd(cmd, args2, timeoutMs) {
17778
17802
  return new Promise((resolve7) => {
@@ -18145,6 +18169,7 @@ var HostAgentSupervisor = class {
18145
18169
  CODEAM_AUTO_TOKEN: payload.autoPairToken
18146
18170
  };
18147
18171
  }
18172
+ childEnv = { ...childEnv, CODEAM_AUTO_APPROVE: "1" };
18148
18173
  if (payload.agentInstallScript) {
18149
18174
  report("installing", "installing agent CLI");
18150
18175
  await this.runAgentInstall(payload.agentInstallScript);
@@ -25718,10 +25743,13 @@ async function start(requestedAgent) {
25718
25743
  adapter,
25719
25744
  cwd,
25720
25745
  getBeads,
25721
- // AUTO mode in a headless GitHub Codespace: no human at the phone to
25722
- // answer permission prompts, so auto-approve them rather than stall the
25723
- // turn (the agent-agnostic equivalent of --dangerously-skip-permissions).
25724
- autoApprovePermissions: process.env.CODESPACES === "true"
25746
+ // AUTO mode for headless, mobile-driven sessions: no human at the box
25747
+ // to answer permission prompts, so auto-approve them rather than stall
25748
+ // the turn (the agent-agnostic equivalent of
25749
+ // --dangerously-skip-permissions). Both surfaces qualify — a GitHub
25750
+ // Codespace (`CODESPACES=true`) and a self-hosted deploy (the host-agent
25751
+ // sets `CODEAM_AUTO_APPROVE=1` on the pair-auto child).
25752
+ autoApprovePermissions: process.env.CODESPACES === "true" || process.env.CODEAM_AUTO_APPROVE === "1"
25725
25753
  });
25726
25754
  return;
25727
25755
  }
@@ -28383,7 +28411,7 @@ function checkChokidar() {
28383
28411
  }
28384
28412
  async function doctor(args2 = []) {
28385
28413
  const json = args2.includes("--json");
28386
- const cliVersion = true ? "2.39.64" : "0.0.0-dev";
28414
+ const cliVersion = true ? "2.39.66" : "0.0.0-dev";
28387
28415
  const apiBase2 = resolveApiBaseUrl();
28388
28416
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
28389
28417
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -28582,7 +28610,7 @@ async function completion(args2) {
28582
28610
  // src/commands/version.ts
28583
28611
  var import_picocolors14 = __toESM(require("picocolors"));
28584
28612
  function version2() {
28585
- const v = true ? "2.39.64" : "unknown";
28613
+ const v = true ? "2.39.66" : "unknown";
28586
28614
  console.log(`${import_picocolors14.default.bold("codeam-cli")} ${import_picocolors14.default.cyan(v)}`);
28587
28615
  }
28588
28616
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.39.64",
3
+ "version": "2.39.66",
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",