codeam-cli 2.60.0 → 2.60.2
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 +42 -0
- package/dist/index.js +102 -35
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,48 @@ 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.1] — 2026-07-08
|
|
8
|
+
|
|
9
|
+
### Chore
|
|
10
|
+
|
|
11
|
+
- **cli:** Remove CODEAM_BATON flag — baton unconditional for local sessions
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **cli:** Baton LOCAL_DRIVE mirror publishes turns live via the output stream
|
|
16
|
+
|
|
17
|
+
## [2.60.0] — 2026-07-08
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **cli:** Baton local-session gate + feature flag
|
|
22
|
+
- **cli:** BatonController turn-safe single-driver state machine
|
|
23
|
+
- **cli:** TranscriptMirror emits JSONL deltas for the read-only view
|
|
24
|
+
- **cli:** NativeTuiDriver over AgentService with idle turn-boundary
|
|
25
|
+
- **cli:** AcpDriver over AcpClient (spawn + loadSession resume)
|
|
26
|
+
- **cli:** PostBatonEvent publisher driver-state
|
|
27
|
+
- **cli:** Take_control/handback relay handlers
|
|
28
|
+
- **cli:** Wire local baton branch before the ACP fork (flag-gated)
|
|
29
|
+
- **cli:** Make the session baton drivable after a hand-off
|
|
30
|
+
- **cli:** Enable the session baton by default for local sessions (CODEAM_BATON=0 kill switch)
|
|
31
|
+
- **shared:** Add baton_state to USER_EVENTS (session baton wire type)
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- **cli:** Extract shared ACP command-context assembler + proxy-relaunch helper
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- **cli:** Mark SessionDriver.kind readonly (contract)
|
|
40
|
+
- **cli:** Baton switch-failure recovery + ack-on-failure; test cleanups
|
|
41
|
+
- **cli:** EncodeCwd underscore-collapse + ClaudeRuntimeStrategy.resolveHistoryFile (baton mirror)
|
|
42
|
+
- **cli:** AcpClient.start cleans up on handshake failure (baton take-control recovery)
|
|
43
|
+
|
|
44
|
+
### Tests
|
|
45
|
+
|
|
46
|
+
- **cli:** Prove noteOutput resets the idle window + cover throw/undefined branches
|
|
47
|
+
- **cli:** Gated cross-mode-resume integration test (real claude)
|
|
48
|
+
|
|
7
49
|
## [2.59.0] — 2026-07-08
|
|
8
50
|
|
|
9
51
|
### Added
|
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.2" : "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.2",
|
|
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.2" ? { ideVersion: "2.60.2" } : {}
|
|
6909
6909
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
6910
6910
|
}
|
|
6911
6911
|
/**
|
|
@@ -15921,7 +15921,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
15921
15921
|
if (process.env.NODE_ENV === "test") return;
|
|
15922
15922
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
15923
15923
|
if (process.env.CI) return;
|
|
15924
|
-
const current = true ? "2.60.
|
|
15924
|
+
const current = true ? "2.60.2" : null;
|
|
15925
15925
|
if (!current) return;
|
|
15926
15926
|
const cache = readCache();
|
|
15927
15927
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -15938,7 +15938,7 @@ function checkForUpdates() {
|
|
|
15938
15938
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
15939
15939
|
if (process.env.CI) return;
|
|
15940
15940
|
if (!process.stdout.isTTY) return;
|
|
15941
|
-
const current = true ? "2.60.
|
|
15941
|
+
const current = true ? "2.60.2" : null;
|
|
15942
15942
|
if (!current) return;
|
|
15943
15943
|
const cache = readCache();
|
|
15944
15944
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -15958,7 +15958,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
|
15958
15958
|
var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
|
|
15959
15959
|
var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
15960
15960
|
function currentCliVersion() {
|
|
15961
|
-
return true ? "2.60.
|
|
15961
|
+
return true ? "2.60.2" : null;
|
|
15962
15962
|
}
|
|
15963
15963
|
function runCmd(cmd, args2, timeoutMs) {
|
|
15964
15964
|
return new Promise((resolve7) => {
|
|
@@ -29110,11 +29110,6 @@ function fetchQuotaUsage(runtime, historySvc) {
|
|
|
29110
29110
|
function isLocalSession(env = process.env) {
|
|
29111
29111
|
return env.CODESPACES !== "true" && env.CODEAM_AUTO_APPROVE !== "1" && env.HEADROOM_ENABLED !== "1" && !env.CODEAM_AUTO_TOKEN && !env.CODEAM_ENROLL_TOKEN;
|
|
29112
29112
|
}
|
|
29113
|
-
function batonEnabled(env = process.env) {
|
|
29114
|
-
const v = env.CODEAM_BATON;
|
|
29115
|
-
if (v === void 0 || v === "") return true;
|
|
29116
|
-
return v !== "0" && v.toLowerCase() !== "false";
|
|
29117
|
-
}
|
|
29118
29113
|
|
|
29119
29114
|
// src/baton/baton-controller.ts
|
|
29120
29115
|
var BatonController = class {
|
|
@@ -29415,20 +29410,54 @@ var fs58 = __toESM(require("fs"));
|
|
|
29415
29410
|
var TranscriptMirror = class {
|
|
29416
29411
|
constructor(deps) {
|
|
29417
29412
|
this.deps = deps;
|
|
29413
|
+
this.pollIntervalMs = deps.pollIntervalMs ?? 750;
|
|
29414
|
+
this.waitTimeoutMs = deps.waitTimeoutMs ?? 10 * 6e4;
|
|
29415
|
+
this.setIntervalFn = deps.setInterval ?? ((fn, ms) => setInterval(fn, ms));
|
|
29416
|
+
this.clearIntervalFn = deps.clearInterval ?? ((handle) => clearInterval(handle));
|
|
29418
29417
|
}
|
|
29419
29418
|
deps;
|
|
29420
29419
|
emitted = 0;
|
|
29421
29420
|
unwatch = null;
|
|
29421
|
+
pollHandle = null;
|
|
29422
|
+
attached = false;
|
|
29423
|
+
pollIntervalMs;
|
|
29424
|
+
waitTimeoutMs;
|
|
29425
|
+
setIntervalFn;
|
|
29426
|
+
clearIntervalFn;
|
|
29422
29427
|
start() {
|
|
29428
|
+
if (this.tryAttach()) return;
|
|
29429
|
+
let waited = 0;
|
|
29430
|
+
this.pollHandle = this.setIntervalFn(() => {
|
|
29431
|
+
waited += this.pollIntervalMs;
|
|
29432
|
+
if (this.tryAttach()) {
|
|
29433
|
+
this.clearPoll();
|
|
29434
|
+
return;
|
|
29435
|
+
}
|
|
29436
|
+
if (this.waitTimeoutMs > 0 && waited >= this.waitTimeoutMs) this.clearPoll();
|
|
29437
|
+
}, this.pollIntervalMs);
|
|
29438
|
+
}
|
|
29439
|
+
stop() {
|
|
29440
|
+
this.clearPoll();
|
|
29441
|
+
this.unwatch?.();
|
|
29442
|
+
this.unwatch = null;
|
|
29443
|
+
}
|
|
29444
|
+
/** Resolve the transcript file; if present, emit the current contents and
|
|
29445
|
+
* begin watching. Returns whether it attached. */
|
|
29446
|
+
tryAttach() {
|
|
29447
|
+
if (this.attached) return true;
|
|
29423
29448
|
const file = this.deps.runtime.resolveHistoryFile?.(this.deps.cwd, this.deps.conversationId);
|
|
29424
|
-
if (!file) return;
|
|
29449
|
+
if (!file) return false;
|
|
29450
|
+
this.attached = true;
|
|
29425
29451
|
this.emit(file);
|
|
29426
29452
|
const watch2 = this.deps.watch ?? defaultWatch;
|
|
29427
29453
|
this.unwatch = watch2(file, () => this.emit(file));
|
|
29454
|
+
return true;
|
|
29428
29455
|
}
|
|
29429
|
-
|
|
29430
|
-
this.
|
|
29431
|
-
|
|
29456
|
+
clearPoll() {
|
|
29457
|
+
if (this.pollHandle !== null) {
|
|
29458
|
+
this.clearIntervalFn(this.pollHandle);
|
|
29459
|
+
this.pollHandle = null;
|
|
29460
|
+
}
|
|
29432
29461
|
}
|
|
29433
29462
|
emit(file) {
|
|
29434
29463
|
let all;
|
|
@@ -29480,6 +29509,47 @@ function makeOnCommand(deps) {
|
|
|
29480
29509
|
await deps.dispatchActive(cmd);
|
|
29481
29510
|
};
|
|
29482
29511
|
}
|
|
29512
|
+
async function publishMirroredTurnsLive(publisher, messages) {
|
|
29513
|
+
for (const m of messages) {
|
|
29514
|
+
if (m.role === "user") {
|
|
29515
|
+
await publisher.publishOutput({ type: "clear" });
|
|
29516
|
+
await publisher.publishOutput({ type: "user_message", content: m.text, done: true });
|
|
29517
|
+
await publisher.publishOutput({ type: "new_turn", done: false });
|
|
29518
|
+
} else {
|
|
29519
|
+
await publisher.publishOutput({ type: "text", content: m.text, done: true });
|
|
29520
|
+
}
|
|
29521
|
+
}
|
|
29522
|
+
}
|
|
29523
|
+
function makeMirrorOnNewMessages(deps) {
|
|
29524
|
+
let isFirstEmit = true;
|
|
29525
|
+
let publishChain = Promise.resolve();
|
|
29526
|
+
const conversation = [];
|
|
29527
|
+
return (messages) => {
|
|
29528
|
+
const relevant = messages.filter((m) => m.role !== "system");
|
|
29529
|
+
if (relevant.length === 0) return;
|
|
29530
|
+
const mapped = relevant.map((m) => ({
|
|
29531
|
+
id: m.id,
|
|
29532
|
+
role: m.role === "user" ? "user" : "agent",
|
|
29533
|
+
text: m.text,
|
|
29534
|
+
timestamp: toEpochMs(m.timestamp)
|
|
29535
|
+
}));
|
|
29536
|
+
conversation.push(...mapped);
|
|
29537
|
+
void deps.publisher.pushConversation({
|
|
29538
|
+
agentId: deps.agentId,
|
|
29539
|
+
sessionId: deps.conversationId,
|
|
29540
|
+
messages: conversation.slice()
|
|
29541
|
+
});
|
|
29542
|
+
if (deps.fresh || !isFirstEmit) {
|
|
29543
|
+
publishChain = publishChain.then(() => publishMirroredTurnsLive(deps.publisher, relevant)).catch((err) => {
|
|
29544
|
+
log.warn(
|
|
29545
|
+
"wireBaton",
|
|
29546
|
+
`mirror live-publish failed: ${err instanceof Error ? err.message : String(err)}`
|
|
29547
|
+
);
|
|
29548
|
+
});
|
|
29549
|
+
}
|
|
29550
|
+
isFirstEmit = false;
|
|
29551
|
+
};
|
|
29552
|
+
}
|
|
29483
29553
|
async function runBatonSession(opts) {
|
|
29484
29554
|
const publisher = new AcpPublisher({
|
|
29485
29555
|
sessionId: opts.sessionId,
|
|
@@ -29559,26 +29629,19 @@ async function runBatonSession(opts) {
|
|
|
29559
29629
|
getBeads: opts.getBeads ?? (() => null)
|
|
29560
29630
|
});
|
|
29561
29631
|
let mirror = null;
|
|
29562
|
-
|
|
29632
|
+
let firstLocalDrive = true;
|
|
29633
|
+
const startMirror = (conversationId, fresh) => {
|
|
29563
29634
|
mirror?.stop();
|
|
29564
29635
|
mirror = new TranscriptMirror({
|
|
29565
29636
|
runtime,
|
|
29566
29637
|
cwd: opts.cwd,
|
|
29567
29638
|
conversationId,
|
|
29568
|
-
onNewMessages: (
|
|
29569
|
-
|
|
29570
|
-
|
|
29571
|
-
|
|
29572
|
-
|
|
29573
|
-
|
|
29574
|
-
}));
|
|
29575
|
-
if (mapped.length === 0) return;
|
|
29576
|
-
void publisher.pushConversation({
|
|
29577
|
-
agentId: opts.agent,
|
|
29578
|
-
sessionId: conversationId,
|
|
29579
|
-
messages: mapped
|
|
29580
|
-
});
|
|
29581
|
-
}
|
|
29639
|
+
onNewMessages: makeMirrorOnNewMessages({
|
|
29640
|
+
publisher,
|
|
29641
|
+
agentId: opts.agent,
|
|
29642
|
+
conversationId,
|
|
29643
|
+
fresh
|
|
29644
|
+
})
|
|
29582
29645
|
});
|
|
29583
29646
|
mirror.start();
|
|
29584
29647
|
};
|
|
@@ -29594,8 +29657,12 @@ async function runBatonSession(opts) {
|
|
|
29594
29657
|
driver,
|
|
29595
29658
|
conversationId
|
|
29596
29659
|
});
|
|
29597
|
-
if (state === "LOCAL_DRIVE" && conversationId)
|
|
29598
|
-
|
|
29660
|
+
if (state === "LOCAL_DRIVE" && conversationId) {
|
|
29661
|
+
startMirror(conversationId, firstLocalDrive);
|
|
29662
|
+
firstLocalDrive = false;
|
|
29663
|
+
} else if (state !== "LOCAL_DRIVE") {
|
|
29664
|
+
mirror?.stop();
|
|
29665
|
+
}
|
|
29599
29666
|
}
|
|
29600
29667
|
});
|
|
29601
29668
|
const dispatchActive = (cmd) => controller.activeSessionDriver.dispatch(cmd);
|
|
@@ -29786,7 +29853,7 @@ async function start(requestedAgent) {
|
|
|
29786
29853
|
`agent-spawn gate released \u2014 beads ${beads ? "ready" : "pending"}; project deps provisioned`
|
|
29787
29854
|
);
|
|
29788
29855
|
}
|
|
29789
|
-
if (isLocalSession() &&
|
|
29856
|
+
if (isLocalSession() && requiresAcp(session.agent)) {
|
|
29790
29857
|
const adapter = getAcpAdapter(session.agent);
|
|
29791
29858
|
if (adapter && session.pluginAuthToken) {
|
|
29792
29859
|
await runBatonSession({
|
|
@@ -32549,7 +32616,7 @@ function checkChokidar() {
|
|
|
32549
32616
|
}
|
|
32550
32617
|
async function doctor(args2 = []) {
|
|
32551
32618
|
const json = args2.includes("--json");
|
|
32552
|
-
const cliVersion = true ? "2.60.
|
|
32619
|
+
const cliVersion = true ? "2.60.2" : "0.0.0-dev";
|
|
32553
32620
|
const apiBase2 = resolveApiBaseUrl();
|
|
32554
32621
|
const diagnosticId = (0, import_node_crypto9.randomUUID)();
|
|
32555
32622
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -32748,7 +32815,7 @@ async function completion(args2) {
|
|
|
32748
32815
|
// src/commands/version.ts
|
|
32749
32816
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
32750
32817
|
function version2() {
|
|
32751
|
-
const v = true ? "2.60.
|
|
32818
|
+
const v = true ? "2.60.2" : "unknown";
|
|
32752
32819
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
32753
32820
|
}
|
|
32754
32821
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.60.
|
|
3
|
+
"version": "2.60.2",
|
|
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",
|