codeam-cli 2.71.0 → 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 +16 -0
- package/dist/index.js +152 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@ 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
|
+
|
|
17
|
+
## [2.71.0] — 2026-08-29
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **cli:** Decir que base de datos falta, y con que variable se arregla (#668)
|
|
22
|
+
|
|
7
23
|
## [2.70.0] — 2026-08-29
|
|
8
24
|
|
|
9
25
|
### Fixed
|
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",
|
|
@@ -8689,6 +8700,22 @@ async function postBatonEvent(input) {
|
|
|
8689
8700
|
};
|
|
8690
8701
|
}
|
|
8691
8702
|
}
|
|
8703
|
+
async function postTurnEvent(input) {
|
|
8704
|
+
try {
|
|
8705
|
+
await _transport.postJsonAuthed(
|
|
8706
|
+
`${API_BASE}/api/commands/turn-events`,
|
|
8707
|
+
{
|
|
8708
|
+
pluginId: input.pluginId,
|
|
8709
|
+
commandId: input.commandId,
|
|
8710
|
+
phase: input.phase,
|
|
8711
|
+
agentId: input.agentId,
|
|
8712
|
+
errorCode: input.errorCode
|
|
8713
|
+
},
|
|
8714
|
+
input.pluginAuthToken
|
|
8715
|
+
);
|
|
8716
|
+
} catch {
|
|
8717
|
+
}
|
|
8718
|
+
}
|
|
8692
8719
|
async function postHeadroomEvent(input) {
|
|
8693
8720
|
try {
|
|
8694
8721
|
await _transport.postJsonAuthed(
|
|
@@ -9473,9 +9500,14 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9473
9500
|
// evicted (a command that old is long gone from the server queue anyway).
|
|
9474
9501
|
processedIds = /* @__PURE__ */ new Set();
|
|
9475
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;
|
|
9476
9506
|
/** Polling backoff state (only used on the fallback). */
|
|
9477
9507
|
pollTimer = null;
|
|
9478
9508
|
pollFailures = 0;
|
|
9509
|
+
/** Consecutive ack POST failures — drives the visibility escalation below. */
|
|
9510
|
+
ackFailures = 0;
|
|
9479
9511
|
// Successive polls that returned no commands. Drives an idle
|
|
9480
9512
|
// backoff so a CLI sitting on the polling fallback (SSE was
|
|
9481
9513
|
// unavailable) doesn't keep hammering the API every 2 s when
|
|
@@ -9840,13 +9872,49 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9840
9872
|
}
|
|
9841
9873
|
}
|
|
9842
9874
|
/** Confirm receipt of command ids so the backend removes them from the queue
|
|
9843
|
-
* (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
|
+
*/
|
|
9844
9889
|
ackCommands(ids) {
|
|
9845
9890
|
const commandIds = ids.filter((id) => typeof id === "string" && id.length > 0);
|
|
9846
9891
|
if (commandIds.length === 0) return;
|
|
9847
9892
|
void _postJson(`${API_BASE2}/api/commands/ack`, { pluginId: this.pluginId, commandIds }, {
|
|
9848
9893
|
...this.pollSecretHeader()
|
|
9849
|
-
}).
|
|
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
|
+
});
|
|
9850
9918
|
}
|
|
9851
9919
|
// ─── Heartbeat + agents ──────────────────────────────────────────
|
|
9852
9920
|
async sendHeartbeat(online) {
|
|
@@ -9859,7 +9927,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9859
9927
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
9860
9928
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
9861
9929
|
// pair/reconnect). Older backends ignore the extra field.
|
|
9862
|
-
..."2.71.
|
|
9930
|
+
..."2.71.2" ? { ideVersion: "2.71.2" } : {}
|
|
9863
9931
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
9864
9932
|
}
|
|
9865
9933
|
/**
|
|
@@ -22118,7 +22186,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
22118
22186
|
if (process.env.NODE_ENV === "test") return;
|
|
22119
22187
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22120
22188
|
if (process.env.CI) return;
|
|
22121
|
-
const current2 = true ? "2.71.
|
|
22189
|
+
const current2 = true ? "2.71.2" : null;
|
|
22122
22190
|
if (!current2) return;
|
|
22123
22191
|
const cache = readCache();
|
|
22124
22192
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22135,7 +22203,7 @@ function checkForUpdates() {
|
|
|
22135
22203
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22136
22204
|
if (process.env.CI) return;
|
|
22137
22205
|
if (!process.stdout.isTTY) return;
|
|
22138
|
-
const current2 = true ? "2.71.
|
|
22206
|
+
const current2 = true ? "2.71.2" : null;
|
|
22139
22207
|
if (!current2) return;
|
|
22140
22208
|
const cache = readCache();
|
|
22141
22209
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22158,7 +22226,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
|
22158
22226
|
var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
|
|
22159
22227
|
var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
|
|
22160
22228
|
function currentCliVersion() {
|
|
22161
|
-
return true ? "2.71.
|
|
22229
|
+
return true ? "2.71.2" : null;
|
|
22162
22230
|
}
|
|
22163
22231
|
async function installedVersion(deps) {
|
|
22164
22232
|
const ls = await deps.run(
|
|
@@ -24709,6 +24777,14 @@ var REQUIRED_FIELDS2 = [
|
|
|
24709
24777
|
];
|
|
24710
24778
|
function safeParseDetection(raw) {
|
|
24711
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) {
|
|
24712
24788
|
let parsed = tryParseObject(raw.trim());
|
|
24713
24789
|
if (!parsed) {
|
|
24714
24790
|
const stripped = raw.replace(/^```(?:json)?\s*/m, "").replace(/\s*```\s*$/m, "").trim();
|
|
@@ -24720,12 +24796,7 @@ function safeParseDetection(raw) {
|
|
|
24720
24796
|
const candidate = extractFirstJsonObject(raw);
|
|
24721
24797
|
if (candidate) parsed = tryParseObject(candidate);
|
|
24722
24798
|
}
|
|
24723
|
-
|
|
24724
|
-
const obj = parsed;
|
|
24725
|
-
for (const field of REQUIRED_FIELDS2) {
|
|
24726
|
-
if (!(field in obj)) return null;
|
|
24727
|
-
}
|
|
24728
|
-
return obj;
|
|
24799
|
+
return parsed ?? null;
|
|
24729
24800
|
}
|
|
24730
24801
|
function tryParseObject(s) {
|
|
24731
24802
|
try {
|
|
@@ -24777,6 +24848,38 @@ function parseExpoUrl(stdout) {
|
|
|
24777
24848
|
const match = stdout.match(EXPO_URL_RE);
|
|
24778
24849
|
return match ? match[0] : null;
|
|
24779
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
|
+
}
|
|
24780
24883
|
|
|
24781
24884
|
// src/services/preview/port-ready.ts
|
|
24782
24885
|
var net2 = __toESM(require("net"));
|
|
@@ -27870,7 +27973,9 @@ function dispatchPrompt(ctx, prompt) {
|
|
|
27870
27973
|
}
|
|
27871
27974
|
var startTask = async (ctx, cmd, parsed) => {
|
|
27872
27975
|
const { prompt, files } = parsed;
|
|
27976
|
+
reportTurn(ctx, cmd.id, "received");
|
|
27873
27977
|
if (parsed.agentId && parsed.agentId !== ctx.agentId) {
|
|
27978
|
+
reportTurn(ctx, cmd.id, "failed", "AGENT_SWITCH_UNSUPPORTED");
|
|
27874
27979
|
await ctx.relay.sendResult(cmd.id, "failed", {
|
|
27875
27980
|
error: "Switching agents isn't supported on this session."
|
|
27876
27981
|
});
|
|
@@ -27890,8 +27995,23 @@ var startTask = async (ctx, cmd, parsed) => {
|
|
|
27890
27995
|
}, 12e4);
|
|
27891
27996
|
} else if (effectivePrompt) {
|
|
27892
27997
|
dispatchPrompt(ctx, effectivePrompt);
|
|
27998
|
+
} else {
|
|
27999
|
+
reportTurn(ctx, cmd.id, "failed", "EMPTY_PROMPT");
|
|
28000
|
+
return;
|
|
27893
28001
|
}
|
|
28002
|
+
reportTurn(ctx, cmd.id, "started");
|
|
27894
28003
|
};
|
|
28004
|
+
function reportTurn(ctx, commandId, phase, errorCode) {
|
|
28005
|
+
if (!ctx.pluginId || !ctx.pluginAuthToken) return;
|
|
28006
|
+
void postTurnEvent({
|
|
28007
|
+
pluginId: ctx.pluginId,
|
|
28008
|
+
pluginAuthToken: ctx.pluginAuthToken,
|
|
28009
|
+
commandId,
|
|
28010
|
+
phase,
|
|
28011
|
+
agentId: ctx.agentId,
|
|
28012
|
+
errorCode
|
|
28013
|
+
});
|
|
28014
|
+
}
|
|
27895
28015
|
var provideInput = (ctx, _cmd, parsed) => {
|
|
27896
28016
|
if (parsed.input) dispatchPrompt(ctx, parsed.input);
|
|
27897
28017
|
};
|
|
@@ -29106,7 +29226,13 @@ var requestPreviewDetectH = (ctx) => {
|
|
|
29106
29226
|
const tookMs = Date.now() - startedAt;
|
|
29107
29227
|
const detection = safeParseDetection(raw);
|
|
29108
29228
|
if (!detection) {
|
|
29109
|
-
|
|
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
|
+
);
|
|
29110
29236
|
emitPreviewEvent({
|
|
29111
29237
|
sessionId: ctx.sessionId,
|
|
29112
29238
|
pluginId: ctx.pluginId,
|
|
@@ -29114,7 +29240,9 @@ var requestPreviewDetectH = (ctx) => {
|
|
|
29114
29240
|
type: USER_EVENTS.PREVIEW_ERROR,
|
|
29115
29241
|
payload: {
|
|
29116
29242
|
stage: "detection",
|
|
29117
|
-
|
|
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."
|
|
29118
29246
|
}
|
|
29119
29247
|
});
|
|
29120
29248
|
return;
|
|
@@ -29182,7 +29310,12 @@ function prewarmPreviewDetection(runtime) {
|
|
|
29182
29310
|
if (await readPreviewConfig(cwd)) return;
|
|
29183
29311
|
const raw = await runtime.generateOneShot(PREVIEW_DETECT_PROMPT).catch(() => null);
|
|
29184
29312
|
const detection = safeParseDetection(raw);
|
|
29185
|
-
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;
|
|
29186
29319
|
await writePreviewConfig(cwd, detection);
|
|
29187
29320
|
log.info(
|
|
29188
29321
|
"preview",
|
|
@@ -46726,7 +46859,7 @@ function checkChokidar() {
|
|
|
46726
46859
|
}
|
|
46727
46860
|
async function doctor(args2 = []) {
|
|
46728
46861
|
const json = args2.includes("--json");
|
|
46729
|
-
const cliVersion = true ? "2.71.
|
|
46862
|
+
const cliVersion = true ? "2.71.2" : "0.0.0-dev";
|
|
46730
46863
|
const apiBase2 = resolveApiBaseUrl();
|
|
46731
46864
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
46732
46865
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -47117,7 +47250,7 @@ async function mcpRun(args2) {
|
|
|
47117
47250
|
// src/commands/version.ts
|
|
47118
47251
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
47119
47252
|
function version2() {
|
|
47120
|
-
const v = true ? "2.71.
|
|
47253
|
+
const v = true ? "2.71.2" : "unknown";
|
|
47121
47254
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
47122
47255
|
}
|
|
47123
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",
|