codeam-cli 2.60.3 → 2.60.5

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.js +101 -21
  3. 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.3" : "0.0.0-dev",
5656
+ cliVersion: true ? "2.60.5" : "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.3",
5837
+ version: "2.60.5",
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.3" ? { ideVersion: "2.60.3" } : {}
6908
+ ..."2.60.5" ? { ideVersion: "2.60.5" } : {}
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
- void cwd;
13405
- return HISTORY_ROOT;
13424
+ const dir = path27.join(HISTORY_ROOT, encodeCursorCwd(cwd));
13425
+ return fs23.existsSync(dir) ? dir : null;
13406
13426
  }
13407
- function parseHistoryFile3(_filePath) {
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.3" : null;
15989
+ const current = true ? "2.60.5" : 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.3" : null;
16006
+ const current = true ? "2.60.5" : 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.3" : null;
16026
+ return true ? "2.60.5" : 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 extractText2(content) {
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 = extractText2(msg.content).trim();
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 = extractText2(msg.content).trim();
21838
+ const text = extractText3(msg.content).trim();
21774
21839
  if (text) messages.push({ id: uuid, role: "agent", text, timestamp });
21775
21840
  }
21776
21841
  }
@@ -25853,12 +25918,12 @@ function mapSessionUpdate(notification) {
25853
25918
  const update = notification.update;
25854
25919
  switch (update.sessionUpdate) {
25855
25920
  case "agent_message_chunk": {
25856
- const text = extractText3(update.content);
25921
+ const text = extractText4(update.content);
25857
25922
  if (!text) return [];
25858
25923
  return [{ chunkId: messageChunkId(update.messageId), kind: "text", delta: text }];
25859
25924
  }
25860
25925
  case "agent_thought_chunk": {
25861
- const text = extractText3(update.content);
25926
+ const text = extractText4(update.content);
25862
25927
  if (!text) return [];
25863
25928
  return [
25864
25929
  {
@@ -25923,7 +25988,7 @@ function messageChunkId(messageId) {
25923
25988
  if (typeof messageId === "string" && messageId.length > 0) return messageId;
25924
25989
  return (0, import_node_crypto6.randomUUID)();
25925
25990
  }
25926
- function extractText3(content) {
25991
+ function extractText4(content) {
25927
25992
  if (!content || typeof content !== "object") return null;
25928
25993
  if ("type" in content && content.type === "text") {
25929
25994
  const t2 = content.text;
@@ -25953,7 +26018,7 @@ function describeToolCallUpdate(update) {
25953
26018
  for (const item of update.content) {
25954
26019
  if (!item || typeof item !== "object") continue;
25955
26020
  if (item.type === "content" && item.content) {
25956
- const text = extractText3(item.content);
26021
+ const text = extractText4(item.content);
25957
26022
  if (text) parts.push(text);
25958
26023
  } else if (item.type === "diff") {
25959
26024
  const p2 = item.path;
@@ -29131,6 +29196,9 @@ function fetchQuotaUsage(runtime, historySvc) {
29131
29196
  }
29132
29197
 
29133
29198
  // src/baton/gate.ts
29199
+ function runtimeSupportsBaton(runtime) {
29200
+ return typeof runtime.resolveHistoryFile === "function";
29201
+ }
29134
29202
  function isLocalSession(env = process.env) {
29135
29203
  return env.CODESPACES !== "true" && env.CODEAM_AUTO_APPROVE !== "1" && env.HEADROOM_ENABLED !== "1" && !env.CODEAM_AUTO_TOKEN && !env.CODEAM_ENROLL_TOKEN;
29136
29204
  }
@@ -29595,6 +29663,16 @@ function makeMirrorOnNewMessages(deps) {
29595
29663
  isFirstEmit = false;
29596
29664
  };
29597
29665
  }
29666
+ function makeSerializedBatonPoster(post2) {
29667
+ let chain = Promise.resolve();
29668
+ return (args2) => {
29669
+ chain = chain.then(() => post2(args2)).then(
29670
+ () => void 0,
29671
+ () => void 0
29672
+ );
29673
+ void chain;
29674
+ };
29675
+ }
29598
29676
  async function runBatonSession(opts) {
29599
29677
  const publisher = new AcpPublisher({
29600
29678
  sessionId: opts.sessionId,
@@ -29690,11 +29768,12 @@ async function runBatonSession(opts) {
29690
29768
  });
29691
29769
  mirror.start();
29692
29770
  };
29771
+ const postBatonState = makeSerializedBatonPoster(postBatonEvent);
29693
29772
  const controller = new BatonController({
29694
29773
  local: nativeDriver,
29695
29774
  mobile: mobileDriver,
29696
29775
  publishState: (state, driver, conversationId) => {
29697
- void postBatonEvent({
29776
+ postBatonState({
29698
29777
  sessionId: opts.sessionId,
29699
29778
  pluginId: opts.pluginId,
29700
29779
  pluginAuthToken: opts.pluginAuthToken,
@@ -29900,7 +29979,8 @@ async function start(requestedAgent) {
29900
29979
  }
29901
29980
  if (isLocalSession() && requiresAcp(session.agent)) {
29902
29981
  const adapter = getAcpAdapter(session.agent);
29903
- if (adapter && session.pluginAuthToken) {
29982
+ const batonCapable = runtimeSupportsBaton(createRuntimeStrategy(session.agent));
29983
+ if (batonCapable && adapter && session.pluginAuthToken) {
29904
29984
  await runBatonSession({
29905
29985
  agent: session.agent,
29906
29986
  sessionId: session.id,
@@ -32661,7 +32741,7 @@ function checkChokidar() {
32661
32741
  }
32662
32742
  async function doctor(args2 = []) {
32663
32743
  const json = args2.includes("--json");
32664
- const cliVersion = true ? "2.60.3" : "0.0.0-dev";
32744
+ const cliVersion = true ? "2.60.5" : "0.0.0-dev";
32665
32745
  const apiBase2 = resolveApiBaseUrl();
32666
32746
  const diagnosticId = (0, import_node_crypto9.randomUUID)();
32667
32747
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -32860,7 +32940,7 @@ async function completion(args2) {
32860
32940
  // src/commands/version.ts
32861
32941
  var import_picocolors15 = __toESM(require("picocolors"));
32862
32942
  function version2() {
32863
- const v = true ? "2.60.3" : "unknown";
32943
+ const v = true ? "2.60.5" : "unknown";
32864
32944
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
32865
32945
  }
32866
32946
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.60.3",
3
+ "version": "2.60.5",
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",