codeam-cli 2.74.1 → 2.74.3
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 +83 -31
- 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.74.2] — 2026-09-04
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** A "no dev server" detection is a verdict, not a malformed answer (#704)
|
|
12
|
+
|
|
13
|
+
## [2.74.1] — 2026-09-04
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **cli:** Npx integration servers start with npm audit off; bound mcp-warm; 25-min fleet-box step (#701)
|
|
18
|
+
|
|
7
19
|
## [2.74.0] — 2026-09-03
|
|
8
20
|
|
|
9
21
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -8136,7 +8136,7 @@ function readAnonId() {
|
|
|
8136
8136
|
}
|
|
8137
8137
|
function superProperties() {
|
|
8138
8138
|
return {
|
|
8139
|
-
cliVersion: true ? "2.74.
|
|
8139
|
+
cliVersion: true ? "2.74.3" : "0.0.0-dev",
|
|
8140
8140
|
nodeVersion: process.version,
|
|
8141
8141
|
platform: process.platform,
|
|
8142
8142
|
arch: process.arch,
|
|
@@ -8378,7 +8378,7 @@ var http = __toESM(require("http"));
|
|
|
8378
8378
|
// package.json
|
|
8379
8379
|
var package_default = {
|
|
8380
8380
|
name: "codeam-cli",
|
|
8381
|
-
version: "2.74.
|
|
8381
|
+
version: "2.74.3",
|
|
8382
8382
|
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.",
|
|
8383
8383
|
type: "commonjs",
|
|
8384
8384
|
main: "dist/index.js",
|
|
@@ -9971,7 +9971,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9971
9971
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
9972
9972
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
9973
9973
|
// pair/reconnect). Older backends ignore the extra field.
|
|
9974
|
-
..."2.74.
|
|
9974
|
+
..."2.74.3" ? { ideVersion: "2.74.3" } : {}
|
|
9975
9975
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
9976
9976
|
}
|
|
9977
9977
|
/**
|
|
@@ -22272,7 +22272,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
22272
22272
|
if (process.env.NODE_ENV === "test") return;
|
|
22273
22273
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22274
22274
|
if (process.env.CI) return;
|
|
22275
|
-
const current2 = true ? "2.74.
|
|
22275
|
+
const current2 = true ? "2.74.3" : null;
|
|
22276
22276
|
if (!current2) return;
|
|
22277
22277
|
const cache = readCache();
|
|
22278
22278
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22289,7 +22289,7 @@ function checkForUpdates() {
|
|
|
22289
22289
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22290
22290
|
if (process.env.CI) return;
|
|
22291
22291
|
if (!process.stdout.isTTY) return;
|
|
22292
|
-
const current2 = true ? "2.74.
|
|
22292
|
+
const current2 = true ? "2.74.3" : null;
|
|
22293
22293
|
if (!current2) return;
|
|
22294
22294
|
const cache = readCache();
|
|
22295
22295
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22312,7 +22312,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
|
22312
22312
|
var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
|
|
22313
22313
|
var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
|
|
22314
22314
|
function currentCliVersion() {
|
|
22315
|
-
return true ? "2.74.
|
|
22315
|
+
return true ? "2.74.3" : null;
|
|
22316
22316
|
}
|
|
22317
22317
|
async function installedVersion(deps) {
|
|
22318
22318
|
const ls = await deps.run(
|
|
@@ -24956,11 +24956,23 @@ function safeParseDetection(raw) {
|
|
|
24956
24956
|
if (!raw) return null;
|
|
24957
24957
|
const parsed = parseDetectionObject(raw);
|
|
24958
24958
|
if (!parsed) return null;
|
|
24959
|
+
if (isUnsupportedAnswer(parsed)) {
|
|
24960
|
+
return {
|
|
24961
|
+
framework: "unsupported",
|
|
24962
|
+
notes: typeof parsed.notes === "string" ? parsed.notes : void 0
|
|
24963
|
+
};
|
|
24964
|
+
}
|
|
24959
24965
|
for (const field of REQUIRED_FIELDS2) {
|
|
24960
24966
|
if (!(field in parsed)) return null;
|
|
24961
24967
|
}
|
|
24962
24968
|
return parsed;
|
|
24963
24969
|
}
|
|
24970
|
+
function isUnsupportedAnswer(parsed) {
|
|
24971
|
+
return parsed.framework === "unsupported";
|
|
24972
|
+
}
|
|
24973
|
+
function isUnsupportedDetection(d3) {
|
|
24974
|
+
return d3.framework === "unsupported";
|
|
24975
|
+
}
|
|
24964
24976
|
function parseDetectionObject(raw) {
|
|
24965
24977
|
let parsed = tryParseObject(raw.trim());
|
|
24966
24978
|
if (!parsed) {
|
|
@@ -25044,6 +25056,7 @@ function describeDetectionFailure(raw) {
|
|
|
25044
25056
|
rawExcerpt
|
|
25045
25057
|
};
|
|
25046
25058
|
}
|
|
25059
|
+
if (isUnsupportedAnswer(parsed)) return null;
|
|
25047
25060
|
const missing = REQUIRED_FIELDS2.filter((f) => !(f in parsed)).map(String);
|
|
25048
25061
|
if (missing.length > 0) {
|
|
25049
25062
|
return {
|
|
@@ -25507,6 +25520,25 @@ async function ensureYarnInstalled(deps) {
|
|
|
25507
25520
|
if (!res.ok) return res;
|
|
25508
25521
|
return await deps.hasYarn() ? { ok: true, code: 0 } : { ok: false, code: res.code };
|
|
25509
25522
|
}
|
|
25523
|
+
function isExpoTunnelCommand(command2, args2) {
|
|
25524
|
+
const flat = [import_path8.default.basename(command2), ...args2];
|
|
25525
|
+
return flat.includes("expo") && flat.includes("--tunnel");
|
|
25526
|
+
}
|
|
25527
|
+
var EXPO_NGROK_SPEC = "@expo/ngrok@^4.1.0";
|
|
25528
|
+
async function ensureExpoTunnelDeps(deps) {
|
|
25529
|
+
if (await deps.hasNgrok()) return { ok: true, code: 0, installed: false };
|
|
25530
|
+
const res = await deps.installNgrok();
|
|
25531
|
+
if (!res.ok) return { ...res, installed: false };
|
|
25532
|
+
return await deps.hasNgrok() ? { ok: true, code: 0, installed: true } : { ok: false, code: res.code, installed: false };
|
|
25533
|
+
}
|
|
25534
|
+
function ngrokResolvesFrom(cwd) {
|
|
25535
|
+
try {
|
|
25536
|
+
require.resolve("@expo/ngrok/package.json", { paths: [cwd] });
|
|
25537
|
+
return true;
|
|
25538
|
+
} catch {
|
|
25539
|
+
return false;
|
|
25540
|
+
}
|
|
25541
|
+
}
|
|
25510
25542
|
function isJsInstallCommand(cmd, args2) {
|
|
25511
25543
|
const known = ["npm", "pnpm", "yarn", "bun"];
|
|
25512
25544
|
if (!known.includes(cmd)) return false;
|
|
@@ -26383,10 +26415,7 @@ async function runPreviewStart(args2) {
|
|
|
26383
26415
|
};
|
|
26384
26416
|
const existing = activePreviews.get(sessionId);
|
|
26385
26417
|
if (existing && existing.devServer?.exitCode !== 0 && existing.devServer?.exitCode == null) {
|
|
26386
|
-
log.info(
|
|
26387
|
-
"preview",
|
|
26388
|
-
`reusing running preview for session=${sessionId} url=${existing.url}`
|
|
26389
|
-
);
|
|
26418
|
+
log.info("preview", `reusing running preview for session=${sessionId} url=${existing.url}`);
|
|
26390
26419
|
ctx.emitProgress("READY_DETECTED", "reusing running preview");
|
|
26391
26420
|
emit2(USER_EVENTS.PREVIEW_READY, {
|
|
26392
26421
|
url: existing.url,
|
|
@@ -26435,6 +26464,7 @@ async function provisionDeps(ctx) {
|
|
|
26435
26464
|
pluginAuthToken: ctx.projectEnvAuth.pluginAuthToken
|
|
26436
26465
|
});
|
|
26437
26466
|
}
|
|
26467
|
+
const needsExpoNgrok = isExpoTunnelCommand(detection.command, detection.args ?? []);
|
|
26438
26468
|
const missingDeps = detectMissingNodeDeps(cwd);
|
|
26439
26469
|
let preflightRan = false;
|
|
26440
26470
|
if (missingDeps) {
|
|
@@ -26488,6 +26518,32 @@ async function provisionDeps(ctx) {
|
|
|
26488
26518
|
}
|
|
26489
26519
|
preflightRan = true;
|
|
26490
26520
|
}
|
|
26521
|
+
if (needsExpoNgrok) {
|
|
26522
|
+
const ensured = await ensureExpoTunnelDeps({
|
|
26523
|
+
hasNgrok: async () => ngrokResolvesFrom(cwd),
|
|
26524
|
+
installNgrok: async () => {
|
|
26525
|
+
emitProgress(
|
|
26526
|
+
"SETUP_RUN",
|
|
26527
|
+
`npm install --no-save ${EXPO_NGROK_SPEC} (Expo tunnel needs ngrok; not found)`
|
|
26528
|
+
);
|
|
26529
|
+
const r = await runSetupCommand(
|
|
26530
|
+
"npm",
|
|
26531
|
+
["install", "--no-save", "--no-audit", "--no-fund", EXPO_NGROK_SPEC],
|
|
26532
|
+
cwd,
|
|
26533
|
+
detection.env,
|
|
26534
|
+
{ timeoutMs: INSTALL_TIMEOUT_MS2 }
|
|
26535
|
+
);
|
|
26536
|
+
return { ok: r.status === "ok", code: r.code };
|
|
26537
|
+
}
|
|
26538
|
+
});
|
|
26539
|
+
if (!ensured.ok) {
|
|
26540
|
+
emit2(USER_EVENTS.PREVIEW_ERROR, {
|
|
26541
|
+
stage: "spawn",
|
|
26542
|
+
message: `Expo's tunnel needs @expo/ngrok and installing it automatically failed (npm install --no-save ${EXPO_NGROK_SPEC}, exit ${ensured.code}). Install it in the project and try the preview again.`
|
|
26543
|
+
});
|
|
26544
|
+
return false;
|
|
26545
|
+
}
|
|
26546
|
+
}
|
|
26491
26547
|
const normalizedSetup = (detection.setup_commands ?? []).map((entry) => {
|
|
26492
26548
|
if (typeof entry === "string") {
|
|
26493
26549
|
const parts = entry.trim().split(/\s+/).filter(Boolean);
|
|
@@ -26503,13 +26559,9 @@ async function provisionDeps(ctx) {
|
|
|
26503
26559
|
for (const setup of agentSetupCommands) {
|
|
26504
26560
|
emitProgress("SETUP_RUN", `${setup.cmd} ${setup.args.join(" ")}`);
|
|
26505
26561
|
const timeoutMs = isJsInstallCommand(setup.cmd, setup.args) ? INSTALL_TIMEOUT_MS2 : SETUP_TIMEOUT_MS;
|
|
26506
|
-
const result = await runSetupCommand(
|
|
26507
|
-
|
|
26508
|
-
|
|
26509
|
-
cwd,
|
|
26510
|
-
detection.env,
|
|
26511
|
-
{ timeoutMs }
|
|
26512
|
-
);
|
|
26562
|
+
const result = await runSetupCommand(setup.cmd, setup.args, cwd, detection.env, {
|
|
26563
|
+
timeoutMs
|
|
26564
|
+
});
|
|
26513
26565
|
if (result.status === "timeout") {
|
|
26514
26566
|
emit2(USER_EVENTS.PREVIEW_ERROR, {
|
|
26515
26567
|
stage: "ready_timeout",
|
|
@@ -26577,10 +26629,7 @@ async function startDevServer(ctx) {
|
|
|
26577
26629
|
} else {
|
|
26578
26630
|
const verdict = await canAdoptPort(detection.port);
|
|
26579
26631
|
if (verdict.adopt) {
|
|
26580
|
-
log.info(
|
|
26581
|
-
"preview",
|
|
26582
|
-
`adopting the dev server already serving on port ${detection.port}`
|
|
26583
|
-
);
|
|
26632
|
+
log.info("preview", `adopting the dev server already serving on port ${detection.port}`);
|
|
26584
26633
|
emitProgress("READY_DETECTED", `adopted the server already on port ${detection.port}`);
|
|
26585
26634
|
return { devServer: null, expoUrlRef: { current: null } };
|
|
26586
26635
|
}
|
|
@@ -26731,11 +26780,9 @@ async function establishTunnel(ctx, dev) {
|
|
|
26731
26780
|
`cloudflared quick tunnel (retry ${attempt}/${MAX_TUNNEL_ATTEMPTS})`
|
|
26732
26781
|
);
|
|
26733
26782
|
}
|
|
26734
|
-
const candidate = (0, import_child_process23.spawn)(
|
|
26735
|
-
|
|
26736
|
-
|
|
26737
|
-
{ stdio: ["ignore", "pipe", "pipe"] }
|
|
26738
|
-
);
|
|
26783
|
+
const candidate = (0, import_child_process23.spawn)(bin, ["tunnel", "--url", `http://localhost:${tunnelPort}`], {
|
|
26784
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
26785
|
+
});
|
|
26739
26786
|
const outcome = await awaitTunnelRegistered(
|
|
26740
26787
|
candidate,
|
|
26741
26788
|
null,
|
|
@@ -29462,7 +29509,7 @@ ${failure?.rawExcerpt ?? ""}`
|
|
|
29462
29509
|
});
|
|
29463
29510
|
return;
|
|
29464
29511
|
}
|
|
29465
|
-
if (detection
|
|
29512
|
+
if (isUnsupportedDetection(detection)) {
|
|
29466
29513
|
log.info("preview", "detect: framework=unsupported");
|
|
29467
29514
|
emitPreviewEvent({
|
|
29468
29515
|
sessionId: ctx.sessionId,
|
|
@@ -29530,7 +29577,7 @@ function prewarmPreviewDetection(runtime) {
|
|
|
29530
29577
|
log.info("preview", `prewarm: detection failed reason=${failure?.reason ?? "unknown"}`);
|
|
29531
29578
|
return;
|
|
29532
29579
|
}
|
|
29533
|
-
if (detection
|
|
29580
|
+
if (isUnsupportedDetection(detection)) return;
|
|
29534
29581
|
await writePreviewConfig(cwd, detection);
|
|
29535
29582
|
log.info(
|
|
29536
29583
|
"preview",
|
|
@@ -47437,7 +47484,7 @@ function checkChokidar() {
|
|
|
47437
47484
|
}
|
|
47438
47485
|
async function doctor(args2 = []) {
|
|
47439
47486
|
const json = args2.includes("--json");
|
|
47440
|
-
const cliVersion = true ? "2.74.
|
|
47487
|
+
const cliVersion = true ? "2.74.3" : "0.0.0-dev";
|
|
47441
47488
|
const apiBase2 = resolveApiBaseUrl();
|
|
47442
47489
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
47443
47490
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -47884,7 +47931,12 @@ function runCapturing(command2, args2, timeoutMs) {
|
|
|
47884
47931
|
return new Promise((resolve11) => {
|
|
47885
47932
|
const child = (0, import_node_child_process35.spawn)(command2, args2, {
|
|
47886
47933
|
env: { ...process.env, ...LAUNCHER_ENV },
|
|
47887
|
-
stdio: ["ignore", "ignore", "pipe"]
|
|
47934
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
47935
|
+
// `npx`/`uv` are `.cmd` shims on Windows; Node ≥ 20.12 refuses to spawn a
|
|
47936
|
+
// `.cmd` without a shell and a bare `npx` is ENOENT — the same reason the
|
|
47937
|
+
// licence audit test needed it. The launchers here are ours, never user
|
|
47938
|
+
// input, so the shell is safe.
|
|
47939
|
+
shell: process.platform === "win32"
|
|
47888
47940
|
});
|
|
47889
47941
|
let stderr = "";
|
|
47890
47942
|
child.stderr?.setEncoding("utf8");
|
|
@@ -47956,7 +48008,7 @@ async function mcpWarm(argv = [], opts = {}) {
|
|
|
47956
48008
|
// src/commands/version.ts
|
|
47957
48009
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
47958
48010
|
function version2() {
|
|
47959
|
-
const v = true ? "2.74.
|
|
48011
|
+
const v = true ? "2.74.3" : "unknown";
|
|
47960
48012
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
47961
48013
|
}
|
|
47962
48014
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.74.
|
|
3
|
+
"version": "2.74.3",
|
|
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",
|