codeam-cli 2.74.2 → 2.74.4
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 +13 -0
- package/dist/index.js +132 -52
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ 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.74.3] — 2026-09-04
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** Mcp-warm spawns its launcher through a shell on Windows (#706)
|
|
12
|
+
- **cli:** Expo preview — make sure @expo/ngrok exists before `expo start --tunnel` (#707)
|
|
13
|
+
|
|
14
|
+
## [2.74.2] — 2026-09-04
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **cli:** A "no dev server" detection is a verdict, not a malformed answer (#704)
|
|
19
|
+
|
|
7
20
|
## [2.74.1] — 2026-09-04
|
|
8
21
|
|
|
9
22
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -8136,7 +8136,7 @@ function readAnonId() {
|
|
|
8136
8136
|
}
|
|
8137
8137
|
function superProperties() {
|
|
8138
8138
|
return {
|
|
8139
|
-
cliVersion: true ? "2.74.
|
|
8139
|
+
cliVersion: true ? "2.74.4" : "0.0.0-dev",
|
|
8140
8140
|
nodeVersion: process.version,
|
|
8141
8141
|
platform: process.platform,
|
|
8142
8142
|
arch: process.arch,
|
|
@@ -8378,7 +8378,7 @@ var http = __toESM(require("http"));
|
|
|
8378
8378
|
// package.json
|
|
8379
8379
|
var package_default = {
|
|
8380
8380
|
name: "codeam-cli",
|
|
8381
|
-
version: "2.74.
|
|
8381
|
+
version: "2.74.4",
|
|
8382
8382
|
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.",
|
|
8383
8383
|
type: "commonjs",
|
|
8384
8384
|
main: "dist/index.js",
|
|
@@ -9971,7 +9971,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9971
9971
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
9972
9972
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
9973
9973
|
// pair/reconnect). Older backends ignore the extra field.
|
|
9974
|
-
..."2.74.
|
|
9974
|
+
..."2.74.4" ? { ideVersion: "2.74.4" } : {}
|
|
9975
9975
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
9976
9976
|
}
|
|
9977
9977
|
/**
|
|
@@ -22272,7 +22272,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
22272
22272
|
if (process.env.NODE_ENV === "test") return;
|
|
22273
22273
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22274
22274
|
if (process.env.CI) return;
|
|
22275
|
-
const current2 = true ? "2.74.
|
|
22275
|
+
const current2 = true ? "2.74.4" : null;
|
|
22276
22276
|
if (!current2) return;
|
|
22277
22277
|
const cache = readCache();
|
|
22278
22278
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22289,7 +22289,7 @@ function checkForUpdates() {
|
|
|
22289
22289
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22290
22290
|
if (process.env.CI) return;
|
|
22291
22291
|
if (!process.stdout.isTTY) return;
|
|
22292
|
-
const current2 = true ? "2.74.
|
|
22292
|
+
const current2 = true ? "2.74.4" : null;
|
|
22293
22293
|
if (!current2) return;
|
|
22294
22294
|
const cache = readCache();
|
|
22295
22295
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22312,7 +22312,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
|
22312
22312
|
var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
|
|
22313
22313
|
var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
|
|
22314
22314
|
function currentCliVersion() {
|
|
22315
|
-
return true ? "2.74.
|
|
22315
|
+
return true ? "2.74.4" : null;
|
|
22316
22316
|
}
|
|
22317
22317
|
async function installedVersion(deps) {
|
|
22318
22318
|
const ls = await deps.run(
|
|
@@ -25520,6 +25520,25 @@ async function ensureYarnInstalled(deps) {
|
|
|
25520
25520
|
if (!res.ok) return res;
|
|
25521
25521
|
return await deps.hasYarn() ? { ok: true, code: 0 } : { ok: false, code: res.code };
|
|
25522
25522
|
}
|
|
25523
|
+
function isExpoTunnelCommand(command2, args2) {
|
|
25524
|
+
const flat = [import_path8.default.basename(command2), ...args2];
|
|
25525
|
+
return flat.includes("expo") && flat.includes("--tunnel");
|
|
25526
|
+
}
|
|
25527
|
+
var EXPO_NGROK_SPEC = "@expo/ngrok@^4.1.0";
|
|
25528
|
+
async function ensureExpoTunnelDeps(deps) {
|
|
25529
|
+
if (await deps.hasNgrok()) return { ok: true, code: 0, installed: false };
|
|
25530
|
+
const res = await deps.installNgrok();
|
|
25531
|
+
if (!res.ok) return { ...res, installed: false };
|
|
25532
|
+
return await deps.hasNgrok() ? { ok: true, code: 0, installed: true } : { ok: false, code: res.code, installed: false };
|
|
25533
|
+
}
|
|
25534
|
+
function ngrokResolvesFrom(cwd) {
|
|
25535
|
+
try {
|
|
25536
|
+
require.resolve("@expo/ngrok/package.json", { paths: [cwd] });
|
|
25537
|
+
return true;
|
|
25538
|
+
} catch {
|
|
25539
|
+
return false;
|
|
25540
|
+
}
|
|
25541
|
+
}
|
|
25523
25542
|
function isJsInstallCommand(cmd, args2) {
|
|
25524
25543
|
const known = ["npm", "pnpm", "yarn", "bun"];
|
|
25525
25544
|
if (!known.includes(cmd)) return false;
|
|
@@ -26396,10 +26415,7 @@ async function runPreviewStart(args2) {
|
|
|
26396
26415
|
};
|
|
26397
26416
|
const existing = activePreviews.get(sessionId);
|
|
26398
26417
|
if (existing && existing.devServer?.exitCode !== 0 && existing.devServer?.exitCode == null) {
|
|
26399
|
-
log.info(
|
|
26400
|
-
"preview",
|
|
26401
|
-
`reusing running preview for session=${sessionId} url=${existing.url}`
|
|
26402
|
-
);
|
|
26418
|
+
log.info("preview", `reusing running preview for session=${sessionId} url=${existing.url}`);
|
|
26403
26419
|
ctx.emitProgress("READY_DETECTED", "reusing running preview");
|
|
26404
26420
|
emit2(USER_EVENTS.PREVIEW_READY, {
|
|
26405
26421
|
url: existing.url,
|
|
@@ -26448,6 +26464,7 @@ async function provisionDeps(ctx) {
|
|
|
26448
26464
|
pluginAuthToken: ctx.projectEnvAuth.pluginAuthToken
|
|
26449
26465
|
});
|
|
26450
26466
|
}
|
|
26467
|
+
const needsExpoNgrok = isExpoTunnelCommand(detection.command, detection.args ?? []);
|
|
26451
26468
|
const missingDeps = detectMissingNodeDeps(cwd);
|
|
26452
26469
|
let preflightRan = false;
|
|
26453
26470
|
if (missingDeps) {
|
|
@@ -26501,6 +26518,32 @@ async function provisionDeps(ctx) {
|
|
|
26501
26518
|
}
|
|
26502
26519
|
preflightRan = true;
|
|
26503
26520
|
}
|
|
26521
|
+
if (needsExpoNgrok) {
|
|
26522
|
+
const ensured = await ensureExpoTunnelDeps({
|
|
26523
|
+
hasNgrok: async () => ngrokResolvesFrom(cwd),
|
|
26524
|
+
installNgrok: async () => {
|
|
26525
|
+
emitProgress(
|
|
26526
|
+
"SETUP_RUN",
|
|
26527
|
+
`npm install --no-save ${EXPO_NGROK_SPEC} (Expo tunnel needs ngrok; not found)`
|
|
26528
|
+
);
|
|
26529
|
+
const r = await runSetupCommand(
|
|
26530
|
+
"npm",
|
|
26531
|
+
["install", "--no-save", "--no-audit", "--no-fund", EXPO_NGROK_SPEC],
|
|
26532
|
+
cwd,
|
|
26533
|
+
detection.env,
|
|
26534
|
+
{ timeoutMs: INSTALL_TIMEOUT_MS2 }
|
|
26535
|
+
);
|
|
26536
|
+
return { ok: r.status === "ok", code: r.code };
|
|
26537
|
+
}
|
|
26538
|
+
});
|
|
26539
|
+
if (!ensured.ok) {
|
|
26540
|
+
emit2(USER_EVENTS.PREVIEW_ERROR, {
|
|
26541
|
+
stage: "spawn",
|
|
26542
|
+
message: `Expo's tunnel needs @expo/ngrok and installing it automatically failed (npm install --no-save ${EXPO_NGROK_SPEC}, exit ${ensured.code}). Install it in the project and try the preview again.`
|
|
26543
|
+
});
|
|
26544
|
+
return false;
|
|
26545
|
+
}
|
|
26546
|
+
}
|
|
26504
26547
|
const normalizedSetup = (detection.setup_commands ?? []).map((entry) => {
|
|
26505
26548
|
if (typeof entry === "string") {
|
|
26506
26549
|
const parts = entry.trim().split(/\s+/).filter(Boolean);
|
|
@@ -26516,13 +26559,9 @@ async function provisionDeps(ctx) {
|
|
|
26516
26559
|
for (const setup of agentSetupCommands) {
|
|
26517
26560
|
emitProgress("SETUP_RUN", `${setup.cmd} ${setup.args.join(" ")}`);
|
|
26518
26561
|
const timeoutMs = isJsInstallCommand(setup.cmd, setup.args) ? INSTALL_TIMEOUT_MS2 : SETUP_TIMEOUT_MS;
|
|
26519
|
-
const result = await runSetupCommand(
|
|
26520
|
-
|
|
26521
|
-
|
|
26522
|
-
cwd,
|
|
26523
|
-
detection.env,
|
|
26524
|
-
{ timeoutMs }
|
|
26525
|
-
);
|
|
26562
|
+
const result = await runSetupCommand(setup.cmd, setup.args, cwd, detection.env, {
|
|
26563
|
+
timeoutMs
|
|
26564
|
+
});
|
|
26526
26565
|
if (result.status === "timeout") {
|
|
26527
26566
|
emit2(USER_EVENTS.PREVIEW_ERROR, {
|
|
26528
26567
|
stage: "ready_timeout",
|
|
@@ -26590,10 +26629,7 @@ async function startDevServer(ctx) {
|
|
|
26590
26629
|
} else {
|
|
26591
26630
|
const verdict = await canAdoptPort(detection.port);
|
|
26592
26631
|
if (verdict.adopt) {
|
|
26593
|
-
log.info(
|
|
26594
|
-
"preview",
|
|
26595
|
-
`adopting the dev server already serving on port ${detection.port}`
|
|
26596
|
-
);
|
|
26632
|
+
log.info("preview", `adopting the dev server already serving on port ${detection.port}`);
|
|
26597
26633
|
emitProgress("READY_DETECTED", `adopted the server already on port ${detection.port}`);
|
|
26598
26634
|
return { devServer: null, expoUrlRef: { current: null } };
|
|
26599
26635
|
}
|
|
@@ -26744,11 +26780,9 @@ async function establishTunnel(ctx, dev) {
|
|
|
26744
26780
|
`cloudflared quick tunnel (retry ${attempt}/${MAX_TUNNEL_ATTEMPTS})`
|
|
26745
26781
|
);
|
|
26746
26782
|
}
|
|
26747
|
-
const candidate = (0, import_child_process23.spawn)(
|
|
26748
|
-
|
|
26749
|
-
|
|
26750
|
-
{ stdio: ["ignore", "pipe", "pipe"] }
|
|
26751
|
-
);
|
|
26783
|
+
const candidate = (0, import_child_process23.spawn)(bin, ["tunnel", "--url", `http://localhost:${tunnelPort}`], {
|
|
26784
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
26785
|
+
});
|
|
26752
26786
|
const outcome = await awaitTunnelRegistered(
|
|
26753
26787
|
candidate,
|
|
26754
26788
|
null,
|
|
@@ -30959,7 +30993,27 @@ function extractText3(content) {
|
|
|
30959
30993
|
}
|
|
30960
30994
|
return "";
|
|
30961
30995
|
}
|
|
30962
|
-
var
|
|
30996
|
+
var CONVERSATION_BATCH_MAX_MESSAGES = 150;
|
|
30997
|
+
var CONVERSATION_BATCH_MAX_BYTES = 2 * 1024 * 1024;
|
|
30998
|
+
function batchConversation(messages, limits = {}) {
|
|
30999
|
+
const maxMessages = limits.maxMessages ?? CONVERSATION_BATCH_MAX_MESSAGES;
|
|
31000
|
+
const maxBytes = limits.maxBytes ?? CONVERSATION_BATCH_MAX_BYTES;
|
|
31001
|
+
const out2 = [];
|
|
31002
|
+
let cur = [];
|
|
31003
|
+
let curBytes = 0;
|
|
31004
|
+
for (const m of messages) {
|
|
31005
|
+
const size = Buffer.byteLength(JSON.stringify(m));
|
|
31006
|
+
if (cur.length > 0 && (cur.length >= maxMessages || curBytes + size > maxBytes)) {
|
|
31007
|
+
out2.push(cur);
|
|
31008
|
+
cur = [];
|
|
31009
|
+
curBytes = 0;
|
|
31010
|
+
}
|
|
31011
|
+
cur.push(m);
|
|
31012
|
+
curBytes += size;
|
|
31013
|
+
}
|
|
31014
|
+
if (cur.length > 0) out2.push(cur);
|
|
31015
|
+
return out2;
|
|
31016
|
+
}
|
|
30963
31017
|
function scrubSquadContext(messages) {
|
|
30964
31018
|
const out2 = [];
|
|
30965
31019
|
for (const m of messages) {
|
|
@@ -31018,7 +31072,7 @@ function parseJsonl(filePath) {
|
|
|
31018
31072
|
}
|
|
31019
31073
|
return messages;
|
|
31020
31074
|
}
|
|
31021
|
-
function
|
|
31075
|
+
function postWithStatus(endpoint, body, pluginAuthToken) {
|
|
31022
31076
|
return new Promise((resolve11) => {
|
|
31023
31077
|
const payload = JSON.stringify(body);
|
|
31024
31078
|
const u2 = new URL(`${API_BASE8}${endpoint}`);
|
|
@@ -31042,24 +31096,33 @@ function post(endpoint, body, pluginAuthToken) {
|
|
|
31042
31096
|
},
|
|
31043
31097
|
(res) => {
|
|
31044
31098
|
res.resume();
|
|
31045
|
-
const
|
|
31046
|
-
|
|
31047
|
-
|
|
31099
|
+
const status2 = res.statusCode ?? null;
|
|
31100
|
+
const ok = status2 !== null && status2 >= 200 && status2 < 300;
|
|
31101
|
+
if (!ok) log.warn("history:post", `${endpoint} \u2192 HTTP ${status2}`);
|
|
31102
|
+
let retryAfterMs = null;
|
|
31103
|
+
if (status2 === 429) {
|
|
31104
|
+
const ra = Number(res.headers["retry-after"]);
|
|
31105
|
+
retryAfterMs = Number.isFinite(ra) && ra > 0 ? ra * 1e3 : 2e4;
|
|
31106
|
+
}
|
|
31107
|
+
resolve11({ ok, status: status2, retryAfterMs });
|
|
31048
31108
|
}
|
|
31049
31109
|
);
|
|
31050
31110
|
req.on("error", (err) => {
|
|
31051
31111
|
log.warn("history:post", `${endpoint} network error`, err);
|
|
31052
|
-
resolve11(false);
|
|
31112
|
+
resolve11({ ok: false, status: null, retryAfterMs: null });
|
|
31053
31113
|
});
|
|
31054
31114
|
req.on("timeout", () => {
|
|
31055
31115
|
log.warn("history:post", `${endpoint} timeout after 15s`);
|
|
31056
31116
|
req.destroy();
|
|
31057
|
-
resolve11(false);
|
|
31117
|
+
resolve11({ ok: false, status: null, retryAfterMs: null });
|
|
31058
31118
|
});
|
|
31059
31119
|
req.write(payload);
|
|
31060
31120
|
req.end();
|
|
31061
31121
|
});
|
|
31062
31122
|
}
|
|
31123
|
+
function post(endpoint, body, pluginAuthToken) {
|
|
31124
|
+
return postWithStatus(endpoint, body, pluginAuthToken).then((r) => r.ok);
|
|
31125
|
+
}
|
|
31063
31126
|
var HistoryService = class _HistoryService {
|
|
31064
31127
|
constructor(runtime, pluginId, cwd, options) {
|
|
31065
31128
|
this.pluginId = pluginId;
|
|
@@ -31419,33 +31482,45 @@ var HistoryService = class _HistoryService {
|
|
|
31419
31482
|
}
|
|
31420
31483
|
return parseJsonl(path74.join(this.projectDir, `${sessionId}.jsonl`));
|
|
31421
31484
|
}
|
|
31485
|
+
/** One in-flight full upload per conversation — the baton mirror and a
|
|
31486
|
+
* `get_conversation` used to upload the same transcript concurrently. */
|
|
31487
|
+
uploadsInFlight = /* @__PURE__ */ new Map();
|
|
31422
31488
|
async loadConversation(sessionId) {
|
|
31489
|
+
const inFlight = this.uploadsInFlight.get(sessionId);
|
|
31490
|
+
if (inFlight) return inFlight;
|
|
31491
|
+
const run = this.loadConversationNow(sessionId).finally(() => {
|
|
31492
|
+
this.uploadsInFlight.delete(sessionId);
|
|
31493
|
+
});
|
|
31494
|
+
this.uploadsInFlight.set(sessionId, run);
|
|
31495
|
+
return run;
|
|
31496
|
+
}
|
|
31497
|
+
async loadConversationNow(sessionId) {
|
|
31423
31498
|
const messages = this.readConversation(sessionId);
|
|
31424
31499
|
if (messages.length === 0) return;
|
|
31425
|
-
const
|
|
31500
|
+
const batches = batchConversation(messages);
|
|
31426
31501
|
const RETRY_DELAYS = [500, 1e3, 2e3, 4e3, 8e3];
|
|
31427
|
-
for (let i = 0; i <
|
|
31428
|
-
const batch = messages.slice(i * CONVERSATION_BATCH_SIZE, (i + 1) * CONVERSATION_BATCH_SIZE);
|
|
31502
|
+
for (let i = 0; i < batches.length; i++) {
|
|
31429
31503
|
const body = {
|
|
31430
31504
|
pluginId: this.pluginId,
|
|
31431
|
-
// `agentId` keys the backend's per-agent conversation cache.
|
|
31432
|
-
// Older backends that don't recognise the field silently
|
|
31433
|
-
// ignore it and default to `claude-code` server-side — same
|
|
31434
|
-
// outcome as before the per-agent split.
|
|
31435
31505
|
agentId: this.runtime.id,
|
|
31436
31506
|
sessionId,
|
|
31437
|
-
messages:
|
|
31507
|
+
messages: batches[i],
|
|
31438
31508
|
batchIndex: i,
|
|
31439
|
-
totalBatches
|
|
31509
|
+
totalBatches: batches.length
|
|
31440
31510
|
};
|
|
31441
|
-
let
|
|
31442
|
-
|
|
31443
|
-
|
|
31444
|
-
|
|
31445
|
-
|
|
31446
|
-
|
|
31511
|
+
let res = await postWithStatus("/api/sessions/conversation", body, this.pluginAuthToken);
|
|
31512
|
+
let attempt = 0;
|
|
31513
|
+
let throttled = 0;
|
|
31514
|
+
while (!res.ok && (res.retryAfterMs !== null ? throttled < 6 : attempt < RETRY_DELAYS.length)) {
|
|
31515
|
+
const delay = res.retryAfterMs ?? RETRY_DELAYS[attempt];
|
|
31516
|
+
if (res.retryAfterMs !== null) throttled++;
|
|
31517
|
+
else attempt++;
|
|
31518
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
31519
|
+
res = await postWithStatus("/api/sessions/conversation", body, this.pluginAuthToken);
|
|
31520
|
+
}
|
|
31521
|
+
if (!res.ok) {
|
|
31447
31522
|
throw new Error(
|
|
31448
|
-
`Failed to upload conversation batch ${i + 1}/${
|
|
31523
|
+
`Failed to upload conversation batch ${i + 1}/${batches.length} after all retries (last HTTP ${res.status})`
|
|
31449
31524
|
);
|
|
31450
31525
|
}
|
|
31451
31526
|
}
|
|
@@ -47450,7 +47525,7 @@ function checkChokidar() {
|
|
|
47450
47525
|
}
|
|
47451
47526
|
async function doctor(args2 = []) {
|
|
47452
47527
|
const json = args2.includes("--json");
|
|
47453
|
-
const cliVersion = true ? "2.74.
|
|
47528
|
+
const cliVersion = true ? "2.74.4" : "0.0.0-dev";
|
|
47454
47529
|
const apiBase2 = resolveApiBaseUrl();
|
|
47455
47530
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
47456
47531
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -47897,7 +47972,12 @@ function runCapturing(command2, args2, timeoutMs) {
|
|
|
47897
47972
|
return new Promise((resolve11) => {
|
|
47898
47973
|
const child = (0, import_node_child_process35.spawn)(command2, args2, {
|
|
47899
47974
|
env: { ...process.env, ...LAUNCHER_ENV },
|
|
47900
|
-
stdio: ["ignore", "ignore", "pipe"]
|
|
47975
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
47976
|
+
// `npx`/`uv` are `.cmd` shims on Windows; Node ≥ 20.12 refuses to spawn a
|
|
47977
|
+
// `.cmd` without a shell and a bare `npx` is ENOENT — the same reason the
|
|
47978
|
+
// licence audit test needed it. The launchers here are ours, never user
|
|
47979
|
+
// input, so the shell is safe.
|
|
47980
|
+
shell: process.platform === "win32"
|
|
47901
47981
|
});
|
|
47902
47982
|
let stderr = "";
|
|
47903
47983
|
child.stderr?.setEncoding("utf8");
|
|
@@ -47969,7 +48049,7 @@ async function mcpWarm(argv = [], opts = {}) {
|
|
|
47969
48049
|
// src/commands/version.ts
|
|
47970
48050
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
47971
48051
|
function version2() {
|
|
47972
|
-
const v = true ? "2.74.
|
|
48052
|
+
const v = true ? "2.74.4" : "unknown";
|
|
47973
48053
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
47974
48054
|
}
|
|
47975
48055
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.74.
|
|
3
|
+
"version": "2.74.4",
|
|
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",
|