codeam-cli 2.61.69 → 2.61.71
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 +156 -16
- 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.61.70] — 2026-07-28
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **cli:** Self_hosted_cleanup — rm a deleted session's on-disk workspace on the box
|
|
12
|
+
|
|
13
|
+
## [2.61.69] — 2026-07-28
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **cli:** ACP internals guard — deny agent access to CodeAgent platform files on managed deploys
|
|
18
|
+
|
|
7
19
|
## [2.61.68] — 2026-07-28
|
|
8
20
|
|
|
9
21
|
### Changed
|
package/dist/index.js
CHANGED
|
@@ -7074,7 +7074,7 @@ function readAnonId() {
|
|
|
7074
7074
|
}
|
|
7075
7075
|
function superProperties() {
|
|
7076
7076
|
return {
|
|
7077
|
-
cliVersion: true ? "2.61.
|
|
7077
|
+
cliVersion: true ? "2.61.71" : "0.0.0-dev",
|
|
7078
7078
|
nodeVersion: process.version,
|
|
7079
7079
|
platform: process.platform,
|
|
7080
7080
|
arch: process.arch,
|
|
@@ -7255,7 +7255,7 @@ var os4 = __toESM(require("os"));
|
|
|
7255
7255
|
// package.json
|
|
7256
7256
|
var package_default = {
|
|
7257
7257
|
name: "codeam-cli",
|
|
7258
|
-
version: "2.61.
|
|
7258
|
+
version: "2.61.71",
|
|
7259
7259
|
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.",
|
|
7260
7260
|
type: "commonjs",
|
|
7261
7261
|
main: "dist/index.js",
|
|
@@ -8427,7 +8427,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
8427
8427
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
8428
8428
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
8429
8429
|
// pair/reconnect). Older backends ignore the extra field.
|
|
8430
|
-
..."2.61.
|
|
8430
|
+
..."2.61.71" ? { ideVersion: "2.61.71" } : {}
|
|
8431
8431
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
8432
8432
|
}
|
|
8433
8433
|
/**
|
|
@@ -8763,15 +8763,15 @@ var FileWatcherService = class {
|
|
|
8763
8763
|
);
|
|
8764
8764
|
return;
|
|
8765
8765
|
}
|
|
8766
|
-
const
|
|
8767
|
-
if (!
|
|
8766
|
+
const chokidar3 = _chokidarSeam.load();
|
|
8767
|
+
if (!chokidar3) {
|
|
8768
8768
|
log.warn(
|
|
8769
8769
|
"fileWatcher",
|
|
8770
8770
|
`chokidar unavailable \u2014 file change emission disabled`
|
|
8771
8771
|
);
|
|
8772
8772
|
return;
|
|
8773
8773
|
}
|
|
8774
|
-
const watcher =
|
|
8774
|
+
const watcher = chokidar3.watch(this.opts.workingDir, {
|
|
8775
8775
|
ignored: [
|
|
8776
8776
|
/(^|[\\/])\../,
|
|
8777
8777
|
// dot-files & dot-dirs (.git, .next, .expo, .DS_Store, …)
|
|
@@ -19536,7 +19536,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
19536
19536
|
if (process.env.NODE_ENV === "test") return;
|
|
19537
19537
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
19538
19538
|
if (process.env.CI) return;
|
|
19539
|
-
const current = true ? "2.61.
|
|
19539
|
+
const current = true ? "2.61.71" : null;
|
|
19540
19540
|
if (!current) return;
|
|
19541
19541
|
const cache = readCache();
|
|
19542
19542
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -19553,7 +19553,7 @@ function checkForUpdates() {
|
|
|
19553
19553
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
19554
19554
|
if (process.env.CI) return;
|
|
19555
19555
|
if (!process.stdout.isTTY) return;
|
|
19556
|
-
const current = true ? "2.61.
|
|
19556
|
+
const current = true ? "2.61.71" : null;
|
|
19557
19557
|
if (!current) return;
|
|
19558
19558
|
const cache = readCache();
|
|
19559
19559
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -19573,7 +19573,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
|
19573
19573
|
var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
|
|
19574
19574
|
var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
19575
19575
|
function currentCliVersion() {
|
|
19576
|
-
return true ? "2.61.
|
|
19576
|
+
return true ? "2.61.71" : null;
|
|
19577
19577
|
}
|
|
19578
19578
|
function runCmd(cmd, args2, timeoutMs) {
|
|
19579
19579
|
return new Promise((resolve9) => {
|
|
@@ -19807,6 +19807,9 @@ function isDeployPayload(p2) {
|
|
|
19807
19807
|
function isStopPayload(p2) {
|
|
19808
19808
|
return typeof p2.sessionId === "string";
|
|
19809
19809
|
}
|
|
19810
|
+
function isCleanupPayload(p2) {
|
|
19811
|
+
return typeof p2.deployId === "string" && p2.deployId.length > 0;
|
|
19812
|
+
}
|
|
19810
19813
|
var FLEET_CONTAINER_NAME_RE = /^codeam-box-[a-z0-9]+$/;
|
|
19811
19814
|
function isFleetContainerName(v) {
|
|
19812
19815
|
return typeof v === "string" && FLEET_CONTAINER_NAME_RE.test(v);
|
|
@@ -20250,6 +20253,14 @@ var HostAgentSupervisor = class {
|
|
|
20250
20253
|
this.stopChild(cmd.payload.sessionId);
|
|
20251
20254
|
return;
|
|
20252
20255
|
}
|
|
20256
|
+
if (cmd.type === "self_hosted_cleanup") {
|
|
20257
|
+
if (!isCleanupPayload(cmd.payload)) {
|
|
20258
|
+
log.warn("host-agent", `ignoring malformed self_hosted_cleanup id=${cmd.id}`);
|
|
20259
|
+
return;
|
|
20260
|
+
}
|
|
20261
|
+
this.cleanupWorkspace(cmd.payload.deployId);
|
|
20262
|
+
return;
|
|
20263
|
+
}
|
|
20253
20264
|
if (cmd.type === "host_list_dir") {
|
|
20254
20265
|
await this.listDir(cmd);
|
|
20255
20266
|
return;
|
|
@@ -20838,6 +20849,44 @@ var HostAgentSupervisor = class {
|
|
|
20838
20849
|
}
|
|
20839
20850
|
this.children.delete(child.deployId);
|
|
20840
20851
|
}
|
|
20852
|
+
/**
|
|
20853
|
+
* Handle `self_hosted_cleanup { deployId }` — the app deleted this deploy's
|
|
20854
|
+
* session. Remove its on-disk workspace so a persistent box doesn't accumulate
|
|
20855
|
+
* one dir per deleted session. Stops any still-running child for the deploy
|
|
20856
|
+
* first (the delete also pushes `session_terminated` to the child, but this is
|
|
20857
|
+
* race-safe), then removes the two `~/.codeam/...<deployId>` dirs.
|
|
20858
|
+
*
|
|
20859
|
+
* Idempotent (`force: true` → no throw if already gone) and inherently safe:
|
|
20860
|
+
* the dir is keyed by the UNIQUE `deployId`, so it can never be another live
|
|
20861
|
+
* session's workspace, and these paths only ever exist for a cloned
|
|
20862
|
+
* self-hosted deploy (a local / absolute-path target has no such dir → no-op).
|
|
20863
|
+
*/
|
|
20864
|
+
cleanupWorkspace(deployId) {
|
|
20865
|
+
const short = deployId.slice(0, 8);
|
|
20866
|
+
const child = this.children.get(deployId);
|
|
20867
|
+
if (child) {
|
|
20868
|
+
try {
|
|
20869
|
+
child.proc.kill("SIGTERM");
|
|
20870
|
+
} catch {
|
|
20871
|
+
}
|
|
20872
|
+
this.children.delete(deployId);
|
|
20873
|
+
}
|
|
20874
|
+
const dirs = [
|
|
20875
|
+
path46.join(selfHostedWorkspaceRoot(), deployId),
|
|
20876
|
+
path46.join(os38.homedir(), ".codeam", "house-claude", deployId)
|
|
20877
|
+
];
|
|
20878
|
+
for (const dir of dirs) {
|
|
20879
|
+
try {
|
|
20880
|
+
fs43.rmSync(dir, { recursive: true, force: true });
|
|
20881
|
+
} catch (err) {
|
|
20882
|
+
log.warn(
|
|
20883
|
+
"host-agent",
|
|
20884
|
+
`cleanup: failed to remove ${dir}: ${err.message}`
|
|
20885
|
+
);
|
|
20886
|
+
}
|
|
20887
|
+
}
|
|
20888
|
+
log.info("host-agent", `cleaned up workspace for deleted deploy=${short}`);
|
|
20889
|
+
}
|
|
20841
20890
|
/**
|
|
20842
20891
|
* Handle `self_hosted_refresh_credentials` (the user re-linked the agent).
|
|
20843
20892
|
* Unseal the fresh credential and re-provision the agent's on-disk auth IN
|
|
@@ -23251,12 +23300,12 @@ var BeadsWatcher = class {
|
|
|
23251
23300
|
/** Start tailing the change feed. Idempotent (second call is a no-op). */
|
|
23252
23301
|
start() {
|
|
23253
23302
|
if (this.watcher || this.stopped) return;
|
|
23254
|
-
const
|
|
23255
|
-
if (!
|
|
23303
|
+
const chokidar3 = _chokidarSeam2.load();
|
|
23304
|
+
if (!chokidar3) {
|
|
23256
23305
|
log.warn("beads", "chokidar unavailable \u2014 beads watcher disabled");
|
|
23257
23306
|
return;
|
|
23258
23307
|
}
|
|
23259
|
-
const watcher =
|
|
23308
|
+
const watcher = chokidar3.watch(this.feedPath, {
|
|
23260
23309
|
ignoreInitial: false,
|
|
23261
23310
|
// push the current state once on start
|
|
23262
23311
|
persistent: true,
|
|
@@ -24535,7 +24584,7 @@ function sweepStaleCliStagingDirs(nodeModulesDir, now = Date.now(), deps) {
|
|
|
24535
24584
|
if (!nodeModulesDir) return 0;
|
|
24536
24585
|
const readdirSync12 = deps?.readdirSync ?? fs57.readdirSync;
|
|
24537
24586
|
const statSync15 = deps?.statSync ?? fs57.statSync;
|
|
24538
|
-
const
|
|
24587
|
+
const rmSync8 = deps?.rmSync ?? fs57.rmSync;
|
|
24539
24588
|
let removed = 0;
|
|
24540
24589
|
let entries;
|
|
24541
24590
|
try {
|
|
@@ -24549,7 +24598,7 @@ function sweepStaleCliStagingDirs(nodeModulesDir, now = Date.now(), deps) {
|
|
|
24549
24598
|
try {
|
|
24550
24599
|
const st3 = statSync15(full);
|
|
24551
24600
|
if (now - st3.mtimeMs < STALE_STAGING_AGE_MS) continue;
|
|
24552
|
-
|
|
24601
|
+
rmSync8(full, { recursive: true, force: true });
|
|
24553
24602
|
removed++;
|
|
24554
24603
|
} catch {
|
|
24555
24604
|
}
|
|
@@ -33584,6 +33633,38 @@ async function reportCredentialInvalid(opts, fetchImpl = fetch) {
|
|
|
33584
33633
|
} catch {
|
|
33585
33634
|
}
|
|
33586
33635
|
}
|
|
33636
|
+
async function postCredentialSync(opts, fetchImpl = fetch) {
|
|
33637
|
+
const url2 = `${resolveApiBaseUrl()}/api/plugin/agents/${encodeURIComponent(opts.agent)}/credential-sync`;
|
|
33638
|
+
const body = JSON.stringify({
|
|
33639
|
+
sessionId: opts.sessionId,
|
|
33640
|
+
pluginId: opts.pluginId,
|
|
33641
|
+
method: opts.method,
|
|
33642
|
+
credential: opts.credential,
|
|
33643
|
+
...opts.agentState ? { agentState: opts.agentState } : {}
|
|
33644
|
+
});
|
|
33645
|
+
try {
|
|
33646
|
+
const makeHeaders = (token) => ({
|
|
33647
|
+
"Content-Type": "application/json",
|
|
33648
|
+
"X-Plugin-Auth-Token": token
|
|
33649
|
+
});
|
|
33650
|
+
const response = await fetchImpl(url2, {
|
|
33651
|
+
method: "POST",
|
|
33652
|
+
headers: makeHeaders(opts.pluginAuthToken),
|
|
33653
|
+
body
|
|
33654
|
+
});
|
|
33655
|
+
if (response.status === 401 || response.status === 403) {
|
|
33656
|
+
const freshToken = await fetchCurrentPluginAuthToken(
|
|
33657
|
+
opts.sessionId,
|
|
33658
|
+
opts.pluginId,
|
|
33659
|
+
opts.pollSecret
|
|
33660
|
+
);
|
|
33661
|
+
if (freshToken !== null) {
|
|
33662
|
+
await fetchImpl(url2, { method: "POST", headers: makeHeaders(freshToken), body });
|
|
33663
|
+
}
|
|
33664
|
+
}
|
|
33665
|
+
} catch {
|
|
33666
|
+
}
|
|
33667
|
+
}
|
|
33587
33668
|
async function postBudgetReached(opts, fetchImpl = fetch) {
|
|
33588
33669
|
const url2 = `${resolveApiBaseUrl()}/api/sessions/${encodeURIComponent(opts.sessionId)}/headroom-budget-reached`;
|
|
33589
33670
|
try {
|
|
@@ -36125,6 +36206,56 @@ function fetchQuotaUsage(runtime, historySvc) {
|
|
|
36125
36206
|
});
|
|
36126
36207
|
}
|
|
36127
36208
|
|
|
36209
|
+
// src/agents/claude/credential-sync.ts
|
|
36210
|
+
var import_chokidar2 = __toESM(require("chokidar"));
|
|
36211
|
+
var crypto5 = __toESM(require("crypto"));
|
|
36212
|
+
var CLAUDE_PUBLIC_AGENT_ID = "claude_code";
|
|
36213
|
+
function startClaudeCredentialSync(opts) {
|
|
36214
|
+
const push = opts.push ?? postCredentialSync;
|
|
36215
|
+
const read2 = opts.read ?? extractLocalClaudeToken;
|
|
36216
|
+
let lastHash = "";
|
|
36217
|
+
let stopped = false;
|
|
36218
|
+
const syncNow = async () => {
|
|
36219
|
+
if (stopped) return;
|
|
36220
|
+
try {
|
|
36221
|
+
const tok = await read2();
|
|
36222
|
+
if (!tok || !tok.credential) return;
|
|
36223
|
+
const hash = crypto5.createHash("sha256").update(tok.credential).digest("hex");
|
|
36224
|
+
if (hash === lastHash) return;
|
|
36225
|
+
lastHash = hash;
|
|
36226
|
+
await push({
|
|
36227
|
+
agent: CLAUDE_PUBLIC_AGENT_ID,
|
|
36228
|
+
sessionId: opts.sessionId,
|
|
36229
|
+
pluginId: opts.pluginId,
|
|
36230
|
+
pluginAuthToken: opts.pluginAuthToken,
|
|
36231
|
+
pollSecret: opts.pollSecret,
|
|
36232
|
+
method: "oauth",
|
|
36233
|
+
credential: tok.credential,
|
|
36234
|
+
agentState: tok.agentState
|
|
36235
|
+
});
|
|
36236
|
+
log.trace("cred-sync", "pushed fresh claude credential to the vault");
|
|
36237
|
+
} catch {
|
|
36238
|
+
}
|
|
36239
|
+
};
|
|
36240
|
+
const watcher = import_chokidar2.default.watch(claudeCredentialsPaths(), {
|
|
36241
|
+
ignoreInitial: true,
|
|
36242
|
+
// Debounce a rotation's multi-write until the file settles before reading.
|
|
36243
|
+
awaitWriteFinish: { stabilityThreshold: 400, pollInterval: 100 }
|
|
36244
|
+
});
|
|
36245
|
+
watcher.on("add", () => void syncNow());
|
|
36246
|
+
watcher.on("change", () => void syncNow());
|
|
36247
|
+
watcher.on("error", () => {
|
|
36248
|
+
});
|
|
36249
|
+
void syncNow();
|
|
36250
|
+
return {
|
|
36251
|
+
syncNow,
|
|
36252
|
+
stop: async () => {
|
|
36253
|
+
stopped = true;
|
|
36254
|
+
await watcher.close().catch(() => void 0);
|
|
36255
|
+
}
|
|
36256
|
+
};
|
|
36257
|
+
}
|
|
36258
|
+
|
|
36128
36259
|
// src/beads/workflow-hint.ts
|
|
36129
36260
|
var fs66 = __toESM(require("fs"));
|
|
36130
36261
|
var path72 = __toESM(require("path"));
|
|
@@ -37020,6 +37151,15 @@ async function start(requestedAgent, presetSession) {
|
|
|
37020
37151
|
process.once("exit", () => {
|
|
37021
37152
|
localHeadroomReporter?.stop();
|
|
37022
37153
|
});
|
|
37154
|
+
const claudeCredentialSync = session.agent === "claude" && session.pluginAuthToken ? startClaudeCredentialSync({
|
|
37155
|
+
sessionId: session.id,
|
|
37156
|
+
pluginId,
|
|
37157
|
+
pluginAuthToken: session.pluginAuthToken,
|
|
37158
|
+
pollSecret: session.pollSecret
|
|
37159
|
+
}) : null;
|
|
37160
|
+
process.once("exit", () => {
|
|
37161
|
+
void claudeCredentialSync?.stop();
|
|
37162
|
+
});
|
|
37023
37163
|
if (!isLocalSession()) ensureClaudeOnboarded(cwd);
|
|
37024
37164
|
let beads = null;
|
|
37025
37165
|
const getBeads = () => beads;
|
|
@@ -39862,7 +40002,7 @@ function checkChokidar() {
|
|
|
39862
40002
|
}
|
|
39863
40003
|
async function doctor(args2 = []) {
|
|
39864
40004
|
const json = args2.includes("--json");
|
|
39865
|
-
const cliVersion = true ? "2.61.
|
|
40005
|
+
const cliVersion = true ? "2.61.71" : "0.0.0-dev";
|
|
39866
40006
|
const apiBase2 = resolveApiBaseUrl();
|
|
39867
40007
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
39868
40008
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -40384,7 +40524,7 @@ async function mcpRun(args2) {
|
|
|
40384
40524
|
// src/commands/version.ts
|
|
40385
40525
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
40386
40526
|
function version2() {
|
|
40387
|
-
const v = true ? "2.61.
|
|
40527
|
+
const v = true ? "2.61.71" : "unknown";
|
|
40388
40528
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
40389
40529
|
}
|
|
40390
40530
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.61.
|
|
3
|
+
"version": "2.61.71",
|
|
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",
|