codeam-cli 2.60.4 → 2.60.6
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 +103 -20
- 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.60.4] — 2026-07-08
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** Serialize baton state POSTs so handback never sticks on "Switching…"
|
|
12
|
+
|
|
13
|
+
## [2.60.3] — 2026-07-08
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **cli:** Clean up terminal + mobile activity on baton Take Control
|
|
18
|
+
|
|
7
19
|
## [2.60.2] — 2026-07-08
|
|
8
20
|
|
|
9
21
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -5653,7 +5653,7 @@ function readAnonId() {
|
|
|
5653
5653
|
}
|
|
5654
5654
|
function superProperties() {
|
|
5655
5655
|
return {
|
|
5656
|
-
cliVersion: true ? "2.60.
|
|
5656
|
+
cliVersion: true ? "2.60.6" : "0.0.0-dev",
|
|
5657
5657
|
nodeVersion: process.version,
|
|
5658
5658
|
platform: process.platform,
|
|
5659
5659
|
arch: process.arch,
|
|
@@ -5834,7 +5834,7 @@ var os4 = __toESM(require("os"));
|
|
|
5834
5834
|
// package.json
|
|
5835
5835
|
var package_default = {
|
|
5836
5836
|
name: "codeam-cli",
|
|
5837
|
-
version: "2.60.
|
|
5837
|
+
version: "2.60.6",
|
|
5838
5838
|
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.",
|
|
5839
5839
|
type: "commonjs",
|
|
5840
5840
|
main: "dist/index.js",
|
|
@@ -6905,7 +6905,7 @@ var CommandRelayService = class {
|
|
|
6905
6905
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
6906
6906
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
6907
6907
|
// pair/reconnect). Older backends ignore the extra field.
|
|
6908
|
-
..."2.60.
|
|
6908
|
+
..."2.60.6" ? { ideVersion: "2.60.6" } : {}
|
|
6909
6909
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
6910
6910
|
}
|
|
6911
6911
|
/**
|
|
@@ -13399,13 +13399,71 @@ var fs23 = __toESM(require("fs"));
|
|
|
13399
13399
|
var os21 = __toESM(require("os"));
|
|
13400
13400
|
var path27 = __toESM(require("path"));
|
|
13401
13401
|
var HISTORY_ROOT = path27.join(os21.homedir(), ".cursor", "projects");
|
|
13402
|
+
function encodeCursorCwd(cwd) {
|
|
13403
|
+
return cwd.replace(/^[/\\]+/, "").replace(/[/\\:]/g, "-");
|
|
13404
|
+
}
|
|
13405
|
+
function resolveHistoryFile3(cwd, sessionId, root = HISTORY_ROOT) {
|
|
13406
|
+
const rel = path27.join("agent-transcripts", sessionId, `${sessionId}.jsonl`);
|
|
13407
|
+
const primary = path27.join(root, encodeCursorCwd(cwd), rel);
|
|
13408
|
+
if (fs23.existsSync(primary)) return primary;
|
|
13409
|
+
let entries;
|
|
13410
|
+
try {
|
|
13411
|
+
entries = fs23.readdirSync(root, { withFileTypes: true });
|
|
13412
|
+
} catch {
|
|
13413
|
+
return null;
|
|
13414
|
+
}
|
|
13415
|
+
for (const e of entries) {
|
|
13416
|
+
if (!e.isDirectory()) continue;
|
|
13417
|
+
const candidate = path27.join(root, e.name, rel);
|
|
13418
|
+
if (fs23.existsSync(candidate)) return candidate;
|
|
13419
|
+
}
|
|
13420
|
+
return null;
|
|
13421
|
+
}
|
|
13402
13422
|
function resolveHistoryDir3(cwd) {
|
|
13403
13423
|
if (!fs23.existsSync(HISTORY_ROOT)) return null;
|
|
13404
|
-
|
|
13405
|
-
return
|
|
13424
|
+
const dir = path27.join(HISTORY_ROOT, encodeCursorCwd(cwd));
|
|
13425
|
+
return fs23.existsSync(dir) ? dir : null;
|
|
13406
13426
|
}
|
|
13407
|
-
function
|
|
13408
|
-
return
|
|
13427
|
+
function extractText2(content) {
|
|
13428
|
+
if (!Array.isArray(content)) return "";
|
|
13429
|
+
return content.filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("");
|
|
13430
|
+
}
|
|
13431
|
+
function unwrapUserQuery(text) {
|
|
13432
|
+
const m = text.match(/<user_query>\s*([\s\S]*?)\s*<\/user_query>/);
|
|
13433
|
+
return (m ? m[1] : text).trim();
|
|
13434
|
+
}
|
|
13435
|
+
function parseHistoryFile3(filePath) {
|
|
13436
|
+
let raw;
|
|
13437
|
+
try {
|
|
13438
|
+
raw = fs23.readFileSync(filePath, "utf8");
|
|
13439
|
+
} catch {
|
|
13440
|
+
return [];
|
|
13441
|
+
}
|
|
13442
|
+
const out2 = [];
|
|
13443
|
+
let idx = 0;
|
|
13444
|
+
for (const line of raw.split("\n")) {
|
|
13445
|
+
if (!line.trim()) continue;
|
|
13446
|
+
let rec;
|
|
13447
|
+
try {
|
|
13448
|
+
rec = JSON.parse(line);
|
|
13449
|
+
} catch {
|
|
13450
|
+
continue;
|
|
13451
|
+
}
|
|
13452
|
+
if (rec.role !== "user" && rec.role !== "assistant") continue;
|
|
13453
|
+
const blockText = extractText2(rec.message?.content);
|
|
13454
|
+
const text = rec.role === "user" ? unwrapUserQuery(blockText) : blockText.trim();
|
|
13455
|
+
if (!text) continue;
|
|
13456
|
+
out2.push({
|
|
13457
|
+
id: `cursor:${idx}`,
|
|
13458
|
+
role: rec.role === "user" ? "user" : "agent",
|
|
13459
|
+
text,
|
|
13460
|
+
// Cursor records carry no per-message timestamp; order is preserved by
|
|
13461
|
+
// file position, so a stable epoch keeps the shape valid without lying.
|
|
13462
|
+
timestamp: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
13463
|
+
});
|
|
13464
|
+
idx += 1;
|
|
13465
|
+
}
|
|
13466
|
+
return out2;
|
|
13409
13467
|
}
|
|
13410
13468
|
function getCurrentUsage3(_historyDir) {
|
|
13411
13469
|
return null;
|
|
@@ -13527,6 +13585,13 @@ var CursorRuntimeStrategy = class {
|
|
|
13527
13585
|
resolveHistoryDir(cwd) {
|
|
13528
13586
|
return resolveHistoryDir3(cwd);
|
|
13529
13587
|
}
|
|
13588
|
+
/** Session transcript at
|
|
13589
|
+
* `~/.cursor/projects/<encoded-cwd>/agent-transcripts/<id>/<id>.jsonl`.
|
|
13590
|
+
* Presence of this method is what lets the baton engage for Cursor
|
|
13591
|
+
* (`runtimeSupportsBaton`), so the LOCAL_DRIVE mirror can tail it. */
|
|
13592
|
+
resolveHistoryFile(cwd, sessionId) {
|
|
13593
|
+
return resolveHistoryFile3(cwd, sessionId);
|
|
13594
|
+
}
|
|
13530
13595
|
parseHistoryFile(filePath) {
|
|
13531
13596
|
return parseHistoryFile3(filePath);
|
|
13532
13597
|
}
|
|
@@ -15921,7 +15986,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
15921
15986
|
if (process.env.NODE_ENV === "test") return;
|
|
15922
15987
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
15923
15988
|
if (process.env.CI) return;
|
|
15924
|
-
const current = true ? "2.60.
|
|
15989
|
+
const current = true ? "2.60.6" : null;
|
|
15925
15990
|
if (!current) return;
|
|
15926
15991
|
const cache = readCache();
|
|
15927
15992
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -15938,7 +16003,7 @@ function checkForUpdates() {
|
|
|
15938
16003
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
15939
16004
|
if (process.env.CI) return;
|
|
15940
16005
|
if (!process.stdout.isTTY) return;
|
|
15941
|
-
const current = true ? "2.60.
|
|
16006
|
+
const current = true ? "2.60.6" : null;
|
|
15942
16007
|
if (!current) return;
|
|
15943
16008
|
const cache = readCache();
|
|
15944
16009
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -15958,7 +16023,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
|
15958
16023
|
var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
|
|
15959
16024
|
var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
15960
16025
|
function currentCliVersion() {
|
|
15961
|
-
return true ? "2.60.
|
|
16026
|
+
return true ? "2.60.6" : null;
|
|
15962
16027
|
}
|
|
15963
16028
|
function runCmd(cmd, args2, timeoutMs) {
|
|
15964
16029
|
return new Promise((resolve7) => {
|
|
@@ -21728,7 +21793,7 @@ var historyRecordSchema = import_zod2.z.object({
|
|
|
21728
21793
|
}).passthrough().optional()
|
|
21729
21794
|
}).passthrough();
|
|
21730
21795
|
var API_BASE8 = resolveApiBaseUrl();
|
|
21731
|
-
function
|
|
21796
|
+
function extractText3(content) {
|
|
21732
21797
|
if (typeof content === "string") return content;
|
|
21733
21798
|
if (Array.isArray(content)) {
|
|
21734
21799
|
return content.filter((b) => b["type"] === "text").map((b) => b["text"]).join("\n");
|
|
@@ -21767,10 +21832,10 @@ function parseJsonl(filePath) {
|
|
|
21767
21832
|
const uuid = record.uuid ?? `${Date.now()}-${Math.random()}`;
|
|
21768
21833
|
const msg = record.message;
|
|
21769
21834
|
if (record.type === "user" && msg) {
|
|
21770
|
-
const text =
|
|
21835
|
+
const text = extractText3(msg.content).trim();
|
|
21771
21836
|
if (text) messages.push({ id: uuid, role: "user", text, timestamp });
|
|
21772
21837
|
} else if (record.type === "assistant" && msg) {
|
|
21773
|
-
const text =
|
|
21838
|
+
const text = extractText3(msg.content).trim();
|
|
21774
21839
|
if (text) messages.push({ id: uuid, role: "agent", text, timestamp });
|
|
21775
21840
|
}
|
|
21776
21841
|
}
|
|
@@ -24815,6 +24880,12 @@ var AcpClient = class {
|
|
|
24815
24880
|
* (the agent is demonstrably working, just blocked on the tool), and
|
|
24816
24881
|
* re-arm only once the last one finishes. Reset per prompt. */
|
|
24817
24882
|
pendingToolCalls = /* @__PURE__ */ new Set();
|
|
24883
|
+
/** Serializes {@link prompt}: each turn waits for the previous to fully
|
|
24884
|
+
* settle before arming its own watchdog. `promptIdle`/`pendingToolCalls`
|
|
24885
|
+
* are single-instance fields, so a 2nd concurrent prompt() would overwrite
|
|
24886
|
+
* them mid-turn and break turn A's idle watchdog (mobile "send-while-active"
|
|
24887
|
+
* can fire two prompts back-to-back). See {@link prompt}. */
|
|
24888
|
+
promptChain = Promise.resolve();
|
|
24818
24889
|
/** Last few adapter stderr lines — so a startup failure surfaces the REAL
|
|
24819
24890
|
* cause (e.g. gemini's `IneligibleTierError`) instead of a bare timeout. */
|
|
24820
24891
|
recentStderr = [];
|
|
@@ -24969,6 +25040,14 @@ var AcpClient = class {
|
|
|
24969
25040
|
* shows a permanent "Thinking…" spinner with no way to recover.
|
|
24970
25041
|
*/
|
|
24971
25042
|
async prompt(input) {
|
|
25043
|
+
const run = this.promptChain.then(() => this.runPrompt(input));
|
|
25044
|
+
this.promptChain = run.then(
|
|
25045
|
+
() => void 0,
|
|
25046
|
+
() => void 0
|
|
25047
|
+
);
|
|
25048
|
+
return run;
|
|
25049
|
+
}
|
|
25050
|
+
async runPrompt(input) {
|
|
24972
25051
|
if (!this.connection || !this.sessionId) {
|
|
24973
25052
|
throw new Error("AcpClient.prompt called before start()");
|
|
24974
25053
|
}
|
|
@@ -25853,12 +25932,12 @@ function mapSessionUpdate(notification) {
|
|
|
25853
25932
|
const update = notification.update;
|
|
25854
25933
|
switch (update.sessionUpdate) {
|
|
25855
25934
|
case "agent_message_chunk": {
|
|
25856
|
-
const text =
|
|
25935
|
+
const text = extractText4(update.content);
|
|
25857
25936
|
if (!text) return [];
|
|
25858
25937
|
return [{ chunkId: messageChunkId(update.messageId), kind: "text", delta: text }];
|
|
25859
25938
|
}
|
|
25860
25939
|
case "agent_thought_chunk": {
|
|
25861
|
-
const text =
|
|
25940
|
+
const text = extractText4(update.content);
|
|
25862
25941
|
if (!text) return [];
|
|
25863
25942
|
return [
|
|
25864
25943
|
{
|
|
@@ -25923,7 +26002,7 @@ function messageChunkId(messageId) {
|
|
|
25923
26002
|
if (typeof messageId === "string" && messageId.length > 0) return messageId;
|
|
25924
26003
|
return (0, import_node_crypto6.randomUUID)();
|
|
25925
26004
|
}
|
|
25926
|
-
function
|
|
26005
|
+
function extractText4(content) {
|
|
25927
26006
|
if (!content || typeof content !== "object") return null;
|
|
25928
26007
|
if ("type" in content && content.type === "text") {
|
|
25929
26008
|
const t2 = content.text;
|
|
@@ -25953,7 +26032,7 @@ function describeToolCallUpdate(update) {
|
|
|
25953
26032
|
for (const item of update.content) {
|
|
25954
26033
|
if (!item || typeof item !== "object") continue;
|
|
25955
26034
|
if (item.type === "content" && item.content) {
|
|
25956
|
-
const text =
|
|
26035
|
+
const text = extractText4(item.content);
|
|
25957
26036
|
if (text) parts.push(text);
|
|
25958
26037
|
} else if (item.type === "diff") {
|
|
25959
26038
|
const p2 = item.path;
|
|
@@ -29131,6 +29210,9 @@ function fetchQuotaUsage(runtime, historySvc) {
|
|
|
29131
29210
|
}
|
|
29132
29211
|
|
|
29133
29212
|
// src/baton/gate.ts
|
|
29213
|
+
function runtimeSupportsBaton(runtime) {
|
|
29214
|
+
return typeof runtime.resolveHistoryFile === "function";
|
|
29215
|
+
}
|
|
29134
29216
|
function isLocalSession(env = process.env) {
|
|
29135
29217
|
return env.CODESPACES !== "true" && env.CODEAM_AUTO_APPROVE !== "1" && env.HEADROOM_ENABLED !== "1" && !env.CODEAM_AUTO_TOKEN && !env.CODEAM_ENROLL_TOKEN;
|
|
29136
29218
|
}
|
|
@@ -29911,7 +29993,8 @@ async function start(requestedAgent) {
|
|
|
29911
29993
|
}
|
|
29912
29994
|
if (isLocalSession() && requiresAcp(session.agent)) {
|
|
29913
29995
|
const adapter = getAcpAdapter(session.agent);
|
|
29914
|
-
|
|
29996
|
+
const batonCapable = runtimeSupportsBaton(createRuntimeStrategy(session.agent));
|
|
29997
|
+
if (batonCapable && adapter && session.pluginAuthToken) {
|
|
29915
29998
|
await runBatonSession({
|
|
29916
29999
|
agent: session.agent,
|
|
29917
30000
|
sessionId: session.id,
|
|
@@ -32672,7 +32755,7 @@ function checkChokidar() {
|
|
|
32672
32755
|
}
|
|
32673
32756
|
async function doctor(args2 = []) {
|
|
32674
32757
|
const json = args2.includes("--json");
|
|
32675
|
-
const cliVersion = true ? "2.60.
|
|
32758
|
+
const cliVersion = true ? "2.60.6" : "0.0.0-dev";
|
|
32676
32759
|
const apiBase2 = resolveApiBaseUrl();
|
|
32677
32760
|
const diagnosticId = (0, import_node_crypto9.randomUUID)();
|
|
32678
32761
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -32871,7 +32954,7 @@ async function completion(args2) {
|
|
|
32871
32954
|
// src/commands/version.ts
|
|
32872
32955
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
32873
32956
|
function version2() {
|
|
32874
|
-
const v = true ? "2.60.
|
|
32957
|
+
const v = true ? "2.60.6" : "unknown";
|
|
32875
32958
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
32876
32959
|
}
|
|
32877
32960
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.60.
|
|
3
|
+
"version": "2.60.6",
|
|
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",
|