codeam-cli 2.71.1 → 2.72.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/CHANGELOG.md +10 -0
- package/dist/index.js +165 -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.
|
|
8095
|
+
cliVersion: true ? "2.72.0" : "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.
|
|
8337
|
+
version: "2.72.0",
|
|
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 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
|
+
);
|
|
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.
|
|
9930
|
+
..."2.72.0" ? { ideVersion: "2.72.0" } : {}
|
|
9879
9931
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
9880
9932
|
}
|
|
9881
9933
|
/**
|
|
@@ -11475,6 +11527,13 @@ var startCommandSchema = import_zod.z.object({
|
|
|
11475
11527
|
value: import_zod.z.string().max(32768)
|
|
11476
11528
|
})
|
|
11477
11529
|
).max(512).optional(),
|
|
11530
|
+
// `env_parse` — how a pasted `.env` blob meets the project's current one.
|
|
11531
|
+
// `merge` (the default when absent) keeps variables the paste does not
|
|
11532
|
+
// mention, because a PARTIAL paste is the common case: people copy the block
|
|
11533
|
+
// they care about, not the whole file. `replace` is opt-in and destructive
|
|
11534
|
+
// in intent, so it must be asked for explicitly. The blob itself rides the
|
|
11535
|
+
// existing `content` field.
|
|
11536
|
+
mode: import_zod.z.enum(["merge", "replace"]).optional(),
|
|
11478
11537
|
// `skills_configure` — the curated `SkillId` to add/remove. `list` (and a
|
|
11479
11538
|
// malformed/unknown id on add/remove) sends no `skillId` or an invalid one;
|
|
11480
11539
|
// `configureSkill` itself validates against the shared registry.
|
|
@@ -22134,7 +22193,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
22134
22193
|
if (process.env.NODE_ENV === "test") return;
|
|
22135
22194
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22136
22195
|
if (process.env.CI) return;
|
|
22137
|
-
const current2 = true ? "2.
|
|
22196
|
+
const current2 = true ? "2.72.0" : null;
|
|
22138
22197
|
if (!current2) return;
|
|
22139
22198
|
const cache = readCache();
|
|
22140
22199
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22151,7 +22210,7 @@ function checkForUpdates() {
|
|
|
22151
22210
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22152
22211
|
if (process.env.CI) return;
|
|
22153
22212
|
if (!process.stdout.isTTY) return;
|
|
22154
|
-
const current2 = true ? "2.
|
|
22213
|
+
const current2 = true ? "2.72.0" : null;
|
|
22155
22214
|
if (!current2) return;
|
|
22156
22215
|
const cache = readCache();
|
|
22157
22216
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22174,7 +22233,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
|
22174
22233
|
var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
|
|
22175
22234
|
var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
|
|
22176
22235
|
function currentCliVersion() {
|
|
22177
|
-
return true ? "2.
|
|
22236
|
+
return true ? "2.72.0" : null;
|
|
22178
22237
|
}
|
|
22179
22238
|
async function installedVersion(deps) {
|
|
22180
22239
|
const ls = await deps.run(
|
|
@@ -24725,6 +24784,14 @@ var REQUIRED_FIELDS2 = [
|
|
|
24725
24784
|
];
|
|
24726
24785
|
function safeParseDetection(raw) {
|
|
24727
24786
|
if (!raw) return null;
|
|
24787
|
+
const parsed = parseDetectionObject(raw);
|
|
24788
|
+
if (!parsed) return null;
|
|
24789
|
+
for (const field of REQUIRED_FIELDS2) {
|
|
24790
|
+
if (!(field in parsed)) return null;
|
|
24791
|
+
}
|
|
24792
|
+
return parsed;
|
|
24793
|
+
}
|
|
24794
|
+
function parseDetectionObject(raw) {
|
|
24728
24795
|
let parsed = tryParseObject(raw.trim());
|
|
24729
24796
|
if (!parsed) {
|
|
24730
24797
|
const stripped = raw.replace(/^```(?:json)?\s*/m, "").replace(/\s*```\s*$/m, "").trim();
|
|
@@ -24736,12 +24803,7 @@ function safeParseDetection(raw) {
|
|
|
24736
24803
|
const candidate = extractFirstJsonObject(raw);
|
|
24737
24804
|
if (candidate) parsed = tryParseObject(candidate);
|
|
24738
24805
|
}
|
|
24739
|
-
|
|
24740
|
-
const obj = parsed;
|
|
24741
|
-
for (const field of REQUIRED_FIELDS2) {
|
|
24742
|
-
if (!(field in obj)) return null;
|
|
24743
|
-
}
|
|
24744
|
-
return obj;
|
|
24806
|
+
return parsed ?? null;
|
|
24745
24807
|
}
|
|
24746
24808
|
function tryParseObject(s) {
|
|
24747
24809
|
try {
|
|
@@ -24793,6 +24855,38 @@ function parseExpoUrl(stdout) {
|
|
|
24793
24855
|
const match = stdout.match(EXPO_URL_RE);
|
|
24794
24856
|
return match ? match[0] : null;
|
|
24795
24857
|
}
|
|
24858
|
+
var RAW_EXCERPT_LIMIT = 1e3;
|
|
24859
|
+
function describeDetectionFailure(raw) {
|
|
24860
|
+
const text = raw?.trim() ?? "";
|
|
24861
|
+
const rawExcerpt = text.slice(0, RAW_EXCERPT_LIMIT);
|
|
24862
|
+
if (!text) {
|
|
24863
|
+
return {
|
|
24864
|
+
reason: "no_output",
|
|
24865
|
+
message: "The agent didn't return anything for this project. Try again, or add a .codeam/preview.json override.",
|
|
24866
|
+
rawExcerpt
|
|
24867
|
+
};
|
|
24868
|
+
}
|
|
24869
|
+
const parsed = parseDetectionObject(text);
|
|
24870
|
+
if (!parsed) {
|
|
24871
|
+
return {
|
|
24872
|
+
reason: "no_json",
|
|
24873
|
+
message: "The agent replied without a JSON block. Try again, or add a .codeam/preview.json override.",
|
|
24874
|
+
rawExcerpt
|
|
24875
|
+
};
|
|
24876
|
+
}
|
|
24877
|
+
const missing = REQUIRED_FIELDS2.filter((f) => !(f in parsed)).map(String);
|
|
24878
|
+
if (missing.length > 0) {
|
|
24879
|
+
return {
|
|
24880
|
+
reason: "missing_fields",
|
|
24881
|
+
// Decir CUALES: es lo unico con lo que alguien puede escribir el
|
|
24882
|
+
// override a mano y desbloquearse sin esperar a nadie.
|
|
24883
|
+
message: `The agent's answer is missing ${missing.join(", ")}. Try again, or add a .codeam/preview.json override.`,
|
|
24884
|
+
missing,
|
|
24885
|
+
rawExcerpt
|
|
24886
|
+
};
|
|
24887
|
+
}
|
|
24888
|
+
return null;
|
|
24889
|
+
}
|
|
24796
24890
|
|
|
24797
24891
|
// src/services/preview/port-ready.ts
|
|
24798
24892
|
var net2 = __toESM(require("net"));
|
|
@@ -28126,6 +28220,44 @@ var envReadH = async (ctx, cmd) => {
|
|
|
28126
28220
|
await ctx.relay.sendResult(cmd.id, "failed", { error: err.message });
|
|
28127
28221
|
}
|
|
28128
28222
|
};
|
|
28223
|
+
var envParseH = async (ctx, cmd, parsed) => {
|
|
28224
|
+
const content = typeof parsed.content === "string" ? parsed.content : null;
|
|
28225
|
+
if (content === null) {
|
|
28226
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing content" });
|
|
28227
|
+
return;
|
|
28228
|
+
}
|
|
28229
|
+
const replace = parsed.mode === "replace";
|
|
28230
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
28231
|
+
for (const v of parseDotenv(content)) incoming.set(v.key, v.value);
|
|
28232
|
+
const distinctAssignments = new Set(
|
|
28233
|
+
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())
|
|
28234
|
+
);
|
|
28235
|
+
const unrecognized = Math.max(0, distinctAssignments.size - incoming.size);
|
|
28236
|
+
const existing = /* @__PURE__ */ new Map();
|
|
28237
|
+
if (!replace) {
|
|
28238
|
+
try {
|
|
28239
|
+
const raw = await fs65.promises.readFile(path71.join(process.cwd(), ".env"), "utf8");
|
|
28240
|
+
for (const v of parseDotenv(raw)) existing.set(v.key, v.value);
|
|
28241
|
+
} catch {
|
|
28242
|
+
}
|
|
28243
|
+
}
|
|
28244
|
+
let added = 0;
|
|
28245
|
+
let updated = 0;
|
|
28246
|
+
for (const [key, value] of incoming) {
|
|
28247
|
+
if (existing.has(key)) {
|
|
28248
|
+
if (existing.get(key) !== value) updated += 1;
|
|
28249
|
+
} else {
|
|
28250
|
+
added += 1;
|
|
28251
|
+
}
|
|
28252
|
+
existing.set(key, value);
|
|
28253
|
+
}
|
|
28254
|
+
await ctx.relay.sendResult(cmd.id, "completed", {
|
|
28255
|
+
vars: [...existing].map(([key, value]) => ({ key, value })),
|
|
28256
|
+
added,
|
|
28257
|
+
updated,
|
|
28258
|
+
unrecognized
|
|
28259
|
+
});
|
|
28260
|
+
};
|
|
28129
28261
|
var envWriteH = async (ctx, cmd, parsed) => {
|
|
28130
28262
|
const vars = parsed.vars;
|
|
28131
28263
|
if (!Array.isArray(vars)) {
|
|
@@ -29139,7 +29271,13 @@ var requestPreviewDetectH = (ctx) => {
|
|
|
29139
29271
|
const tookMs = Date.now() - startedAt;
|
|
29140
29272
|
const detection = safeParseDetection(raw);
|
|
29141
29273
|
if (!detection) {
|
|
29142
|
-
|
|
29274
|
+
const failure = describeDetectionFailure(raw);
|
|
29275
|
+
log.info(
|
|
29276
|
+
"preview",
|
|
29277
|
+
`detect: failed after ${tookMs}ms reason=${failure?.reason ?? "unknown"}` + (failure?.missing ? ` missing=${failure.missing.join(",")}` : "") + `
|
|
29278
|
+
--- salida cruda del agente (acotada) ---
|
|
29279
|
+
${failure?.rawExcerpt ?? ""}`
|
|
29280
|
+
);
|
|
29143
29281
|
emitPreviewEvent({
|
|
29144
29282
|
sessionId: ctx.sessionId,
|
|
29145
29283
|
pluginId: ctx.pluginId,
|
|
@@ -29147,7 +29285,9 @@ var requestPreviewDetectH = (ctx) => {
|
|
|
29147
29285
|
type: USER_EVENTS.PREVIEW_ERROR,
|
|
29148
29286
|
payload: {
|
|
29149
29287
|
stage: "detection",
|
|
29150
|
-
|
|
29288
|
+
// El mensaje sale del diagnostico: decir "JSON invalido" cuando el
|
|
29289
|
+
// agente no contesto manda al usuario a mirar un JSON que no existe.
|
|
29290
|
+
message: failure?.message ?? "Agent returned invalid JSON. Try again, or add a .codeam/preview.json override."
|
|
29151
29291
|
}
|
|
29152
29292
|
});
|
|
29153
29293
|
return;
|
|
@@ -29215,7 +29355,12 @@ function prewarmPreviewDetection(runtime) {
|
|
|
29215
29355
|
if (await readPreviewConfig(cwd)) return;
|
|
29216
29356
|
const raw = await runtime.generateOneShot(PREVIEW_DETECT_PROMPT).catch(() => null);
|
|
29217
29357
|
const detection = safeParseDetection(raw);
|
|
29218
|
-
if (!detection
|
|
29358
|
+
if (!detection) {
|
|
29359
|
+
const failure = describeDetectionFailure(raw);
|
|
29360
|
+
log.info("preview", `prewarm: detection failed reason=${failure?.reason ?? "unknown"}`);
|
|
29361
|
+
return;
|
|
29362
|
+
}
|
|
29363
|
+
if (detection.framework === "unsupported") return;
|
|
29219
29364
|
await writePreviewConfig(cwd, detection);
|
|
29220
29365
|
log.info(
|
|
29221
29366
|
"preview",
|
|
@@ -29390,6 +29535,7 @@ var handlers = {
|
|
|
29390
29535
|
save_preview_config: savePreviewConfigH,
|
|
29391
29536
|
env_read: envReadH,
|
|
29392
29537
|
env_write: envWriteH,
|
|
29538
|
+
env_parse: envParseH,
|
|
29393
29539
|
skills_configure: skillsConfigureH,
|
|
29394
29540
|
take_control: takeControlH,
|
|
29395
29541
|
handback: handbackH,
|
|
@@ -46759,7 +46905,7 @@ function checkChokidar() {
|
|
|
46759
46905
|
}
|
|
46760
46906
|
async function doctor(args2 = []) {
|
|
46761
46907
|
const json = args2.includes("--json");
|
|
46762
|
-
const cliVersion = true ? "2.
|
|
46908
|
+
const cliVersion = true ? "2.72.0" : "0.0.0-dev";
|
|
46763
46909
|
const apiBase2 = resolveApiBaseUrl();
|
|
46764
46910
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
46765
46911
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -47150,7 +47296,7 @@ async function mcpRun(args2) {
|
|
|
47150
47296
|
// src/commands/version.ts
|
|
47151
47297
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
47152
47298
|
function version2() {
|
|
47153
|
-
const v = true ? "2.
|
|
47299
|
+
const v = true ? "2.72.0" : "unknown";
|
|
47154
47300
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
47155
47301
|
}
|
|
47156
47302
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.72.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",
|