codeam-cli 2.27.7 → 2.27.9
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 +167 -100
- 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.27.8] — 2026-06-06
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** ACP — accumulate cumulative content per chunkId, isFinal on prompt-end
|
|
12
|
+
|
|
13
|
+
## [2.27.7] — 2026-06-06
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **cli:** ACP — wire-level instrumentation + prompt timeout
|
|
18
|
+
|
|
7
19
|
## [2.27.6] — 2026-06-06
|
|
8
20
|
|
|
9
21
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -498,7 +498,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
498
498
|
// package.json
|
|
499
499
|
var package_default = {
|
|
500
500
|
name: "codeam-cli",
|
|
501
|
-
version: "2.27.
|
|
501
|
+
version: "2.27.9",
|
|
502
502
|
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.",
|
|
503
503
|
type: "commonjs",
|
|
504
504
|
main: "dist/index.js",
|
|
@@ -889,8 +889,8 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
|
|
|
889
889
|
(res) => {
|
|
890
890
|
res.on("error", reject);
|
|
891
891
|
let responseBody = "";
|
|
892
|
-
res.on("data", (
|
|
893
|
-
responseBody +=
|
|
892
|
+
res.on("data", (chunk) => {
|
|
893
|
+
responseBody += chunk.toString();
|
|
894
894
|
});
|
|
895
895
|
res.on("end", () => {
|
|
896
896
|
if (res.statusCode && res.statusCode >= 400) {
|
|
@@ -937,8 +937,8 @@ async function _postJson(url, body) {
|
|
|
937
937
|
(res) => {
|
|
938
938
|
res.on("error", reject);
|
|
939
939
|
let body2 = "";
|
|
940
|
-
res.on("data", (
|
|
941
|
-
body2 +=
|
|
940
|
+
res.on("data", (chunk) => {
|
|
941
|
+
body2 += chunk.toString();
|
|
942
942
|
});
|
|
943
943
|
res.on("end", () => {
|
|
944
944
|
if (res.statusCode && res.statusCode >= 400) {
|
|
@@ -978,8 +978,8 @@ async function _getJson(url) {
|
|
|
978
978
|
(res) => {
|
|
979
979
|
res.on("error", reject);
|
|
980
980
|
let body = "";
|
|
981
|
-
res.on("data", (
|
|
982
|
-
body +=
|
|
981
|
+
res.on("data", (chunk) => {
|
|
982
|
+
body += chunk.toString();
|
|
983
983
|
});
|
|
984
984
|
res.on("end", () => {
|
|
985
985
|
if (res.statusCode && res.statusCode >= 400) {
|
|
@@ -5873,7 +5873,7 @@ function readAnonId() {
|
|
|
5873
5873
|
}
|
|
5874
5874
|
function superProperties() {
|
|
5875
5875
|
return {
|
|
5876
|
-
cliVersion: true ? "2.27.
|
|
5876
|
+
cliVersion: true ? "2.27.9" : "0.0.0-dev",
|
|
5877
5877
|
nodeVersion: process.version,
|
|
5878
5878
|
platform: process.platform,
|
|
5879
5879
|
arch: process.arch,
|
|
@@ -6080,9 +6080,9 @@ var CommandRelayService = class {
|
|
|
6080
6080
|
this.armSseWatchdog();
|
|
6081
6081
|
let buffer = "";
|
|
6082
6082
|
res.setEncoding("utf8");
|
|
6083
|
-
res.on("data", (
|
|
6083
|
+
res.on("data", (chunk) => {
|
|
6084
6084
|
this.sseLastByteAt = Date.now();
|
|
6085
|
-
buffer +=
|
|
6085
|
+
buffer += chunk;
|
|
6086
6086
|
let frameEnd;
|
|
6087
6087
|
while ((frameEnd = buffer.indexOf("\n\n")) !== -1) {
|
|
6088
6088
|
const frame = buffer.slice(0, frameEnd);
|
|
@@ -6751,9 +6751,9 @@ var UnixPtyStrategy = class {
|
|
|
6751
6751
|
);
|
|
6752
6752
|
process.exit(1);
|
|
6753
6753
|
});
|
|
6754
|
-
this.proc.stdout?.on("data", (
|
|
6755
|
-
process.stdout.write(
|
|
6756
|
-
this.opts.onData(
|
|
6754
|
+
this.proc.stdout?.on("data", (chunk) => {
|
|
6755
|
+
process.stdout.write(chunk);
|
|
6756
|
+
this.opts.onData(chunk.toString("utf8"));
|
|
6757
6757
|
});
|
|
6758
6758
|
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
6759
6759
|
process.stdin.resume();
|
|
@@ -6845,8 +6845,8 @@ var UnixPtyStrategy = class {
|
|
|
6845
6845
|
}
|
|
6846
6846
|
}
|
|
6847
6847
|
}
|
|
6848
|
-
stdinHandler = (
|
|
6849
|
-
this.proc?.stdin?.write(
|
|
6848
|
+
stdinHandler = (chunk) => {
|
|
6849
|
+
this.proc?.stdin?.write(chunk);
|
|
6850
6850
|
};
|
|
6851
6851
|
handleResize = () => {
|
|
6852
6852
|
if (this.proc?.pid) {
|
|
@@ -7032,8 +7032,8 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
|
|
|
7032
7032
|
}
|
|
7033
7033
|
this.pty = null;
|
|
7034
7034
|
}
|
|
7035
|
-
stdinHandler = (
|
|
7036
|
-
this.pty?.write(
|
|
7035
|
+
stdinHandler = (chunk) => {
|
|
7036
|
+
this.pty?.write(chunk.toString("utf8"));
|
|
7037
7037
|
};
|
|
7038
7038
|
};
|
|
7039
7039
|
|
|
@@ -7066,9 +7066,9 @@ var WindowsPtyStrategy = class {
|
|
|
7066
7066
|
);
|
|
7067
7067
|
process.exit(1);
|
|
7068
7068
|
});
|
|
7069
|
-
this.proc.stdout?.on("data", (
|
|
7070
|
-
process.stdout.write(
|
|
7071
|
-
this.opts.onData(
|
|
7069
|
+
this.proc.stdout?.on("data", (chunk) => {
|
|
7070
|
+
process.stdout.write(chunk);
|
|
7071
|
+
this.opts.onData(chunk.toString("utf8"));
|
|
7072
7072
|
});
|
|
7073
7073
|
this.proc.stdin?.write("");
|
|
7074
7074
|
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
@@ -7095,8 +7095,8 @@ var WindowsPtyStrategy = class {
|
|
|
7095
7095
|
}
|
|
7096
7096
|
}
|
|
7097
7097
|
}
|
|
7098
|
-
stdinHandler = (
|
|
7099
|
-
this.proc?.stdin?.write(
|
|
7098
|
+
stdinHandler = (chunk) => {
|
|
7099
|
+
this.proc?.stdin?.write(chunk);
|
|
7100
7100
|
};
|
|
7101
7101
|
};
|
|
7102
7102
|
|
|
@@ -9379,8 +9379,8 @@ async function fetchClaudeQuota() {
|
|
|
9379
9379
|
});
|
|
9380
9380
|
let output = "";
|
|
9381
9381
|
let resolved = false;
|
|
9382
|
-
proc.stdout?.on("data", (
|
|
9383
|
-
output +=
|
|
9382
|
+
proc.stdout?.on("data", (chunk) => {
|
|
9383
|
+
output += chunk.toString("utf8");
|
|
9384
9384
|
});
|
|
9385
9385
|
setTimeout(() => {
|
|
9386
9386
|
proc.stdin?.write("/usage\r");
|
|
@@ -9453,11 +9453,11 @@ async function spawnAndCapture(cmd, args2, opts = {}) {
|
|
|
9453
9453
|
}
|
|
9454
9454
|
activeChildren.add(child);
|
|
9455
9455
|
let stdout = "";
|
|
9456
|
-
child.stdout?.on("data", (
|
|
9457
|
-
stdout +=
|
|
9456
|
+
child.stdout?.on("data", (chunk) => {
|
|
9457
|
+
stdout += chunk.toString("utf8");
|
|
9458
9458
|
});
|
|
9459
|
-
child.stderr?.on("data", (
|
|
9460
|
-
opts.onStderr?.(
|
|
9459
|
+
child.stderr?.on("data", (chunk) => {
|
|
9460
|
+
opts.onStderr?.(chunk.toString("utf8"));
|
|
9461
9461
|
});
|
|
9462
9462
|
const timer = setTimeout(() => {
|
|
9463
9463
|
try {
|
|
@@ -12049,9 +12049,6 @@ function getAcpAdapter(agent) {
|
|
|
12049
12049
|
return factory ? factory() : null;
|
|
12050
12050
|
}
|
|
12051
12051
|
|
|
12052
|
-
// src/agents/acp/runner.ts
|
|
12053
|
-
var import_node_crypto6 = require("crypto");
|
|
12054
|
-
|
|
12055
12052
|
// src/agents/acp/client.ts
|
|
12056
12053
|
var import_node_child_process11 = require("child_process");
|
|
12057
12054
|
var fs21 = __toESM(require("fs/promises"));
|
|
@@ -12091,8 +12088,8 @@ var AcpClient = class {
|
|
|
12091
12088
|
});
|
|
12092
12089
|
this.child = child;
|
|
12093
12090
|
child.stderr?.setEncoding("utf8");
|
|
12094
|
-
child.stderr?.on("data", (
|
|
12095
|
-
for (const line of
|
|
12091
|
+
child.stderr?.on("data", (chunk) => {
|
|
12092
|
+
for (const line of chunk.split(/\r?\n/)) {
|
|
12096
12093
|
const trimmed = line.trim();
|
|
12097
12094
|
if (trimmed) {
|
|
12098
12095
|
log.info("acpAdapter", trimmed);
|
|
@@ -12374,39 +12371,51 @@ var AcpPublisher = class {
|
|
|
12374
12371
|
apiBase;
|
|
12375
12372
|
headers;
|
|
12376
12373
|
/**
|
|
12377
|
-
* Wrap the
|
|
12374
|
+
* Wrap the body with `sessionId` + `pluginId` at the top level.
|
|
12378
12375
|
* The backend's `PluginAuthGuard` reads both fields from the JSON
|
|
12379
|
-
* body even when `X-Plugin-Auth-Token` is set on the header
|
|
12380
|
-
* `:sessionId` is on the URL path. Without the body fields it
|
|
12381
|
-
* rejects every POST with `PLUGIN_TOKEN_REQUIRED` — same shape the
|
|
12382
|
-
* legacy `streaming-emitter.service.ts` `postWithRetries` uses.
|
|
12376
|
+
* body even when `X-Plugin-Auth-Token` is set on the header.
|
|
12383
12377
|
*/
|
|
12384
|
-
envelope(
|
|
12378
|
+
envelope(body) {
|
|
12385
12379
|
return JSON.stringify({
|
|
12386
12380
|
sessionId: this.opts.sessionId,
|
|
12387
12381
|
pluginId: this.opts.pluginId,
|
|
12388
|
-
...
|
|
12382
|
+
...body
|
|
12389
12383
|
});
|
|
12390
12384
|
}
|
|
12391
12385
|
/**
|
|
12392
|
-
*
|
|
12393
|
-
*
|
|
12394
|
-
*
|
|
12395
|
-
*
|
|
12386
|
+
* POST one event to the legacy chat-render pipeline at
|
|
12387
|
+
* `/api/commands/output`. Mobile reads this feed for the chat
|
|
12388
|
+
* surface — every "Thinking…" → reply → done bubble flows through
|
|
12389
|
+
* here. Accepts arbitrary body shapes (the legacy emitter is a
|
|
12390
|
+
* thin pipe; mobile branches on `type`):
|
|
12391
|
+
*
|
|
12392
|
+
* { type: 'clear' } wipe screen
|
|
12393
|
+
* { type: 'new_turn', done: false } "Agent is typing…"
|
|
12394
|
+
* { type: 'text', content: '…', done: false } streaming delta
|
|
12395
|
+
* { type: 'text', content: '…', done: true } turn complete
|
|
12396
|
+
*
|
|
12397
|
+
* Errors are logged but never thrown — a missed chunk shouldn't
|
|
12398
|
+
* bring down the whole session.
|
|
12396
12399
|
*/
|
|
12397
|
-
async
|
|
12398
|
-
const url = `${this.apiBase}/api/
|
|
12400
|
+
async publishOutput(body) {
|
|
12401
|
+
const url = `${this.apiBase}/api/commands/output`;
|
|
12399
12402
|
try {
|
|
12400
|
-
const { statusCode, body } = await _transport2.post(
|
|
12403
|
+
const { statusCode, body: resBody } = await _transport2.post(
|
|
12401
12404
|
url,
|
|
12402
12405
|
this.headers,
|
|
12403
|
-
this.envelope(
|
|
12406
|
+
this.envelope(body)
|
|
12404
12407
|
);
|
|
12405
12408
|
if (statusCode < 200 || statusCode >= 300) {
|
|
12406
|
-
log.warn(
|
|
12409
|
+
log.warn(
|
|
12410
|
+
"acpPublisher",
|
|
12411
|
+
`output type=${String(body.type)} done=${body.done === true} status=${statusCode} body=${resBody.slice(0, 200)}`
|
|
12412
|
+
);
|
|
12407
12413
|
}
|
|
12408
12414
|
} catch (err) {
|
|
12409
|
-
log.
|
|
12415
|
+
log.warn(
|
|
12416
|
+
"acpPublisher",
|
|
12417
|
+
`output type=${String(body.type)} post failed: ${err instanceof Error ? err.message : String(err)}`
|
|
12418
|
+
);
|
|
12410
12419
|
}
|
|
12411
12420
|
}
|
|
12412
12421
|
/**
|
|
@@ -12483,17 +12492,17 @@ function mapSessionUpdate(notification) {
|
|
|
12483
12492
|
case "agent_message_chunk": {
|
|
12484
12493
|
const text = extractText2(update.content);
|
|
12485
12494
|
if (!text) return [];
|
|
12486
|
-
return [
|
|
12495
|
+
return [{ chunkId: messageChunkId(update.messageId), kind: "text", delta: text }];
|
|
12487
12496
|
}
|
|
12488
12497
|
case "agent_thought_chunk": {
|
|
12489
12498
|
const text = extractText2(update.content);
|
|
12490
12499
|
if (!text) return [];
|
|
12491
|
-
return [
|
|
12500
|
+
return [{ chunkId: messageChunkId(update.messageId), kind: "thinking", delta: text }];
|
|
12492
12501
|
}
|
|
12493
12502
|
case "tool_call": {
|
|
12494
12503
|
const summary = describeToolCall(update);
|
|
12495
12504
|
if (!summary) return [];
|
|
12496
|
-
return [
|
|
12505
|
+
return [{ chunkId: update.toolCallId, kind: "tool_use", delta: summary }];
|
|
12497
12506
|
}
|
|
12498
12507
|
case "tool_call_update": {
|
|
12499
12508
|
if (update.status !== "completed" && update.status !== "failed") {
|
|
@@ -12502,7 +12511,7 @@ function mapSessionUpdate(notification) {
|
|
|
12502
12511
|
const body = describeToolCallUpdate(update);
|
|
12503
12512
|
if (!body) return [];
|
|
12504
12513
|
const prefix = update.status === "failed" ? "[failed] " : "";
|
|
12505
|
-
return [
|
|
12514
|
+
return [{ chunkId: update.toolCallId, kind: "tool_result", delta: prefix + body }];
|
|
12506
12515
|
}
|
|
12507
12516
|
case "user_message_chunk":
|
|
12508
12517
|
return [];
|
|
@@ -12541,9 +12550,6 @@ function mapPermissionRequest(request) {
|
|
|
12541
12550
|
kindByLabel
|
|
12542
12551
|
};
|
|
12543
12552
|
}
|
|
12544
|
-
function chunk(chunkId, kind, content) {
|
|
12545
|
-
return { chunkId, kind, content, isFinal: true };
|
|
12546
|
-
}
|
|
12547
12553
|
function messageChunkId(messageId) {
|
|
12548
12554
|
if (typeof messageId === "string" && messageId.length > 0) return messageId;
|
|
12549
12555
|
return (0, import_node_crypto5.randomUUID)();
|
|
@@ -12609,6 +12615,63 @@ function humanizeKind(kind) {
|
|
|
12609
12615
|
}
|
|
12610
12616
|
|
|
12611
12617
|
// src/agents/acp/runner.ts
|
|
12618
|
+
var StreamingState = class {
|
|
12619
|
+
constructor(publisher) {
|
|
12620
|
+
this.publisher = publisher;
|
|
12621
|
+
}
|
|
12622
|
+
publisher;
|
|
12623
|
+
open = /* @__PURE__ */ new Map();
|
|
12624
|
+
/**
|
|
12625
|
+
* Boundary events emitted at the start of every turn so mobile
|
|
12626
|
+
* wipes the previous reply and shows "Agent is typing…". Mirrors
|
|
12627
|
+
* the legacy `outputSvc.newTurn()` — `critical: true` on those
|
|
12628
|
+
* sends is implicit here (publishOutput retries on transient
|
|
12629
|
+
* failures so the boundary always lands).
|
|
12630
|
+
*/
|
|
12631
|
+
async beginTurn() {
|
|
12632
|
+
this.open.clear();
|
|
12633
|
+
await this.publisher.publishOutput({ type: "clear" });
|
|
12634
|
+
await this.publisher.publishOutput({ type: "new_turn", done: false });
|
|
12635
|
+
}
|
|
12636
|
+
append(delta) {
|
|
12637
|
+
const existing = this.open.get(delta.chunkId);
|
|
12638
|
+
const content = (existing?.content ?? "") + delta.delta;
|
|
12639
|
+
if (existing && existing.kind !== delta.kind) {
|
|
12640
|
+
log.warn(
|
|
12641
|
+
"acpRunner",
|
|
12642
|
+
`chunk kind flip detected chunkId=${delta.chunkId.slice(0, 8)} from=${existing.kind} to=${delta.kind} \u2014 using new kind`
|
|
12643
|
+
);
|
|
12644
|
+
}
|
|
12645
|
+
this.open.set(delta.chunkId, { kind: delta.kind, content });
|
|
12646
|
+
void this.publisher.publishOutput({ type: "text", content, done: false });
|
|
12647
|
+
}
|
|
12648
|
+
/**
|
|
12649
|
+
* Flush every open buffer with `done: true` so mobile flips out
|
|
12650
|
+
* of "Thinking…". Idempotent — safe to call multiple times per
|
|
12651
|
+
* turn (prompt-completed, cancel, adapter-exit).
|
|
12652
|
+
*
|
|
12653
|
+
* Also emits an empty `{type:'text', content:'', done:true}` when
|
|
12654
|
+
* the turn produced no text at all (e.g. Claude responded with
|
|
12655
|
+
* tool calls only) so mobile doesn't sit on "Thinking…" forever
|
|
12656
|
+
* waiting for content that will never arrive.
|
|
12657
|
+
*/
|
|
12658
|
+
async closeAll() {
|
|
12659
|
+
if (this.open.size === 0) {
|
|
12660
|
+
await this.publisher.publishOutput({ type: "text", content: "", done: true });
|
|
12661
|
+
return;
|
|
12662
|
+
}
|
|
12663
|
+
const closing = Array.from(this.open.values());
|
|
12664
|
+
this.open.clear();
|
|
12665
|
+
for (let i = 0; i < closing.length; i += 1) {
|
|
12666
|
+
const isLast = i === closing.length - 1;
|
|
12667
|
+
await this.publisher.publishOutput({
|
|
12668
|
+
type: "text",
|
|
12669
|
+
content: closing[i].content,
|
|
12670
|
+
done: isLast
|
|
12671
|
+
});
|
|
12672
|
+
}
|
|
12673
|
+
}
|
|
12674
|
+
};
|
|
12612
12675
|
var ANSWER_POLL_MS = 1500;
|
|
12613
12676
|
var PERMISSION_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
12614
12677
|
async function runAcpSession(opts) {
|
|
@@ -12617,6 +12680,7 @@ async function runAcpSession(opts) {
|
|
|
12617
12680
|
pluginId: opts.pluginId,
|
|
12618
12681
|
pluginAuthToken: opts.pluginAuthToken
|
|
12619
12682
|
});
|
|
12683
|
+
const streaming = new StreamingState(publisher);
|
|
12620
12684
|
let updateCount = 0;
|
|
12621
12685
|
const client2 = new AcpClient({
|
|
12622
12686
|
adapter: opts.adapter,
|
|
@@ -12624,15 +12688,13 @@ async function runAcpSession(opts) {
|
|
|
12624
12688
|
onSessionUpdate: (notification) => {
|
|
12625
12689
|
updateCount += 1;
|
|
12626
12690
|
const variant = notification.update?.sessionUpdate ?? "unknown";
|
|
12627
|
-
const
|
|
12691
|
+
const deltas = mapSessionUpdate(notification);
|
|
12628
12692
|
log.info(
|
|
12629
12693
|
"acpRunner",
|
|
12630
|
-
`update #${updateCount} variant=${variant}
|
|
12694
|
+
`update #${updateCount} variant=${variant} mappedDeltas=${deltas.length}`
|
|
12631
12695
|
);
|
|
12632
|
-
for (const
|
|
12633
|
-
|
|
12634
|
-
log.warn("acpRunner", `publishChunk failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
12635
|
-
});
|
|
12696
|
+
for (const delta of deltas) {
|
|
12697
|
+
streaming.append(delta);
|
|
12636
12698
|
}
|
|
12637
12699
|
},
|
|
12638
12700
|
onRequestPermission: async (request) => {
|
|
@@ -12656,12 +12718,13 @@ async function runAcpSession(opts) {
|
|
|
12656
12718
|
},
|
|
12657
12719
|
onUnexpectedExit: (code, signal) => {
|
|
12658
12720
|
log.warn("acpRunner", `adapter died code=${code} signal=${signal}; shutting down session`);
|
|
12659
|
-
void
|
|
12660
|
-
|
|
12661
|
-
|
|
12662
|
-
|
|
12663
|
-
|
|
12664
|
-
|
|
12721
|
+
void streaming.closeAll().then(
|
|
12722
|
+
() => publisher.publishOutput({
|
|
12723
|
+
type: "text",
|
|
12724
|
+
content: `Agent adapter exited unexpectedly (code=${code ?? "null"} signal=${signal ?? "null"}).`,
|
|
12725
|
+
done: true
|
|
12726
|
+
})
|
|
12727
|
+
);
|
|
12665
12728
|
process.exit(1);
|
|
12666
12729
|
}
|
|
12667
12730
|
});
|
|
@@ -12677,7 +12740,7 @@ async function runAcpSession(opts) {
|
|
|
12677
12740
|
const relay = new CommandRelayService(
|
|
12678
12741
|
opts.pluginId,
|
|
12679
12742
|
async (cmd) => {
|
|
12680
|
-
await handleCommand(cmd, client2, relay, acpSessionId, models);
|
|
12743
|
+
await handleCommand(cmd, client2, relay, acpSessionId, models, streaming);
|
|
12681
12744
|
},
|
|
12682
12745
|
{ id: opts.agent, name: opts.agent, displayName: opts.agent }
|
|
12683
12746
|
);
|
|
@@ -12694,7 +12757,7 @@ async function runAcpSession(opts) {
|
|
|
12694
12757
|
await new Promise(() => {
|
|
12695
12758
|
});
|
|
12696
12759
|
}
|
|
12697
|
-
async function handleCommand(cmd, client2, relay, acpSessionId, models) {
|
|
12760
|
+
async function handleCommand(cmd, client2, relay, acpSessionId, models, streaming) {
|
|
12698
12761
|
switch (cmd.type) {
|
|
12699
12762
|
case "start_task": {
|
|
12700
12763
|
const payload = cmd.payload;
|
|
@@ -12705,11 +12768,14 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models) {
|
|
|
12705
12768
|
return;
|
|
12706
12769
|
}
|
|
12707
12770
|
log.info("acpRunner", `start_task \u2192 forwarding prompt chars=${prompt.length} id=${cmd.id.slice(0, 8)}`);
|
|
12771
|
+
await streaming.beginTurn();
|
|
12708
12772
|
try {
|
|
12709
12773
|
const reply = await client2.prompt(prompt);
|
|
12774
|
+
await streaming.closeAll();
|
|
12710
12775
|
log.info("acpRunner", `start_task \u2190 done stopReason=${reply.stopReason ?? "?"} id=${cmd.id.slice(0, 8)}`);
|
|
12711
12776
|
await relay.sendResult(cmd.id, "completed", { stopReason: reply.stopReason });
|
|
12712
12777
|
} catch (err) {
|
|
12778
|
+
await streaming.closeAll();
|
|
12713
12779
|
log.warn("acpRunner", `prompt failed: ${describeError(err)}`);
|
|
12714
12780
|
await relay.sendResult(cmd.id, "failed", { error: describeError(err) });
|
|
12715
12781
|
}
|
|
@@ -12719,6 +12785,7 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models) {
|
|
|
12719
12785
|
case "escape_key": {
|
|
12720
12786
|
try {
|
|
12721
12787
|
await client2.cancel();
|
|
12788
|
+
await streaming.closeAll();
|
|
12722
12789
|
await relay.sendResult(cmd.id, "completed", {});
|
|
12723
12790
|
} catch (err) {
|
|
12724
12791
|
log.warn("acpRunner", `cancel failed: ${describeError(err)}`);
|
|
@@ -15188,13 +15255,13 @@ var TurnFileAggregator = class {
|
|
|
15188
15255
|
return;
|
|
15189
15256
|
}
|
|
15190
15257
|
const chunks = chunkArray(novel, MAX_BATCH_SIZE);
|
|
15191
|
-
for (const
|
|
15258
|
+
for (const chunk of chunks) {
|
|
15192
15259
|
const entry = {
|
|
15193
15260
|
turnId: (0, import_crypto2.randomUUID)(),
|
|
15194
15261
|
sessionId: this.opts.sessionId,
|
|
15195
15262
|
pluginId: this.opts.pluginId,
|
|
15196
15263
|
enqueuedAt: Date.now(),
|
|
15197
|
-
files:
|
|
15264
|
+
files: chunk
|
|
15198
15265
|
};
|
|
15199
15266
|
await this.outbox.enqueue(entry);
|
|
15200
15267
|
}
|
|
@@ -15355,11 +15422,11 @@ var StreamingEmitterService = class {
|
|
|
15355
15422
|
}
|
|
15356
15423
|
if (this.activeChunk) {
|
|
15357
15424
|
const finalContent = this.activeChunk.currentContent;
|
|
15358
|
-
const
|
|
15425
|
+
const chunk = this.activeChunk;
|
|
15359
15426
|
this.activeChunk = null;
|
|
15360
15427
|
await this.postChunk({
|
|
15361
|
-
chunkId:
|
|
15362
|
-
kind:
|
|
15428
|
+
chunkId: chunk.chunkId,
|
|
15429
|
+
kind: chunk.kind,
|
|
15363
15430
|
content: finalContent,
|
|
15364
15431
|
isFinal: true
|
|
15365
15432
|
});
|
|
@@ -15431,17 +15498,17 @@ var StreamingEmitterService = class {
|
|
|
15431
15498
|
this.maybeFlushActive(false);
|
|
15432
15499
|
}
|
|
15433
15500
|
maybeFlushActive(force) {
|
|
15434
|
-
const
|
|
15435
|
-
if (!
|
|
15501
|
+
const chunk = this.activeChunk;
|
|
15502
|
+
if (!chunk) return;
|
|
15436
15503
|
const now = Date.now();
|
|
15437
|
-
if (!force && now -
|
|
15438
|
-
if (
|
|
15439
|
-
|
|
15440
|
-
|
|
15504
|
+
if (!force && now - chunk.lastEmitAt < TICK_MS) return;
|
|
15505
|
+
if (chunk.currentContent === chunk.emittedContent) return;
|
|
15506
|
+
chunk.emittedContent = chunk.currentContent;
|
|
15507
|
+
chunk.lastEmitAt = now;
|
|
15441
15508
|
void this.postChunk({
|
|
15442
|
-
chunkId:
|
|
15443
|
-
kind:
|
|
15444
|
-
content:
|
|
15509
|
+
chunkId: chunk.chunkId,
|
|
15510
|
+
kind: chunk.kind,
|
|
15511
|
+
content: chunk.currentContent,
|
|
15445
15512
|
isFinal: false
|
|
15446
15513
|
});
|
|
15447
15514
|
}
|
|
@@ -16655,7 +16722,7 @@ function findGitRoot2(startDir) {
|
|
|
16655
16722
|
}
|
|
16656
16723
|
|
|
16657
16724
|
// src/commands/link.ts
|
|
16658
|
-
var
|
|
16725
|
+
var import_node_crypto6 = require("crypto");
|
|
16659
16726
|
var fs28 = __toESM(require("fs"));
|
|
16660
16727
|
var path34 = __toESM(require("path"));
|
|
16661
16728
|
var import_chokidar = __toESM(require("chokidar"));
|
|
@@ -16744,7 +16811,7 @@ async function link(args2 = []) {
|
|
|
16744
16811
|
await linkDryRunPreflight(ctx);
|
|
16745
16812
|
return;
|
|
16746
16813
|
}
|
|
16747
|
-
const pluginId = (0,
|
|
16814
|
+
const pluginId = (0, import_node_crypto6.randomUUID)();
|
|
16748
16815
|
const spin = dist_exports.spinner();
|
|
16749
16816
|
spin.start("Requesting pairing code...");
|
|
16750
16817
|
const pairing = await requestCode(pluginId);
|
|
@@ -17978,8 +18045,8 @@ var previewStartH = (ctx, _cmd, parsed) => {
|
|
|
17978
18045
|
let readyMatched = false;
|
|
17979
18046
|
let expoUrl = null;
|
|
17980
18047
|
const readyRe = new RegExp(detection.ready_pattern);
|
|
17981
|
-
const onChunk = (
|
|
17982
|
-
const s =
|
|
18048
|
+
const onChunk = (chunk) => {
|
|
18049
|
+
const s = chunk.toString();
|
|
17983
18050
|
if (!readyMatched && readyRe.test(s)) readyMatched = true;
|
|
17984
18051
|
if (!expoUrl && detection.framework === "Expo") expoUrl = parseExpoUrl(s);
|
|
17985
18052
|
};
|
|
@@ -18102,8 +18169,8 @@ var previewStartH = (ctx, _cmd, parsed) => {
|
|
|
18102
18169
|
stdio: ["ignore", "pipe", "pipe"]
|
|
18103
18170
|
});
|
|
18104
18171
|
let parsedUrl = null;
|
|
18105
|
-
const onTunnelChunk = (
|
|
18106
|
-
const s =
|
|
18172
|
+
const onTunnelChunk = (chunk) => {
|
|
18173
|
+
const s = chunk.toString();
|
|
18107
18174
|
if (!parsedUrl) parsedUrl = parseCloudflaredUrl(s);
|
|
18108
18175
|
const trimmed = s.replace(/\n+$/g, "");
|
|
18109
18176
|
if (trimmed.length > 0) log.info("preview", `cloudflared: ${trimmed}`);
|
|
@@ -18202,8 +18269,8 @@ function runOnce(cmd, args2, cwd, env) {
|
|
|
18202
18269
|
stdio: ["ignore", "pipe", "pipe"]
|
|
18203
18270
|
});
|
|
18204
18271
|
const tag = `setup:${cmd}`;
|
|
18205
|
-
const onChunk = (
|
|
18206
|
-
const text =
|
|
18272
|
+
const onChunk = (chunk) => {
|
|
18273
|
+
const text = chunk.toString().replace(/\n+$/g, "");
|
|
18207
18274
|
if (text.length === 0) return;
|
|
18208
18275
|
log.info("preview", `${tag}: ${text}`);
|
|
18209
18276
|
};
|
|
@@ -21060,7 +21127,7 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
21060
21127
|
// src/commands/doctor.ts
|
|
21061
21128
|
var import_node_dns = require("dns");
|
|
21062
21129
|
var import_node_util4 = require("util");
|
|
21063
|
-
var
|
|
21130
|
+
var import_node_crypto7 = require("crypto");
|
|
21064
21131
|
var fs34 = __toESM(require("fs"));
|
|
21065
21132
|
var path43 = __toESM(require("path"));
|
|
21066
21133
|
var import_picocolors12 = __toESM(require("picocolors"));
|
|
@@ -21230,9 +21297,9 @@ function checkChokidar() {
|
|
|
21230
21297
|
}
|
|
21231
21298
|
async function doctor(args2 = []) {
|
|
21232
21299
|
const json = args2.includes("--json");
|
|
21233
|
-
const cliVersion = true ? "2.27.
|
|
21300
|
+
const cliVersion = true ? "2.27.9" : "0.0.0-dev";
|
|
21234
21301
|
const apiBase = resolveApiBaseUrl();
|
|
21235
|
-
const diagnosticId = (0,
|
|
21302
|
+
const diagnosticId = (0, import_node_crypto7.randomUUID)();
|
|
21236
21303
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
21237
21304
|
const [dns, health] = await Promise.all([
|
|
21238
21305
|
checkDns(apiBase),
|
|
@@ -21429,7 +21496,7 @@ async function completion(args2) {
|
|
|
21429
21496
|
// src/commands/version.ts
|
|
21430
21497
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
21431
21498
|
function version2() {
|
|
21432
|
-
const v = true ? "2.27.
|
|
21499
|
+
const v = true ? "2.27.9" : "unknown";
|
|
21433
21500
|
console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
|
|
21434
21501
|
}
|
|
21435
21502
|
|
|
@@ -21616,8 +21683,8 @@ function fetchLatest() {
|
|
|
21616
21683
|
}
|
|
21617
21684
|
let buf = "";
|
|
21618
21685
|
res.setEncoding("utf8");
|
|
21619
|
-
res.on("data", (
|
|
21620
|
-
buf +=
|
|
21686
|
+
res.on("data", (chunk) => {
|
|
21687
|
+
buf += chunk;
|
|
21621
21688
|
});
|
|
21622
21689
|
res.on("end", () => {
|
|
21623
21690
|
try {
|
|
@@ -21657,7 +21724,7 @@ function checkForUpdates() {
|
|
|
21657
21724
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
21658
21725
|
if (process.env.CI) return;
|
|
21659
21726
|
if (!process.stdout.isTTY) return;
|
|
21660
|
-
const current = true ? "2.27.
|
|
21727
|
+
const current = true ? "2.27.9" : null;
|
|
21661
21728
|
if (!current) return;
|
|
21662
21729
|
const cache = readCache();
|
|
21663
21730
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.27.
|
|
3
|
+
"version": "2.27.9",
|
|
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",
|