codeam-cli 2.27.5 → 2.27.7
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 +117 -19
- 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.6] — 2026-06-06
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** ACP runner — ack every relay command + body envelope for auth
|
|
12
|
+
|
|
13
|
+
## [2.27.5] — 2026-06-06
|
|
14
|
+
|
|
15
|
+
### Chore
|
|
16
|
+
|
|
17
|
+
- **cli:** Drop cursor-agent-acp — pulls deprecated SDK
|
|
18
|
+
|
|
7
19
|
## [2.27.4] — 2026-06-06
|
|
8
20
|
|
|
9
21
|
### Tests
|
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.7",
|
|
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",
|
|
@@ -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.7" : "0.0.0-dev",
|
|
5877
5877
|
nodeVersion: process.version,
|
|
5878
5878
|
platform: process.platform,
|
|
5879
5879
|
arch: process.arch,
|
|
@@ -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,
|
|
@@ -12089,7 +12094,10 @@ var AcpClient = class {
|
|
|
12089
12094
|
child.stderr?.on("data", (chunk2) => {
|
|
12090
12095
|
for (const line of chunk2.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.
|
|
@@ -12324,6 +12373,21 @@ var AcpPublisher = class {
|
|
|
12324
12373
|
opts;
|
|
12325
12374
|
apiBase;
|
|
12326
12375
|
headers;
|
|
12376
|
+
/**
|
|
12377
|
+
* Wrap the event with `sessionId` + `pluginId` at the top level.
|
|
12378
|
+
* The backend's `PluginAuthGuard` reads both fields from the JSON
|
|
12379
|
+
* body even when `X-Plugin-Auth-Token` is set on the header and
|
|
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.
|
|
12383
|
+
*/
|
|
12384
|
+
envelope(event) {
|
|
12385
|
+
return JSON.stringify({
|
|
12386
|
+
sessionId: this.opts.sessionId,
|
|
12387
|
+
pluginId: this.opts.pluginId,
|
|
12388
|
+
...event
|
|
12389
|
+
});
|
|
12390
|
+
}
|
|
12327
12391
|
/**
|
|
12328
12392
|
* Fire-and-forget chunk POST. The backend's per-user SSE bus
|
|
12329
12393
|
* forwards each chunk to mobile/landing within ~20 ms (PRO) /
|
|
@@ -12336,7 +12400,7 @@ var AcpPublisher = class {
|
|
|
12336
12400
|
const { statusCode, body } = await _transport2.post(
|
|
12337
12401
|
url,
|
|
12338
12402
|
this.headers,
|
|
12339
|
-
|
|
12403
|
+
this.envelope(event)
|
|
12340
12404
|
);
|
|
12341
12405
|
if (statusCode < 200 || statusCode >= 300) {
|
|
12342
12406
|
log.warn("acpPublisher", `chunk status=${statusCode} body=${body.slice(0, 200)}`);
|
|
@@ -12357,7 +12421,7 @@ var AcpPublisher = class {
|
|
|
12357
12421
|
const { statusCode, body } = await _transport2.post(
|
|
12358
12422
|
url,
|
|
12359
12423
|
this.headers,
|
|
12360
|
-
|
|
12424
|
+
this.envelope(event)
|
|
12361
12425
|
);
|
|
12362
12426
|
if (statusCode < 200 || statusCode >= 300) {
|
|
12363
12427
|
log.warn("acpPublisher", `awaiting-answer status=${statusCode} body=${body.slice(0, 200)}`);
|
|
@@ -12553,13 +12617,22 @@ async function runAcpSession(opts) {
|
|
|
12553
12617
|
pluginId: opts.pluginId,
|
|
12554
12618
|
pluginAuthToken: opts.pluginAuthToken
|
|
12555
12619
|
});
|
|
12620
|
+
let updateCount = 0;
|
|
12556
12621
|
const client2 = new AcpClient({
|
|
12557
12622
|
adapter: opts.adapter,
|
|
12558
12623
|
cwd: opts.cwd,
|
|
12559
12624
|
onSessionUpdate: (notification) => {
|
|
12625
|
+
updateCount += 1;
|
|
12626
|
+
const variant = notification.update?.sessionUpdate ?? "unknown";
|
|
12560
12627
|
const chunks = mapSessionUpdate(notification);
|
|
12628
|
+
log.info(
|
|
12629
|
+
"acpRunner",
|
|
12630
|
+
`update #${updateCount} variant=${variant} mappedChunks=${chunks.length}`
|
|
12631
|
+
);
|
|
12561
12632
|
for (const chunk2 of chunks) {
|
|
12562
|
-
void publisher.publishChunk(chunk2)
|
|
12633
|
+
void publisher.publishChunk(chunk2).catch((err) => {
|
|
12634
|
+
log.warn("acpRunner", `publishChunk failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
12635
|
+
});
|
|
12563
12636
|
}
|
|
12564
12637
|
},
|
|
12565
12638
|
onRequestPermission: async (request) => {
|
|
@@ -12579,8 +12652,7 @@ async function runAcpSession(opts) {
|
|
|
12579
12652
|
}
|
|
12580
12653
|
return { outcome: { outcome: "selected", optionId } };
|
|
12581
12654
|
},
|
|
12582
|
-
onStderr: (
|
|
12583
|
-
log.trace("acpAdapter", line);
|
|
12655
|
+
onStderr: (_line) => {
|
|
12584
12656
|
},
|
|
12585
12657
|
onUnexpectedExit: (code, signal) => {
|
|
12586
12658
|
log.warn("acpRunner", `adapter died code=${code} signal=${signal}; shutting down session`);
|
|
@@ -12599,10 +12671,13 @@ async function runAcpSession(opts) {
|
|
|
12599
12671
|
"acpRunner",
|
|
12600
12672
|
`adapter handshake ok protocolVersion=${initialize.protocolVersion} sessionId=${acpSessionId.slice(0, 8)}`
|
|
12601
12673
|
);
|
|
12674
|
+
showSuccess(`${opts.agent} online (ACP) \u2014 awaiting prompts from mobile.`);
|
|
12675
|
+
const runtime = createInteractiveAgentStrategy(opts.agent, createOsStrategy());
|
|
12676
|
+
const models = await runtime.listModels();
|
|
12602
12677
|
const relay = new CommandRelayService(
|
|
12603
12678
|
opts.pluginId,
|
|
12604
12679
|
async (cmd) => {
|
|
12605
|
-
await handleCommand(cmd, client2);
|
|
12680
|
+
await handleCommand(cmd, client2, relay, acpSessionId, models);
|
|
12606
12681
|
},
|
|
12607
12682
|
{ id: opts.agent, name: opts.agent, displayName: opts.agent }
|
|
12608
12683
|
);
|
|
@@ -12619,19 +12694,24 @@ async function runAcpSession(opts) {
|
|
|
12619
12694
|
await new Promise(() => {
|
|
12620
12695
|
});
|
|
12621
12696
|
}
|
|
12622
|
-
async function handleCommand(cmd, client2) {
|
|
12697
|
+
async function handleCommand(cmd, client2, relay, acpSessionId, models) {
|
|
12623
12698
|
switch (cmd.type) {
|
|
12624
12699
|
case "start_task": {
|
|
12625
12700
|
const payload = cmd.payload;
|
|
12626
12701
|
const prompt = payload?.prompt?.trim();
|
|
12627
12702
|
if (!prompt) {
|
|
12628
12703
|
log.warn("acpRunner", "start_task with empty prompt; ignoring");
|
|
12704
|
+
await relay.sendResult(cmd.id, "failed", { error: "empty prompt" });
|
|
12629
12705
|
return;
|
|
12630
12706
|
}
|
|
12707
|
+
log.info("acpRunner", `start_task \u2192 forwarding prompt chars=${prompt.length} id=${cmd.id.slice(0, 8)}`);
|
|
12631
12708
|
try {
|
|
12632
|
-
await client2.prompt(prompt);
|
|
12709
|
+
const reply = await client2.prompt(prompt);
|
|
12710
|
+
log.info("acpRunner", `start_task \u2190 done stopReason=${reply.stopReason ?? "?"} id=${cmd.id.slice(0, 8)}`);
|
|
12711
|
+
await relay.sendResult(cmd.id, "completed", { stopReason: reply.stopReason });
|
|
12633
12712
|
} catch (err) {
|
|
12634
12713
|
log.warn("acpRunner", `prompt failed: ${describeError(err)}`);
|
|
12714
|
+
await relay.sendResult(cmd.id, "failed", { error: describeError(err) });
|
|
12635
12715
|
}
|
|
12636
12716
|
return;
|
|
12637
12717
|
}
|
|
@@ -12639,13 +12719,31 @@ async function handleCommand(cmd, client2) {
|
|
|
12639
12719
|
case "escape_key": {
|
|
12640
12720
|
try {
|
|
12641
12721
|
await client2.cancel();
|
|
12722
|
+
await relay.sendResult(cmd.id, "completed", {});
|
|
12642
12723
|
} catch (err) {
|
|
12643
12724
|
log.warn("acpRunner", `cancel failed: ${describeError(err)}`);
|
|
12725
|
+
await relay.sendResult(cmd.id, "failed", { error: describeError(err) });
|
|
12644
12726
|
}
|
|
12645
12727
|
return;
|
|
12646
12728
|
}
|
|
12729
|
+
case "get_conversation": {
|
|
12730
|
+
await relay.sendResult(cmd.id, "completed", { conversationId: acpSessionId });
|
|
12731
|
+
return;
|
|
12732
|
+
}
|
|
12733
|
+
case "list_models": {
|
|
12734
|
+
await relay.sendResult(cmd.id, "completed", { models });
|
|
12735
|
+
return;
|
|
12736
|
+
}
|
|
12737
|
+
case "set_keep_alive":
|
|
12738
|
+
case "get_context": {
|
|
12739
|
+
await relay.sendResult(cmd.id, "completed", {});
|
|
12740
|
+
return;
|
|
12741
|
+
}
|
|
12647
12742
|
default:
|
|
12648
12743
|
log.trace("acpRunner", `command type "${cmd.type}" not supported in Phase 1 ACP mode`);
|
|
12744
|
+
await relay.sendResult(cmd.id, "failed", {
|
|
12745
|
+
error: `Command "${cmd.type}" is not supported in Phase 1 ACP mode.`
|
|
12746
|
+
});
|
|
12649
12747
|
return;
|
|
12650
12748
|
}
|
|
12651
12749
|
}
|
|
@@ -14816,7 +14914,7 @@ async function discoverRepos(workingDir, maxDepth = 4) {
|
|
|
14816
14914
|
// src/services/turn-files/files-outbox.ts
|
|
14817
14915
|
var fs24 = __toESM(require("fs/promises"));
|
|
14818
14916
|
var path29 = __toESM(require("path"));
|
|
14819
|
-
var
|
|
14917
|
+
var import_os8 = require("os");
|
|
14820
14918
|
var HOME_OUTBOX_DIR = ".codeam/outbox";
|
|
14821
14919
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
14822
14920
|
var BACKOFF_STEPS_MS = [
|
|
@@ -14983,7 +15081,7 @@ function applyJitter(ms) {
|
|
|
14983
15081
|
return Math.round(ms * factor);
|
|
14984
15082
|
}
|
|
14985
15083
|
function homeDir() {
|
|
14986
|
-
return process.env.HOME ?? process.env.USERPROFILE ?? (0,
|
|
15084
|
+
return process.env.HOME ?? process.env.USERPROFILE ?? (0, import_os8.tmpdir)();
|
|
14987
15085
|
}
|
|
14988
15086
|
|
|
14989
15087
|
// src/services/turn-files/turn-file-aggregator.ts
|
|
@@ -16932,11 +17030,11 @@ async function linkDryRunPreflight(ctx) {
|
|
|
16932
17030
|
var import_promises = require("dns/promises");
|
|
16933
17031
|
var import_fs = require("fs");
|
|
16934
17032
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
16935
|
-
var
|
|
17033
|
+
var import_os9 = __toESM(require("os"));
|
|
16936
17034
|
var import_path4 = __toESM(require("path"));
|
|
16937
17035
|
var import_promises3 = require("stream/promises");
|
|
16938
17036
|
var import_which = __toESM(require("which"));
|
|
16939
|
-
var CACHED_BINARY = import_path4.default.join(
|
|
17037
|
+
var CACHED_BINARY = import_path4.default.join(import_os9.default.homedir(), ".codeam", "bin", "cloudflared");
|
|
16940
17038
|
async function waitForCloudflaredReady(url, timeoutMs = 6e4) {
|
|
16941
17039
|
const hostname3 = new URL(url).hostname;
|
|
16942
17040
|
const resolver = new import_promises.Resolver();
|
|
@@ -21132,7 +21230,7 @@ function checkChokidar() {
|
|
|
21132
21230
|
}
|
|
21133
21231
|
async function doctor(args2 = []) {
|
|
21134
21232
|
const json = args2.includes("--json");
|
|
21135
|
-
const cliVersion = true ? "2.27.
|
|
21233
|
+
const cliVersion = true ? "2.27.7" : "0.0.0-dev";
|
|
21136
21234
|
const apiBase = resolveApiBaseUrl();
|
|
21137
21235
|
const diagnosticId = (0, import_node_crypto8.randomUUID)();
|
|
21138
21236
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -21331,7 +21429,7 @@ async function completion(args2) {
|
|
|
21331
21429
|
// src/commands/version.ts
|
|
21332
21430
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
21333
21431
|
function version2() {
|
|
21334
|
-
const v = true ? "2.27.
|
|
21432
|
+
const v = true ? "2.27.7" : "unknown";
|
|
21335
21433
|
console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
|
|
21336
21434
|
}
|
|
21337
21435
|
|
|
@@ -21559,7 +21657,7 @@ function checkForUpdates() {
|
|
|
21559
21657
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
21560
21658
|
if (process.env.CI) return;
|
|
21561
21659
|
if (!process.stdout.isTTY) return;
|
|
21562
|
-
const current = true ? "2.27.
|
|
21660
|
+
const current = true ? "2.27.7" : null;
|
|
21563
21661
|
if (!current) return;
|
|
21564
21662
|
const cache = readCache();
|
|
21565
21663
|
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.7",
|
|
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",
|