codeam-cli 2.74.5 → 2.74.7
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 +12 -0
- package/dist/index.js +116 -51
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ 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.6] — 2026-09-05
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** Expo preview reads the tunnel URL a non-TTY Expo actually emits (exp.direct) (#713)
|
|
12
|
+
|
|
13
|
+
## [2.74.5] — 2026-09-04
|
|
14
|
+
|
|
15
|
+
### Chore
|
|
16
|
+
|
|
17
|
+
- **cli:** Bump @agentclientprotocol/claude-agent-acp 0.59.0 → 0.74.0 (Claude Code 2.1.257) (#711)
|
|
18
|
+
|
|
7
19
|
## [2.74.4] — 2026-09-04
|
|
8
20
|
|
|
9
21
|
### 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.7" : "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.7",
|
|
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.7" ? { ideVersion: "2.74.7" } : {}
|
|
9975
9975
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
9976
9976
|
}
|
|
9977
9977
|
/**
|
|
@@ -18410,14 +18410,14 @@ function defaultSdkDir() {
|
|
|
18410
18410
|
return resolveSdkDirViaRequire();
|
|
18411
18411
|
}
|
|
18412
18412
|
function resolveClaudeNativeBinary(deps = {}) {
|
|
18413
|
-
const
|
|
18413
|
+
const existsSync29 = deps.existsSync ?? import_fs.default.existsSync;
|
|
18414
18414
|
const platformKey = deps.platformKey ?? currentPlatformKey();
|
|
18415
18415
|
const sdkDir = deps.sdkDir !== void 0 ? deps.sdkDir : defaultSdkDir();
|
|
18416
18416
|
if (!sdkDir) return null;
|
|
18417
18417
|
const scopeDir = import_path4.default.dirname(sdkDir);
|
|
18418
18418
|
const binName = platformKey.startsWith("win32-") ? "claude.exe" : "claude";
|
|
18419
18419
|
const candidate = import_path4.default.join(scopeDir, `claude-agent-sdk-${platformKey}`, binName);
|
|
18420
|
-
return
|
|
18420
|
+
return existsSync29(candidate) ? candidate : null;
|
|
18421
18421
|
}
|
|
18422
18422
|
var realSleep = (ms) => new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
18423
18423
|
async function waitForClaudeNativeBinary(opts = {}) {
|
|
@@ -18499,18 +18499,18 @@ function augmentUserLocalBinPaths(deps = {}) {
|
|
|
18499
18499
|
env.PATH = [...additions, ...parts].join(p2.delimiter);
|
|
18500
18500
|
}
|
|
18501
18501
|
function resolveCursorAgentBinary(deps = {}) {
|
|
18502
|
-
const
|
|
18502
|
+
const existsSync29 = deps.existsSync ?? import_fs.default.existsSync;
|
|
18503
18503
|
const platform3 = deps.platform ?? process.platform;
|
|
18504
18504
|
const env = deps.env ?? process.env;
|
|
18505
18505
|
if (platform3 === "win32") {
|
|
18506
18506
|
const localAppData = env.LOCALAPPDATA;
|
|
18507
18507
|
if (!localAppData) return null;
|
|
18508
18508
|
const exe = import_path4.default.win32.join(localAppData, "cursor-agent", "cursor-agent.exe");
|
|
18509
|
-
return
|
|
18509
|
+
return existsSync29(exe) ? exe : null;
|
|
18510
18510
|
}
|
|
18511
18511
|
const home = deps.homedir ?? import_os4.default.homedir();
|
|
18512
18512
|
const unix = import_path4.default.posix.join(home, ".local", "bin", "cursor-agent");
|
|
18513
|
-
return
|
|
18513
|
+
return existsSync29(unix) ? unix : null;
|
|
18514
18514
|
}
|
|
18515
18515
|
async function waitForCursorAgent(opts = {}) {
|
|
18516
18516
|
const timeoutMs = opts.timeoutMs ?? 18e4;
|
|
@@ -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.7" : 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.7" : 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.7" : null;
|
|
22316
22316
|
}
|
|
22317
22317
|
async function installedVersion(deps) {
|
|
22318
22318
|
const ls = await deps.run(
|
|
@@ -23738,11 +23738,16 @@ var HostAgentSupervisor = class {
|
|
|
23738
23738
|
};
|
|
23739
23739
|
proc.stdout?.on("data", appendTail);
|
|
23740
23740
|
proc.stderr?.on("data", appendTail);
|
|
23741
|
-
proc.once("exit", (code) => {
|
|
23741
|
+
proc.once("exit", (code, signal) => {
|
|
23742
23742
|
if (this.children.get(deployId)?.proc === proc) this.children.delete(deployId);
|
|
23743
23743
|
if (typeof code === "number" && code !== 0) {
|
|
23744
23744
|
this.onResumeChildExit(session, code, tail.trim().slice(-300));
|
|
23745
|
+
return;
|
|
23745
23746
|
}
|
|
23747
|
+
log.warn(
|
|
23748
|
+
"host-agent",
|
|
23749
|
+
`resumed session ${session.id.slice(0, 8)} exited ${signal ?? code ?? 0} without taking over \u2014 tail: ${tail.trim().slice(-300) || "(no output)"}`
|
|
23750
|
+
);
|
|
23746
23751
|
});
|
|
23747
23752
|
log.info(
|
|
23748
23753
|
"host-agent",
|
|
@@ -25032,10 +25037,20 @@ function parseCloudflaredUrl(stderr) {
|
|
|
25032
25037
|
const match = stderr.match(CLOUDFLARED_URL_RE);
|
|
25033
25038
|
return match ? match[0] : null;
|
|
25034
25039
|
}
|
|
25035
|
-
var
|
|
25036
|
-
|
|
25037
|
-
|
|
25038
|
-
|
|
25040
|
+
var EXPO_DEEP_LINK_RE = /exp:\/\/[^\s"'`)]+\.exp\.(?:direct|host)(?::\d+)?/;
|
|
25041
|
+
var EXPO_DEBUG_TUNNEL_RE = /Tunnel URL:\s*(https?:\/\/([^\s"'`)]+\.exp\.(?:direct|host)))/;
|
|
25042
|
+
function parseExpoUrl(output) {
|
|
25043
|
+
const direct = output.match(EXPO_DEEP_LINK_RE);
|
|
25044
|
+
if (direct) return direct[0];
|
|
25045
|
+
const dbg = output.match(EXPO_DEBUG_TUNNEL_RE);
|
|
25046
|
+
if (dbg) return `exp://${dbg[2]}`;
|
|
25047
|
+
return null;
|
|
25048
|
+
}
|
|
25049
|
+
var EXPO_TUNNEL_DEBUG_CHANNEL = "expo:start:server:ngrok";
|
|
25050
|
+
function expoTunnelDebugEnv(existing) {
|
|
25051
|
+
const cur = (existing ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
25052
|
+
if (!cur.includes(EXPO_TUNNEL_DEBUG_CHANNEL)) cur.push(EXPO_TUNNEL_DEBUG_CHANNEL);
|
|
25053
|
+
return { DEBUG: cur.join(",") };
|
|
25039
25054
|
}
|
|
25040
25055
|
var RAW_EXCERPT_LIMIT = 1e3;
|
|
25041
25056
|
function describeDetectionFailure(raw) {
|
|
@@ -26643,9 +26658,11 @@ async function startDevServer(ctx) {
|
|
|
26643
26658
|
await applyPreviewHostAllow(cwd);
|
|
26644
26659
|
const spawnable = normalizeDetectionForSpawn(detection, cwd);
|
|
26645
26660
|
emitProgress("BOOT_SEQUENCE", `${spawnable.command} ${spawnable.args.join(" ")}`);
|
|
26661
|
+
const isExpo = detection.framework === "Expo";
|
|
26662
|
+
const expoDebugEnv = isExpo && isExpoTunnelCommand(detection.command, detection.args ?? []) ? expoTunnelDebugEnv(spawnable.env?.DEBUG ?? process.env.DEBUG) : {};
|
|
26646
26663
|
const devServer = (0, import_child_process23.spawn)(spawnable.command, spawnable.args, {
|
|
26647
26664
|
cwd,
|
|
26648
|
-
env: { ...process.env, ...spawnable.env ?? {} },
|
|
26665
|
+
env: { ...process.env, ...spawnable.env ?? {}, ...expoDebugEnv },
|
|
26649
26666
|
stdio: ["ignore", "pipe", "pipe"],
|
|
26650
26667
|
// POSIX: lead a new process group so teardown can SIGTERM the whole
|
|
26651
26668
|
// tree. Dev servers fork worker children that bind the port; killing
|
|
@@ -26659,14 +26676,17 @@ async function startDevServer(ctx) {
|
|
|
26659
26676
|
emitProgress("WAITING_FOR_READY", detection.ready_pattern);
|
|
26660
26677
|
const expoUrlRef = { current: null };
|
|
26661
26678
|
let outputTail = "";
|
|
26662
|
-
const readyRe =
|
|
26679
|
+
const readyRe = isExpo ? new RegExp(
|
|
26680
|
+
`(?:${compileReadyPattern(detection.ready_pattern).source})|exp:\\/\\/[^\\s]+\\.exp\\.(?:direct|host)|Tunnel URL:`,
|
|
26681
|
+
"i"
|
|
26682
|
+
) : compileReadyPattern(detection.ready_pattern);
|
|
26663
26683
|
const isNextJs = /next/i.test(detection.framework);
|
|
26664
26684
|
const outcome = await waitForDevServerReady(devServer, readyRe, {
|
|
26665
26685
|
timeoutMs: 12e4,
|
|
26666
26686
|
onChunk: (s) => {
|
|
26667
26687
|
outputTail = (outputTail + s).slice(-16e3);
|
|
26668
|
-
if (!expoUrlRef.current &&
|
|
26669
|
-
expoUrlRef.current = parseExpoUrl(
|
|
26688
|
+
if (!expoUrlRef.current && isExpo) {
|
|
26689
|
+
expoUrlRef.current = parseExpoUrl(outputTail);
|
|
26670
26690
|
}
|
|
26671
26691
|
},
|
|
26672
26692
|
portProbe: isNextJs ? () => waitForPortListening(detection.port, { timeoutMs: 1e3, intervalMs: 250 }) : void 0
|
|
@@ -30138,6 +30158,52 @@ async function claim(token, pluginId, pluginSecretHash) {
|
|
|
30138
30158
|
function pairAutoLockPath() {
|
|
30139
30159
|
return path72.join(os55.homedir(), ".codeam", "pair-auto.lock");
|
|
30140
30160
|
}
|
|
30161
|
+
var procRoot = "/proc";
|
|
30162
|
+
function readProcFile(rel) {
|
|
30163
|
+
try {
|
|
30164
|
+
return fs66.readFileSync(path72.join(procRoot, rel), "utf8");
|
|
30165
|
+
} catch {
|
|
30166
|
+
return void 0;
|
|
30167
|
+
}
|
|
30168
|
+
}
|
|
30169
|
+
function procStartTicks(pid) {
|
|
30170
|
+
const stat3 = readProcFile(`${pid}/stat`);
|
|
30171
|
+
if (!stat3) return void 0;
|
|
30172
|
+
const afterComm = stat3.slice(stat3.lastIndexOf(")") + 2).trim().split(/\s+/);
|
|
30173
|
+
return afterComm[19];
|
|
30174
|
+
}
|
|
30175
|
+
function procTgid(pid) {
|
|
30176
|
+
const m = readProcFile(`${pid}/status`)?.match(/^Tgid:\s+(\d+)/m);
|
|
30177
|
+
return m ? Number(m[1]) : void 0;
|
|
30178
|
+
}
|
|
30179
|
+
function currentBootId() {
|
|
30180
|
+
return readProcFile("sys/kernel/random/boot_id")?.trim() || void 0;
|
|
30181
|
+
}
|
|
30182
|
+
function readLockRecord(lockPath) {
|
|
30183
|
+
let raw;
|
|
30184
|
+
try {
|
|
30185
|
+
raw = fs66.readFileSync(lockPath, "utf8");
|
|
30186
|
+
} catch {
|
|
30187
|
+
return void 0;
|
|
30188
|
+
}
|
|
30189
|
+
const [first = "", second = ""] = raw.split("\n");
|
|
30190
|
+
const pid = Number(first.trim());
|
|
30191
|
+
if (!Number.isInteger(pid) || pid <= 0) return void 0;
|
|
30192
|
+
const rec = { pid };
|
|
30193
|
+
const start2 = /(?:^|\s)start=(\S+)/.exec(second)?.[1];
|
|
30194
|
+
const boot = /(?:^|\s)boot=(\S+)/.exec(second)?.[1];
|
|
30195
|
+
if (start2) rec.start = start2;
|
|
30196
|
+
if (boot) rec.boot = boot;
|
|
30197
|
+
return rec;
|
|
30198
|
+
}
|
|
30199
|
+
function ownLockBody() {
|
|
30200
|
+
const start2 = procStartTicks(process.pid);
|
|
30201
|
+
const boot = currentBootId();
|
|
30202
|
+
const tags = [start2 ? `start=${start2}` : "", boot ? `boot=${boot}` : ""].filter(Boolean);
|
|
30203
|
+
return tags.length ? `${process.pid}
|
|
30204
|
+
${tags.join(" ")}
|
|
30205
|
+
` : String(process.pid);
|
|
30206
|
+
}
|
|
30141
30207
|
function isLivePairAuto(pid) {
|
|
30142
30208
|
if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) return false;
|
|
30143
30209
|
try {
|
|
@@ -30145,15 +30211,30 @@ function isLivePairAuto(pid) {
|
|
|
30145
30211
|
} catch (e) {
|
|
30146
30212
|
if (e.code !== "EPERM") return false;
|
|
30147
30213
|
}
|
|
30214
|
+
const cmdline = readProcFile(`${pid}/cmdline`);
|
|
30215
|
+
if (cmdline === void 0) return true;
|
|
30216
|
+
const tgid = procTgid(pid);
|
|
30217
|
+
if (tgid !== void 0 && tgid !== pid) return false;
|
|
30218
|
+
return cmdline.includes("codeam");
|
|
30219
|
+
}
|
|
30220
|
+
function isLiveLockHolder(rec) {
|
|
30221
|
+
if (!rec || rec.pid === process.pid) return false;
|
|
30222
|
+
if (rec.boot) {
|
|
30223
|
+
const boot = currentBootId();
|
|
30224
|
+
if (boot && boot !== rec.boot) return false;
|
|
30225
|
+
}
|
|
30226
|
+
if (rec.start) {
|
|
30227
|
+
const start2 = procStartTicks(rec.pid);
|
|
30228
|
+
if (start2 !== void 0 && start2 !== rec.start) return false;
|
|
30229
|
+
}
|
|
30230
|
+
return isLivePairAuto(rec.pid);
|
|
30231
|
+
}
|
|
30232
|
+
function releaseOwnLock(lockPath) {
|
|
30148
30233
|
try {
|
|
30149
|
-
|
|
30234
|
+
if (readLockRecord(lockPath)?.pid === process.pid) fs66.unlinkSync(lockPath);
|
|
30150
30235
|
} catch {
|
|
30151
|
-
return true;
|
|
30152
30236
|
}
|
|
30153
30237
|
}
|
|
30154
|
-
function isLiveCodeam(pid) {
|
|
30155
|
-
return isLivePairAuto(pid);
|
|
30156
|
-
}
|
|
30157
30238
|
function daemonLockPath(sessionId) {
|
|
30158
30239
|
const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
30159
30240
|
return path72.join(os55.homedir(), ".codeam", `daemon-${safe}.lock`);
|
|
@@ -30163,21 +30244,13 @@ function acquireDaemonLock(sessionId) {
|
|
|
30163
30244
|
try {
|
|
30164
30245
|
fs66.mkdirSync(path72.dirname(lockPath), { recursive: true });
|
|
30165
30246
|
try {
|
|
30166
|
-
fs66.writeFileSync(lockPath,
|
|
30247
|
+
fs66.writeFileSync(lockPath, ownLockBody(), { flag: "wx" });
|
|
30167
30248
|
} catch (e) {
|
|
30168
30249
|
if (e.code !== "EEXIST") throw e;
|
|
30169
|
-
|
|
30170
|
-
|
|
30171
|
-
fs66.writeFileSync(lockPath, String(process.pid));
|
|
30250
|
+
if (isLiveLockHolder(readLockRecord(lockPath))) return false;
|
|
30251
|
+
fs66.writeFileSync(lockPath, ownLockBody());
|
|
30172
30252
|
}
|
|
30173
|
-
const release3 = () =>
|
|
30174
|
-
try {
|
|
30175
|
-
if (fs66.existsSync(lockPath) && Number(fs66.readFileSync(lockPath, "utf8").trim()) === process.pid) {
|
|
30176
|
-
fs66.unlinkSync(lockPath);
|
|
30177
|
-
}
|
|
30178
|
-
} catch {
|
|
30179
|
-
}
|
|
30180
|
-
};
|
|
30253
|
+
const release3 = () => releaseOwnLock(lockPath);
|
|
30181
30254
|
process.once("exit", release3);
|
|
30182
30255
|
process.once("SIGTERM", () => {
|
|
30183
30256
|
release3();
|
|
@@ -30197,21 +30270,13 @@ function acquireSingletonLock() {
|
|
|
30197
30270
|
try {
|
|
30198
30271
|
fs66.mkdirSync(path72.dirname(lockPath), { recursive: true });
|
|
30199
30272
|
try {
|
|
30200
|
-
fs66.writeFileSync(lockPath,
|
|
30273
|
+
fs66.writeFileSync(lockPath, ownLockBody(), { flag: "wx" });
|
|
30201
30274
|
} catch (e) {
|
|
30202
30275
|
if (e.code !== "EEXIST") throw e;
|
|
30203
|
-
|
|
30204
|
-
|
|
30205
|
-
fs66.writeFileSync(lockPath, String(process.pid));
|
|
30276
|
+
if (isLiveLockHolder(readLockRecord(lockPath))) return false;
|
|
30277
|
+
fs66.writeFileSync(lockPath, ownLockBody());
|
|
30206
30278
|
}
|
|
30207
|
-
process.once("exit", () =>
|
|
30208
|
-
try {
|
|
30209
|
-
if (fs66.existsSync(lockPath) && Number(fs66.readFileSync(lockPath, "utf8").trim()) === process.pid) {
|
|
30210
|
-
fs66.unlinkSync(lockPath);
|
|
30211
|
-
}
|
|
30212
|
-
} catch {
|
|
30213
|
-
}
|
|
30214
|
-
});
|
|
30279
|
+
process.once("exit", () => releaseOwnLock(lockPath));
|
|
30215
30280
|
return true;
|
|
30216
30281
|
} catch {
|
|
30217
30282
|
return true;
|
|
@@ -47525,7 +47590,7 @@ function checkChokidar() {
|
|
|
47525
47590
|
}
|
|
47526
47591
|
async function doctor(args2 = []) {
|
|
47527
47592
|
const json = args2.includes("--json");
|
|
47528
|
-
const cliVersion = true ? "2.74.
|
|
47593
|
+
const cliVersion = true ? "2.74.7" : "0.0.0-dev";
|
|
47529
47594
|
const apiBase2 = resolveApiBaseUrl();
|
|
47530
47595
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
47531
47596
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -48049,7 +48114,7 @@ async function mcpWarm(argv = [], opts = {}) {
|
|
|
48049
48114
|
// src/commands/version.ts
|
|
48050
48115
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
48051
48116
|
function version2() {
|
|
48052
|
-
const v = true ? "2.74.
|
|
48117
|
+
const v = true ? "2.74.7" : "unknown";
|
|
48053
48118
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
48054
48119
|
}
|
|
48055
48120
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.74.
|
|
3
|
+
"version": "2.74.7",
|
|
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",
|