codeam-cli 2.71.2 → 2.73.0
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/dist/index.js +164 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1750,7 +1750,12 @@ var INTEGRATION_REGISTRY = {
|
|
|
1750
1750
|
label: "Token",
|
|
1751
1751
|
placeholder: "Your Trello token",
|
|
1752
1752
|
secret: true,
|
|
1753
|
-
|
|
1753
|
+
// WARNING - the Power-Up API-key page also shows a "Secret", and it is
|
|
1754
|
+
// NOT the token: both are 64 hex characters and look identical, so it
|
|
1755
|
+
// is the natural thing to copy. Pasting the Secret fails with Trello's
|
|
1756
|
+
// misleading `invalid key`, which then sends the user to regenerate a
|
|
1757
|
+
// perfectly good API key (owner report, 2026-09-01). Say so here.
|
|
1758
|
+
help: 'On that same API-key page, click the "Token" link to authorize and generate a token (read,write, never-expiring). NOT the "Secret" on that page - it looks identical but will not work.'
|
|
1754
1759
|
}
|
|
1755
1760
|
]
|
|
1756
1761
|
},
|
|
@@ -8092,7 +8097,7 @@ function readAnonId() {
|
|
|
8092
8097
|
}
|
|
8093
8098
|
function superProperties() {
|
|
8094
8099
|
return {
|
|
8095
|
-
cliVersion: true ? "2.
|
|
8100
|
+
cliVersion: true ? "2.73.0" : "0.0.0-dev",
|
|
8096
8101
|
nodeVersion: process.version,
|
|
8097
8102
|
platform: process.platform,
|
|
8098
8103
|
arch: process.arch,
|
|
@@ -8334,7 +8339,7 @@ var http = __toESM(require("http"));
|
|
|
8334
8339
|
// package.json
|
|
8335
8340
|
var package_default = {
|
|
8336
8341
|
name: "codeam-cli",
|
|
8337
|
-
version: "2.
|
|
8342
|
+
version: "2.73.0",
|
|
8338
8343
|
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.",
|
|
8339
8344
|
type: "commonjs",
|
|
8340
8345
|
main: "dist/index.js",
|
|
@@ -9902,7 +9907,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9902
9907
|
if (/PLUGIN_SECRET_REQUIRED|INVALID_PLUGIN_SECRET/.test(reason)) {
|
|
9903
9908
|
log.warn(
|
|
9904
9909
|
"relay",
|
|
9905
|
-
`ack REJECTED (${reason}) \u2014 this host
|
|
9910
|
+
`ack REJECTED (${reason}) \u2014 this host must be re-paired; retrying will never fix it. Delivery still works while this client drains on receive, but the ack rail is dead and ack-mode would stall here.`
|
|
9906
9911
|
);
|
|
9907
9912
|
return;
|
|
9908
9913
|
}
|
|
@@ -9927,7 +9932,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9927
9932
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
9928
9933
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
9929
9934
|
// pair/reconnect). Older backends ignore the extra field.
|
|
9930
|
-
..."2.
|
|
9935
|
+
..."2.73.0" ? { ideVersion: "2.73.0" } : {}
|
|
9931
9936
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
9932
9937
|
}
|
|
9933
9938
|
/**
|
|
@@ -11527,6 +11532,13 @@ var startCommandSchema = import_zod.z.object({
|
|
|
11527
11532
|
value: import_zod.z.string().max(32768)
|
|
11528
11533
|
})
|
|
11529
11534
|
).max(512).optional(),
|
|
11535
|
+
// `env_parse` — how a pasted `.env` blob meets the project's current one.
|
|
11536
|
+
// `merge` (the default when absent) keeps variables the paste does not
|
|
11537
|
+
// mention, because a PARTIAL paste is the common case: people copy the block
|
|
11538
|
+
// they care about, not the whole file. `replace` is opt-in and destructive
|
|
11539
|
+
// in intent, so it must be asked for explicitly. The blob itself rides the
|
|
11540
|
+
// existing `content` field.
|
|
11541
|
+
mode: import_zod.z.enum(["merge", "replace"]).optional(),
|
|
11530
11542
|
// `skills_configure` — the curated `SkillId` to add/remove. `list` (and a
|
|
11531
11543
|
// malformed/unknown id on add/remove) sends no `skillId` or an invalid one;
|
|
11532
11544
|
// `configureSkill` itself validates against the shared registry.
|
|
@@ -22186,7 +22198,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
22186
22198
|
if (process.env.NODE_ENV === "test") return;
|
|
22187
22199
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22188
22200
|
if (process.env.CI) return;
|
|
22189
|
-
const current2 = true ? "2.
|
|
22201
|
+
const current2 = true ? "2.73.0" : null;
|
|
22190
22202
|
if (!current2) return;
|
|
22191
22203
|
const cache = readCache();
|
|
22192
22204
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22203,7 +22215,7 @@ function checkForUpdates() {
|
|
|
22203
22215
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22204
22216
|
if (process.env.CI) return;
|
|
22205
22217
|
if (!process.stdout.isTTY) return;
|
|
22206
|
-
const current2 = true ? "2.
|
|
22218
|
+
const current2 = true ? "2.73.0" : null;
|
|
22207
22219
|
if (!current2) return;
|
|
22208
22220
|
const cache = readCache();
|
|
22209
22221
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22226,7 +22238,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
|
22226
22238
|
var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
|
|
22227
22239
|
var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
|
|
22228
22240
|
function currentCliVersion() {
|
|
22229
|
-
return true ? "2.
|
|
22241
|
+
return true ? "2.73.0" : null;
|
|
22230
22242
|
}
|
|
22231
22243
|
async function installedVersion(deps) {
|
|
22232
22244
|
const ls = await deps.run(
|
|
@@ -22519,6 +22531,14 @@ function isFleetLimits(v) {
|
|
|
22519
22531
|
function isFleetCreateBoxPayload(p2) {
|
|
22520
22532
|
return typeof p2.boxId === "string" && isFleetContainerName(p2.containerName) && typeof p2.enrollToken === "string" && p2.enrollToken.length > 0 && typeof p2.apiOrigin === "string" && p2.apiOrigin.length > 0 && isFleetLimits(p2.limits);
|
|
22521
22533
|
}
|
|
22534
|
+
function isFleetMigrateBoxImagePayload(p2) {
|
|
22535
|
+
return typeof p2.boxId === "string" && isFleetContainerName(p2.containerName) && typeof p2.apiOrigin === "string" && p2.apiOrigin.length > 0 && isFleetLimits(p2.limits) && // ⚠️ REJECT a token outright rather than ignoring it. A migrate that
|
|
22536
|
+
// carried one would bake a 15-minute credential into a container that may
|
|
22537
|
+
// not start for days — a terminal 4xx at boot, i.e. a box that never comes
|
|
22538
|
+
// back. If a future backend starts sending one, this must fail loudly here
|
|
22539
|
+
// rather than silently produce that box.
|
|
22540
|
+
p2.enrollToken === void 0;
|
|
22541
|
+
}
|
|
22522
22542
|
function isFleetBoxRefPayload(p2) {
|
|
22523
22543
|
if (typeof p2.boxId !== "string") return false;
|
|
22524
22544
|
if (!isFleetContainerName(p2.containerName)) return false;
|
|
@@ -22550,6 +22570,9 @@ function isMissingVolumeError(stderr) {
|
|
|
22550
22570
|
function resolveFleetBoxImage() {
|
|
22551
22571
|
return process.env.CODEAM_FLEET_BOX_IMAGE || "ghcr.io/edgar-durand/codeam-box:latest";
|
|
22552
22572
|
}
|
|
22573
|
+
function buildFleetBoxMigrateArgs(p2) {
|
|
22574
|
+
return buildFleetBoxRunArgs(p2).map((a) => a === "run" ? "create" : a).filter((a) => a !== "--pull=always" && a !== "-d");
|
|
22575
|
+
}
|
|
22553
22576
|
function buildFleetBoxRunArgs(p2) {
|
|
22554
22577
|
const userId = fleetUserIdFromContainerName(p2.containerName);
|
|
22555
22578
|
const image = resolveFleetBoxImage();
|
|
@@ -22949,6 +22972,14 @@ var HostAgentSupervisor = class {
|
|
|
22949
22972
|
await this.fleetCreateBox(cmd.payload);
|
|
22950
22973
|
return;
|
|
22951
22974
|
}
|
|
22975
|
+
if (cmd.type === "fleet_migrate_box_image") {
|
|
22976
|
+
if (!isFleetMigrateBoxImagePayload(cmd.payload)) {
|
|
22977
|
+
log.warn("host-agent", `ignoring malformed fleet_migrate_box_image id=${cmd.id}`);
|
|
22978
|
+
return;
|
|
22979
|
+
}
|
|
22980
|
+
await this.fleetMigrateBoxImage(cmd.payload);
|
|
22981
|
+
return;
|
|
22982
|
+
}
|
|
22952
22983
|
if (cmd.type === "fleet_start_box") {
|
|
22953
22984
|
if (!isFleetBoxRefPayload(cmd.payload)) {
|
|
22954
22985
|
log.warn("host-agent", `ignoring malformed fleet_start_box id=${cmd.id}`);
|
|
@@ -23119,6 +23150,90 @@ var HostAgentSupervisor = class {
|
|
|
23119
23150
|
}
|
|
23120
23151
|
/** `fleet_start_box` — wake a sleeping box (`docker start`). Idempotent: a
|
|
23121
23152
|
* container the host already removed is treated as success. */
|
|
23153
|
+
/**
|
|
23154
|
+
* `fleet_migrate_box_image` — re-point a SLEEPING box at the current
|
|
23155
|
+
* `:latest` without waking it, so the version it was pinned to becomes
|
|
23156
|
+
* reclaimable by the (already automated) dangling-image prune.
|
|
23157
|
+
*
|
|
23158
|
+
* WHY THIS IS NEEDED AT ALL. Boxes are created with `--pull=always` on
|
|
23159
|
+
* purpose, so each stays pinned to the image it was born on, and Docker
|
|
23160
|
+
* refuses to delete an image any container references INCLUDING stopped
|
|
23161
|
+
* ones — which is precisely what stops the prune from destroying a sleeping
|
|
23162
|
+
* user's box. So every image release strands ~7.7 GB per lagging box until it
|
|
23163
|
+
* wakes or is reaped on day 7 (fleet-1, 2026-09-02: 9 boxes across 4
|
|
23164
|
+
* versions, ~31 GB held).
|
|
23165
|
+
*
|
|
23166
|
+
* ⚠️ THE HOST RE-CHECKS THAT THE BOX IS STOPPED, even though the backend only
|
|
23167
|
+
* selects `status: 'SLEEPING'`. The DB's view can lag the host by a sweep —
|
|
23168
|
+
* a box that woke seconds ago is still SLEEPING in the row — and re-creating
|
|
23169
|
+
* a RUNNING container kills a live agent mid-turn. The authority on whether a
|
|
23170
|
+
* container is running is the daemon, so we ask it here and refuse rather
|
|
23171
|
+
* than trust the payload.
|
|
23172
|
+
*
|
|
23173
|
+
* ⚠️ `docker create`, and NO enroll token. `run` would start the container,
|
|
23174
|
+
* so migrating the sleeping set in a batch would wake all of them (9 ×
|
|
23175
|
+
* 1536 MB on a 16 GB host) only for the sleep sweep to re-sleep them 30
|
|
23176
|
+
* minutes later. And a token would be a 15-minute credential baked into a
|
|
23177
|
+
* container that may not start for days — a terminal 4xx at boot, i.e. a box
|
|
23178
|
+
* that never comes back. The sealed identity is in the named volume, which
|
|
23179
|
+
* `rm` WITHOUT `-v` preserves.
|
|
23180
|
+
*
|
|
23181
|
+
* Idempotent and best-effort: a box already on `:latest` is skipped, a
|
|
23182
|
+
* missing container is success (a concurrent reap), and nothing here may
|
|
23183
|
+
* throw into the relay — this is housekeeping.
|
|
23184
|
+
*/
|
|
23185
|
+
async fleetMigrateBoxImage(payload) {
|
|
23186
|
+
const { containerName } = payload;
|
|
23187
|
+
const state = await this.docker.run([
|
|
23188
|
+
"inspect",
|
|
23189
|
+
"--format",
|
|
23190
|
+
"{{.State.Running}}",
|
|
23191
|
+
containerName
|
|
23192
|
+
]);
|
|
23193
|
+
if (state.code !== 0) {
|
|
23194
|
+
if (isMissingContainerError(state.stderr)) return;
|
|
23195
|
+
log.warn(
|
|
23196
|
+
"host-agent",
|
|
23197
|
+
`fleet_migrate_box_image: inspect of ${containerName} failed (code=${state.code}) \u2014 skipping`
|
|
23198
|
+
);
|
|
23199
|
+
return;
|
|
23200
|
+
}
|
|
23201
|
+
if (state.stdout.trim() === "true") {
|
|
23202
|
+
log.info(
|
|
23203
|
+
"host-agent",
|
|
23204
|
+
`fleet_migrate_box_image: ${containerName} is RUNNING \u2014 skipping (never touch a live box)`
|
|
23205
|
+
);
|
|
23206
|
+
return;
|
|
23207
|
+
}
|
|
23208
|
+
if (!await this.fleetBoxImageStale(containerName)) {
|
|
23209
|
+
log.info("host-agent", `fleet_migrate_box_image: ${containerName} already current`);
|
|
23210
|
+
return;
|
|
23211
|
+
}
|
|
23212
|
+
const rm = await this.docker.run(["rm", "-f", containerName], {
|
|
23213
|
+
timeoutMs: DOCKER_RUN_TIMEOUT_MS
|
|
23214
|
+
});
|
|
23215
|
+
if (rm.code !== 0 && !isMissingContainerError(rm.stderr)) {
|
|
23216
|
+
log.warn(
|
|
23217
|
+
"host-agent",
|
|
23218
|
+
`fleet_migrate_box_image: rm of ${containerName} failed (code=${rm.code}): ${rm.stderr.trim().slice(-200)} \u2014 NOT creating a replacement`
|
|
23219
|
+
);
|
|
23220
|
+
return;
|
|
23221
|
+
}
|
|
23222
|
+
const res = await this.docker.run(buildFleetBoxMigrateArgs(payload), {
|
|
23223
|
+
timeoutMs: DOCKER_RUN_TIMEOUT_MS
|
|
23224
|
+
});
|
|
23225
|
+
if (res.code !== 0) {
|
|
23226
|
+
log.warn(
|
|
23227
|
+
"host-agent",
|
|
23228
|
+
`fleet_migrate_box_image: create of ${containerName} failed (code=${res.code}): ${res.stderr.trim().slice(-200)}`
|
|
23229
|
+
);
|
|
23230
|
+
return;
|
|
23231
|
+
}
|
|
23232
|
+
log.info(
|
|
23233
|
+
"host-agent",
|
|
23234
|
+
`fleet_migrate_box_image: ${containerName} re-pointed at :latest (still stopped)`
|
|
23235
|
+
);
|
|
23236
|
+
}
|
|
23122
23237
|
async fleetStartBox(payload) {
|
|
23123
23238
|
const { containerName } = payload;
|
|
23124
23239
|
if (fleetRefCanRecreate(payload) && await this.fleetBoxImageStale(containerName)) {
|
|
@@ -28213,6 +28328,44 @@ var envReadH = async (ctx, cmd) => {
|
|
|
28213
28328
|
await ctx.relay.sendResult(cmd.id, "failed", { error: err.message });
|
|
28214
28329
|
}
|
|
28215
28330
|
};
|
|
28331
|
+
var envParseH = async (ctx, cmd, parsed) => {
|
|
28332
|
+
const content = typeof parsed.content === "string" ? parsed.content : null;
|
|
28333
|
+
if (content === null) {
|
|
28334
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing content" });
|
|
28335
|
+
return;
|
|
28336
|
+
}
|
|
28337
|
+
const replace = parsed.mode === "replace";
|
|
28338
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
28339
|
+
for (const v of parseDotenv(content)) incoming.set(v.key, v.value);
|
|
28340
|
+
const distinctAssignments = new Set(
|
|
28341
|
+
content.split(/\r?\n/).map((l) => l.trim()).filter((l) => l !== "" && !l.startsWith("#") && l.includes("=")).map((l) => l.startsWith("export ") ? l.slice("export ".length).trim() : l).map((l) => l.slice(0, l.indexOf("=")).trim())
|
|
28342
|
+
);
|
|
28343
|
+
const unrecognized = Math.max(0, distinctAssignments.size - incoming.size);
|
|
28344
|
+
const existing = /* @__PURE__ */ new Map();
|
|
28345
|
+
if (!replace) {
|
|
28346
|
+
try {
|
|
28347
|
+
const raw = await fs65.promises.readFile(path71.join(process.cwd(), ".env"), "utf8");
|
|
28348
|
+
for (const v of parseDotenv(raw)) existing.set(v.key, v.value);
|
|
28349
|
+
} catch {
|
|
28350
|
+
}
|
|
28351
|
+
}
|
|
28352
|
+
let added = 0;
|
|
28353
|
+
let updated = 0;
|
|
28354
|
+
for (const [key, value] of incoming) {
|
|
28355
|
+
if (existing.has(key)) {
|
|
28356
|
+
if (existing.get(key) !== value) updated += 1;
|
|
28357
|
+
} else {
|
|
28358
|
+
added += 1;
|
|
28359
|
+
}
|
|
28360
|
+
existing.set(key, value);
|
|
28361
|
+
}
|
|
28362
|
+
await ctx.relay.sendResult(cmd.id, "completed", {
|
|
28363
|
+
vars: [...existing].map(([key, value]) => ({ key, value })),
|
|
28364
|
+
added,
|
|
28365
|
+
updated,
|
|
28366
|
+
unrecognized
|
|
28367
|
+
});
|
|
28368
|
+
};
|
|
28216
28369
|
var envWriteH = async (ctx, cmd, parsed) => {
|
|
28217
28370
|
const vars = parsed.vars;
|
|
28218
28371
|
if (!Array.isArray(vars)) {
|
|
@@ -29490,6 +29643,7 @@ var handlers = {
|
|
|
29490
29643
|
save_preview_config: savePreviewConfigH,
|
|
29491
29644
|
env_read: envReadH,
|
|
29492
29645
|
env_write: envWriteH,
|
|
29646
|
+
env_parse: envParseH,
|
|
29493
29647
|
skills_configure: skillsConfigureH,
|
|
29494
29648
|
take_control: takeControlH,
|
|
29495
29649
|
handback: handbackH,
|
|
@@ -46859,7 +47013,7 @@ function checkChokidar() {
|
|
|
46859
47013
|
}
|
|
46860
47014
|
async function doctor(args2 = []) {
|
|
46861
47015
|
const json = args2.includes("--json");
|
|
46862
|
-
const cliVersion = true ? "2.
|
|
47016
|
+
const cliVersion = true ? "2.73.0" : "0.0.0-dev";
|
|
46863
47017
|
const apiBase2 = resolveApiBaseUrl();
|
|
46864
47018
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
46865
47019
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -47250,7 +47404,7 @@ async function mcpRun(args2) {
|
|
|
47250
47404
|
// src/commands/version.ts
|
|
47251
47405
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
47252
47406
|
function version2() {
|
|
47253
|
-
const v = true ? "2.
|
|
47407
|
+
const v = true ? "2.73.0" : "unknown";
|
|
47254
47408
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
47255
47409
|
}
|
|
47256
47410
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.73.0",
|
|
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",
|