codeam-cli 2.27.6 → 2.27.8
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 +174 -71
- 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.7] — 2026-06-06
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** ACP — wire-level instrumentation + prompt timeout
|
|
12
|
+
|
|
13
|
+
## [2.27.6] — 2026-06-06
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **cli:** ACP runner — ack every relay command + body envelope for auth
|
|
18
|
+
|
|
7
19
|
## [2.27.5] — 2026-06-06
|
|
8
20
|
|
|
9
21
|
### Chore
|
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.8",
|
|
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.8" : "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 {
|
|
@@ -12058,6 +12058,7 @@ var fs21 = __toESM(require("fs/promises"));
|
|
|
12058
12058
|
var import_node_stream = require("stream");
|
|
12059
12059
|
var import_sdk = require("@agentclientprotocol/sdk");
|
|
12060
12060
|
var PROTOCOL_VERSION2 = 1;
|
|
12061
|
+
var PROMPT_TIMEOUT_MS = 9e4;
|
|
12061
12062
|
var CLIENT_CAPABILITIES = {
|
|
12062
12063
|
fs: { readTextFile: true, writeTextFile: true },
|
|
12063
12064
|
terminal: false
|
|
@@ -12079,6 +12080,10 @@ var AcpClient = class {
|
|
|
12079
12080
|
async start() {
|
|
12080
12081
|
if (this.child) throw new Error("AcpClient already started");
|
|
12081
12082
|
const { adapter, cwd } = this.opts;
|
|
12083
|
+
log.info(
|
|
12084
|
+
"acpClient",
|
|
12085
|
+
`spawn cmd=${adapter.command} args=[${adapter.args.join(",")}] cwd=${cwd}`
|
|
12086
|
+
);
|
|
12082
12087
|
const child = (0, import_node_child_process11.spawn)(adapter.command, adapter.args, {
|
|
12083
12088
|
cwd,
|
|
12084
12089
|
env: process.env,
|
|
@@ -12086,10 +12091,13 @@ var AcpClient = class {
|
|
|
12086
12091
|
});
|
|
12087
12092
|
this.child = child;
|
|
12088
12093
|
child.stderr?.setEncoding("utf8");
|
|
12089
|
-
child.stderr?.on("data", (
|
|
12090
|
-
for (const line of
|
|
12094
|
+
child.stderr?.on("data", (chunk) => {
|
|
12095
|
+
for (const line of chunk.split(/\r?\n/)) {
|
|
12091
12096
|
const trimmed = line.trim();
|
|
12092
|
-
if (trimmed)
|
|
12097
|
+
if (trimmed) {
|
|
12098
|
+
log.info("acpAdapter", trimmed);
|
|
12099
|
+
this.opts.onStderr?.(trimmed);
|
|
12100
|
+
}
|
|
12093
12101
|
}
|
|
12094
12102
|
});
|
|
12095
12103
|
child.on("exit", (code, signal) => {
|
|
@@ -12107,15 +12115,22 @@ var AcpClient = class {
|
|
|
12107
12115
|
(_agent) => this.buildClient(),
|
|
12108
12116
|
stream
|
|
12109
12117
|
);
|
|
12118
|
+
log.info("acpClient", "initialize \u2192 sending");
|
|
12110
12119
|
const initialize = await this.connection.initialize({
|
|
12111
12120
|
protocolVersion: PROTOCOL_VERSION2,
|
|
12112
12121
|
clientCapabilities: CLIENT_CAPABILITIES
|
|
12113
12122
|
});
|
|
12123
|
+
log.info(
|
|
12124
|
+
"acpClient",
|
|
12125
|
+
`initialize \u2190 ok protocolVersion=${initialize.protocolVersion} agentCaps=${JSON.stringify(initialize.agentCapabilities ?? {}).slice(0, 200)}`
|
|
12126
|
+
);
|
|
12127
|
+
log.info("acpClient", "newSession \u2192 sending");
|
|
12114
12128
|
const newSession = await this.connection.newSession({
|
|
12115
12129
|
cwd,
|
|
12116
12130
|
mcpServers: []
|
|
12117
12131
|
});
|
|
12118
12132
|
this.sessionId = newSession.sessionId;
|
|
12133
|
+
log.info("acpClient", `newSession \u2190 ok sessionId=${newSession.sessionId.slice(0, 8)}`);
|
|
12119
12134
|
return { sessionId: newSession.sessionId, initialize };
|
|
12120
12135
|
}
|
|
12121
12136
|
/**
|
|
@@ -12123,15 +12138,49 @@ var AcpClient = class {
|
|
|
12123
12138
|
* {@link PromptResponse} which carries the agent's stop reason
|
|
12124
12139
|
* once the turn finishes. Session/update notifications keep
|
|
12125
12140
|
* arriving on `onSessionUpdate` while the turn streams.
|
|
12141
|
+
*
|
|
12142
|
+
* Wrapped in a hard timeout because adapters CAN hang silently
|
|
12143
|
+
* when their underlying agent's auth/network is broken — without
|
|
12144
|
+
* a ceiling the relay command sits "pending" forever and mobile
|
|
12145
|
+
* shows a permanent "Thinking…" spinner with no way to recover.
|
|
12126
12146
|
*/
|
|
12127
12147
|
async prompt(text) {
|
|
12128
12148
|
if (!this.connection || !this.sessionId) {
|
|
12129
12149
|
throw new Error("AcpClient.prompt called before start()");
|
|
12130
12150
|
}
|
|
12131
|
-
|
|
12151
|
+
log.info(
|
|
12152
|
+
"acpClient",
|
|
12153
|
+
`prompt \u2192 session=${this.sessionId.slice(0, 8)} chars=${text.length}`
|
|
12154
|
+
);
|
|
12155
|
+
const t0 = Date.now();
|
|
12156
|
+
const send = this.connection.prompt({
|
|
12132
12157
|
sessionId: this.sessionId,
|
|
12133
12158
|
prompt: [{ type: "text", text }]
|
|
12134
12159
|
});
|
|
12160
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
12161
|
+
const id = setTimeout(() => {
|
|
12162
|
+
reject(
|
|
12163
|
+
new Error(
|
|
12164
|
+
`ACP prompt timed out after ${PROMPT_TIMEOUT_MS / 1e3}s \u2014 adapter never responded. Likely the underlying agent's auth or network is misconfigured; check the adapter stderr lines above (acpAdapter tag) for the actual error.`
|
|
12165
|
+
)
|
|
12166
|
+
);
|
|
12167
|
+
}, PROMPT_TIMEOUT_MS);
|
|
12168
|
+
void send.finally(() => clearTimeout(id));
|
|
12169
|
+
});
|
|
12170
|
+
try {
|
|
12171
|
+
const result = await Promise.race([send, timeout]);
|
|
12172
|
+
log.info(
|
|
12173
|
+
"acpClient",
|
|
12174
|
+
`prompt \u2190 ok stopReason=${result.stopReason ?? "?"} elapsedMs=${Date.now() - t0}`
|
|
12175
|
+
);
|
|
12176
|
+
return result;
|
|
12177
|
+
} catch (err) {
|
|
12178
|
+
log.warn(
|
|
12179
|
+
"acpClient",
|
|
12180
|
+
`prompt \u2190 failed elapsedMs=${Date.now() - t0} err=${err instanceof Error ? err.message : String(err)}`
|
|
12181
|
+
);
|
|
12182
|
+
throw err;
|
|
12183
|
+
}
|
|
12135
12184
|
}
|
|
12136
12185
|
/**
|
|
12137
12186
|
* Cancel the in-flight prompt turn. Notification — no response.
|
|
@@ -12434,17 +12483,17 @@ function mapSessionUpdate(notification) {
|
|
|
12434
12483
|
case "agent_message_chunk": {
|
|
12435
12484
|
const text = extractText2(update.content);
|
|
12436
12485
|
if (!text) return [];
|
|
12437
|
-
return [
|
|
12486
|
+
return [{ chunkId: messageChunkId(update.messageId), kind: "text", delta: text }];
|
|
12438
12487
|
}
|
|
12439
12488
|
case "agent_thought_chunk": {
|
|
12440
12489
|
const text = extractText2(update.content);
|
|
12441
12490
|
if (!text) return [];
|
|
12442
|
-
return [
|
|
12491
|
+
return [{ chunkId: messageChunkId(update.messageId), kind: "thinking", delta: text }];
|
|
12443
12492
|
}
|
|
12444
12493
|
case "tool_call": {
|
|
12445
12494
|
const summary = describeToolCall(update);
|
|
12446
12495
|
if (!summary) return [];
|
|
12447
|
-
return [
|
|
12496
|
+
return [{ chunkId: update.toolCallId, kind: "tool_use", delta: summary }];
|
|
12448
12497
|
}
|
|
12449
12498
|
case "tool_call_update": {
|
|
12450
12499
|
if (update.status !== "completed" && update.status !== "failed") {
|
|
@@ -12453,7 +12502,7 @@ function mapSessionUpdate(notification) {
|
|
|
12453
12502
|
const body = describeToolCallUpdate(update);
|
|
12454
12503
|
if (!body) return [];
|
|
12455
12504
|
const prefix = update.status === "failed" ? "[failed] " : "";
|
|
12456
|
-
return [
|
|
12505
|
+
return [{ chunkId: update.toolCallId, kind: "tool_result", delta: prefix + body }];
|
|
12457
12506
|
}
|
|
12458
12507
|
case "user_message_chunk":
|
|
12459
12508
|
return [];
|
|
@@ -12492,9 +12541,6 @@ function mapPermissionRequest(request) {
|
|
|
12492
12541
|
kindByLabel
|
|
12493
12542
|
};
|
|
12494
12543
|
}
|
|
12495
|
-
function chunk(chunkId, kind, content) {
|
|
12496
|
-
return { chunkId, kind, content, isFinal: true };
|
|
12497
|
-
}
|
|
12498
12544
|
function messageChunkId(messageId) {
|
|
12499
12545
|
if (typeof messageId === "string" && messageId.length > 0) return messageId;
|
|
12500
12546
|
return (0, import_node_crypto5.randomUUID)();
|
|
@@ -12560,6 +12606,50 @@ function humanizeKind(kind) {
|
|
|
12560
12606
|
}
|
|
12561
12607
|
|
|
12562
12608
|
// src/agents/acp/runner.ts
|
|
12609
|
+
var StreamingState = class {
|
|
12610
|
+
constructor(publisher) {
|
|
12611
|
+
this.publisher = publisher;
|
|
12612
|
+
}
|
|
12613
|
+
publisher;
|
|
12614
|
+
open = /* @__PURE__ */ new Map();
|
|
12615
|
+
append(delta) {
|
|
12616
|
+
const existing = this.open.get(delta.chunkId);
|
|
12617
|
+
const content = (existing?.content ?? "") + delta.delta;
|
|
12618
|
+
if (existing && existing.kind !== delta.kind) {
|
|
12619
|
+
log.warn(
|
|
12620
|
+
"acpRunner",
|
|
12621
|
+
`chunk kind flip detected chunkId=${delta.chunkId.slice(0, 8)} from=${existing.kind} to=${delta.kind} \u2014 using new kind`
|
|
12622
|
+
);
|
|
12623
|
+
}
|
|
12624
|
+
this.open.set(delta.chunkId, { kind: delta.kind, content });
|
|
12625
|
+
void this.publisher.publishChunk({ chunkId: delta.chunkId, kind: delta.kind, content, isFinal: false }).catch((err) => {
|
|
12626
|
+
log.warn(
|
|
12627
|
+
"acpRunner",
|
|
12628
|
+
`publishChunk (streaming) failed: ${err instanceof Error ? err.message : String(err)}`
|
|
12629
|
+
);
|
|
12630
|
+
});
|
|
12631
|
+
}
|
|
12632
|
+
/**
|
|
12633
|
+
* Close every open chunk with `isFinal: true`. Idempotent — safe
|
|
12634
|
+
* to call multiple times per turn (e.g. once on prompt-completed,
|
|
12635
|
+
* once on cancel, once on adapter-exit).
|
|
12636
|
+
*/
|
|
12637
|
+
async closeAll() {
|
|
12638
|
+
if (this.open.size === 0) return;
|
|
12639
|
+
const closing = Array.from(this.open.entries());
|
|
12640
|
+
this.open.clear();
|
|
12641
|
+
await Promise.all(
|
|
12642
|
+
closing.map(
|
|
12643
|
+
([chunkId, { kind, content }]) => this.publisher.publishChunk({ chunkId, kind, content, isFinal: true }).catch((err) => {
|
|
12644
|
+
log.warn(
|
|
12645
|
+
"acpRunner",
|
|
12646
|
+
`publishChunk (closing) failed: ${err instanceof Error ? err.message : String(err)}`
|
|
12647
|
+
);
|
|
12648
|
+
})
|
|
12649
|
+
)
|
|
12650
|
+
);
|
|
12651
|
+
}
|
|
12652
|
+
};
|
|
12563
12653
|
var ANSWER_POLL_MS = 1500;
|
|
12564
12654
|
var PERMISSION_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
12565
12655
|
async function runAcpSession(opts) {
|
|
@@ -12568,13 +12658,21 @@ async function runAcpSession(opts) {
|
|
|
12568
12658
|
pluginId: opts.pluginId,
|
|
12569
12659
|
pluginAuthToken: opts.pluginAuthToken
|
|
12570
12660
|
});
|
|
12661
|
+
const streaming = new StreamingState(publisher);
|
|
12662
|
+
let updateCount = 0;
|
|
12571
12663
|
const client2 = new AcpClient({
|
|
12572
12664
|
adapter: opts.adapter,
|
|
12573
12665
|
cwd: opts.cwd,
|
|
12574
12666
|
onSessionUpdate: (notification) => {
|
|
12575
|
-
|
|
12576
|
-
|
|
12577
|
-
|
|
12667
|
+
updateCount += 1;
|
|
12668
|
+
const variant = notification.update?.sessionUpdate ?? "unknown";
|
|
12669
|
+
const deltas = mapSessionUpdate(notification);
|
|
12670
|
+
log.info(
|
|
12671
|
+
"acpRunner",
|
|
12672
|
+
`update #${updateCount} variant=${variant} mappedDeltas=${deltas.length}`
|
|
12673
|
+
);
|
|
12674
|
+
for (const delta of deltas) {
|
|
12675
|
+
streaming.append(delta);
|
|
12578
12676
|
}
|
|
12579
12677
|
},
|
|
12580
12678
|
onRequestPermission: async (request) => {
|
|
@@ -12594,11 +12692,11 @@ async function runAcpSession(opts) {
|
|
|
12594
12692
|
}
|
|
12595
12693
|
return { outcome: { outcome: "selected", optionId } };
|
|
12596
12694
|
},
|
|
12597
|
-
onStderr: (
|
|
12598
|
-
log.trace("acpAdapter", line);
|
|
12695
|
+
onStderr: (_line) => {
|
|
12599
12696
|
},
|
|
12600
12697
|
onUnexpectedExit: (code, signal) => {
|
|
12601
12698
|
log.warn("acpRunner", `adapter died code=${code} signal=${signal}; shutting down session`);
|
|
12699
|
+
void streaming.closeAll();
|
|
12602
12700
|
void publisher.publishChunk({
|
|
12603
12701
|
chunkId: (0, import_node_crypto6.randomUUID)(),
|
|
12604
12702
|
kind: "text",
|
|
@@ -12620,7 +12718,7 @@ async function runAcpSession(opts) {
|
|
|
12620
12718
|
const relay = new CommandRelayService(
|
|
12621
12719
|
opts.pluginId,
|
|
12622
12720
|
async (cmd) => {
|
|
12623
|
-
await handleCommand(cmd, client2, relay, acpSessionId, models);
|
|
12721
|
+
await handleCommand(cmd, client2, relay, acpSessionId, models, streaming);
|
|
12624
12722
|
},
|
|
12625
12723
|
{ id: opts.agent, name: opts.agent, displayName: opts.agent }
|
|
12626
12724
|
);
|
|
@@ -12637,7 +12735,7 @@ async function runAcpSession(opts) {
|
|
|
12637
12735
|
await new Promise(() => {
|
|
12638
12736
|
});
|
|
12639
12737
|
}
|
|
12640
|
-
async function handleCommand(cmd, client2, relay, acpSessionId, models) {
|
|
12738
|
+
async function handleCommand(cmd, client2, relay, acpSessionId, models, streaming) {
|
|
12641
12739
|
switch (cmd.type) {
|
|
12642
12740
|
case "start_task": {
|
|
12643
12741
|
const payload = cmd.payload;
|
|
@@ -12647,10 +12745,14 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models) {
|
|
|
12647
12745
|
await relay.sendResult(cmd.id, "failed", { error: "empty prompt" });
|
|
12648
12746
|
return;
|
|
12649
12747
|
}
|
|
12748
|
+
log.info("acpRunner", `start_task \u2192 forwarding prompt chars=${prompt.length} id=${cmd.id.slice(0, 8)}`);
|
|
12650
12749
|
try {
|
|
12651
12750
|
const reply = await client2.prompt(prompt);
|
|
12751
|
+
await streaming.closeAll();
|
|
12752
|
+
log.info("acpRunner", `start_task \u2190 done stopReason=${reply.stopReason ?? "?"} id=${cmd.id.slice(0, 8)}`);
|
|
12652
12753
|
await relay.sendResult(cmd.id, "completed", { stopReason: reply.stopReason });
|
|
12653
12754
|
} catch (err) {
|
|
12755
|
+
await streaming.closeAll();
|
|
12654
12756
|
log.warn("acpRunner", `prompt failed: ${describeError(err)}`);
|
|
12655
12757
|
await relay.sendResult(cmd.id, "failed", { error: describeError(err) });
|
|
12656
12758
|
}
|
|
@@ -12660,6 +12762,7 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models) {
|
|
|
12660
12762
|
case "escape_key": {
|
|
12661
12763
|
try {
|
|
12662
12764
|
await client2.cancel();
|
|
12765
|
+
await streaming.closeAll();
|
|
12663
12766
|
await relay.sendResult(cmd.id, "completed", {});
|
|
12664
12767
|
} catch (err) {
|
|
12665
12768
|
log.warn("acpRunner", `cancel failed: ${describeError(err)}`);
|
|
@@ -15129,13 +15232,13 @@ var TurnFileAggregator = class {
|
|
|
15129
15232
|
return;
|
|
15130
15233
|
}
|
|
15131
15234
|
const chunks = chunkArray(novel, MAX_BATCH_SIZE);
|
|
15132
|
-
for (const
|
|
15235
|
+
for (const chunk of chunks) {
|
|
15133
15236
|
const entry = {
|
|
15134
15237
|
turnId: (0, import_crypto2.randomUUID)(),
|
|
15135
15238
|
sessionId: this.opts.sessionId,
|
|
15136
15239
|
pluginId: this.opts.pluginId,
|
|
15137
15240
|
enqueuedAt: Date.now(),
|
|
15138
|
-
files:
|
|
15241
|
+
files: chunk
|
|
15139
15242
|
};
|
|
15140
15243
|
await this.outbox.enqueue(entry);
|
|
15141
15244
|
}
|
|
@@ -15296,11 +15399,11 @@ var StreamingEmitterService = class {
|
|
|
15296
15399
|
}
|
|
15297
15400
|
if (this.activeChunk) {
|
|
15298
15401
|
const finalContent = this.activeChunk.currentContent;
|
|
15299
|
-
const
|
|
15402
|
+
const chunk = this.activeChunk;
|
|
15300
15403
|
this.activeChunk = null;
|
|
15301
15404
|
await this.postChunk({
|
|
15302
|
-
chunkId:
|
|
15303
|
-
kind:
|
|
15405
|
+
chunkId: chunk.chunkId,
|
|
15406
|
+
kind: chunk.kind,
|
|
15304
15407
|
content: finalContent,
|
|
15305
15408
|
isFinal: true
|
|
15306
15409
|
});
|
|
@@ -15372,17 +15475,17 @@ var StreamingEmitterService = class {
|
|
|
15372
15475
|
this.maybeFlushActive(false);
|
|
15373
15476
|
}
|
|
15374
15477
|
maybeFlushActive(force) {
|
|
15375
|
-
const
|
|
15376
|
-
if (!
|
|
15478
|
+
const chunk = this.activeChunk;
|
|
15479
|
+
if (!chunk) return;
|
|
15377
15480
|
const now = Date.now();
|
|
15378
|
-
if (!force && now -
|
|
15379
|
-
if (
|
|
15380
|
-
|
|
15381
|
-
|
|
15481
|
+
if (!force && now - chunk.lastEmitAt < TICK_MS) return;
|
|
15482
|
+
if (chunk.currentContent === chunk.emittedContent) return;
|
|
15483
|
+
chunk.emittedContent = chunk.currentContent;
|
|
15484
|
+
chunk.lastEmitAt = now;
|
|
15382
15485
|
void this.postChunk({
|
|
15383
|
-
chunkId:
|
|
15384
|
-
kind:
|
|
15385
|
-
content:
|
|
15486
|
+
chunkId: chunk.chunkId,
|
|
15487
|
+
kind: chunk.kind,
|
|
15488
|
+
content: chunk.currentContent,
|
|
15386
15489
|
isFinal: false
|
|
15387
15490
|
});
|
|
15388
15491
|
}
|
|
@@ -17919,8 +18022,8 @@ var previewStartH = (ctx, _cmd, parsed) => {
|
|
|
17919
18022
|
let readyMatched = false;
|
|
17920
18023
|
let expoUrl = null;
|
|
17921
18024
|
const readyRe = new RegExp(detection.ready_pattern);
|
|
17922
|
-
const onChunk = (
|
|
17923
|
-
const s =
|
|
18025
|
+
const onChunk = (chunk) => {
|
|
18026
|
+
const s = chunk.toString();
|
|
17924
18027
|
if (!readyMatched && readyRe.test(s)) readyMatched = true;
|
|
17925
18028
|
if (!expoUrl && detection.framework === "Expo") expoUrl = parseExpoUrl(s);
|
|
17926
18029
|
};
|
|
@@ -18043,8 +18146,8 @@ var previewStartH = (ctx, _cmd, parsed) => {
|
|
|
18043
18146
|
stdio: ["ignore", "pipe", "pipe"]
|
|
18044
18147
|
});
|
|
18045
18148
|
let parsedUrl = null;
|
|
18046
|
-
const onTunnelChunk = (
|
|
18047
|
-
const s =
|
|
18149
|
+
const onTunnelChunk = (chunk) => {
|
|
18150
|
+
const s = chunk.toString();
|
|
18048
18151
|
if (!parsedUrl) parsedUrl = parseCloudflaredUrl(s);
|
|
18049
18152
|
const trimmed = s.replace(/\n+$/g, "");
|
|
18050
18153
|
if (trimmed.length > 0) log.info("preview", `cloudflared: ${trimmed}`);
|
|
@@ -18143,8 +18246,8 @@ function runOnce(cmd, args2, cwd, env) {
|
|
|
18143
18246
|
stdio: ["ignore", "pipe", "pipe"]
|
|
18144
18247
|
});
|
|
18145
18248
|
const tag = `setup:${cmd}`;
|
|
18146
|
-
const onChunk = (
|
|
18147
|
-
const text =
|
|
18249
|
+
const onChunk = (chunk) => {
|
|
18250
|
+
const text = chunk.toString().replace(/\n+$/g, "");
|
|
18148
18251
|
if (text.length === 0) return;
|
|
18149
18252
|
log.info("preview", `${tag}: ${text}`);
|
|
18150
18253
|
};
|
|
@@ -21171,7 +21274,7 @@ function checkChokidar() {
|
|
|
21171
21274
|
}
|
|
21172
21275
|
async function doctor(args2 = []) {
|
|
21173
21276
|
const json = args2.includes("--json");
|
|
21174
|
-
const cliVersion = true ? "2.27.
|
|
21277
|
+
const cliVersion = true ? "2.27.8" : "0.0.0-dev";
|
|
21175
21278
|
const apiBase = resolveApiBaseUrl();
|
|
21176
21279
|
const diagnosticId = (0, import_node_crypto8.randomUUID)();
|
|
21177
21280
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -21370,7 +21473,7 @@ async function completion(args2) {
|
|
|
21370
21473
|
// src/commands/version.ts
|
|
21371
21474
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
21372
21475
|
function version2() {
|
|
21373
|
-
const v = true ? "2.27.
|
|
21476
|
+
const v = true ? "2.27.8" : "unknown";
|
|
21374
21477
|
console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
|
|
21375
21478
|
}
|
|
21376
21479
|
|
|
@@ -21557,8 +21660,8 @@ function fetchLatest() {
|
|
|
21557
21660
|
}
|
|
21558
21661
|
let buf = "";
|
|
21559
21662
|
res.setEncoding("utf8");
|
|
21560
|
-
res.on("data", (
|
|
21561
|
-
buf +=
|
|
21663
|
+
res.on("data", (chunk) => {
|
|
21664
|
+
buf += chunk;
|
|
21562
21665
|
});
|
|
21563
21666
|
res.on("end", () => {
|
|
21564
21667
|
try {
|
|
@@ -21598,7 +21701,7 @@ function checkForUpdates() {
|
|
|
21598
21701
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
21599
21702
|
if (process.env.CI) return;
|
|
21600
21703
|
if (!process.stdout.isTTY) return;
|
|
21601
|
-
const current = true ? "2.27.
|
|
21704
|
+
const current = true ? "2.27.8" : null;
|
|
21602
21705
|
if (!current) return;
|
|
21603
21706
|
const cache = readCache();
|
|
21604
21707
|
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.8",
|
|
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",
|