codeam-cli 2.76.30 → 2.76.32
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 +15 -0
- package/dist/index.js +97 -17
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ 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.30] — 2026-10-14
|
|
8
|
+
|
|
9
|
+
### CI
|
|
10
|
+
|
|
11
|
+
- **release:** Build the Box image only after npm serves the version
|
|
12
|
+
|
|
13
|
+
### Documentation
|
|
14
|
+
|
|
15
|
+
- **release:** Describe the next → canary → latest release gate in the README
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **cli:** Preview prewarm skips an empty scratch project
|
|
20
|
+
- **cli:** Identify with the current plan from /reconnect, not the pair-time one
|
|
21
|
+
|
|
7
22
|
## [2.76.29] — 2026-10-13
|
|
8
23
|
|
|
9
24
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -10627,7 +10627,7 @@ function readAnonId() {
|
|
|
10627
10627
|
}
|
|
10628
10628
|
function superProperties() {
|
|
10629
10629
|
return {
|
|
10630
|
-
cliVersion: true ? "2.76.
|
|
10630
|
+
cliVersion: true ? "2.76.32" : "0.0.0-dev",
|
|
10631
10631
|
nodeVersion: process.version,
|
|
10632
10632
|
platform: process.platform,
|
|
10633
10633
|
arch: process.arch,
|
|
@@ -10876,7 +10876,7 @@ var http = __toESM(require("http"));
|
|
|
10876
10876
|
// package.json
|
|
10877
10877
|
var package_default = {
|
|
10878
10878
|
name: "codeam-cli",
|
|
10879
|
-
version: "2.76.
|
|
10879
|
+
version: "2.76.32",
|
|
10880
10880
|
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
10881
|
type: "commonjs",
|
|
10882
10882
|
main: "dist/index.js",
|
|
@@ -11038,7 +11038,7 @@ async function requestCode(pluginId, pluginSecretHash) {
|
|
|
11038
11038
|
return { ok: false, reason: "network" };
|
|
11039
11039
|
}
|
|
11040
11040
|
}
|
|
11041
|
-
async function fetchCurrentPluginAuthToken(sessionId, pluginId, pollSecret, onUserId) {
|
|
11041
|
+
async function fetchCurrentPluginAuthToken(sessionId, pluginId, pollSecret, onUserId, onSessionGone) {
|
|
11042
11042
|
try {
|
|
11043
11043
|
const result = await _transport.postJson(
|
|
11044
11044
|
`${API_BASE}/api/pairing/reconnect`,
|
|
@@ -11070,10 +11070,21 @@ async function fetchCurrentPluginAuthToken(sessionId, pluginId, pollSecret, onUs
|
|
|
11070
11070
|
}
|
|
11071
11071
|
const token = data.pluginAuthToken;
|
|
11072
11072
|
return typeof token === "string" && token.length > 0 ? token : null;
|
|
11073
|
-
} catch {
|
|
11073
|
+
} catch (err) {
|
|
11074
|
+
if (onSessionGone && isSessionNotFound(err)) onSessionGone();
|
|
11074
11075
|
return null;
|
|
11075
11076
|
}
|
|
11076
11077
|
}
|
|
11078
|
+
function isSessionNotFound(err) {
|
|
11079
|
+
const e = err;
|
|
11080
|
+
if (!e || e.statusCode !== 404 || typeof e.body !== "string") return false;
|
|
11081
|
+
try {
|
|
11082
|
+
const parsed = JSON.parse(e.body);
|
|
11083
|
+
return parsed?.code === "SESSION_NOT_FOUND" || parsed?.error?.code === "SESSION_NOT_FOUND";
|
|
11084
|
+
} catch {
|
|
11085
|
+
return false;
|
|
11086
|
+
}
|
|
11087
|
+
}
|
|
11077
11088
|
var _transport = {
|
|
11078
11089
|
postJson: _postJson,
|
|
11079
11090
|
getJson: _getJson,
|
|
@@ -12241,7 +12252,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
12241
12252
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
12242
12253
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
12243
12254
|
// pair/reconnect). Older backends ignore the extra field.
|
|
12244
|
-
..."2.76.
|
|
12255
|
+
..."2.76.32" ? { ideVersion: "2.76.32" } : {}
|
|
12245
12256
|
};
|
|
12246
12257
|
try {
|
|
12247
12258
|
const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body, this.pollSecretHeader());
|
|
@@ -19489,7 +19500,7 @@ var CodexRuntimeStrategy = class {
|
|
|
19489
19500
|
async generateOneShot(prompt, opts) {
|
|
19490
19501
|
const binary = this.os.findInPath("codex");
|
|
19491
19502
|
if (!binary) return null;
|
|
19492
|
-
const launch = this.os.buildLaunch(binary, ["exec", prompt]);
|
|
19503
|
+
const launch = this.os.buildLaunch(binary, ["exec", "--skip-git-repo-check", prompt]);
|
|
19493
19504
|
const runOnce = () => {
|
|
19494
19505
|
let stderr = "";
|
|
19495
19506
|
return spawnAndCapture(launch.cmd, launch.args, {
|
|
@@ -27280,7 +27291,12 @@ async function startDevServer(ctx, extraEnv = {}) {
|
|
|
27280
27291
|
emitProgress("BOOT_SEQUENCE", `${spawnable.command} ${spawnable.args.join(" ")}`);
|
|
27281
27292
|
const devServer = (0, import_child_process20.spawn)(spawnable.command, spawnable.args, {
|
|
27282
27293
|
cwd,
|
|
27283
|
-
|
|
27294
|
+
// PYTHONUNBUFFERED: Python block-buffers stdout into a pipe, so a
|
|
27295
|
+
// `python3 -m http.server` (what the detector picks for a plain HTML site)
|
|
27296
|
+
// never shows its "Serving HTTP on … (http://0.0.0.0:8000/)" line while it
|
|
27297
|
+
// already serves — every such preview ended in a 120 s ready_timeout
|
|
27298
|
+
// (codeagent-nczt.9). First, so the project's own env can still override it.
|
|
27299
|
+
env: { PYTHONUNBUFFERED: "1", ...process.env, ...spawnable.env ?? {}, ...extraEnv },
|
|
27284
27300
|
stdio: ["ignore", "pipe", "pipe"],
|
|
27285
27301
|
// POSIX: lead a new process group so teardown can SIGTERM the whole
|
|
27286
27302
|
// tree. Dev servers fork worker children that bind the port; killing
|
|
@@ -38906,6 +38922,26 @@ function boundStreamingChunkContent(content, max = STREAMING_CHUNK_SNAPSHOT_MAX_
|
|
|
38906
38922
|
truncated: true
|
|
38907
38923
|
};
|
|
38908
38924
|
}
|
|
38925
|
+
var CHAT_TEXT_MAX_BYTES = 192 * 1024;
|
|
38926
|
+
var CHAT_TEXT_FALLBACK_MAX_BYTES = 32 * 1024;
|
|
38927
|
+
function boundChatText(content, maxBytes = CHAT_TEXT_MAX_BYTES) {
|
|
38928
|
+
const bytes = Buffer.from(content, "utf8");
|
|
38929
|
+
if (bytes.length <= maxBytes) return { content, truncated: false };
|
|
38930
|
+
const budget = Math.max(0, maxBytes - 256);
|
|
38931
|
+
const headBytes = Math.floor(budget * 0.4);
|
|
38932
|
+
const tailBytes = budget - headBytes;
|
|
38933
|
+
const head = bytes.subarray(0, headBytes).toString("utf8").replace(/\uFFFD+$/, "");
|
|
38934
|
+
const tail = bytes.subarray(bytes.length - tailBytes).toString("utf8").replace(/^\uFFFD+/, "");
|
|
38935
|
+
const omittedKb = Math.max(1, Math.round((bytes.length - headBytes - tailBytes) / 1024));
|
|
38936
|
+
return {
|
|
38937
|
+
content: `${head}
|
|
38938
|
+
|
|
38939
|
+
\u2026 [${omittedKb} KB of this reply left out \u2014 too long to show in full] \u2026
|
|
38940
|
+
|
|
38941
|
+
${tail}`,
|
|
38942
|
+
truncated: true
|
|
38943
|
+
};
|
|
38944
|
+
}
|
|
38909
38945
|
var AcpPublisher = class {
|
|
38910
38946
|
constructor(opts) {
|
|
38911
38947
|
this.opts = opts;
|
|
@@ -38924,6 +38960,10 @@ var AcpPublisher = class {
|
|
|
38924
38960
|
* (2026-06-28 incident: a dead-token publisher spammed 401 ×34 while
|
|
38925
38961
|
* the agent's replies silently never reached the phone). */
|
|
38926
38962
|
pairingInvalid = false;
|
|
38963
|
+
/** Set when the server refused a streaming `text` frame with 413: further
|
|
38964
|
+
* partial frames of the turn would be refused too, so they are dropped
|
|
38965
|
+
* until the next `new_turn` / `clear`. The terminal frame still goes. */
|
|
38966
|
+
oversizeTextTurn = false;
|
|
38927
38967
|
authHeaders() {
|
|
38928
38968
|
return {
|
|
38929
38969
|
"Content-Type": "application/json",
|
|
@@ -39005,8 +39045,29 @@ var AcpPublisher = class {
|
|
|
39005
39045
|
*/
|
|
39006
39046
|
async publishOutput(body) {
|
|
39007
39047
|
const url2 = `${this.apiBase}/api/commands/output`;
|
|
39008
|
-
|
|
39009
|
-
|
|
39048
|
+
const isText = body.type === "text" && typeof body.content === "string";
|
|
39049
|
+
const isFinal = body.done === true;
|
|
39050
|
+
if (body.type === "new_turn" || body.type === "clear") this.oversizeTextTurn = false;
|
|
39051
|
+
if (isText && !isFinal && this.oversizeTextTurn) return;
|
|
39052
|
+
if (isText) {
|
|
39053
|
+
const bounded = boundChatText(body.content);
|
|
39054
|
+
if (bounded.truncated) body = { ...body, content: bounded.content };
|
|
39055
|
+
}
|
|
39056
|
+
try {
|
|
39057
|
+
let { statusCode, body: resBody } = await this.postWithReauth(url2, this.envelope(body));
|
|
39058
|
+
if (statusCode === 413 && isText) {
|
|
39059
|
+
if (!isFinal) {
|
|
39060
|
+
this.oversizeTextTurn = true;
|
|
39061
|
+
log.warn("acpPublisher", "output text frame refused 413 \u2014 dropping partial frames until the turn ends");
|
|
39062
|
+
return;
|
|
39063
|
+
}
|
|
39064
|
+
const fallback = boundChatText(body.content, CHAT_TEXT_FALLBACK_MAX_BYTES);
|
|
39065
|
+
log.warn("acpPublisher", "output done:true refused 413 \u2014 retrying once with a shortened reply");
|
|
39066
|
+
({ statusCode, body: resBody } = await this.postWithReauth(
|
|
39067
|
+
url2,
|
|
39068
|
+
this.envelope({ ...body, content: fallback.content })
|
|
39069
|
+
));
|
|
39070
|
+
}
|
|
39010
39071
|
if (statusCode < 200 || statusCode >= 300) {
|
|
39011
39072
|
log.warn(
|
|
39012
39073
|
"acpPublisher",
|
|
@@ -42061,7 +42122,11 @@ function describeResult(raw) {
|
|
|
42061
42122
|
return {
|
|
42062
42123
|
// The agent once answered a failed spawn with "the preview is open, here
|
|
42063
42124
|
// is the link" (2026-10-05). Say plainly that nothing is serving.
|
|
42064
|
-
text: `Preview failed at ${String(r.stage)}: ${String(r.message)}. The preview is NOT running \u2014 do not give the user a preview link
|
|
42125
|
+
text: `Preview failed at ${String(r.stage)}: ${String(r.message).replace(/\.+$/, "")}. The preview is NOT running \u2014 do not give the user a preview link.` + (r.output ? ` The dev server's last output:
|
|
42126
|
+
\`\`\`
|
|
42127
|
+
${String(r.output)}
|
|
42128
|
+
\`\`\`
|
|
42129
|
+
If it shows a mistake in the command (a wrong flag, a missing file), call start_preview again with the fixed command; otherwise tell the user what failed.` : " Tell the user what failed."),
|
|
42065
42130
|
isError: true
|
|
42066
42131
|
};
|
|
42067
42132
|
default:
|
|
@@ -42394,6 +42459,7 @@ var MAX_AGENT_RESTARTS = 3;
|
|
|
42394
42459
|
var AGENT_RESTART_WINDOW_MS = 10 * 6e4;
|
|
42395
42460
|
var MAX_SELECTOR = 500;
|
|
42396
42461
|
var MAX_LABEL = 80;
|
|
42462
|
+
var AGENT_OUTPUT_TAIL = 2e3;
|
|
42397
42463
|
var AgentPreviewBridge = class {
|
|
42398
42464
|
server = null;
|
|
42399
42465
|
listening = null;
|
|
@@ -42599,9 +42665,11 @@ var AgentPreviewBridge = class {
|
|
|
42599
42665
|
origin: "agent",
|
|
42600
42666
|
onEvent: (type, payload) => {
|
|
42601
42667
|
if (type === USER_EVENTS.PREVIEW_ERROR) {
|
|
42668
|
+
const output = String(payload.stderrTail ?? "").trim().slice(-AGENT_OUTPUT_TAIL);
|
|
42602
42669
|
failure = {
|
|
42603
42670
|
stage: String(payload.stage ?? "spawn"),
|
|
42604
|
-
message: String(payload.message ?? "The preview failed to start.")
|
|
42671
|
+
message: String(payload.message ?? "The preview failed to start."),
|
|
42672
|
+
...output ? { output } : {}
|
|
42605
42673
|
};
|
|
42606
42674
|
}
|
|
42607
42675
|
}
|
|
@@ -47796,6 +47864,7 @@ async function start(requestedAgent, presetSession) {
|
|
|
47796
47864
|
const cwd = process.cwd();
|
|
47797
47865
|
let reconnectUserId;
|
|
47798
47866
|
let reconnectPlan;
|
|
47867
|
+
let sessionGone = false;
|
|
47799
47868
|
const refreshed = await fetchCurrentPluginAuthToken(
|
|
47800
47869
|
session.id,
|
|
47801
47870
|
pluginId,
|
|
@@ -47803,8 +47872,19 @@ async function start(requestedAgent, presetSession) {
|
|
|
47803
47872
|
(userId, plan) => {
|
|
47804
47873
|
reconnectUserId = userId;
|
|
47805
47874
|
reconnectPlan = plan;
|
|
47875
|
+
},
|
|
47876
|
+
() => {
|
|
47877
|
+
sessionGone = true;
|
|
47806
47878
|
}
|
|
47807
47879
|
);
|
|
47880
|
+
if (sessionGone) {
|
|
47881
|
+
showInfo("This session was deleted from the app \u2014 not resuming it.");
|
|
47882
|
+
try {
|
|
47883
|
+
removeSession(session.id);
|
|
47884
|
+
} catch {
|
|
47885
|
+
}
|
|
47886
|
+
process.exit(0);
|
|
47887
|
+
}
|
|
47808
47888
|
if (refreshed && refreshed !== session.pluginAuthToken) {
|
|
47809
47889
|
addSession({ ...session, pluginAuthToken: refreshed });
|
|
47810
47890
|
session.pluginAuthToken = refreshed;
|
|
@@ -51421,7 +51501,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
51421
51501
|
if (process.env.NODE_ENV === "test") return;
|
|
51422
51502
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
51423
51503
|
if (process.env.CI) return;
|
|
51424
|
-
const current2 = true ? "2.76.
|
|
51504
|
+
const current2 = true ? "2.76.32" : null;
|
|
51425
51505
|
if (!current2) return;
|
|
51426
51506
|
const cache = readCache();
|
|
51427
51507
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -51438,7 +51518,7 @@ function checkForUpdates() {
|
|
|
51438
51518
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
51439
51519
|
if (process.env.CI) return;
|
|
51440
51520
|
if (!process.stdout.isTTY) return;
|
|
51441
|
-
const current2 = true ? "2.76.
|
|
51521
|
+
const current2 = true ? "2.76.32" : null;
|
|
51442
51522
|
if (!current2) return;
|
|
51443
51523
|
const cache = readCache();
|
|
51444
51524
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -51721,7 +51801,7 @@ var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
|
|
|
51721
51801
|
var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
51722
51802
|
var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
|
|
51723
51803
|
function currentCliVersion() {
|
|
51724
|
-
return true ? "2.76.
|
|
51804
|
+
return true ? "2.76.32" : null;
|
|
51725
51805
|
}
|
|
51726
51806
|
async function installedVersion(deps) {
|
|
51727
51807
|
const ls = await deps.run(
|
|
@@ -51932,7 +52012,7 @@ function signalHostReady(send = process.send?.bind(process)) {
|
|
|
51932
52012
|
}
|
|
51933
52013
|
function defaultDeps() {
|
|
51934
52014
|
return {
|
|
51935
|
-
ownVersion: true ? "2.76.
|
|
52015
|
+
ownVersion: true ? "2.76.32" : null,
|
|
51936
52016
|
root: cliVersionsRoot(),
|
|
51937
52017
|
args: process.argv.slice(3),
|
|
51938
52018
|
execPath: process.execPath,
|
|
@@ -54470,7 +54550,7 @@ function checkChokidar() {
|
|
|
54470
54550
|
}
|
|
54471
54551
|
async function doctor(args2 = []) {
|
|
54472
54552
|
const json = args2.includes("--json");
|
|
54473
|
-
const cliVersion = true ? "2.76.
|
|
54553
|
+
const cliVersion = true ? "2.76.32" : "0.0.0-dev";
|
|
54474
54554
|
const apiBase2 = resolveApiBaseUrl();
|
|
54475
54555
|
const diagnosticId = (0, import_node_crypto17.randomUUID)();
|
|
54476
54556
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -55006,7 +55086,7 @@ async function mcpWarm(argv = [], opts = {}) {
|
|
|
55006
55086
|
// src/commands/version.ts
|
|
55007
55087
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
55008
55088
|
function version2() {
|
|
55009
|
-
const v = true ? "2.76.
|
|
55089
|
+
const v = true ? "2.76.32" : "unknown";
|
|
55010
55090
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
55011
55091
|
}
|
|
55012
55092
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.76.
|
|
3
|
+
"version": "2.76.32",
|
|
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",
|