codeam-cli 2.61.98 → 2.61.99
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 +176 -33
- 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.61.98] — 2026-08-12
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **jetbrains-plugin:** Pairing poll flooded /api/pairing/status and ignored 429s
|
|
12
|
+
|
|
13
|
+
### Tests
|
|
14
|
+
|
|
15
|
+
- **cli:** Integration test for the command-relay SSE primary channel (#608)
|
|
16
|
+
|
|
7
17
|
## [2.61.97] — 2026-08-10
|
|
8
18
|
|
|
9
19
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -2790,6 +2790,7 @@ function resolveApiBaseUrl() {
|
|
|
2790
2790
|
}
|
|
2791
2791
|
|
|
2792
2792
|
// ../../packages/shared/src/headroom/manifest.ts
|
|
2793
|
+
var HEADROOM_PROXY_PORT = 8787;
|
|
2793
2794
|
var HEADROOM_PIP_COMPANIONS = [
|
|
2794
2795
|
"fastapi",
|
|
2795
2796
|
"uvicorn",
|
|
@@ -3635,31 +3636,31 @@ var BucketedRateLimiter = class {
|
|
|
3635
3636
|
this._refillRate = clampToRange(options.refillRate, 0, this._bucketSize, options._logger);
|
|
3636
3637
|
this._refillInterval = clampToRange(options.refillInterval, 0, ONE_DAY_IN_MS, options._logger);
|
|
3637
3638
|
}
|
|
3638
|
-
_applyRefill(
|
|
3639
|
-
const elapsedMs = now -
|
|
3639
|
+
_applyRefill(bucket2, now) {
|
|
3640
|
+
const elapsedMs = now - bucket2.lastAccess;
|
|
3640
3641
|
const refillIntervals = Math.floor(elapsedMs / this._refillInterval);
|
|
3641
3642
|
if (refillIntervals > 0) {
|
|
3642
3643
|
const tokensToAdd = refillIntervals * this._refillRate;
|
|
3643
|
-
|
|
3644
|
-
|
|
3644
|
+
bucket2.tokens = Math.min(bucket2.tokens + tokensToAdd, this._bucketSize);
|
|
3645
|
+
bucket2.lastAccess = bucket2.lastAccess + refillIntervals * this._refillInterval;
|
|
3645
3646
|
}
|
|
3646
3647
|
}
|
|
3647
3648
|
consumeRateLimit(key) {
|
|
3648
3649
|
const now = Date.now();
|
|
3649
3650
|
const keyStr = String(key);
|
|
3650
|
-
let
|
|
3651
|
-
if (
|
|
3651
|
+
let bucket2 = this._buckets[keyStr];
|
|
3652
|
+
if (bucket2) this._applyRefill(bucket2, now);
|
|
3652
3653
|
else {
|
|
3653
|
-
|
|
3654
|
+
bucket2 = {
|
|
3654
3655
|
tokens: this._bucketSize,
|
|
3655
3656
|
lastAccess: now
|
|
3656
3657
|
};
|
|
3657
|
-
this._buckets[keyStr] =
|
|
3658
|
+
this._buckets[keyStr] = bucket2;
|
|
3658
3659
|
}
|
|
3659
|
-
if (0 ===
|
|
3660
|
-
|
|
3661
|
-
if (0 ===
|
|
3662
|
-
return 0 ===
|
|
3660
|
+
if (0 === bucket2.tokens) return true;
|
|
3661
|
+
bucket2.tokens--;
|
|
3662
|
+
if (0 === bucket2.tokens) this._onBucketRateLimited?.(key);
|
|
3663
|
+
return 0 === bucket2.tokens;
|
|
3663
3664
|
}
|
|
3664
3665
|
stop() {
|
|
3665
3666
|
this._buckets = {};
|
|
@@ -8021,7 +8022,7 @@ function readAnonId() {
|
|
|
8021
8022
|
}
|
|
8022
8023
|
function superProperties() {
|
|
8023
8024
|
return {
|
|
8024
|
-
cliVersion: true ? "2.61.
|
|
8025
|
+
cliVersion: true ? "2.61.99" : "0.0.0-dev",
|
|
8025
8026
|
nodeVersion: process.version,
|
|
8026
8027
|
platform: process.platform,
|
|
8027
8028
|
arch: process.arch,
|
|
@@ -8202,7 +8203,7 @@ var os4 = __toESM(require("os"));
|
|
|
8202
8203
|
// package.json
|
|
8203
8204
|
var package_default = {
|
|
8204
8205
|
name: "codeam-cli",
|
|
8205
|
-
version: "2.61.
|
|
8206
|
+
version: "2.61.99",
|
|
8206
8207
|
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.",
|
|
8207
8208
|
type: "commonjs",
|
|
8208
8209
|
main: "dist/index.js",
|
|
@@ -9519,7 +9520,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9519
9520
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
9520
9521
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
9521
9522
|
// pair/reconnect). Older backends ignore the extra field.
|
|
9522
|
-
..."2.61.
|
|
9523
|
+
..."2.61.99" ? { ideVersion: "2.61.99" } : {}
|
|
9523
9524
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
9524
9525
|
}
|
|
9525
9526
|
/**
|
|
@@ -16195,9 +16196,9 @@ function findingsFrom(value) {
|
|
|
16195
16196
|
}
|
|
16196
16197
|
const grouped = flat && !Array.isArray(flat) ? flat : root;
|
|
16197
16198
|
for (const sev of ["critical", "warning", "warn", "error", "info", "note"]) {
|
|
16198
|
-
const
|
|
16199
|
-
if (Array.isArray(
|
|
16200
|
-
for (const f of
|
|
16199
|
+
const bucket2 = grouped[sev];
|
|
16200
|
+
if (Array.isArray(bucket2)) {
|
|
16201
|
+
for (const f of bucket2) {
|
|
16201
16202
|
const h = toHunk(f, sev);
|
|
16202
16203
|
if (h) out2.push(h);
|
|
16203
16204
|
}
|
|
@@ -17427,8 +17428,8 @@ function resolveHistoryFile5(cwd, sessionId) {
|
|
|
17427
17428
|
const p2 = path34.join(fromIndex, WIRE_REL);
|
|
17428
17429
|
if (fs30.existsSync(p2)) return p2;
|
|
17429
17430
|
}
|
|
17430
|
-
const
|
|
17431
|
-
const computed = path34.join(
|
|
17431
|
+
const bucket2 = path34.join(kimiHome(), "sessions", workDirKey(cwd));
|
|
17432
|
+
const computed = path34.join(bucket2, sessionId, WIRE_REL);
|
|
17432
17433
|
if (fs30.existsSync(computed)) return computed;
|
|
17433
17434
|
const scanned = scanBucketsForSession(sessionId);
|
|
17434
17435
|
if (scanned) return scanned;
|
|
@@ -17467,20 +17468,20 @@ function scanBucketsForSession(sessionId) {
|
|
|
17467
17468
|
return null;
|
|
17468
17469
|
}
|
|
17469
17470
|
async function discoverSessionId2(cwd, opts) {
|
|
17470
|
-
const
|
|
17471
|
+
const bucket2 = path34.join(kimiHome(), "sessions", workDirKey(cwd));
|
|
17471
17472
|
const floor = opts.sinceMs - 2e3;
|
|
17472
17473
|
const deadline = Date.now() + (opts.timeoutMs ?? 15e3);
|
|
17473
17474
|
for (; ; ) {
|
|
17474
|
-
const id = newestSessionSince(
|
|
17475
|
+
const id = newestSessionSince(bucket2, floor);
|
|
17475
17476
|
if (id) return id;
|
|
17476
17477
|
if (Date.now() >= deadline) return null;
|
|
17477
17478
|
await sleep3(250);
|
|
17478
17479
|
}
|
|
17479
17480
|
}
|
|
17480
|
-
function newestSessionSince(
|
|
17481
|
+
function newestSessionSince(bucket2, floorMs) {
|
|
17481
17482
|
let entries;
|
|
17482
17483
|
try {
|
|
17483
|
-
entries = fs30.readdirSync(
|
|
17484
|
+
entries = fs30.readdirSync(bucket2, { withFileTypes: true });
|
|
17484
17485
|
} catch {
|
|
17485
17486
|
return null;
|
|
17486
17487
|
}
|
|
@@ -17490,7 +17491,7 @@ function newestSessionSince(bucket, floorMs) {
|
|
|
17490
17491
|
if (!e.isDirectory() || !e.name.startsWith("session_")) continue;
|
|
17491
17492
|
let mtime;
|
|
17492
17493
|
try {
|
|
17493
|
-
mtime = fs30.statSync(path34.join(
|
|
17494
|
+
mtime = fs30.statSync(path34.join(bucket2, e.name)).mtimeMs;
|
|
17494
17495
|
} catch {
|
|
17495
17496
|
continue;
|
|
17496
17497
|
}
|
|
@@ -19862,10 +19863,10 @@ var ZERO = {
|
|
|
19862
19863
|
compressionPct: 0
|
|
19863
19864
|
};
|
|
19864
19865
|
function read(stats, inputPricePerMillion) {
|
|
19865
|
-
const
|
|
19866
|
+
const totals2 = stats.agent_usage?.totals;
|
|
19866
19867
|
const compression = stats.summary?.compression;
|
|
19867
|
-
const rawTokensEst =
|
|
19868
|
-
const sentTokensEst =
|
|
19868
|
+
const rawTokensEst = totals2?.before_tokens ?? compression?.total_tokens_before_with_cli_filtering ?? 0;
|
|
19869
|
+
const sentTokensEst = totals2?.after_tokens ?? rawTokensEst - (compression?.total_tokens_removed ?? 0);
|
|
19869
19870
|
const compressionTokens = compression?.total_tokens_saved_with_cli_filtering ?? compression?.total_tokens_removed ?? Math.max(0, rawTokensEst - sentTokensEst);
|
|
19870
19871
|
const compressionSavingsUsd = stats.summary?.cost?.breakdown?.compression_savings_usd ?? Math.max(0, compressionTokens) / 1e6 * inputPricePerMillion;
|
|
19871
19872
|
return {
|
|
@@ -20722,7 +20723,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
20722
20723
|
if (process.env.NODE_ENV === "test") return;
|
|
20723
20724
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
20724
20725
|
if (process.env.CI) return;
|
|
20725
|
-
const current2 = true ? "2.61.
|
|
20726
|
+
const current2 = true ? "2.61.99" : null;
|
|
20726
20727
|
if (!current2) return;
|
|
20727
20728
|
const cache = readCache();
|
|
20728
20729
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -20739,7 +20740,7 @@ function checkForUpdates() {
|
|
|
20739
20740
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
20740
20741
|
if (process.env.CI) return;
|
|
20741
20742
|
if (!process.stdout.isTTY) return;
|
|
20742
|
-
const current2 = true ? "2.61.
|
|
20743
|
+
const current2 = true ? "2.61.99" : null;
|
|
20743
20744
|
if (!current2) return;
|
|
20744
20745
|
const cache = readCache();
|
|
20745
20746
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -20759,7 +20760,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
|
20759
20760
|
var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
|
|
20760
20761
|
var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
20761
20762
|
function currentCliVersion() {
|
|
20762
|
-
return true ? "2.61.
|
|
20763
|
+
return true ? "2.61.99" : null;
|
|
20763
20764
|
}
|
|
20764
20765
|
function runCmd(cmd, args2, timeoutMs) {
|
|
20765
20766
|
return new Promise((resolve9) => {
|
|
@@ -22362,6 +22363,129 @@ function makeRealApplyBudgetDeps() {
|
|
|
22362
22363
|
};
|
|
22363
22364
|
}
|
|
22364
22365
|
|
|
22366
|
+
// src/services/headroom/usage-report.ts
|
|
22367
|
+
var HOURLY_CAP = 48;
|
|
22368
|
+
var SERIES_CAP = {
|
|
22369
|
+
hourly: HOURLY_CAP,
|
|
22370
|
+
daily: 90,
|
|
22371
|
+
weekly: 52,
|
|
22372
|
+
monthly: 24
|
|
22373
|
+
};
|
|
22374
|
+
var REQUEST_TIMEOUT_MS2 = 5e3;
|
|
22375
|
+
async function fetchWithTimeout2(url2) {
|
|
22376
|
+
const controller = new AbortController();
|
|
22377
|
+
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
|
|
22378
|
+
try {
|
|
22379
|
+
return await fetch(url2, { signal: controller.signal });
|
|
22380
|
+
} finally {
|
|
22381
|
+
clearTimeout(timer);
|
|
22382
|
+
}
|
|
22383
|
+
}
|
|
22384
|
+
function num(v) {
|
|
22385
|
+
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
22386
|
+
}
|
|
22387
|
+
function totals(raw) {
|
|
22388
|
+
const o = raw ?? {};
|
|
22389
|
+
return {
|
|
22390
|
+
requests: num(o.requests),
|
|
22391
|
+
tokens_saved: num(o.tokens_saved),
|
|
22392
|
+
compression_savings_usd: num(o.compression_savings_usd),
|
|
22393
|
+
total_input_tokens: num(o.total_input_tokens),
|
|
22394
|
+
total_input_cost_usd: num(o.total_input_cost_usd)
|
|
22395
|
+
};
|
|
22396
|
+
}
|
|
22397
|
+
function slices(raw) {
|
|
22398
|
+
const out2 = {};
|
|
22399
|
+
if (typeof raw !== "object" || raw === null) return out2;
|
|
22400
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
22401
|
+
const v = value ?? {};
|
|
22402
|
+
out2[key] = {
|
|
22403
|
+
tokens_saved: num(v.tokens_saved),
|
|
22404
|
+
compression_savings_usd_delta: num(v.compression_savings_usd_delta),
|
|
22405
|
+
total_input_tokens_delta: num(v.total_input_tokens_delta),
|
|
22406
|
+
total_input_cost_usd_delta: num(v.total_input_cost_usd_delta)
|
|
22407
|
+
};
|
|
22408
|
+
}
|
|
22409
|
+
return out2;
|
|
22410
|
+
}
|
|
22411
|
+
function bucket(raw) {
|
|
22412
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
22413
|
+
const o = raw;
|
|
22414
|
+
if (typeof o.timestamp !== "string") return null;
|
|
22415
|
+
return {
|
|
22416
|
+
timestamp: o.timestamp,
|
|
22417
|
+
tokens_saved: num(o.tokens_saved),
|
|
22418
|
+
compression_savings_usd_delta: num(o.compression_savings_usd_delta),
|
|
22419
|
+
total_tokens_saved: num(o.total_tokens_saved),
|
|
22420
|
+
compression_savings_usd: num(o.compression_savings_usd),
|
|
22421
|
+
total_input_tokens_delta: num(o.total_input_tokens_delta),
|
|
22422
|
+
total_input_tokens: num(o.total_input_tokens),
|
|
22423
|
+
total_input_cost_usd_delta: num(o.total_input_cost_usd_delta),
|
|
22424
|
+
total_input_cost_usd: num(o.total_input_cost_usd),
|
|
22425
|
+
by_provider: slices(o.by_provider),
|
|
22426
|
+
by_model: slices(o.by_model)
|
|
22427
|
+
};
|
|
22428
|
+
}
|
|
22429
|
+
function toUsageReport(raw, proxyVersion) {
|
|
22430
|
+
const root = raw ?? {};
|
|
22431
|
+
const rawSeries = root.series ?? {};
|
|
22432
|
+
const series = {};
|
|
22433
|
+
for (const key of Object.keys(SERIES_CAP)) {
|
|
22434
|
+
const arr = rawSeries[key];
|
|
22435
|
+
if (!Array.isArray(arr)) continue;
|
|
22436
|
+
const kept = arr.slice(-SERIES_CAP[key]).map(bucket).filter((b) => b !== null);
|
|
22437
|
+
if (kept.length > 0) series[key] = kept;
|
|
22438
|
+
}
|
|
22439
|
+
const displaySession = root.display_session ?? null;
|
|
22440
|
+
const retention = root.retention ?? null;
|
|
22441
|
+
const report = {
|
|
22442
|
+
schemaVersion: num(root.schema_version),
|
|
22443
|
+
generatedAt: typeof root.generated_at === "string" ? root.generated_at : (/* @__PURE__ */ new Date()).toISOString(),
|
|
22444
|
+
lifetime: totals(root.lifetime),
|
|
22445
|
+
series
|
|
22446
|
+
};
|
|
22447
|
+
if (proxyVersion) report.proxyVersion = proxyVersion;
|
|
22448
|
+
if (displaySession) {
|
|
22449
|
+
report.currentSession = {
|
|
22450
|
+
...totals(displaySession),
|
|
22451
|
+
savings_percent: num(displaySession.savings_percent),
|
|
22452
|
+
started_at: typeof displaySession.started_at === "string" ? displaySession.started_at : null,
|
|
22453
|
+
last_activity_at: typeof displaySession.last_activity_at === "string" ? displaySession.last_activity_at : null
|
|
22454
|
+
};
|
|
22455
|
+
}
|
|
22456
|
+
if (retention) {
|
|
22457
|
+
report.retention = {
|
|
22458
|
+
max_history_points: num(retention.max_history_points),
|
|
22459
|
+
max_history_age_days: num(retention.max_history_age_days)
|
|
22460
|
+
};
|
|
22461
|
+
}
|
|
22462
|
+
return report;
|
|
22463
|
+
}
|
|
22464
|
+
async function defaultFetchJson(url2) {
|
|
22465
|
+
const res = await fetchWithTimeout2(url2);
|
|
22466
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
22467
|
+
return await res.json();
|
|
22468
|
+
}
|
|
22469
|
+
async function readUsageReport(deps = {}) {
|
|
22470
|
+
const port = deps.port ?? HEADROOM_PROXY_PORT;
|
|
22471
|
+
const base = `http://127.0.0.1:${port}`;
|
|
22472
|
+
const get2 = deps.fetchJson ?? ((path89) => defaultFetchJson(`${base}${path89}`));
|
|
22473
|
+
let history;
|
|
22474
|
+
try {
|
|
22475
|
+
history = await get2("/stats-history");
|
|
22476
|
+
} catch (err) {
|
|
22477
|
+
log.info("headroomUsage", `stats-history unavailable: ${err.message}`);
|
|
22478
|
+
return null;
|
|
22479
|
+
}
|
|
22480
|
+
let proxyVersion;
|
|
22481
|
+
try {
|
|
22482
|
+
const health = await get2("/health");
|
|
22483
|
+
if (health && typeof health.version === "string") proxyVersion = health.version;
|
|
22484
|
+
} catch {
|
|
22485
|
+
}
|
|
22486
|
+
return toUsageReport(history, proxyVersion);
|
|
22487
|
+
}
|
|
22488
|
+
|
|
22365
22489
|
// src/agents/acp/guardrail-config.ts
|
|
22366
22490
|
var fs47 = __toESM(require("fs"));
|
|
22367
22491
|
var path50 = __toESM(require("path"));
|
|
@@ -25943,6 +26067,24 @@ var vcsAgentReviewH = async (ctx, cmd, parsed) => {
|
|
|
25943
26067
|
}
|
|
25944
26068
|
})();
|
|
25945
26069
|
};
|
|
26070
|
+
var headroomUsageH = async (ctx, cmd) => {
|
|
26071
|
+
try {
|
|
26072
|
+
const report = await readUsageReport();
|
|
26073
|
+
if (!report) {
|
|
26074
|
+
await ctx.relay.sendResult(cmd.id, "completed", {
|
|
26075
|
+
available: false,
|
|
26076
|
+
error: "Headroom is not running in this session."
|
|
26077
|
+
});
|
|
26078
|
+
return;
|
|
26079
|
+
}
|
|
26080
|
+
await ctx.relay.sendResult(cmd.id, "completed", { available: true, report });
|
|
26081
|
+
} catch (err) {
|
|
26082
|
+
await ctx.relay.sendResult(cmd.id, "completed", {
|
|
26083
|
+
available: false,
|
|
26084
|
+
error: err.message
|
|
26085
|
+
});
|
|
26086
|
+
}
|
|
26087
|
+
};
|
|
25946
26088
|
var headroomBudgetH = async (ctx, cmd) => {
|
|
25947
26089
|
const payload = cmd.payload;
|
|
25948
26090
|
let rawAgentId = ctx.agentId || (typeof payload.agentId === "string" ? payload.agentId : "");
|
|
@@ -26731,6 +26873,7 @@ var handlers = {
|
|
|
26731
26873
|
take_control: takeControlH,
|
|
26732
26874
|
handback: handbackH,
|
|
26733
26875
|
headroom_configure: headroomConfigureH,
|
|
26876
|
+
headroom_usage: headroomUsageH,
|
|
26734
26877
|
coderabbit_configure: coderabbitConfigureH,
|
|
26735
26878
|
vcs_agent_review: vcsAgentReviewH,
|
|
26736
26879
|
headroom_budget: headroomBudgetH,
|
|
@@ -42598,7 +42741,7 @@ function checkChokidar() {
|
|
|
42598
42741
|
}
|
|
42599
42742
|
async function doctor(args2 = []) {
|
|
42600
42743
|
const json = args2.includes("--json");
|
|
42601
|
-
const cliVersion = true ? "2.61.
|
|
42744
|
+
const cliVersion = true ? "2.61.99" : "0.0.0-dev";
|
|
42602
42745
|
const apiBase2 = resolveApiBaseUrl();
|
|
42603
42746
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
42604
42747
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -42989,7 +43132,7 @@ async function mcpRun(args2) {
|
|
|
42989
43132
|
// src/commands/version.ts
|
|
42990
43133
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
42991
43134
|
function version2() {
|
|
42992
|
-
const v = true ? "2.61.
|
|
43135
|
+
const v = true ? "2.61.99" : "unknown";
|
|
42993
43136
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
42994
43137
|
}
|
|
42995
43138
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.61.
|
|
3
|
+
"version": "2.61.99",
|
|
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",
|