codeam-cli 2.76.37 → 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 +6 -0
- package/dist/index.js +127 -37
- package/dist/postinstall.js +33 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ 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
|
+
|
|
7
13
|
## [2.76.36] — 2026-10-25
|
|
8
14
|
|
|
9
15
|
### Added
|
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());
|
|
@@ -39471,6 +39471,95 @@ function _get(url2, headers) {
|
|
|
39471
39471
|
});
|
|
39472
39472
|
}
|
|
39473
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
|
+
|
|
39474
39563
|
// src/agents/acp/publisher.ts
|
|
39475
39564
|
var STREAMING_CHUNK_SNAPSHOT_MAX_CHARS = 56 * 1024;
|
|
39476
39565
|
var STREAMING_CHUNK_COALESCE_MIN_CHARS = 8 * 1024;
|
|
@@ -39516,8 +39605,12 @@ var AcpPublisher = class {
|
|
|
39516
39605
|
/** chunkIds whose truncated snapshot already went out — nothing new to show
|
|
39517
39606
|
* until the terminal frame. */
|
|
39518
39607
|
truncatedChunks = /* @__PURE__ */ new Set();
|
|
39519
|
-
|
|
39520
|
-
|
|
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
|
+
});
|
|
39521
39614
|
/** Latched on an unrecoverable 401/403 — every surface stops posting
|
|
39522
39615
|
* (2026-06-28 incident: a dead-token publisher spammed 401 ×34 while
|
|
39523
39616
|
* the agent's replies silently never reached the phone). */
|
|
@@ -39681,14 +39774,13 @@ var AcpPublisher = class {
|
|
|
39681
39774
|
* ({@link publishOutput}) is independent and carries `type:'text'`
|
|
39682
39775
|
* for the main chat bubble; this feed is purely additive for the
|
|
39683
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.
|
|
39684
39781
|
*/
|
|
39685
39782
|
async publishStreamingChunk(event) {
|
|
39686
39783
|
const { chunkId, isFinal } = event;
|
|
39687
|
-
const pending2 = this.pendingChunkPosts.get(chunkId);
|
|
39688
|
-
if (pending2) {
|
|
39689
|
-
clearTimeout(pending2.timer);
|
|
39690
|
-
this.pendingChunkPosts.delete(chunkId);
|
|
39691
|
-
}
|
|
39692
39784
|
const bounded = boundStreamingChunkContent(event.content);
|
|
39693
39785
|
if (bounded.truncated) {
|
|
39694
39786
|
if (!isFinal && this.truncatedChunks.has(chunkId)) return;
|
|
@@ -39700,27 +39792,16 @@ var AcpPublisher = class {
|
|
|
39700
39792
|
}
|
|
39701
39793
|
this.truncatedChunks.add(chunkId);
|
|
39702
39794
|
}
|
|
39703
|
-
if (isFinal)
|
|
39704
|
-
|
|
39705
|
-
|
|
39706
|
-
|
|
39707
|
-
|
|
39708
|
-
|
|
39709
|
-
|
|
39710
|
-
const timer = setTimeout(() => {
|
|
39711
|
-
const latest = this.pendingChunkPosts.get(chunkId);
|
|
39712
|
-
this.pendingChunkPosts.delete(chunkId);
|
|
39713
|
-
if (latest) void this.postStreamingChunk(latest.event);
|
|
39714
|
-
}, wait);
|
|
39715
|
-
this.pendingChunkPosts.set(chunkId, { timer, event });
|
|
39716
|
-
return;
|
|
39717
|
-
}
|
|
39718
|
-
}
|
|
39719
|
-
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();
|
|
39720
39802
|
}
|
|
39721
39803
|
async postStreamingChunk(event) {
|
|
39722
39804
|
const url2 = `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/streaming-chunk`;
|
|
39723
|
-
if (!event.isFinal) this.lastChunkPostAt.set(event.chunkId, Date.now());
|
|
39724
39805
|
try {
|
|
39725
39806
|
const { statusCode, body } = await this.postWithReauth(
|
|
39726
39807
|
url2,
|
|
@@ -45987,6 +46068,10 @@ async function runAcpSession(opts) {
|
|
|
45987
46068
|
const shutdown = async (signal) => {
|
|
45988
46069
|
showInfo(`Shutting down ACP session (${signal})\u2026`);
|
|
45989
46070
|
clearTimeout(prewarmTimer);
|
|
46071
|
+
await Promise.race([
|
|
46072
|
+
publisher.flushStreamingChunks(),
|
|
46073
|
+
new Promise((resolve13) => setTimeout(resolve13, 2e3).unref())
|
|
46074
|
+
]);
|
|
45990
46075
|
await stopRelayWithGoodbye(relay);
|
|
45991
46076
|
void fileWatcher.stop();
|
|
45992
46077
|
turnFiles.stop();
|
|
@@ -46736,6 +46821,10 @@ var StreamingEmitterService = class {
|
|
|
46736
46821
|
*/
|
|
46737
46822
|
lastSelectorSignature = null;
|
|
46738
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
|
+
});
|
|
46739
46828
|
// ─── Lifecycle ─────────────────────────────────────────────────────
|
|
46740
46829
|
start() {
|
|
46741
46830
|
if (this.active) return;
|
|
@@ -46766,13 +46855,14 @@ var StreamingEmitterService = class {
|
|
|
46766
46855
|
const finalContent = this.activeChunk.currentContent;
|
|
46767
46856
|
const chunk = this.activeChunk;
|
|
46768
46857
|
this.activeChunk = null;
|
|
46769
|
-
await this.
|
|
46858
|
+
await this.chunkCoalescer.submit({
|
|
46770
46859
|
chunkId: chunk.chunkId,
|
|
46771
46860
|
kind: chunk.kind,
|
|
46772
46861
|
content: finalContent,
|
|
46773
46862
|
isFinal: true
|
|
46774
46863
|
});
|
|
46775
46864
|
}
|
|
46865
|
+
await this.chunkCoalescer.flush();
|
|
46776
46866
|
log.trace("streamingEmitter", `stopped session=${this.opts.sessionId.slice(0, 8)}`);
|
|
46777
46867
|
}
|
|
46778
46868
|
// ─── Input pump ────────────────────────────────────────────────────
|
|
@@ -46823,7 +46913,7 @@ var StreamingEmitterService = class {
|
|
|
46823
46913
|
if (this.activeChunk) {
|
|
46824
46914
|
const closing = this.activeChunk;
|
|
46825
46915
|
this.activeChunk = null;
|
|
46826
|
-
void this.
|
|
46916
|
+
void this.chunkCoalescer.submit({
|
|
46827
46917
|
chunkId: closing.chunkId,
|
|
46828
46918
|
kind: closing.kind,
|
|
46829
46919
|
content: closing.currentContent,
|
|
@@ -46847,7 +46937,7 @@ var StreamingEmitterService = class {
|
|
|
46847
46937
|
if (chunk.currentContent === chunk.emittedContent) return;
|
|
46848
46938
|
chunk.emittedContent = chunk.currentContent;
|
|
46849
46939
|
chunk.lastEmitAt = now;
|
|
46850
|
-
void this.
|
|
46940
|
+
void this.chunkCoalescer.submit({
|
|
46851
46941
|
chunkId: chunk.chunkId,
|
|
46852
46942
|
kind: chunk.kind,
|
|
46853
46943
|
content: chunk.currentContent,
|
|
@@ -52063,7 +52153,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
52063
52153
|
if (process.env.NODE_ENV === "test") return;
|
|
52064
52154
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
52065
52155
|
if (process.env.CI) return;
|
|
52066
|
-
const current2 = true ? "2.76.
|
|
52156
|
+
const current2 = true ? "2.76.38" : null;
|
|
52067
52157
|
if (!current2) return;
|
|
52068
52158
|
const cache = readCache();
|
|
52069
52159
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -52080,7 +52170,7 @@ function checkForUpdates() {
|
|
|
52080
52170
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
52081
52171
|
if (process.env.CI) return;
|
|
52082
52172
|
if (!process.stdout.isTTY) return;
|
|
52083
|
-
const current2 = true ? "2.76.
|
|
52173
|
+
const current2 = true ? "2.76.38" : null;
|
|
52084
52174
|
if (!current2) return;
|
|
52085
52175
|
const cache = readCache();
|
|
52086
52176
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -52386,7 +52476,7 @@ var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
|
|
|
52386
52476
|
var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
52387
52477
|
var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
|
|
52388
52478
|
function currentCliVersion() {
|
|
52389
|
-
return true ? "2.76.
|
|
52479
|
+
return true ? "2.76.38" : null;
|
|
52390
52480
|
}
|
|
52391
52481
|
async function installedVersion(deps) {
|
|
52392
52482
|
const ls = await deps.run(
|
|
@@ -52609,7 +52699,7 @@ function signalHostReady(send = process.send?.bind(process)) {
|
|
|
52609
52699
|
}
|
|
52610
52700
|
function defaultDeps() {
|
|
52611
52701
|
return {
|
|
52612
|
-
ownVersion: true ? "2.76.
|
|
52702
|
+
ownVersion: true ? "2.76.38" : null,
|
|
52613
52703
|
root: cliVersionsRoot(),
|
|
52614
52704
|
args: process.argv.slice(3),
|
|
52615
52705
|
execPath: process.execPath,
|
|
@@ -55147,7 +55237,7 @@ function checkChokidar() {
|
|
|
55147
55237
|
}
|
|
55148
55238
|
async function doctor(args2 = []) {
|
|
55149
55239
|
const json = args2.includes("--json");
|
|
55150
|
-
const cliVersion = true ? "2.76.
|
|
55240
|
+
const cliVersion = true ? "2.76.38" : "0.0.0-dev";
|
|
55151
55241
|
const apiBase2 = resolveApiBaseUrl();
|
|
55152
55242
|
const diagnosticId = (0, import_node_crypto17.randomUUID)();
|
|
55153
55243
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -55683,7 +55773,7 @@ async function mcpWarm(argv = [], opts = {}) {
|
|
|
55683
55773
|
// src/commands/version.ts
|
|
55684
55774
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
55685
55775
|
function version2() {
|
|
55686
|
-
const v = true ? "2.76.
|
|
55776
|
+
const v = true ? "2.76.38" : "unknown";
|
|
55687
55777
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
55688
55778
|
}
|
|
55689
55779
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// src/postinstall.ts
|
|
5
|
+
var c = {
|
|
6
|
+
reset: "\x1B[0m",
|
|
7
|
+
bold: "\x1B[1m",
|
|
8
|
+
dim: "\x1B[2m",
|
|
9
|
+
green: "\x1B[32m",
|
|
10
|
+
cyan: "\x1B[36m",
|
|
11
|
+
violet: "\x1B[35m",
|
|
12
|
+
white: "\x1B[97m"
|
|
13
|
+
};
|
|
14
|
+
var lines = [
|
|
15
|
+
"",
|
|
16
|
+
` ${c.violet}${c.bold}codeam-cli${c.reset} ${c.dim}\u2014 Claude Code remote control${c.reset}`,
|
|
17
|
+
"",
|
|
18
|
+
` ${c.dim}1.${c.reset} Pair your phone:`,
|
|
19
|
+
` ${c.cyan}codeam pair${c.reset}`,
|
|
20
|
+
"",
|
|
21
|
+
` ${c.dim}2.${c.reset} Launch Claude Code with mobile control:`,
|
|
22
|
+
` ${c.cyan}codeam${c.reset}`,
|
|
23
|
+
"",
|
|
24
|
+
` ${c.dim}Other commands:${c.reset}`,
|
|
25
|
+
` ${c.white}codeam sessions${c.reset} ${c.dim}list paired devices${c.reset}`,
|
|
26
|
+
` ${c.white}codeam status${c.reset} ${c.dim}show connection info${c.reset}`,
|
|
27
|
+
` ${c.white}codeam logout${c.reset} ${c.dim}remove all sessions${c.reset}`,
|
|
28
|
+
"",
|
|
29
|
+
` ${c.dim}Requires Claude Code:${c.reset} ${c.green}npm install -g @anthropic-ai/claude-code${c.reset}`,
|
|
30
|
+
` ${c.dim}Mobile app:${c.reset} ${c.green}https://www.codeagent-mobile.com${c.reset}`,
|
|
31
|
+
""
|
|
32
|
+
];
|
|
33
|
+
process.stdout.write(lines.join("\n") + "\n");
|
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",
|