codeam-cli 2.26.7 → 2.26.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 -47
- 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.26.8] — 2026-06-03
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **cli:** Push resumable-session list per active agent (#241)
|
|
12
|
+
|
|
13
|
+
## [2.26.7] — 2026-06-03
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **cli:** Preview no longer pollutes the host terminal + waits for tunnel DNS (#240)
|
|
18
|
+
|
|
7
19
|
## [2.26.6] — 2026-06-03
|
|
8
20
|
|
|
9
21
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -472,7 +472,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
472
472
|
// package.json
|
|
473
473
|
var package_default = {
|
|
474
474
|
name: "codeam-cli",
|
|
475
|
-
version: "2.26.
|
|
475
|
+
version: "2.26.9",
|
|
476
476
|
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.",
|
|
477
477
|
type: "commonjs",
|
|
478
478
|
main: "dist/index.js",
|
|
@@ -5829,7 +5829,7 @@ function readAnonId() {
|
|
|
5829
5829
|
}
|
|
5830
5830
|
function superProperties() {
|
|
5831
5831
|
return {
|
|
5832
|
-
cliVersion: true ? "2.26.
|
|
5832
|
+
cliVersion: true ? "2.26.9" : "0.0.0-dev",
|
|
5833
5833
|
nodeVersion: process.version,
|
|
5834
5834
|
platform: process.platform,
|
|
5835
5835
|
arch: process.arch,
|
|
@@ -9572,6 +9572,50 @@ function parseHistoryFile(filePath) {
|
|
|
9572
9572
|
}
|
|
9573
9573
|
return out2;
|
|
9574
9574
|
}
|
|
9575
|
+
function listResumableSessions(cwd) {
|
|
9576
|
+
const dir = resolveHistoryDir(cwd);
|
|
9577
|
+
if (!dir) return [];
|
|
9578
|
+
let entries;
|
|
9579
|
+
try {
|
|
9580
|
+
entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
9581
|
+
} catch {
|
|
9582
|
+
return [];
|
|
9583
|
+
}
|
|
9584
|
+
const out2 = [];
|
|
9585
|
+
for (const entry of entries) {
|
|
9586
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
9587
|
+
const id = entry.name.slice(0, -".jsonl".length);
|
|
9588
|
+
const filePath = path12.join(dir, entry.name);
|
|
9589
|
+
let timestamp = Date.now();
|
|
9590
|
+
try {
|
|
9591
|
+
timestamp = fs9.statSync(filePath).mtimeMs;
|
|
9592
|
+
} catch {
|
|
9593
|
+
}
|
|
9594
|
+
let summary = "";
|
|
9595
|
+
try {
|
|
9596
|
+
const raw = fs9.readFileSync(filePath, "utf8");
|
|
9597
|
+
for (const line of raw.split("\n")) {
|
|
9598
|
+
if (!line.trim()) continue;
|
|
9599
|
+
try {
|
|
9600
|
+
const record = JSON.parse(line);
|
|
9601
|
+
if (record["type"] === "user") {
|
|
9602
|
+
const msg = record["message"];
|
|
9603
|
+
const text = extractText(msg?.["content"]).trim();
|
|
9604
|
+
if (text) {
|
|
9605
|
+
summary = text.slice(0, 120);
|
|
9606
|
+
break;
|
|
9607
|
+
}
|
|
9608
|
+
}
|
|
9609
|
+
} catch {
|
|
9610
|
+
}
|
|
9611
|
+
}
|
|
9612
|
+
} catch {
|
|
9613
|
+
}
|
|
9614
|
+
if (summary) out2.push({ id, summary, timestamp });
|
|
9615
|
+
}
|
|
9616
|
+
out2.sort((a, b) => b.timestamp - a.timestamp);
|
|
9617
|
+
return out2;
|
|
9618
|
+
}
|
|
9575
9619
|
|
|
9576
9620
|
// src/agents/claude/parsing.ts
|
|
9577
9621
|
function filterChrome(lines) {
|
|
@@ -9933,6 +9977,9 @@ var ClaudeRuntimeStrategy = class {
|
|
|
9933
9977
|
getCurrentUsage(historyDir) {
|
|
9934
9978
|
return getCurrentUsage(historyDir);
|
|
9935
9979
|
}
|
|
9980
|
+
listResumableSessions(cwd) {
|
|
9981
|
+
return listResumableSessions(cwd);
|
|
9982
|
+
}
|
|
9936
9983
|
async fetchWeeklyUsage() {
|
|
9937
9984
|
return fetchClaudeQuota();
|
|
9938
9985
|
}
|
|
@@ -10297,6 +10344,84 @@ function parseHistoryFile2(filePath) {
|
|
|
10297
10344
|
}
|
|
10298
10345
|
return out2;
|
|
10299
10346
|
}
|
|
10347
|
+
function listResumableSessions2(cwd, homeOverride) {
|
|
10348
|
+
const home = homeOverride ?? import_node_os.default.homedir();
|
|
10349
|
+
const sessionsRoot = import_node_path2.default.join(home, ".codex", "sessions");
|
|
10350
|
+
if (!import_node_fs3.default.existsSync(sessionsRoot)) return [];
|
|
10351
|
+
let resolvedCurrent;
|
|
10352
|
+
try {
|
|
10353
|
+
resolvedCurrent = import_node_fs3.default.realpathSync(cwd);
|
|
10354
|
+
} catch {
|
|
10355
|
+
resolvedCurrent = import_node_path2.default.resolve(cwd);
|
|
10356
|
+
}
|
|
10357
|
+
const out2 = [];
|
|
10358
|
+
const now = /* @__PURE__ */ new Date();
|
|
10359
|
+
for (let dayOffset = 0; dayOffset < 7; dayOffset += 1) {
|
|
10360
|
+
const d3 = new Date(now.getTime() - dayOffset * 24 * 60 * 60 * 1e3);
|
|
10361
|
+
const yyyy = String(d3.getUTCFullYear());
|
|
10362
|
+
const mm = String(d3.getUTCMonth() + 1).padStart(2, "0");
|
|
10363
|
+
const dd = String(d3.getUTCDate()).padStart(2, "0");
|
|
10364
|
+
const dayDir = import_node_path2.default.join(sessionsRoot, yyyy, mm, dd);
|
|
10365
|
+
if (!import_node_fs3.default.existsSync(dayDir)) continue;
|
|
10366
|
+
let dayFiles;
|
|
10367
|
+
try {
|
|
10368
|
+
dayFiles = import_node_fs3.default.readdirSync(dayDir, { withFileTypes: true });
|
|
10369
|
+
} catch {
|
|
10370
|
+
continue;
|
|
10371
|
+
}
|
|
10372
|
+
for (const entry of dayFiles) {
|
|
10373
|
+
if (!entry.isFile()) continue;
|
|
10374
|
+
if (!entry.name.startsWith("rollout-") || !entry.name.endsWith(".jsonl")) {
|
|
10375
|
+
continue;
|
|
10376
|
+
}
|
|
10377
|
+
const filePath = import_node_path2.default.join(dayDir, entry.name);
|
|
10378
|
+
let timestamp = Date.now();
|
|
10379
|
+
try {
|
|
10380
|
+
timestamp = import_node_fs3.default.statSync(filePath).mtimeMs;
|
|
10381
|
+
} catch {
|
|
10382
|
+
}
|
|
10383
|
+
let metaCwd;
|
|
10384
|
+
let metaId;
|
|
10385
|
+
let summary = "";
|
|
10386
|
+
try {
|
|
10387
|
+
const raw = import_node_fs3.default.readFileSync(filePath, "utf8");
|
|
10388
|
+
for (const line of raw.split("\n")) {
|
|
10389
|
+
if (!line.trim()) continue;
|
|
10390
|
+
const rec = parseLine(line);
|
|
10391
|
+
if (!rec) continue;
|
|
10392
|
+
if (rec.type === "session_meta") {
|
|
10393
|
+
const meta = rec.payload;
|
|
10394
|
+
metaCwd = typeof meta?.cwd === "string" ? meta.cwd : void 0;
|
|
10395
|
+
metaId = typeof meta?.id === "string" ? meta.id : void 0;
|
|
10396
|
+
continue;
|
|
10397
|
+
}
|
|
10398
|
+
if (!summary && rec.type === "response_item") {
|
|
10399
|
+
const payload = rec.payload;
|
|
10400
|
+
const msg = payload?.Message;
|
|
10401
|
+
if (msg && msg.role === "user") {
|
|
10402
|
+
const text = extractMessageText(msg.content).trim();
|
|
10403
|
+
if (text) summary = text.slice(0, 120);
|
|
10404
|
+
}
|
|
10405
|
+
}
|
|
10406
|
+
if (metaCwd !== void 0 && summary) break;
|
|
10407
|
+
}
|
|
10408
|
+
} catch {
|
|
10409
|
+
continue;
|
|
10410
|
+
}
|
|
10411
|
+
if (!metaCwd || !metaId || !summary) continue;
|
|
10412
|
+
let resolvedMeta;
|
|
10413
|
+
try {
|
|
10414
|
+
resolvedMeta = import_node_fs3.default.realpathSync(metaCwd);
|
|
10415
|
+
} catch {
|
|
10416
|
+
resolvedMeta = import_node_path2.default.resolve(metaCwd);
|
|
10417
|
+
}
|
|
10418
|
+
if (resolvedMeta !== resolvedCurrent) continue;
|
|
10419
|
+
out2.push({ id: metaId, summary, timestamp });
|
|
10420
|
+
}
|
|
10421
|
+
}
|
|
10422
|
+
out2.sort((a, b) => b.timestamp - a.timestamp);
|
|
10423
|
+
return out2;
|
|
10424
|
+
}
|
|
10300
10425
|
function getCurrentUsage2(historyDir) {
|
|
10301
10426
|
if (!import_node_fs3.default.existsSync(historyDir)) return null;
|
|
10302
10427
|
const files = import_node_fs3.default.readdirSync(historyDir).filter((f) => f.startsWith("rollout-") && f.endsWith(".jsonl")).map((f) => ({ name: f, full: import_node_path2.default.join(historyDir, f) })).map((e) => ({ ...e, mtime: import_node_fs3.default.statSync(e.full).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
@@ -10830,6 +10955,9 @@ var CodexRuntimeStrategy = class {
|
|
|
10830
10955
|
getCurrentUsage(historyDir) {
|
|
10831
10956
|
return getCurrentUsage2(historyDir);
|
|
10832
10957
|
}
|
|
10958
|
+
listResumableSessions(cwd) {
|
|
10959
|
+
return listResumableSessions2(cwd);
|
|
10960
|
+
}
|
|
10833
10961
|
/**
|
|
10834
10962
|
* Codex's quota lives behind the `account/get_account_rate_limits` RPC,
|
|
10835
10963
|
* not a TUI slash command. Phase 2 ships with this stubbed to null so the
|
|
@@ -12731,52 +12859,32 @@ var HistoryService = class _HistoryService {
|
|
|
12731
12859
|
return Math.round(totalCost * 100) / 100;
|
|
12732
12860
|
}
|
|
12733
12861
|
/**
|
|
12734
|
-
*
|
|
12735
|
-
*
|
|
12862
|
+
* Push the active agent's resumable-sessions list to the backend.
|
|
12863
|
+
* Delegates the per-agent JSONL/rollout walk to
|
|
12864
|
+
* `runtime.listResumableSessions(cwd)` so each agent reads its own
|
|
12865
|
+
* on-disk format (Claude's JSONL files vs Codex's date-bucketed
|
|
12866
|
+
* rollouts). Strategies that don't yet expose the helper (Cursor,
|
|
12867
|
+
* Aider) cause this to no-op — the Conversations sheet on mobile
|
|
12868
|
+
* just shows the empty state for those agents until each one's
|
|
12869
|
+
* `listResumableSessions` lands.
|
|
12870
|
+
*
|
|
12871
|
+
* The push body now includes `agentId` so the backend keys by
|
|
12872
|
+
* (pluginId, agentId). Old CLI clients that omit `agentId` continue
|
|
12873
|
+
* to land in the `claude-code` slot via the backend's default.
|
|
12874
|
+
*
|
|
12875
|
+
* Called once ~2 s after the agent spawns (non-blocking).
|
|
12736
12876
|
*/
|
|
12737
12877
|
async load() {
|
|
12738
|
-
|
|
12739
|
-
let entries;
|
|
12740
|
-
try {
|
|
12741
|
-
entries = fs20.readdirSync(dir, { withFileTypes: true });
|
|
12742
|
-
} catch {
|
|
12878
|
+
if (!this.runtime.listResumableSessions) {
|
|
12743
12879
|
return;
|
|
12744
12880
|
}
|
|
12745
|
-
const sessions3 =
|
|
12746
|
-
for (const entry of entries) {
|
|
12747
|
-
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
12748
|
-
const id = path24.basename(entry.name, ".jsonl");
|
|
12749
|
-
const filePath = path24.join(dir, entry.name);
|
|
12750
|
-
let mtime = Date.now();
|
|
12751
|
-
try {
|
|
12752
|
-
mtime = fs20.statSync(filePath).mtimeMs;
|
|
12753
|
-
} catch {
|
|
12754
|
-
}
|
|
12755
|
-
let summary = "";
|
|
12756
|
-
try {
|
|
12757
|
-
const raw = fs20.readFileSync(filePath, "utf8");
|
|
12758
|
-
for (const line of raw.split("\n")) {
|
|
12759
|
-
if (!line.trim()) continue;
|
|
12760
|
-
try {
|
|
12761
|
-
const record = JSON.parse(line);
|
|
12762
|
-
if (record["type"] === "user") {
|
|
12763
|
-
const msg = record["message"];
|
|
12764
|
-
const text = extractText2(msg?.["content"]).trim();
|
|
12765
|
-
if (text) {
|
|
12766
|
-
summary = text.slice(0, 120);
|
|
12767
|
-
break;
|
|
12768
|
-
}
|
|
12769
|
-
}
|
|
12770
|
-
} catch {
|
|
12771
|
-
}
|
|
12772
|
-
}
|
|
12773
|
-
} catch {
|
|
12774
|
-
}
|
|
12775
|
-
if (summary) sessions3.push({ id, summary, timestamp: mtime });
|
|
12776
|
-
}
|
|
12881
|
+
const sessions3 = this.runtime.listResumableSessions(this.cwd);
|
|
12777
12882
|
if (sessions3.length === 0) return;
|
|
12778
|
-
|
|
12779
|
-
|
|
12883
|
+
await post("/api/sessions/list", {
|
|
12884
|
+
pluginId: this.pluginId,
|
|
12885
|
+
agentId: this.runtime.id,
|
|
12886
|
+
sessions: sessions3
|
|
12887
|
+
});
|
|
12780
12888
|
}
|
|
12781
12889
|
/**
|
|
12782
12890
|
* Read a specific session's full conversation and POST it to the API in batches.
|
|
@@ -12794,7 +12902,18 @@ var HistoryService = class _HistoryService {
|
|
|
12794
12902
|
const RETRY_DELAYS = [500, 1e3, 2e3, 4e3, 8e3];
|
|
12795
12903
|
for (let i = 0; i < totalBatches; i++) {
|
|
12796
12904
|
const batch = messages.slice(i * CONVERSATION_BATCH_SIZE, (i + 1) * CONVERSATION_BATCH_SIZE);
|
|
12797
|
-
const body = {
|
|
12905
|
+
const body = {
|
|
12906
|
+
pluginId: this.pluginId,
|
|
12907
|
+
// `agentId` keys the backend's per-agent conversation cache.
|
|
12908
|
+
// Older backends that don't recognise the field silently
|
|
12909
|
+
// ignore it and default to `claude-code` server-side — same
|
|
12910
|
+
// outcome as before the per-agent split.
|
|
12911
|
+
agentId: this.runtime.id,
|
|
12912
|
+
sessionId,
|
|
12913
|
+
messages: batch,
|
|
12914
|
+
batchIndex: i,
|
|
12915
|
+
totalBatches
|
|
12916
|
+
};
|
|
12798
12917
|
let ok = await post("/api/sessions/conversation", body);
|
|
12799
12918
|
for (let attempt = 0; !ok && attempt < RETRY_DELAYS.length; attempt++) {
|
|
12800
12919
|
await new Promise((r) => setTimeout(r, RETRY_DELAYS[attempt]));
|
|
@@ -12844,6 +12963,7 @@ var HistoryService = class _HistoryService {
|
|
|
12844
12963
|
if (newMessages.length === 0) return 0;
|
|
12845
12964
|
const body = {
|
|
12846
12965
|
pluginId: this.pluginId,
|
|
12966
|
+
agentId: this.runtime.id,
|
|
12847
12967
|
sessionId,
|
|
12848
12968
|
messages: newMessages,
|
|
12849
12969
|
mode: "append"
|
|
@@ -20036,7 +20156,7 @@ function checkChokidar() {
|
|
|
20036
20156
|
}
|
|
20037
20157
|
async function doctor(args2 = []) {
|
|
20038
20158
|
const json = args2.includes("--json");
|
|
20039
|
-
const cliVersion = true ? "2.26.
|
|
20159
|
+
const cliVersion = true ? "2.26.9" : "0.0.0-dev";
|
|
20040
20160
|
const apiBase = resolveApiBaseUrl();
|
|
20041
20161
|
const diagnosticId = (0, import_node_crypto6.randomUUID)();
|
|
20042
20162
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -20235,7 +20355,7 @@ async function completion(args2) {
|
|
|
20235
20355
|
// src/commands/version.ts
|
|
20236
20356
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
20237
20357
|
function version2() {
|
|
20238
|
-
const v = true ? "2.26.
|
|
20358
|
+
const v = true ? "2.26.9" : "unknown";
|
|
20239
20359
|
console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
|
|
20240
20360
|
}
|
|
20241
20361
|
|
|
@@ -20463,7 +20583,7 @@ function checkForUpdates() {
|
|
|
20463
20583
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
20464
20584
|
if (process.env.CI) return;
|
|
20465
20585
|
if (!process.stdout.isTTY) return;
|
|
20466
|
-
const current = true ? "2.26.
|
|
20586
|
+
const current = true ? "2.26.9" : null;
|
|
20467
20587
|
if (!current) return;
|
|
20468
20588
|
const cache = readCache();
|
|
20469
20589
|
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.26.
|
|
3
|
+
"version": "2.26.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",
|