codeam-cli 2.39.50 → 2.39.52
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 +151 -7
- 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.51] — 2026-06-20
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** Reuse running preview on re-open instead of re-spawn (EADDRINUSE)
|
|
12
|
+
|
|
13
|
+
## [2.39.50] — 2026-06-19
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **cli:** Parse real headroom /stats shape so savings reach the dashboard
|
|
18
|
+
|
|
7
19
|
## [2.39.49] — 2026-06-19
|
|
8
20
|
|
|
9
21
|
### Fixed
|
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.52" : "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.52",
|
|
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",
|
|
@@ -16073,6 +16073,22 @@ var previewStartH = (ctx, _cmd, parsed) => {
|
|
|
16073
16073
|
});
|
|
16074
16074
|
};
|
|
16075
16075
|
void (async () => {
|
|
16076
|
+
const existing = activePreviews.get(ctx.sessionId);
|
|
16077
|
+
if (existing && existing.devServer.exitCode === null) {
|
|
16078
|
+
log.info(
|
|
16079
|
+
"preview",
|
|
16080
|
+
`reusing running preview for session=${ctx.sessionId} url=${existing.url}`
|
|
16081
|
+
);
|
|
16082
|
+
emitProgress("READY_DETECTED", "reusing running preview");
|
|
16083
|
+
void postPreviewEvent({
|
|
16084
|
+
sessionId: ctx.sessionId,
|
|
16085
|
+
pluginId: ctx.pluginId,
|
|
16086
|
+
pluginAuthToken,
|
|
16087
|
+
type: "preview_ready",
|
|
16088
|
+
payload: { url: existing.url, framework: existing.framework, port: detection.port }
|
|
16089
|
+
});
|
|
16090
|
+
return;
|
|
16091
|
+
}
|
|
16076
16092
|
void postPreviewEvent({
|
|
16077
16093
|
sessionId: ctx.sessionId,
|
|
16078
16094
|
pluginId: ctx.pluginId,
|
|
@@ -16193,6 +16209,22 @@ var previewStartH = (ctx, _cmd, parsed) => {
|
|
|
16193
16209
|
}
|
|
16194
16210
|
}
|
|
16195
16211
|
if (await isPortListening(detection.port)) {
|
|
16212
|
+
const raceExisting = activePreviews.get(ctx.sessionId);
|
|
16213
|
+
if (raceExisting && raceExisting.devServer.exitCode === null) {
|
|
16214
|
+
log.info(
|
|
16215
|
+
"preview",
|
|
16216
|
+
`port race: reusing running preview for session=${ctx.sessionId} url=${raceExisting.url}`
|
|
16217
|
+
);
|
|
16218
|
+
emitProgress("READY_DETECTED", "reusing running preview");
|
|
16219
|
+
void postPreviewEvent({
|
|
16220
|
+
sessionId: ctx.sessionId,
|
|
16221
|
+
pluginId: ctx.pluginId,
|
|
16222
|
+
pluginAuthToken,
|
|
16223
|
+
type: "preview_ready",
|
|
16224
|
+
payload: { url: raceExisting.url, framework: raceExisting.framework, port: detection.port }
|
|
16225
|
+
});
|
|
16226
|
+
return;
|
|
16227
|
+
}
|
|
16196
16228
|
void postPreviewEvent({
|
|
16197
16229
|
sessionId: ctx.sessionId,
|
|
16198
16230
|
pluginId: ctx.pluginId,
|
|
@@ -17213,6 +17245,15 @@ function isDeployPayload(p2) {
|
|
|
17213
17245
|
if (p2.cloneToken !== void 0 && typeof p2.cloneToken !== "string") {
|
|
17214
17246
|
return false;
|
|
17215
17247
|
}
|
|
17248
|
+
if (p2.headroomEnabled !== void 0 && typeof p2.headroomEnabled !== "boolean") {
|
|
17249
|
+
return false;
|
|
17250
|
+
}
|
|
17251
|
+
if (p2.headroomAgent !== void 0 && typeof p2.headroomAgent !== "string") {
|
|
17252
|
+
return false;
|
|
17253
|
+
}
|
|
17254
|
+
if (p2.headroomSavingsIngestUrl !== void 0 && typeof p2.headroomSavingsIngestUrl !== "string") {
|
|
17255
|
+
return false;
|
|
17256
|
+
}
|
|
17216
17257
|
const hasHouse = isHouseProxy(p2.houseProxy);
|
|
17217
17258
|
const hasSealed = typeof p2.sealedAgentAuth === "string";
|
|
17218
17259
|
return hasHouse || hasSealed;
|
|
@@ -17228,6 +17269,95 @@ var CONTROL_AGENT_META = {
|
|
|
17228
17269
|
supportedAuthKinds: ["oauth_token"],
|
|
17229
17270
|
preferredAuthKind: "oauth_token"
|
|
17230
17271
|
};
|
|
17272
|
+
async function setupHeadroomForSelfHosted(agent) {
|
|
17273
|
+
const INSTALL_TIMEOUT_MS2 = 12e4;
|
|
17274
|
+
const PIP_PACKAGES = [
|
|
17275
|
+
"headroom-ai",
|
|
17276
|
+
"fastapi",
|
|
17277
|
+
"uvicorn",
|
|
17278
|
+
"httpx[http2]",
|
|
17279
|
+
"websockets",
|
|
17280
|
+
"zstandard"
|
|
17281
|
+
];
|
|
17282
|
+
const installOk = await new Promise((resolve7) => {
|
|
17283
|
+
const tryPip = (cmd) => new Promise((res) => {
|
|
17284
|
+
const child = (0, import_node_child_process12.spawn)(cmd, ["install", "--quiet", ...PIP_PACKAGES], {
|
|
17285
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
17286
|
+
});
|
|
17287
|
+
let settled = false;
|
|
17288
|
+
const done = (ok) => {
|
|
17289
|
+
if (settled) return;
|
|
17290
|
+
settled = true;
|
|
17291
|
+
res(ok);
|
|
17292
|
+
};
|
|
17293
|
+
const onData = (b) => {
|
|
17294
|
+
const line = b.toString().replace(/\n+$/g, "");
|
|
17295
|
+
if (line) log.info("host-agent", `headroom-install: ${line}`);
|
|
17296
|
+
};
|
|
17297
|
+
child.stdout?.on("data", onData);
|
|
17298
|
+
child.stderr?.on("data", onData);
|
|
17299
|
+
const timer = setTimeout(() => {
|
|
17300
|
+
log.warn("host-agent", `headroom pip install timed out (${INSTALL_TIMEOUT_MS2 / 1e3}s) \u2014 skipping Headroom`);
|
|
17301
|
+
try {
|
|
17302
|
+
child.kill("SIGTERM");
|
|
17303
|
+
} catch {
|
|
17304
|
+
}
|
|
17305
|
+
done(false);
|
|
17306
|
+
}, INSTALL_TIMEOUT_MS2);
|
|
17307
|
+
child.once("exit", (code) => {
|
|
17308
|
+
clearTimeout(timer);
|
|
17309
|
+
if (code === 0) {
|
|
17310
|
+
log.info("host-agent", "headroom pip install succeeded");
|
|
17311
|
+
} else {
|
|
17312
|
+
log.warn("host-agent", `headroom pip install exited code=${String(code)} \u2014 skipping Headroom`);
|
|
17313
|
+
}
|
|
17314
|
+
done(code === 0);
|
|
17315
|
+
});
|
|
17316
|
+
child.once("error", (e) => {
|
|
17317
|
+
clearTimeout(timer);
|
|
17318
|
+
done(false);
|
|
17319
|
+
log.trace("host-agent", `headroom pip spawn error (${cmd}): ${e.message}`);
|
|
17320
|
+
});
|
|
17321
|
+
});
|
|
17322
|
+
tryPip("pip").then((ok) => ok ? resolve7(true) : tryPip("pip3").then(resolve7)).catch(() => resolve7(false));
|
|
17323
|
+
});
|
|
17324
|
+
if (!installOk) {
|
|
17325
|
+
return false;
|
|
17326
|
+
}
|
|
17327
|
+
const initOk = await new Promise((resolve7) => {
|
|
17328
|
+
(0, import_node_child_process12.execFile)("which", ["headroom"], (whichErr) => {
|
|
17329
|
+
if (whichErr) {
|
|
17330
|
+
log.warn("host-agent", "headroom not found on PATH after install \u2014 skipping init");
|
|
17331
|
+
resolve7(false);
|
|
17332
|
+
return;
|
|
17333
|
+
}
|
|
17334
|
+
(0, import_node_child_process12.execFile)("headroom", ["init", "--global", agent], (initErr, stdout, stderr) => {
|
|
17335
|
+
if (initErr) {
|
|
17336
|
+
const detail = (stderr || initErr.message).replace(/\n+$/g, "");
|
|
17337
|
+
log.warn("host-agent", `headroom init failed (best-effort): ${detail}`);
|
|
17338
|
+
resolve7(false);
|
|
17339
|
+
} else {
|
|
17340
|
+
if (stdout.trim()) log.info("host-agent", `headroom init: ${stdout.trim()}`);
|
|
17341
|
+
log.info("host-agent", "headroom init --global succeeded");
|
|
17342
|
+
resolve7(true);
|
|
17343
|
+
}
|
|
17344
|
+
});
|
|
17345
|
+
});
|
|
17346
|
+
});
|
|
17347
|
+
if (!initOk) {
|
|
17348
|
+
return false;
|
|
17349
|
+
}
|
|
17350
|
+
try {
|
|
17351
|
+
const proxy = (0, import_node_child_process12.spawn)("headroom", ["proxy", "--port", "8787"], {
|
|
17352
|
+
stdio: "ignore",
|
|
17353
|
+
detached: true
|
|
17354
|
+
});
|
|
17355
|
+
proxy.unref();
|
|
17356
|
+
} catch (e) {
|
|
17357
|
+
log.warn("host-agent", `headroom proxy warm-start failed (best-effort): ${e instanceof Error ? e.message : String(e)}`);
|
|
17358
|
+
}
|
|
17359
|
+
return true;
|
|
17360
|
+
}
|
|
17231
17361
|
var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process12.spawn)(process.execPath, [process.argv[1], "pair-auto", ...args2], {
|
|
17232
17362
|
cwd,
|
|
17233
17363
|
env: { ...process.env, ...env },
|
|
@@ -17251,6 +17381,7 @@ var HostAgentSupervisor = class {
|
|
|
17251
17381
|
this.deps = deps;
|
|
17252
17382
|
this.spawnChild = deps.spawnChild ?? defaultSpawner;
|
|
17253
17383
|
this.resolveAgentAuth = deps.resolveAgentAuth ?? unsealAgentAuth;
|
|
17384
|
+
this.setupHeadroom = deps.setupHeadroom ?? setupHeadroomForSelfHosted;
|
|
17254
17385
|
this.metrics = deps.metricsCollector ?? new MetricsCollector();
|
|
17255
17386
|
this.onIdentityRejected = deps.onIdentityRejected ?? defaultOnIdentityRejected;
|
|
17256
17387
|
this.disableService = deps.disableService ?? defaultDisableService;
|
|
@@ -17260,6 +17391,7 @@ var HostAgentSupervisor = class {
|
|
|
17260
17391
|
children = /* @__PURE__ */ new Map();
|
|
17261
17392
|
spawnChild;
|
|
17262
17393
|
resolveAgentAuth;
|
|
17394
|
+
setupHeadroom;
|
|
17263
17395
|
relay = null;
|
|
17264
17396
|
heartbeatTimer = null;
|
|
17265
17397
|
/** Guards the one-shot 'connected' telemetry on the first heartbeat. */
|
|
@@ -17432,6 +17564,18 @@ var HostAgentSupervisor = class {
|
|
|
17432
17564
|
childEnv.PREVIEW_TUNNEL_TOKEN = payload.previewTunnelToken;
|
|
17433
17565
|
childEnv.PREVIEW_TUNNEL_HOSTNAME = payload.previewHostname;
|
|
17434
17566
|
}
|
|
17567
|
+
if (payload.headroomEnabled && payload.headroomAgent && payload.headroomSavingsIngestUrl) {
|
|
17568
|
+
report("headroom", "setting up Headroom proxy");
|
|
17569
|
+
const headroomOk = await this.setupHeadroom(payload.headroomAgent);
|
|
17570
|
+
if (headroomOk) {
|
|
17571
|
+
childEnv.HEADROOM_ENABLED = "1";
|
|
17572
|
+
childEnv.HEADROOM_AGENT = payload.headroomAgent;
|
|
17573
|
+
childEnv.HEADROOM_SAVINGS_INGEST_URL = payload.headroomSavingsIngestUrl;
|
|
17574
|
+
log.info("host-agent", "Headroom proxy ready; HEADROOM_* env injected into child");
|
|
17575
|
+
} else {
|
|
17576
|
+
log.warn("host-agent", "Headroom setup failed (best-effort) \u2014 child will run without Headroom");
|
|
17577
|
+
}
|
|
17578
|
+
}
|
|
17435
17579
|
report("spawning", "starting agent");
|
|
17436
17580
|
const proc = this.spawnChild(childEnv, cwd, extraArgs);
|
|
17437
17581
|
const child = { deployId: payload.deployId, proc };
|
|
@@ -27341,9 +27485,9 @@ async function probeCodeamPair(provider, workspace) {
|
|
|
27341
27485
|
}
|
|
27342
27486
|
async function stopWorkspaceFromLocal(target) {
|
|
27343
27487
|
if (target.provider.id === "github-codespaces") {
|
|
27344
|
-
const { execFile:
|
|
27488
|
+
const { execFile: execFile12 } = await import("child_process");
|
|
27345
27489
|
const { promisify: promisify11 } = await import("util");
|
|
27346
|
-
const execFileP10 = promisify11(
|
|
27490
|
+
const execFileP10 = promisify11(execFile12);
|
|
27347
27491
|
await execFileP10("gh", ["codespace", "stop", "-c", target.id], { maxBuffer: 8 * 1024 * 1024 });
|
|
27348
27492
|
return;
|
|
27349
27493
|
}
|
|
@@ -27567,7 +27711,7 @@ function checkChokidar() {
|
|
|
27567
27711
|
}
|
|
27568
27712
|
async function doctor(args2 = []) {
|
|
27569
27713
|
const json = args2.includes("--json");
|
|
27570
|
-
const cliVersion = true ? "2.39.
|
|
27714
|
+
const cliVersion = true ? "2.39.52" : "0.0.0-dev";
|
|
27571
27715
|
const apiBase2 = resolveApiBaseUrl();
|
|
27572
27716
|
const diagnosticId = (0, import_node_crypto8.randomUUID)();
|
|
27573
27717
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -27766,7 +27910,7 @@ async function completion(args2) {
|
|
|
27766
27910
|
// src/commands/version.ts
|
|
27767
27911
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
27768
27912
|
function version2() {
|
|
27769
|
-
const v = true ? "2.39.
|
|
27913
|
+
const v = true ? "2.39.52" : "unknown";
|
|
27770
27914
|
console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
|
|
27771
27915
|
}
|
|
27772
27916
|
|
|
@@ -28052,7 +28196,7 @@ function checkForUpdates() {
|
|
|
28052
28196
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
28053
28197
|
if (process.env.CI) return;
|
|
28054
28198
|
if (!process.stdout.isTTY) return;
|
|
28055
|
-
const current = true ? "2.39.
|
|
28199
|
+
const current = true ? "2.39.52" : null;
|
|
28056
28200
|
if (!current) return;
|
|
28057
28201
|
const cache = readCache();
|
|
28058
28202
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.39.
|
|
3
|
+
"version": "2.39.52",
|
|
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",
|