codeam-cli 2.39.65 → 2.39.68
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 +12 -0
- package/dist/index.js +96 -48
- package/package.json +1 -1
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.66] — 2026-06-20
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** Kill preview dev-server process group to stop port leaks (EADDRINUSE)
|
|
12
|
+
|
|
13
|
+
## [2.39.65] — 2026-06-20
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **self-hosted:** Run sessions in AUTO mode (auto-approve permissions)
|
|
18
|
+
|
|
7
19
|
## [2.39.64] — 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.
|
|
5391
|
+
cliVersion: true ? "2.39.68" : "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.
|
|
5550
|
+
version: "2.39.68",
|
|
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
|
-
|
|
14147
|
-
const
|
|
14148
|
-
if (
|
|
14149
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
14162
|
-
|
|
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
|
-
|
|
16229
|
-
|
|
16230
|
-
|
|
16231
|
-
|
|
16232
|
-
|
|
16233
|
-
|
|
16234
|
-
|
|
16235
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
17394
|
+
const current = true ? "2.39.68" : 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.
|
|
17799
|
+
return true ? "2.39.68" : null;
|
|
17776
17800
|
}
|
|
17777
17801
|
function runCmd(cmd, args2, timeoutMs) {
|
|
17778
17802
|
return new Promise((resolve7) => {
|
|
@@ -17843,6 +17867,20 @@ var defaultDisableService = () => {
|
|
|
17843
17867
|
} catch {
|
|
17844
17868
|
}
|
|
17845
17869
|
};
|
|
17870
|
+
var defaultTeardownHeadroom = () => {
|
|
17871
|
+
try {
|
|
17872
|
+
const kind = JSON.parse(fs39.readFileSync(headroomConfigPath(), "utf8")).agent;
|
|
17873
|
+
if (kind) {
|
|
17874
|
+
(0, import_node_child_process13.execFileSync)("headroom", ["unwrap", kind], { stdio: "ignore", timeout: 15e3 });
|
|
17875
|
+
}
|
|
17876
|
+
} catch {
|
|
17877
|
+
}
|
|
17878
|
+
try {
|
|
17879
|
+
(0, import_node_child_process13.execFileSync)("pkill", ["-TERM", "-f", "headroom.*proxy"], { stdio: "ignore" });
|
|
17880
|
+
} catch {
|
|
17881
|
+
}
|
|
17882
|
+
persistHeadroomConfig({ enabled: false });
|
|
17883
|
+
};
|
|
17846
17884
|
var HostAgentSupervisor = class {
|
|
17847
17885
|
constructor(identity, deps = {}) {
|
|
17848
17886
|
this.identity = identity;
|
|
@@ -17853,6 +17891,7 @@ var HostAgentSupervisor = class {
|
|
|
17853
17891
|
this.metrics = deps.metricsCollector ?? new MetricsCollector();
|
|
17854
17892
|
this.onIdentityRejected = deps.onIdentityRejected ?? defaultOnIdentityRejected;
|
|
17855
17893
|
this.disableService = deps.disableService ?? defaultDisableService;
|
|
17894
|
+
this.teardownHeadroom = deps.teardownHeadroom ?? defaultTeardownHeadroom;
|
|
17856
17895
|
this.selfUpdate = deps.selfUpdate ?? runSelfUpdate;
|
|
17857
17896
|
this.onUpdated = deps.onUpdated ?? defaultOnUpdated;
|
|
17858
17897
|
}
|
|
@@ -17886,6 +17925,7 @@ var HostAgentSupervisor = class {
|
|
|
17886
17925
|
onIdentityRejected;
|
|
17887
17926
|
/** Best-effort systemd de-provision used by `self_hosted_wipe`. */
|
|
17888
17927
|
disableService;
|
|
17928
|
+
teardownHeadroom;
|
|
17889
17929
|
/** Guards against firing the self-heal more than once. */
|
|
17890
17930
|
healing = false;
|
|
17891
17931
|
/**
|
|
@@ -18083,6 +18123,7 @@ var HostAgentSupervisor = class {
|
|
|
18083
18123
|
if (cmd.type === "self_hosted_wipe") {
|
|
18084
18124
|
log.warn("host-agent", `self_hosted_wipe received id=${cmd.id} \u2014 de-provisioning`);
|
|
18085
18125
|
this.stop();
|
|
18126
|
+
this.teardownHeadroom();
|
|
18086
18127
|
this.disableService();
|
|
18087
18128
|
if (!this.healing) {
|
|
18088
18129
|
this.healing = true;
|
|
@@ -21771,6 +21812,13 @@ var AcpClient = class {
|
|
|
21771
21812
|
if (!this.connection) {
|
|
21772
21813
|
throw new Error("AcpClient.loadSession called before start()");
|
|
21773
21814
|
}
|
|
21815
|
+
if (sessionId === this.sessionId) {
|
|
21816
|
+
log.info(
|
|
21817
|
+
"acpClient",
|
|
21818
|
+
`loadSession skipped \u2014 already the active session (${sessionId.slice(0, 8)})`
|
|
21819
|
+
);
|
|
21820
|
+
return;
|
|
21821
|
+
}
|
|
21774
21822
|
log.info("acpClient", `loadSession \u2192 sessionId=${sessionId.slice(0, 8)}`);
|
|
21775
21823
|
await this.connection.loadSession({
|
|
21776
21824
|
sessionId,
|
|
@@ -28387,7 +28435,7 @@ function checkChokidar() {
|
|
|
28387
28435
|
}
|
|
28388
28436
|
async function doctor(args2 = []) {
|
|
28389
28437
|
const json = args2.includes("--json");
|
|
28390
|
-
const cliVersion = true ? "2.39.
|
|
28438
|
+
const cliVersion = true ? "2.39.68" : "0.0.0-dev";
|
|
28391
28439
|
const apiBase2 = resolveApiBaseUrl();
|
|
28392
28440
|
const diagnosticId = (0, import_node_crypto8.randomUUID)();
|
|
28393
28441
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -28586,7 +28634,7 @@ async function completion(args2) {
|
|
|
28586
28634
|
// src/commands/version.ts
|
|
28587
28635
|
var import_picocolors14 = __toESM(require("picocolors"));
|
|
28588
28636
|
function version2() {
|
|
28589
|
-
const v = true ? "2.39.
|
|
28637
|
+
const v = true ? "2.39.68" : "unknown";
|
|
28590
28638
|
console.log(`${import_picocolors14.default.bold("codeam-cli")} ${import_picocolors14.default.cyan(v)}`);
|
|
28591
28639
|
}
|
|
28592
28640
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.39.
|
|
3
|
+
"version": "2.39.68",
|
|
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",
|