codeam-cli 2.76.36 → 2.76.38
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 +17 -0
- package/dist/index.js +264 -48
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@ 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.76.37] — 2026-10-26
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** Require cloudflared >= 2026.10.2 for previews (#994)
|
|
12
|
+
|
|
13
|
+
## [2.76.36] — 2026-10-25
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **shared:** One Point-at-it picker for Remix and the Preview
|
|
18
|
+
- **cli:** The Preview picker is Remix's Point at it
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **shared:** Point picker swallows the post-pick click, valid selectors, reload-safe mark ids
|
|
23
|
+
|
|
7
24
|
## [2.76.35] — 2026-10-22
|
|
8
25
|
|
|
9
26
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -11354,7 +11354,7 @@ function readAnonId() {
|
|
|
11354
11354
|
}
|
|
11355
11355
|
function superProperties() {
|
|
11356
11356
|
return {
|
|
11357
|
-
cliVersion: true ? "2.76.
|
|
11357
|
+
cliVersion: true ? "2.76.38" : "0.0.0-dev",
|
|
11358
11358
|
nodeVersion: process.version,
|
|
11359
11359
|
platform: process.platform,
|
|
11360
11360
|
arch: process.arch,
|
|
@@ -11603,7 +11603,7 @@ var http = __toESM(require("http"));
|
|
|
11603
11603
|
// package.json
|
|
11604
11604
|
var package_default = {
|
|
11605
11605
|
name: "codeam-cli",
|
|
11606
|
-
version: "2.76.
|
|
11606
|
+
version: "2.76.38",
|
|
11607
11607
|
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.",
|
|
11608
11608
|
type: "commonjs",
|
|
11609
11609
|
main: "dist/index.js",
|
|
@@ -12979,7 +12979,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
12979
12979
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
12980
12980
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
12981
12981
|
// pair/reconnect). Older backends ignore the extra field.
|
|
12982
|
-
..."2.76.
|
|
12982
|
+
..."2.76.38" ? { ideVersion: "2.76.38" } : {}
|
|
12983
12983
|
};
|
|
12984
12984
|
try {
|
|
12985
12985
|
const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body, this.pollSecretHeader());
|
|
@@ -24113,27 +24113,153 @@ var import_os10 = __toESM(require("os"));
|
|
|
24113
24113
|
var import_path5 = __toESM(require("path"));
|
|
24114
24114
|
var import_promises3 = require("stream/promises");
|
|
24115
24115
|
var import_which = __toESM(require("which"));
|
|
24116
|
-
var
|
|
24116
|
+
var CACHE_DIR = import_path5.default.join(import_os10.default.homedir(), ".codeam", "bin");
|
|
24117
|
+
var LEGACY_CACHED_BINARY = import_path5.default.join(CACHE_DIR, "cloudflared");
|
|
24118
|
+
function cachedCloudflaredPath(platform3 = process.platform) {
|
|
24119
|
+
return platform3 === "win32" ? import_path5.default.join(CACHE_DIR, "cloudflared.exe") : LEGACY_CACHED_BINARY;
|
|
24120
|
+
}
|
|
24121
|
+
var MIN_CLOUDFLARED_VERSION = [2026, 10, 2];
|
|
24122
|
+
var MIN_CLOUDFLARED_VERSION_DARWIN_NON_BREW = [2026, 10, 3];
|
|
24123
|
+
var DEFAULT_HOMEBREW_PREFIXES = [
|
|
24124
|
+
"/opt/homebrew/",
|
|
24125
|
+
"/usr/local/Cellar/",
|
|
24126
|
+
"/usr/local/Homebrew/",
|
|
24127
|
+
"/home/linuxbrew/"
|
|
24128
|
+
];
|
|
24129
|
+
function homebrewPrefixes(env = process.env) {
|
|
24130
|
+
const custom = env.HOMEBREW_PREFIX?.replace(/\/+$/, "");
|
|
24131
|
+
return custom ? [...DEFAULT_HOMEBREW_PREFIXES, `${custom}/`, `${custom}/Cellar/`] : [...DEFAULT_HOMEBREW_PREFIXES];
|
|
24132
|
+
}
|
|
24133
|
+
function parseCloudflaredVersion(output) {
|
|
24134
|
+
const m = /cloudflared version (\d+)\.(\d+)\.(\d+)/.exec(output);
|
|
24135
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
24136
|
+
}
|
|
24137
|
+
function isHomebrewPath(bin, prefixes = homebrewPrefixes()) {
|
|
24138
|
+
return prefixes.some((prefix) => bin.startsWith(prefix));
|
|
24139
|
+
}
|
|
24140
|
+
function formatCloudflaredVersion(v) {
|
|
24141
|
+
return v.join(".");
|
|
24142
|
+
}
|
|
24143
|
+
function minimumCloudflaredVersion(bin, platform3 = process.platform, prefixes = homebrewPrefixes()) {
|
|
24144
|
+
return platform3 === "darwin" && !isHomebrewPath(bin, prefixes) ? MIN_CLOUDFLARED_VERSION_DARWIN_NON_BREW : MIN_CLOUDFLARED_VERSION;
|
|
24145
|
+
}
|
|
24146
|
+
function compareVersions(a, b) {
|
|
24147
|
+
for (let i = 0; i < 3; i++) {
|
|
24148
|
+
if (a[i] !== b[i]) return a[i] - b[i];
|
|
24149
|
+
}
|
|
24150
|
+
return 0;
|
|
24151
|
+
}
|
|
24152
|
+
function meetsMinimumCloudflaredVersion(version3, bin, platform3 = process.platform, prefixes = homebrewPrefixes()) {
|
|
24153
|
+
if (!version3) return false;
|
|
24154
|
+
return compareVersions(version3, minimumCloudflaredVersion(bin, platform3, prefixes)) >= 0;
|
|
24155
|
+
}
|
|
24156
|
+
var versionCache = /* @__PURE__ */ new Map();
|
|
24157
|
+
var warnedOutdated = /* @__PURE__ */ new Set();
|
|
24158
|
+
function readCloudflaredVersion(bin) {
|
|
24159
|
+
let cached2 = versionCache.get(bin);
|
|
24160
|
+
if (!cached2) {
|
|
24161
|
+
const pending2 = new Promise((resolve13) => {
|
|
24162
|
+
(0, import_child_process14.execFile)(bin, ["--version"], { timeout: 1e4 }, (err, stdout, stderr) => {
|
|
24163
|
+
resolve13(err ? null : parseCloudflaredVersion(`${stdout}
|
|
24164
|
+
${stderr}`));
|
|
24165
|
+
});
|
|
24166
|
+
}).then((version3) => {
|
|
24167
|
+
if (!version3 && versionCache.get(bin) === pending2) versionCache.delete(bin);
|
|
24168
|
+
return version3;
|
|
24169
|
+
});
|
|
24170
|
+
cached2 = pending2;
|
|
24171
|
+
versionCache.set(bin, pending2);
|
|
24172
|
+
}
|
|
24173
|
+
return cached2;
|
|
24174
|
+
}
|
|
24175
|
+
async function realpathOrSelf(p2) {
|
|
24176
|
+
try {
|
|
24177
|
+
return await import_promises2.default.realpath(p2);
|
|
24178
|
+
} catch {
|
|
24179
|
+
return p2;
|
|
24180
|
+
}
|
|
24181
|
+
}
|
|
24182
|
+
async function checkCloudflaredVersion(bin) {
|
|
24183
|
+
const version3 = await readCloudflaredVersion(bin);
|
|
24184
|
+
const real = await realpathOrSelf(bin);
|
|
24185
|
+
const prefixes = homebrewPrefixes();
|
|
24186
|
+
const customPrefix = process.env.HOMEBREW_PREFIX?.replace(/\/+$/, "");
|
|
24187
|
+
if (customPrefix) {
|
|
24188
|
+
const realPrefix = await realpathOrSelf(customPrefix);
|
|
24189
|
+
if (realPrefix !== customPrefix) prefixes.push(`${realPrefix}/`, `${realPrefix}/Cellar/`);
|
|
24190
|
+
}
|
|
24191
|
+
const brewBin = isHomebrewPath(bin, prefixes) ? bin : real;
|
|
24192
|
+
return {
|
|
24193
|
+
ok: meetsMinimumCloudflaredVersion(version3, brewBin, process.platform, prefixes),
|
|
24194
|
+
version: version3,
|
|
24195
|
+
min: minimumCloudflaredVersion(brewBin, process.platform, prefixes)
|
|
24196
|
+
};
|
|
24197
|
+
}
|
|
24198
|
+
async function migrateLegacyWindowsCache(target) {
|
|
24199
|
+
if (target === LEGACY_CACHED_BINARY) return;
|
|
24200
|
+
try {
|
|
24201
|
+
await import_promises2.default.access(target);
|
|
24202
|
+
return;
|
|
24203
|
+
} catch {
|
|
24204
|
+
}
|
|
24205
|
+
try {
|
|
24206
|
+
await import_promises2.default.access(LEGACY_CACHED_BINARY);
|
|
24207
|
+
await import_promises2.default.rename(LEGACY_CACHED_BINARY, target);
|
|
24208
|
+
} catch {
|
|
24209
|
+
}
|
|
24210
|
+
}
|
|
24211
|
+
function describeVersion(v) {
|
|
24212
|
+
return v ? formatCloudflaredVersion(v) : "an unknown version";
|
|
24213
|
+
}
|
|
24117
24214
|
async function resolveCloudflared(opts = {}) {
|
|
24215
|
+
let pathBin;
|
|
24118
24216
|
try {
|
|
24119
|
-
|
|
24217
|
+
pathBin = await (0, import_which.default)("cloudflared");
|
|
24120
24218
|
} catch {
|
|
24121
24219
|
}
|
|
24220
|
+
if (pathBin) {
|
|
24221
|
+
const check2 = await checkCloudflaredVersion(pathBin);
|
|
24222
|
+
if (check2.ok) return pathBin;
|
|
24223
|
+
if (!warnedOutdated.has(pathBin)) {
|
|
24224
|
+
warnedOutdated.add(pathBin);
|
|
24225
|
+
log.warn(
|
|
24226
|
+
"preview",
|
|
24227
|
+
`ignoring ${pathBin} (${describeVersion(check2.version)}): previews need cloudflared >= ${formatCloudflaredVersion(check2.min)}; upgrade cloudflared with your package manager (e.g. \`brew upgrade cloudflared\`). Using a CodeAgent-managed copy instead.`
|
|
24228
|
+
);
|
|
24229
|
+
}
|
|
24230
|
+
}
|
|
24231
|
+
const cached2 = cachedCloudflaredPath();
|
|
24232
|
+
await migrateLegacyWindowsCache(cached2);
|
|
24233
|
+
let staleCache = false;
|
|
24122
24234
|
try {
|
|
24123
|
-
await import_promises2.default.access(
|
|
24124
|
-
if (await isExecutableBinary(
|
|
24125
|
-
|
|
24235
|
+
await import_promises2.default.access(cached2);
|
|
24236
|
+
if (!await isExecutableBinary(cached2)) {
|
|
24237
|
+
await import_promises2.default.rm(cached2, { force: true });
|
|
24238
|
+
} else if ((await checkCloudflaredVersion(cached2)).ok) {
|
|
24239
|
+
return cached2;
|
|
24240
|
+
} else {
|
|
24241
|
+
staleCache = true;
|
|
24126
24242
|
}
|
|
24127
|
-
await import_promises2.default.rm(CACHED_BINARY, { force: true });
|
|
24128
24243
|
} catch {
|
|
24129
24244
|
}
|
|
24130
24245
|
if (opts.skipDownload) {
|
|
24131
24246
|
throw new Error(
|
|
24132
|
-
|
|
24247
|
+
`cloudflared not installed (or older than ${formatCloudflaredVersion(minimumCloudflaredVersion(cached2))}). Install or upgrade via \`brew install cloudflared\` (macOS) or download from https://github.com/cloudflare/cloudflared/releases.`
|
|
24133
24248
|
);
|
|
24134
24249
|
}
|
|
24135
|
-
await
|
|
24136
|
-
|
|
24250
|
+
if (staleCache) await import_promises2.default.rm(cached2, { force: true });
|
|
24251
|
+
versionCache.delete(cached2);
|
|
24252
|
+
await downloadCloudflared(cached2);
|
|
24253
|
+
versionCache.delete(cached2);
|
|
24254
|
+
const check = await checkCloudflaredVersion(cached2);
|
|
24255
|
+
if (!check.ok) {
|
|
24256
|
+
await import_promises2.default.rm(cached2, { force: true });
|
|
24257
|
+
versionCache.delete(cached2);
|
|
24258
|
+
throw new Error(
|
|
24259
|
+
`Downloaded cloudflared reports ${describeVersion(check.version)}, but previews need cloudflared >= ${formatCloudflaredVersion(check.min)}. Install the latest version manually via \`brew install cloudflared\` (macOS) or from https://github.com/cloudflare/cloudflared/releases.`
|
|
24260
|
+
);
|
|
24261
|
+
}
|
|
24262
|
+
return cached2;
|
|
24137
24263
|
}
|
|
24138
24264
|
async function downloadCloudflared(target) {
|
|
24139
24265
|
const url2 = downloadUrlForPlatform();
|
|
@@ -39345,6 +39471,95 @@ function _get(url2, headers) {
|
|
|
39345
39471
|
});
|
|
39346
39472
|
}
|
|
39347
39473
|
|
|
39474
|
+
// src/services/streaming/chunk-coalescer.ts
|
|
39475
|
+
var STREAMING_CHUNK_MIN_INTERVAL_MS = 100;
|
|
39476
|
+
var FINALIZED_MEMORY = 512;
|
|
39477
|
+
var StreamingChunkCoalescer = class {
|
|
39478
|
+
constructor(opts) {
|
|
39479
|
+
this.opts = opts;
|
|
39480
|
+
}
|
|
39481
|
+
opts;
|
|
39482
|
+
chunks = /* @__PURE__ */ new Map();
|
|
39483
|
+
finalized = /* @__PURE__ */ new Set();
|
|
39484
|
+
submit(event) {
|
|
39485
|
+
const { chunkId } = event;
|
|
39486
|
+
if (this.finalized.has(chunkId)) return Promise.resolve();
|
|
39487
|
+
const state = this.chunks.get(chunkId) ?? this.open(chunkId);
|
|
39488
|
+
const settled = new Promise((resolve13) => {
|
|
39489
|
+
const waiters = state.pending ? state.pending.waiters : [];
|
|
39490
|
+
waiters.push(resolve13);
|
|
39491
|
+
state.pending = { event, waiters };
|
|
39492
|
+
});
|
|
39493
|
+
if (event.isFinal) {
|
|
39494
|
+
this.markFinalized(chunkId);
|
|
39495
|
+
this.drain(chunkId, state);
|
|
39496
|
+
} else {
|
|
39497
|
+
this.schedule(chunkId, state);
|
|
39498
|
+
}
|
|
39499
|
+
return settled;
|
|
39500
|
+
}
|
|
39501
|
+
/** Send every pending snapshot now (ignoring the spacing) and wait for all
|
|
39502
|
+
* requests to settle. Used on shutdown so the latest content is not lost. */
|
|
39503
|
+
async flush() {
|
|
39504
|
+
const work = Array.from(this.chunks.entries()).map(async ([chunkId, state]) => {
|
|
39505
|
+
while (state.inFlight || state.pending) {
|
|
39506
|
+
if (!state.inFlight) this.drain(chunkId, state);
|
|
39507
|
+
await state.inFlight;
|
|
39508
|
+
}
|
|
39509
|
+
});
|
|
39510
|
+
await Promise.all(work);
|
|
39511
|
+
}
|
|
39512
|
+
open(chunkId) {
|
|
39513
|
+
const state = { pending: null, inFlight: null, timer: null, lastSentAt: 0 };
|
|
39514
|
+
this.chunks.set(chunkId, state);
|
|
39515
|
+
return state;
|
|
39516
|
+
}
|
|
39517
|
+
schedule(chunkId, state) {
|
|
39518
|
+
if (state.inFlight || state.timer || !state.pending) return;
|
|
39519
|
+
const wait = this.intervalFor(state.pending.event) - (Date.now() - state.lastSentAt);
|
|
39520
|
+
if (wait <= 0) {
|
|
39521
|
+
this.drain(chunkId, state);
|
|
39522
|
+
return;
|
|
39523
|
+
}
|
|
39524
|
+
state.timer = setTimeout(() => {
|
|
39525
|
+
state.timer = null;
|
|
39526
|
+
this.drain(chunkId, state);
|
|
39527
|
+
}, wait);
|
|
39528
|
+
}
|
|
39529
|
+
/** Send the pending snapshot now, unless a request is still in flight
|
|
39530
|
+
* (its completion re-enters here or {@link schedule}). */
|
|
39531
|
+
drain(chunkId, state) {
|
|
39532
|
+
if (state.inFlight || !state.pending) return;
|
|
39533
|
+
if (state.timer) {
|
|
39534
|
+
clearTimeout(state.timer);
|
|
39535
|
+
state.timer = null;
|
|
39536
|
+
}
|
|
39537
|
+
const { event, waiters } = state.pending;
|
|
39538
|
+
state.pending = null;
|
|
39539
|
+
state.lastSentAt = Date.now();
|
|
39540
|
+
state.inFlight = this.opts.send(event).catch(() => void 0).then(() => {
|
|
39541
|
+
state.inFlight = null;
|
|
39542
|
+
for (const resolve13 of waiters) resolve13();
|
|
39543
|
+
if (!state.pending) {
|
|
39544
|
+
if (event.isFinal) this.chunks.delete(chunkId);
|
|
39545
|
+
return;
|
|
39546
|
+
}
|
|
39547
|
+
if (state.pending.event.isFinal) this.drain(chunkId, state);
|
|
39548
|
+
else this.schedule(chunkId, state);
|
|
39549
|
+
});
|
|
39550
|
+
}
|
|
39551
|
+
intervalFor(event) {
|
|
39552
|
+
return this.opts.intervalFor?.(event) ?? STREAMING_CHUNK_MIN_INTERVAL_MS;
|
|
39553
|
+
}
|
|
39554
|
+
markFinalized(chunkId) {
|
|
39555
|
+
this.finalized.add(chunkId);
|
|
39556
|
+
if (this.finalized.size > FINALIZED_MEMORY) {
|
|
39557
|
+
const oldest = this.finalized.values().next().value;
|
|
39558
|
+
if (oldest !== void 0) this.finalized.delete(oldest);
|
|
39559
|
+
}
|
|
39560
|
+
}
|
|
39561
|
+
};
|
|
39562
|
+
|
|
39348
39563
|
// src/agents/acp/publisher.ts
|
|
39349
39564
|
var STREAMING_CHUNK_SNAPSHOT_MAX_CHARS = 56 * 1024;
|
|
39350
39565
|
var STREAMING_CHUNK_COALESCE_MIN_CHARS = 8 * 1024;
|
|
@@ -39390,8 +39605,12 @@ var AcpPublisher = class {
|
|
|
39390
39605
|
/** chunkIds whose truncated snapshot already went out — nothing new to show
|
|
39391
39606
|
* until the terminal frame. */
|
|
39392
39607
|
truncatedChunks = /* @__PURE__ */ new Set();
|
|
39393
|
-
|
|
39394
|
-
|
|
39608
|
+
/** Latest-wins, one-in-flight-per-chunkId pacing of the streaming-chunk
|
|
39609
|
+
* feed (2026-10-29: one CLI posted 10–30 cumulative snapshots/s). */
|
|
39610
|
+
chunkCoalescer = new StreamingChunkCoalescer({
|
|
39611
|
+
send: (event) => this.postStreamingChunk(event),
|
|
39612
|
+
intervalFor: (event) => event.content.length >= STREAMING_CHUNK_COALESCE_MIN_CHARS ? STREAMING_CHUNK_COALESCE_WINDOW_MS : STREAMING_CHUNK_MIN_INTERVAL_MS
|
|
39613
|
+
});
|
|
39395
39614
|
/** Latched on an unrecoverable 401/403 — every surface stops posting
|
|
39396
39615
|
* (2026-06-28 incident: a dead-token publisher spammed 401 ×34 while
|
|
39397
39616
|
* the agent's replies silently never reached the phone). */
|
|
@@ -39555,14 +39774,13 @@ var AcpPublisher = class {
|
|
|
39555
39774
|
* ({@link publishOutput}) is independent and carries `type:'text'`
|
|
39556
39775
|
* for the main chat bubble; this feed is purely additive for the
|
|
39557
39776
|
* richer SessionDetail surface.
|
|
39777
|
+
*
|
|
39778
|
+
* POSTs are paced per chunkId by {@link StreamingChunkCoalescer}: latest
|
|
39779
|
+
* snapshot wins, one request in flight, and the `isFinal` frame always goes
|
|
39780
|
+
* last. The promise settles once this content (or a newer one) is sent.
|
|
39558
39781
|
*/
|
|
39559
39782
|
async publishStreamingChunk(event) {
|
|
39560
39783
|
const { chunkId, isFinal } = event;
|
|
39561
|
-
const pending2 = this.pendingChunkPosts.get(chunkId);
|
|
39562
|
-
if (pending2) {
|
|
39563
|
-
clearTimeout(pending2.timer);
|
|
39564
|
-
this.pendingChunkPosts.delete(chunkId);
|
|
39565
|
-
}
|
|
39566
39784
|
const bounded = boundStreamingChunkContent(event.content);
|
|
39567
39785
|
if (bounded.truncated) {
|
|
39568
39786
|
if (!isFinal && this.truncatedChunks.has(chunkId)) return;
|
|
@@ -39574,27 +39792,16 @@ var AcpPublisher = class {
|
|
|
39574
39792
|
}
|
|
39575
39793
|
this.truncatedChunks.add(chunkId);
|
|
39576
39794
|
}
|
|
39577
|
-
if (isFinal)
|
|
39578
|
-
|
|
39579
|
-
|
|
39580
|
-
|
|
39581
|
-
|
|
39582
|
-
|
|
39583
|
-
|
|
39584
|
-
const timer = setTimeout(() => {
|
|
39585
|
-
const latest = this.pendingChunkPosts.get(chunkId);
|
|
39586
|
-
this.pendingChunkPosts.delete(chunkId);
|
|
39587
|
-
if (latest) void this.postStreamingChunk(latest.event);
|
|
39588
|
-
}, wait);
|
|
39589
|
-
this.pendingChunkPosts.set(chunkId, { timer, event });
|
|
39590
|
-
return;
|
|
39591
|
-
}
|
|
39592
|
-
}
|
|
39593
|
-
await this.postStreamingChunk({ ...event, content: bounded.content });
|
|
39795
|
+
if (isFinal) this.truncatedChunks.delete(chunkId);
|
|
39796
|
+
await this.chunkCoalescer.submit({ ...event, content: bounded.content });
|
|
39797
|
+
}
|
|
39798
|
+
/** Send any coalesced streaming-chunk snapshot still waiting, and wait for
|
|
39799
|
+
* the in-flight ones — so shutdown never loses the latest content. */
|
|
39800
|
+
flushStreamingChunks() {
|
|
39801
|
+
return this.chunkCoalescer.flush();
|
|
39594
39802
|
}
|
|
39595
39803
|
async postStreamingChunk(event) {
|
|
39596
39804
|
const url2 = `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/streaming-chunk`;
|
|
39597
|
-
if (!event.isFinal) this.lastChunkPostAt.set(event.chunkId, Date.now());
|
|
39598
39805
|
try {
|
|
39599
39806
|
const { statusCode, body } = await this.postWithReauth(
|
|
39600
39807
|
url2,
|
|
@@ -45861,6 +46068,10 @@ async function runAcpSession(opts) {
|
|
|
45861
46068
|
const shutdown = async (signal) => {
|
|
45862
46069
|
showInfo(`Shutting down ACP session (${signal})\u2026`);
|
|
45863
46070
|
clearTimeout(prewarmTimer);
|
|
46071
|
+
await Promise.race([
|
|
46072
|
+
publisher.flushStreamingChunks(),
|
|
46073
|
+
new Promise((resolve13) => setTimeout(resolve13, 2e3).unref())
|
|
46074
|
+
]);
|
|
45864
46075
|
await stopRelayWithGoodbye(relay);
|
|
45865
46076
|
void fileWatcher.stop();
|
|
45866
46077
|
turnFiles.stop();
|
|
@@ -46610,6 +46821,10 @@ var StreamingEmitterService = class {
|
|
|
46610
46821
|
*/
|
|
46611
46822
|
lastSelectorSignature = null;
|
|
46612
46823
|
selectorFirstSeenAt = 0;
|
|
46824
|
+
/** Latest-wins, one-in-flight-per-chunkId pacing of the chunk POSTs. */
|
|
46825
|
+
chunkCoalescer = new StreamingChunkCoalescer({
|
|
46826
|
+
send: (event) => this.postChunk(event)
|
|
46827
|
+
});
|
|
46613
46828
|
// ─── Lifecycle ─────────────────────────────────────────────────────
|
|
46614
46829
|
start() {
|
|
46615
46830
|
if (this.active) return;
|
|
@@ -46640,13 +46855,14 @@ var StreamingEmitterService = class {
|
|
|
46640
46855
|
const finalContent = this.activeChunk.currentContent;
|
|
46641
46856
|
const chunk = this.activeChunk;
|
|
46642
46857
|
this.activeChunk = null;
|
|
46643
|
-
await this.
|
|
46858
|
+
await this.chunkCoalescer.submit({
|
|
46644
46859
|
chunkId: chunk.chunkId,
|
|
46645
46860
|
kind: chunk.kind,
|
|
46646
46861
|
content: finalContent,
|
|
46647
46862
|
isFinal: true
|
|
46648
46863
|
});
|
|
46649
46864
|
}
|
|
46865
|
+
await this.chunkCoalescer.flush();
|
|
46650
46866
|
log.trace("streamingEmitter", `stopped session=${this.opts.sessionId.slice(0, 8)}`);
|
|
46651
46867
|
}
|
|
46652
46868
|
// ─── Input pump ────────────────────────────────────────────────────
|
|
@@ -46697,7 +46913,7 @@ var StreamingEmitterService = class {
|
|
|
46697
46913
|
if (this.activeChunk) {
|
|
46698
46914
|
const closing = this.activeChunk;
|
|
46699
46915
|
this.activeChunk = null;
|
|
46700
|
-
void this.
|
|
46916
|
+
void this.chunkCoalescer.submit({
|
|
46701
46917
|
chunkId: closing.chunkId,
|
|
46702
46918
|
kind: closing.kind,
|
|
46703
46919
|
content: closing.currentContent,
|
|
@@ -46721,7 +46937,7 @@ var StreamingEmitterService = class {
|
|
|
46721
46937
|
if (chunk.currentContent === chunk.emittedContent) return;
|
|
46722
46938
|
chunk.emittedContent = chunk.currentContent;
|
|
46723
46939
|
chunk.lastEmitAt = now;
|
|
46724
|
-
void this.
|
|
46940
|
+
void this.chunkCoalescer.submit({
|
|
46725
46941
|
chunkId: chunk.chunkId,
|
|
46726
46942
|
kind: chunk.kind,
|
|
46727
46943
|
content: chunk.currentContent,
|
|
@@ -51037,9 +51253,9 @@ async function probeCodeamPair(provider, workspace) {
|
|
|
51037
51253
|
}
|
|
51038
51254
|
async function stopWorkspaceFromLocal(target) {
|
|
51039
51255
|
if (target.provider.id === "github-codespaces") {
|
|
51040
|
-
const { execFile:
|
|
51256
|
+
const { execFile: execFile22 } = await import("child_process");
|
|
51041
51257
|
const { promisify: promisify17 } = await import("util");
|
|
51042
|
-
const execFileP10 = promisify17(
|
|
51258
|
+
const execFileP10 = promisify17(execFile22);
|
|
51043
51259
|
await execFileP10("gh", ["codespace", "stop", "-c", target.id], { maxBuffer: 8 * 1024 * 1024 });
|
|
51044
51260
|
return;
|
|
51045
51261
|
}
|
|
@@ -51937,7 +52153,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
51937
52153
|
if (process.env.NODE_ENV === "test") return;
|
|
51938
52154
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
51939
52155
|
if (process.env.CI) return;
|
|
51940
|
-
const current2 = true ? "2.76.
|
|
52156
|
+
const current2 = true ? "2.76.38" : null;
|
|
51941
52157
|
if (!current2) return;
|
|
51942
52158
|
const cache = readCache();
|
|
51943
52159
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -51954,7 +52170,7 @@ function checkForUpdates() {
|
|
|
51954
52170
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
51955
52171
|
if (process.env.CI) return;
|
|
51956
52172
|
if (!process.stdout.isTTY) return;
|
|
51957
|
-
const current2 = true ? "2.76.
|
|
52173
|
+
const current2 = true ? "2.76.38" : null;
|
|
51958
52174
|
if (!current2) return;
|
|
51959
52175
|
const cache = readCache();
|
|
51960
52176
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -52260,7 +52476,7 @@ var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
|
|
|
52260
52476
|
var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
52261
52477
|
var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
|
|
52262
52478
|
function currentCliVersion() {
|
|
52263
|
-
return true ? "2.76.
|
|
52479
|
+
return true ? "2.76.38" : null;
|
|
52264
52480
|
}
|
|
52265
52481
|
async function installedVersion(deps) {
|
|
52266
52482
|
const ls = await deps.run(
|
|
@@ -52483,7 +52699,7 @@ function signalHostReady(send = process.send?.bind(process)) {
|
|
|
52483
52699
|
}
|
|
52484
52700
|
function defaultDeps() {
|
|
52485
52701
|
return {
|
|
52486
|
-
ownVersion: true ? "2.76.
|
|
52702
|
+
ownVersion: true ? "2.76.38" : null,
|
|
52487
52703
|
root: cliVersionsRoot(),
|
|
52488
52704
|
args: process.argv.slice(3),
|
|
52489
52705
|
execPath: process.execPath,
|
|
@@ -55021,7 +55237,7 @@ function checkChokidar() {
|
|
|
55021
55237
|
}
|
|
55022
55238
|
async function doctor(args2 = []) {
|
|
55023
55239
|
const json = args2.includes("--json");
|
|
55024
|
-
const cliVersion = true ? "2.76.
|
|
55240
|
+
const cliVersion = true ? "2.76.38" : "0.0.0-dev";
|
|
55025
55241
|
const apiBase2 = resolveApiBaseUrl();
|
|
55026
55242
|
const diagnosticId = (0, import_node_crypto17.randomUUID)();
|
|
55027
55243
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -55557,7 +55773,7 @@ async function mcpWarm(argv = [], opts = {}) {
|
|
|
55557
55773
|
// src/commands/version.ts
|
|
55558
55774
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
55559
55775
|
function version2() {
|
|
55560
|
-
const v = true ? "2.76.
|
|
55776
|
+
const v = true ? "2.76.38" : "unknown";
|
|
55561
55777
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
55562
55778
|
}
|
|
55563
55779
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.76.
|
|
3
|
+
"version": "2.76.38",
|
|
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",
|