codeam-cli 2.71.1 → 2.71.2
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 +10 -0
- package/dist/index.js +119 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ 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.71.1] — 2026-08-30
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **cli:** Reportar en qué fase va cada turno — la mitad del embudo que el servidor no ve (#670)
|
|
12
|
+
|
|
13
|
+
### Tests
|
|
14
|
+
|
|
15
|
+
- **cli:** Dar plazo realista a los dos tests que conducen start() de verdad (#669)
|
|
16
|
+
|
|
7
17
|
## [2.71.0] — 2026-08-29
|
|
8
18
|
|
|
9
19
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -2909,6 +2909,17 @@ var USER_EVENTS = {
|
|
|
2909
2909
|
SELF_HOSTED_HOST_STATUS: "self_hosted_host_status",
|
|
2910
2910
|
SELF_HOSTED_HOST_REMOVED: "self_hosted_host_removed",
|
|
2911
2911
|
SELF_HOSTED_HOST_TELEMETRY: "self_hosted_host_telemetry",
|
|
2912
|
+
/**
|
|
2913
|
+
* Una orden que NINGUN CLI recogio y que expiro de la cola.
|
|
2914
|
+
*
|
|
2915
|
+
* La cola `commands:<pluginId>` caduca a los 600 s y Redis no avisa a nadie:
|
|
2916
|
+
* la tarea deja de existir en silencio. Medido el 2026-08-30 en un usuario
|
|
2917
|
+
* real, SIETE de sus 17 `start_task` acabaron asi — no recibio nada y no
|
|
2918
|
+
* habia forma de saber que se habian descartado. Lo publica el barrido
|
|
2919
|
+
* `command-expiry-sweep` del backend; nadie puede notificar su propio
|
|
2920
|
+
* silencio.
|
|
2921
|
+
*/
|
|
2922
|
+
COMMAND_EXPIRED: "command_expired",
|
|
2912
2923
|
SELF_HOSTED_HOST_METRICS: "self_hosted_host_metrics",
|
|
2913
2924
|
SELF_HOSTED_HOST_SESSIONS: "self_hosted_host_sessions",
|
|
2914
2925
|
SELF_HOSTED_DEPLOY_PROGRESS: "self_hosted_deploy_progress",
|
|
@@ -8081,7 +8092,7 @@ function readAnonId() {
|
|
|
8081
8092
|
}
|
|
8082
8093
|
function superProperties() {
|
|
8083
8094
|
return {
|
|
8084
|
-
cliVersion: true ? "2.71.
|
|
8095
|
+
cliVersion: true ? "2.71.2" : "0.0.0-dev",
|
|
8085
8096
|
nodeVersion: process.version,
|
|
8086
8097
|
platform: process.platform,
|
|
8087
8098
|
arch: process.arch,
|
|
@@ -8323,7 +8334,7 @@ var http = __toESM(require("http"));
|
|
|
8323
8334
|
// package.json
|
|
8324
8335
|
var package_default = {
|
|
8325
8336
|
name: "codeam-cli",
|
|
8326
|
-
version: "2.71.
|
|
8337
|
+
version: "2.71.2",
|
|
8327
8338
|
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.",
|
|
8328
8339
|
type: "commonjs",
|
|
8329
8340
|
main: "dist/index.js",
|
|
@@ -9489,9 +9500,14 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9489
9500
|
// evicted (a command that old is long gone from the server queue anyway).
|
|
9490
9501
|
processedIds = /* @__PURE__ */ new Set();
|
|
9491
9502
|
static PROCESSED_ID_CAP = 1e3;
|
|
9503
|
+
/** After the first, re-warn every Nth consecutive ack failure — loud enough
|
|
9504
|
+
* to notice, quiet enough not to drown the log during a network outage. */
|
|
9505
|
+
static ACK_WARN_EVERY = 20;
|
|
9492
9506
|
/** Polling backoff state (only used on the fallback). */
|
|
9493
9507
|
pollTimer = null;
|
|
9494
9508
|
pollFailures = 0;
|
|
9509
|
+
/** Consecutive ack POST failures — drives the visibility escalation below. */
|
|
9510
|
+
ackFailures = 0;
|
|
9495
9511
|
// Successive polls that returned no commands. Drives an idle
|
|
9496
9512
|
// backoff so a CLI sitting on the polling fallback (SSE was
|
|
9497
9513
|
// unavailable) doesn't keep hammering the API every 2 s when
|
|
@@ -9856,13 +9872,49 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9856
9872
|
}
|
|
9857
9873
|
}
|
|
9858
9874
|
/** Confirm receipt of command ids so the backend removes them from the queue
|
|
9859
|
-
* (the at-least-once delivery guarantee). Best-effort — never throws.
|
|
9875
|
+
* (the at-least-once delivery guarantee). Best-effort — never throws.
|
|
9876
|
+
*
|
|
9877
|
+
* ⚠️ A ONE-OFF failure here is harmless (the command redelivers and we dedupe),
|
|
9878
|
+
* but a PERSISTENT one is not: the queue never drains, so every batch is
|
|
9879
|
+
* redelivered forever and the host looks perfectly healthy from outside —
|
|
9880
|
+
* heartbeats land, commands still arrive, `status` stays `online`. That is
|
|
9881
|
+
* exactly how the fleet host ran from 2026-07-15 to 2026-08-31 with NO poll
|
|
9882
|
+
* secret enrolled, 401-ing every single ack, with nobody the wiser: the
|
|
9883
|
+
* failure lived in `log.trace`, below the level anyone reads.
|
|
9884
|
+
*
|
|
9885
|
+
* So: keep it best-effort, but make a SUSTAINED failure impossible to miss,
|
|
9886
|
+
* and name `PLUGIN_SECRET_REQUIRED` explicitly — it is not a network blip,
|
|
9887
|
+
* it is a host that must be re-enrolled and will never recover on its own.
|
|
9888
|
+
*/
|
|
9860
9889
|
ackCommands(ids) {
|
|
9861
9890
|
const commandIds = ids.filter((id) => typeof id === "string" && id.length > 0);
|
|
9862
9891
|
if (commandIds.length === 0) return;
|
|
9863
9892
|
void _postJson(`${API_BASE2}/api/commands/ack`, { pluginId: this.pluginId, commandIds }, {
|
|
9864
9893
|
...this.pollSecretHeader()
|
|
9865
|
-
}).
|
|
9894
|
+
}).then(() => {
|
|
9895
|
+
if (this.ackFailures > 0) {
|
|
9896
|
+
log.warn("relay", `ack recovered after ${this.ackFailures} consecutive failure(s)`);
|
|
9897
|
+
this.ackFailures = 0;
|
|
9898
|
+
}
|
|
9899
|
+
}).catch((err) => {
|
|
9900
|
+
this.ackFailures += 1;
|
|
9901
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
9902
|
+
if (/PLUGIN_SECRET_REQUIRED|INVALID_PLUGIN_SECRET/.test(reason)) {
|
|
9903
|
+
log.warn(
|
|
9904
|
+
"relay",
|
|
9905
|
+
`ack REJECTED (${reason}) \u2014 this host needs to be re-paired; commands will be redelivered forever until it is`
|
|
9906
|
+
);
|
|
9907
|
+
return;
|
|
9908
|
+
}
|
|
9909
|
+
if (this.ackFailures === 1 || this.ackFailures % _CommandRelayService.ACK_WARN_EVERY === 0) {
|
|
9910
|
+
log.warn(
|
|
9911
|
+
"relay",
|
|
9912
|
+
`ack failed ${this.ackFailures}x in a row \u2014 the backend queue is not draining: ${reason}`
|
|
9913
|
+
);
|
|
9914
|
+
return;
|
|
9915
|
+
}
|
|
9916
|
+
log.trace("relay", "ack post failed (will redeliver+dedupe)", err);
|
|
9917
|
+
});
|
|
9866
9918
|
}
|
|
9867
9919
|
// ─── Heartbeat + agents ──────────────────────────────────────────
|
|
9868
9920
|
async sendHeartbeat(online) {
|
|
@@ -9875,7 +9927,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9875
9927
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
9876
9928
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
9877
9929
|
// pair/reconnect). Older backends ignore the extra field.
|
|
9878
|
-
..."2.71.
|
|
9930
|
+
..."2.71.2" ? { ideVersion: "2.71.2" } : {}
|
|
9879
9931
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
9880
9932
|
}
|
|
9881
9933
|
/**
|
|
@@ -22134,7 +22186,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
22134
22186
|
if (process.env.NODE_ENV === "test") return;
|
|
22135
22187
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22136
22188
|
if (process.env.CI) return;
|
|
22137
|
-
const current2 = true ? "2.71.
|
|
22189
|
+
const current2 = true ? "2.71.2" : null;
|
|
22138
22190
|
if (!current2) return;
|
|
22139
22191
|
const cache = readCache();
|
|
22140
22192
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22151,7 +22203,7 @@ function checkForUpdates() {
|
|
|
22151
22203
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22152
22204
|
if (process.env.CI) return;
|
|
22153
22205
|
if (!process.stdout.isTTY) return;
|
|
22154
|
-
const current2 = true ? "2.71.
|
|
22206
|
+
const current2 = true ? "2.71.2" : null;
|
|
22155
22207
|
if (!current2) return;
|
|
22156
22208
|
const cache = readCache();
|
|
22157
22209
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22174,7 +22226,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
|
22174
22226
|
var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
|
|
22175
22227
|
var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
|
|
22176
22228
|
function currentCliVersion() {
|
|
22177
|
-
return true ? "2.71.
|
|
22229
|
+
return true ? "2.71.2" : null;
|
|
22178
22230
|
}
|
|
22179
22231
|
async function installedVersion(deps) {
|
|
22180
22232
|
const ls = await deps.run(
|
|
@@ -24725,6 +24777,14 @@ var REQUIRED_FIELDS2 = [
|
|
|
24725
24777
|
];
|
|
24726
24778
|
function safeParseDetection(raw) {
|
|
24727
24779
|
if (!raw) return null;
|
|
24780
|
+
const parsed = parseDetectionObject(raw);
|
|
24781
|
+
if (!parsed) return null;
|
|
24782
|
+
for (const field of REQUIRED_FIELDS2) {
|
|
24783
|
+
if (!(field in parsed)) return null;
|
|
24784
|
+
}
|
|
24785
|
+
return parsed;
|
|
24786
|
+
}
|
|
24787
|
+
function parseDetectionObject(raw) {
|
|
24728
24788
|
let parsed = tryParseObject(raw.trim());
|
|
24729
24789
|
if (!parsed) {
|
|
24730
24790
|
const stripped = raw.replace(/^```(?:json)?\s*/m, "").replace(/\s*```\s*$/m, "").trim();
|
|
@@ -24736,12 +24796,7 @@ function safeParseDetection(raw) {
|
|
|
24736
24796
|
const candidate = extractFirstJsonObject(raw);
|
|
24737
24797
|
if (candidate) parsed = tryParseObject(candidate);
|
|
24738
24798
|
}
|
|
24739
|
-
|
|
24740
|
-
const obj = parsed;
|
|
24741
|
-
for (const field of REQUIRED_FIELDS2) {
|
|
24742
|
-
if (!(field in obj)) return null;
|
|
24743
|
-
}
|
|
24744
|
-
return obj;
|
|
24799
|
+
return parsed ?? null;
|
|
24745
24800
|
}
|
|
24746
24801
|
function tryParseObject(s) {
|
|
24747
24802
|
try {
|
|
@@ -24793,6 +24848,38 @@ function parseExpoUrl(stdout) {
|
|
|
24793
24848
|
const match = stdout.match(EXPO_URL_RE);
|
|
24794
24849
|
return match ? match[0] : null;
|
|
24795
24850
|
}
|
|
24851
|
+
var RAW_EXCERPT_LIMIT = 1e3;
|
|
24852
|
+
function describeDetectionFailure(raw) {
|
|
24853
|
+
const text = raw?.trim() ?? "";
|
|
24854
|
+
const rawExcerpt = text.slice(0, RAW_EXCERPT_LIMIT);
|
|
24855
|
+
if (!text) {
|
|
24856
|
+
return {
|
|
24857
|
+
reason: "no_output",
|
|
24858
|
+
message: "The agent didn't return anything for this project. Try again, or add a .codeam/preview.json override.",
|
|
24859
|
+
rawExcerpt
|
|
24860
|
+
};
|
|
24861
|
+
}
|
|
24862
|
+
const parsed = parseDetectionObject(text);
|
|
24863
|
+
if (!parsed) {
|
|
24864
|
+
return {
|
|
24865
|
+
reason: "no_json",
|
|
24866
|
+
message: "The agent replied without a JSON block. Try again, or add a .codeam/preview.json override.",
|
|
24867
|
+
rawExcerpt
|
|
24868
|
+
};
|
|
24869
|
+
}
|
|
24870
|
+
const missing = REQUIRED_FIELDS2.filter((f) => !(f in parsed)).map(String);
|
|
24871
|
+
if (missing.length > 0) {
|
|
24872
|
+
return {
|
|
24873
|
+
reason: "missing_fields",
|
|
24874
|
+
// Decir CUALES: es lo unico con lo que alguien puede escribir el
|
|
24875
|
+
// override a mano y desbloquearse sin esperar a nadie.
|
|
24876
|
+
message: `The agent's answer is missing ${missing.join(", ")}. Try again, or add a .codeam/preview.json override.`,
|
|
24877
|
+
missing,
|
|
24878
|
+
rawExcerpt
|
|
24879
|
+
};
|
|
24880
|
+
}
|
|
24881
|
+
return null;
|
|
24882
|
+
}
|
|
24796
24883
|
|
|
24797
24884
|
// src/services/preview/port-ready.ts
|
|
24798
24885
|
var net2 = __toESM(require("net"));
|
|
@@ -29139,7 +29226,13 @@ var requestPreviewDetectH = (ctx) => {
|
|
|
29139
29226
|
const tookMs = Date.now() - startedAt;
|
|
29140
29227
|
const detection = safeParseDetection(raw);
|
|
29141
29228
|
if (!detection) {
|
|
29142
|
-
|
|
29229
|
+
const failure = describeDetectionFailure(raw);
|
|
29230
|
+
log.info(
|
|
29231
|
+
"preview",
|
|
29232
|
+
`detect: failed after ${tookMs}ms reason=${failure?.reason ?? "unknown"}` + (failure?.missing ? ` missing=${failure.missing.join(",")}` : "") + `
|
|
29233
|
+
--- salida cruda del agente (acotada) ---
|
|
29234
|
+
${failure?.rawExcerpt ?? ""}`
|
|
29235
|
+
);
|
|
29143
29236
|
emitPreviewEvent({
|
|
29144
29237
|
sessionId: ctx.sessionId,
|
|
29145
29238
|
pluginId: ctx.pluginId,
|
|
@@ -29147,7 +29240,9 @@ var requestPreviewDetectH = (ctx) => {
|
|
|
29147
29240
|
type: USER_EVENTS.PREVIEW_ERROR,
|
|
29148
29241
|
payload: {
|
|
29149
29242
|
stage: "detection",
|
|
29150
|
-
|
|
29243
|
+
// El mensaje sale del diagnostico: decir "JSON invalido" cuando el
|
|
29244
|
+
// agente no contesto manda al usuario a mirar un JSON que no existe.
|
|
29245
|
+
message: failure?.message ?? "Agent returned invalid JSON. Try again, or add a .codeam/preview.json override."
|
|
29151
29246
|
}
|
|
29152
29247
|
});
|
|
29153
29248
|
return;
|
|
@@ -29215,7 +29310,12 @@ function prewarmPreviewDetection(runtime) {
|
|
|
29215
29310
|
if (await readPreviewConfig(cwd)) return;
|
|
29216
29311
|
const raw = await runtime.generateOneShot(PREVIEW_DETECT_PROMPT).catch(() => null);
|
|
29217
29312
|
const detection = safeParseDetection(raw);
|
|
29218
|
-
if (!detection
|
|
29313
|
+
if (!detection) {
|
|
29314
|
+
const failure = describeDetectionFailure(raw);
|
|
29315
|
+
log.info("preview", `prewarm: detection failed reason=${failure?.reason ?? "unknown"}`);
|
|
29316
|
+
return;
|
|
29317
|
+
}
|
|
29318
|
+
if (detection.framework === "unsupported") return;
|
|
29219
29319
|
await writePreviewConfig(cwd, detection);
|
|
29220
29320
|
log.info(
|
|
29221
29321
|
"preview",
|
|
@@ -46759,7 +46859,7 @@ function checkChokidar() {
|
|
|
46759
46859
|
}
|
|
46760
46860
|
async function doctor(args2 = []) {
|
|
46761
46861
|
const json = args2.includes("--json");
|
|
46762
|
-
const cliVersion = true ? "2.71.
|
|
46862
|
+
const cliVersion = true ? "2.71.2" : "0.0.0-dev";
|
|
46763
46863
|
const apiBase2 = resolveApiBaseUrl();
|
|
46764
46864
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
46765
46865
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -47150,7 +47250,7 @@ async function mcpRun(args2) {
|
|
|
47150
47250
|
// src/commands/version.ts
|
|
47151
47251
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
47152
47252
|
function version2() {
|
|
47153
|
-
const v = true ? "2.71.
|
|
47253
|
+
const v = true ? "2.71.2" : "unknown";
|
|
47154
47254
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
47155
47255
|
}
|
|
47156
47256
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.71.
|
|
3
|
+
"version": "2.71.2",
|
|
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",
|