codeam-cli 2.15.7 → 2.16.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 +12 -0
- package/dist/index.js +417 -60
- 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.15.8] — 2026-05-21
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** Pairing-box alignment + countdown actually ticks (#39)
|
|
12
|
+
|
|
13
|
+
## [2.15.7] — 2026-05-21
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **clients:** Point default API URL at api.codeagent-mobile.com + centralize via shared constant (#38)
|
|
18
|
+
|
|
7
19
|
## [2.15.6] — 2026-05-20
|
|
8
20
|
|
|
9
21
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -389,7 +389,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
389
389
|
// package.json
|
|
390
390
|
var package_default = {
|
|
391
391
|
name: "codeam-cli",
|
|
392
|
-
version: "2.
|
|
392
|
+
version: "2.16.0",
|
|
393
393
|
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.",
|
|
394
394
|
type: "commonjs",
|
|
395
395
|
main: "dist/index.js",
|
|
@@ -493,21 +493,30 @@ function showError(msg) {
|
|
|
493
493
|
function showInfo(msg) {
|
|
494
494
|
console.log(` ${import_picocolors.default.dim("\xB7")} ${msg}`);
|
|
495
495
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
console.log(
|
|
496
|
+
var BOX_INTERIOR = 30;
|
|
497
|
+
var BOX_BORDER_TOP = ` \u250C${"\u2500".repeat(BOX_INTERIOR)}\u2510`;
|
|
498
|
+
var BOX_BORDER_BOT = ` \u2514${"\u2500".repeat(BOX_INTERIOR)}\u2518`;
|
|
499
|
+
function boxRow(content, visibleLength) {
|
|
500
|
+
const pad = " ".repeat(Math.max(0, BOX_INTERIOR - visibleLength));
|
|
501
|
+
return ` \u2502${content}${pad}\u2502`;
|
|
502
|
+
}
|
|
503
|
+
function showPairingCode(code) {
|
|
504
|
+
console.log(BOX_BORDER_TOP);
|
|
505
|
+
const codeVisible = ` Code: ${code}`.length;
|
|
506
|
+
console.log(
|
|
507
|
+
boxRow(` Code: ${import_picocolors.default.bold(import_picocolors.default.yellow(code))}`, codeVisible)
|
|
508
|
+
);
|
|
509
|
+
console.log(BOX_BORDER_BOT);
|
|
505
510
|
console.log("");
|
|
506
511
|
import_qrcode_terminal.default.generate(code, { small: true }, (qr) => {
|
|
507
512
|
qr.split("\n").forEach((line) => console.log(" " + line));
|
|
508
513
|
});
|
|
509
514
|
console.log("");
|
|
510
515
|
}
|
|
516
|
+
function formatRemaining(expiresAt) {
|
|
517
|
+
const secs = Math.max(0, Math.floor((expiresAt - Date.now()) / 1e3));
|
|
518
|
+
return `${Math.floor(secs / 60)}:${String(secs % 60).padStart(2, "0")}`;
|
|
519
|
+
}
|
|
511
520
|
|
|
512
521
|
// src/services/command-relay.service.ts
|
|
513
522
|
var https2 = __toESM(require("https"));
|
|
@@ -637,8 +646,84 @@ function pollStatus(pluginId, onPaired, onTimeout) {
|
|
|
637
646
|
}
|
|
638
647
|
var _transport = {
|
|
639
648
|
postJson: _postJson,
|
|
640
|
-
getJson: _getJson
|
|
649
|
+
getJson: _getJson,
|
|
650
|
+
postJsonAuthed: _postJsonAuthed
|
|
641
651
|
};
|
|
652
|
+
async function postLinkCredential(input) {
|
|
653
|
+
const body = {
|
|
654
|
+
sessionId: input.sessionId,
|
|
655
|
+
pluginId: input.pluginId,
|
|
656
|
+
method: input.method,
|
|
657
|
+
credential: input.credential
|
|
658
|
+
};
|
|
659
|
+
if (input.modelPreference) {
|
|
660
|
+
body.modelPreference = input.modelPreference;
|
|
661
|
+
}
|
|
662
|
+
try {
|
|
663
|
+
await _transport.postJsonAuthed(
|
|
664
|
+
`${API_BASE}/api/plugin/agents/${input.agentId}/link`,
|
|
665
|
+
body,
|
|
666
|
+
input.pluginAuthToken
|
|
667
|
+
);
|
|
668
|
+
return { ok: true };
|
|
669
|
+
} catch (err) {
|
|
670
|
+
const e = err;
|
|
671
|
+
return {
|
|
672
|
+
ok: false,
|
|
673
|
+
status: typeof e.statusCode === "number" ? e.statusCode : 0,
|
|
674
|
+
message: e.message || "unknown"
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
async function _postJsonAuthed(url, body, pluginAuthToken) {
|
|
679
|
+
return new Promise((resolve2, reject) => {
|
|
680
|
+
const data = JSON.stringify(body);
|
|
681
|
+
const u2 = new URL(url);
|
|
682
|
+
const transport = u2.protocol === "https:" ? https : http;
|
|
683
|
+
const req = transport.request(
|
|
684
|
+
{
|
|
685
|
+
hostname: u2.hostname,
|
|
686
|
+
port: u2.port || (u2.protocol === "https:" ? 443 : 80),
|
|
687
|
+
path: u2.pathname + u2.search,
|
|
688
|
+
method: "POST",
|
|
689
|
+
headers: {
|
|
690
|
+
"Content-Type": "application/json",
|
|
691
|
+
"Content-Length": Buffer.byteLength(data),
|
|
692
|
+
"X-Plugin-Auth-Token": pluginAuthToken,
|
|
693
|
+
...vercelBypassHeader()
|
|
694
|
+
},
|
|
695
|
+
timeout: 15e3
|
|
696
|
+
},
|
|
697
|
+
(res) => {
|
|
698
|
+
res.on("error", reject);
|
|
699
|
+
let responseBody = "";
|
|
700
|
+
res.on("data", (chunk) => {
|
|
701
|
+
responseBody += chunk.toString();
|
|
702
|
+
});
|
|
703
|
+
res.on("end", () => {
|
|
704
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
705
|
+
const err = new Error(`HTTP ${res.statusCode}: ${responseBody.slice(0, 200)}`);
|
|
706
|
+
err.statusCode = res.statusCode;
|
|
707
|
+
reject(err);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
try {
|
|
711
|
+
resolve2(JSON.parse(responseBody));
|
|
712
|
+
} catch {
|
|
713
|
+
resolve2(null);
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
);
|
|
718
|
+
req.on("error", reject);
|
|
719
|
+
req.on("timeout", () => {
|
|
720
|
+
req.destroy();
|
|
721
|
+
reject(new Error("timeout"));
|
|
722
|
+
});
|
|
723
|
+
req.write(data);
|
|
724
|
+
req.end();
|
|
725
|
+
});
|
|
726
|
+
}
|
|
642
727
|
async function _postJson(url, body) {
|
|
643
728
|
return new Promise((resolve2, reject) => {
|
|
644
729
|
const data = JSON.stringify(body);
|
|
@@ -8442,14 +8527,20 @@ async function pair(args2 = []) {
|
|
|
8442
8527
|
process.exit(1);
|
|
8443
8528
|
}
|
|
8444
8529
|
spin.stop("Got pairing code");
|
|
8445
|
-
showPairingCode(result.code
|
|
8530
|
+
showPairingCode(result.code);
|
|
8446
8531
|
console.log(import_picocolors3.default.dim(" Scan the QR code or enter the code in CodeAgent Mobile."));
|
|
8447
8532
|
console.log("");
|
|
8448
8533
|
const waitSpin = dist_exports.spinner();
|
|
8449
|
-
|
|
8534
|
+
const waitMessage = () => `Waiting for mobile app... \xB7 expires in ${formatRemaining(result.expiresAt)}`;
|
|
8535
|
+
waitSpin.start(waitMessage());
|
|
8536
|
+
const countdownInterval = setInterval(() => {
|
|
8537
|
+
waitSpin.message(waitMessage());
|
|
8538
|
+
}, 1e3);
|
|
8539
|
+
countdownInterval.unref?.();
|
|
8450
8540
|
await new Promise((resolve2) => {
|
|
8451
8541
|
let stopPolling = null;
|
|
8452
8542
|
function sigintHandler() {
|
|
8543
|
+
clearInterval(countdownInterval);
|
|
8453
8544
|
stopPolling?.();
|
|
8454
8545
|
console.log("");
|
|
8455
8546
|
process.exit(0);
|
|
@@ -8458,6 +8549,7 @@ async function pair(args2 = []) {
|
|
|
8458
8549
|
pluginId,
|
|
8459
8550
|
(info) => {
|
|
8460
8551
|
process.removeListener("SIGINT", sigintHandler);
|
|
8552
|
+
clearInterval(countdownInterval);
|
|
8461
8553
|
waitSpin.stop("Paired!");
|
|
8462
8554
|
addSession({
|
|
8463
8555
|
id: info.sessionId,
|
|
@@ -8475,6 +8567,7 @@ async function pair(args2 = []) {
|
|
|
8475
8567
|
resolve2();
|
|
8476
8568
|
},
|
|
8477
8569
|
() => {
|
|
8570
|
+
clearInterval(countdownInterval);
|
|
8478
8571
|
waitSpin.stop("Timed out");
|
|
8479
8572
|
showError("Pairing timed out after 5 minutes. Run codeam pair to try again.");
|
|
8480
8573
|
process.exit(1);
|
|
@@ -8504,12 +8597,12 @@ function readTokenFromArgs(args2) {
|
|
|
8504
8597
|
}
|
|
8505
8598
|
const fileFlag = args2.find((a) => a.startsWith("--token-file="));
|
|
8506
8599
|
if (fileFlag) {
|
|
8507
|
-
const
|
|
8600
|
+
const path27 = fileFlag.slice("--token-file=".length);
|
|
8508
8601
|
try {
|
|
8509
|
-
const content = fs15.readFileSync(
|
|
8510
|
-
if (content.length === 0) fail(`--token-file ${
|
|
8602
|
+
const content = fs15.readFileSync(path27, "utf8").trim();
|
|
8603
|
+
if (content.length === 0) fail(`--token-file ${path27} is empty`);
|
|
8511
8604
|
try {
|
|
8512
|
-
fs15.unlinkSync(
|
|
8605
|
+
fs15.unlinkSync(path27);
|
|
8513
8606
|
} catch {
|
|
8514
8607
|
}
|
|
8515
8608
|
return content;
|
|
@@ -10613,74 +10706,336 @@ async function probeCodeamPair(provider, workspace) {
|
|
|
10613
10706
|
}
|
|
10614
10707
|
async function stopWorkspaceFromLocal(target) {
|
|
10615
10708
|
if (target.provider.id === "github-codespaces") {
|
|
10616
|
-
const { execFile:
|
|
10617
|
-
const { promisify:
|
|
10618
|
-
const
|
|
10619
|
-
await
|
|
10709
|
+
const { execFile: execFile8 } = await import("child_process");
|
|
10710
|
+
const { promisify: promisify8 } = await import("util");
|
|
10711
|
+
const execFileP8 = promisify8(execFile8);
|
|
10712
|
+
await execFileP8("gh", ["codespace", "stop", "-c", target.id], { maxBuffer: 8 * 1024 * 1024 });
|
|
10620
10713
|
return;
|
|
10621
10714
|
}
|
|
10622
10715
|
}
|
|
10623
10716
|
|
|
10624
|
-
// src/commands/
|
|
10717
|
+
// src/commands/link.ts
|
|
10718
|
+
var import_node_child_process3 = require("child_process");
|
|
10719
|
+
var import_node_crypto = require("crypto");
|
|
10625
10720
|
var import_picocolors11 = __toESM(require("picocolors"));
|
|
10721
|
+
|
|
10722
|
+
// src/agents/claude/local-token.ts
|
|
10723
|
+
var import_node_child_process2 = require("child_process");
|
|
10724
|
+
var fs16 = __toESM(require("fs"));
|
|
10725
|
+
var os15 = __toESM(require("os"));
|
|
10726
|
+
var path24 = __toESM(require("path"));
|
|
10727
|
+
var import_node_util3 = require("util");
|
|
10728
|
+
var execFileP7 = (0, import_node_util3.promisify)(import_node_child_process2.execFile);
|
|
10729
|
+
function claudeCredentialsPath() {
|
|
10730
|
+
return path24.join(os15.homedir(), ".claude", ".credentials.json");
|
|
10731
|
+
}
|
|
10732
|
+
async function extractLocalClaudeToken() {
|
|
10733
|
+
const flat = claudeCredentialsPath();
|
|
10734
|
+
if (fs16.existsSync(flat)) {
|
|
10735
|
+
const credential = fs16.readFileSync(flat, "utf8").trim();
|
|
10736
|
+
if (credential.length > 0) {
|
|
10737
|
+
return { method: "oauth", credential, source: "flat-file" };
|
|
10738
|
+
}
|
|
10739
|
+
}
|
|
10740
|
+
if (process.platform === "darwin") {
|
|
10741
|
+
try {
|
|
10742
|
+
const { stdout } = await execFileP7(
|
|
10743
|
+
"security",
|
|
10744
|
+
["find-generic-password", "-s", "Claude Code-credentials", "-w"],
|
|
10745
|
+
{ maxBuffer: 1024 * 1024 }
|
|
10746
|
+
);
|
|
10747
|
+
const credential = stdout.trim();
|
|
10748
|
+
if (credential.length > 0) {
|
|
10749
|
+
return { method: "oauth", credential, source: "macos-keychain" };
|
|
10750
|
+
}
|
|
10751
|
+
} catch {
|
|
10752
|
+
}
|
|
10753
|
+
}
|
|
10754
|
+
return null;
|
|
10755
|
+
}
|
|
10756
|
+
function claudeCredentialsMtime() {
|
|
10757
|
+
const flat = claudeCredentialsPath();
|
|
10758
|
+
try {
|
|
10759
|
+
return fs16.statSync(flat).mtimeMs;
|
|
10760
|
+
} catch {
|
|
10761
|
+
return null;
|
|
10762
|
+
}
|
|
10763
|
+
}
|
|
10764
|
+
|
|
10765
|
+
// src/agents/codex/local-token.ts
|
|
10766
|
+
var fs17 = __toESM(require("fs"));
|
|
10767
|
+
var os16 = __toESM(require("os"));
|
|
10768
|
+
var path25 = __toESM(require("path"));
|
|
10769
|
+
function codexCredentialsPath() {
|
|
10770
|
+
return path25.join(os16.homedir(), ".codex", "auth.json");
|
|
10771
|
+
}
|
|
10772
|
+
async function extractLocalCodexToken() {
|
|
10773
|
+
const file = codexCredentialsPath();
|
|
10774
|
+
if (!fs17.existsSync(file)) return null;
|
|
10775
|
+
const credential = fs17.readFileSync(file, "utf8").trim();
|
|
10776
|
+
if (credential.length === 0) return null;
|
|
10777
|
+
return { method: "oauth", credential, source: "flat-file" };
|
|
10778
|
+
}
|
|
10779
|
+
function codexCredentialsMtime() {
|
|
10780
|
+
const file = codexCredentialsPath();
|
|
10781
|
+
try {
|
|
10782
|
+
return fs17.statSync(file).mtimeMs;
|
|
10783
|
+
} catch {
|
|
10784
|
+
return null;
|
|
10785
|
+
}
|
|
10786
|
+
}
|
|
10787
|
+
|
|
10788
|
+
// src/commands/link.ts
|
|
10789
|
+
var AGENT_META = {
|
|
10790
|
+
claude: {
|
|
10791
|
+
internalId: "claude",
|
|
10792
|
+
publicId: "claude_code",
|
|
10793
|
+
binary: "claude",
|
|
10794
|
+
loginArgs: ["login"],
|
|
10795
|
+
displayName: "Claude Code",
|
|
10796
|
+
vendor: "Anthropic",
|
|
10797
|
+
credentialsHint: "~/.claude/.credentials.json (or macOS Keychain)",
|
|
10798
|
+
extract: extractLocalClaudeToken,
|
|
10799
|
+
mtime: claudeCredentialsMtime
|
|
10800
|
+
},
|
|
10801
|
+
codex: {
|
|
10802
|
+
internalId: "codex",
|
|
10803
|
+
publicId: "codex",
|
|
10804
|
+
binary: "codex",
|
|
10805
|
+
loginArgs: ["login"],
|
|
10806
|
+
displayName: "Codex",
|
|
10807
|
+
vendor: "OpenAI",
|
|
10808
|
+
credentialsHint: "~/.codex/auth.json",
|
|
10809
|
+
extract: extractLocalCodexToken,
|
|
10810
|
+
mtime: codexCredentialsMtime
|
|
10811
|
+
}
|
|
10812
|
+
};
|
|
10813
|
+
function parseLinkArgs(args2) {
|
|
10814
|
+
const positional = args2.find((a) => !a.startsWith("--"));
|
|
10815
|
+
if (!positional) {
|
|
10816
|
+
throw new Error(
|
|
10817
|
+
`Usage: codeam link <agent>
|
|
10818
|
+
agent: ${Object.keys(AGENT_META).join(" | ")}`
|
|
10819
|
+
);
|
|
10820
|
+
}
|
|
10821
|
+
const normalised = positional === "claude_code" ? "claude" : positional;
|
|
10822
|
+
if (normalised !== "claude" && normalised !== "codex") {
|
|
10823
|
+
throw new Error(
|
|
10824
|
+
`Unknown agent "${positional}". Valid: ${Object.keys(AGENT_META).join(", ")}`
|
|
10825
|
+
);
|
|
10826
|
+
}
|
|
10827
|
+
const reuseExisting = args2.includes("--reuse-existing");
|
|
10828
|
+
return { agent: normalised, reuseExisting };
|
|
10829
|
+
}
|
|
10830
|
+
async function link(args2 = []) {
|
|
10831
|
+
const { agent, reuseExisting } = parseLinkArgs(args2);
|
|
10832
|
+
const meta = AGENT_META[agent];
|
|
10833
|
+
showIntro();
|
|
10834
|
+
console.log(
|
|
10835
|
+
import_picocolors11.default.bold(` Link ${meta.displayName}`) + import_picocolors11.default.dim(` \xB7 ${meta.vendor}`)
|
|
10836
|
+
);
|
|
10837
|
+
console.log("");
|
|
10838
|
+
const pluginId = (0, import_node_crypto.randomUUID)();
|
|
10839
|
+
const spin = dist_exports.spinner();
|
|
10840
|
+
spin.start("Requesting pairing code...");
|
|
10841
|
+
const pairing = await requestCode(pluginId);
|
|
10842
|
+
if (!pairing) {
|
|
10843
|
+
spin.stop("Failed");
|
|
10844
|
+
showError("Could not reach the server. Check your connection and try again.");
|
|
10845
|
+
process.exit(1);
|
|
10846
|
+
}
|
|
10847
|
+
spin.stop("Got pairing code");
|
|
10848
|
+
showPairingCode(pairing.code);
|
|
10849
|
+
console.log(import_picocolors11.default.dim(" Scan the QR or enter the code in CodeAgent Mobile."));
|
|
10850
|
+
console.log("");
|
|
10851
|
+
const waitSpin = dist_exports.spinner();
|
|
10852
|
+
const waitMsg = () => `Waiting for mobile pair... \xB7 expires in ${formatRemaining(pairing.expiresAt)}`;
|
|
10853
|
+
waitSpin.start(waitMsg());
|
|
10854
|
+
const countdown = setInterval(() => waitSpin.message(waitMsg()), 1e3);
|
|
10855
|
+
countdown.unref?.();
|
|
10856
|
+
const paired = await new Promise((resolve2, reject) => {
|
|
10857
|
+
let stopPoll = null;
|
|
10858
|
+
const sigint = () => {
|
|
10859
|
+
clearInterval(countdown);
|
|
10860
|
+
stopPoll?.();
|
|
10861
|
+
reject(new Error("cancelled"));
|
|
10862
|
+
};
|
|
10863
|
+
stopPoll = pollStatus(
|
|
10864
|
+
pluginId,
|
|
10865
|
+
(info) => {
|
|
10866
|
+
process.removeListener("SIGINT", sigint);
|
|
10867
|
+
clearInterval(countdown);
|
|
10868
|
+
waitSpin.stop("Paired");
|
|
10869
|
+
resolve2(info);
|
|
10870
|
+
},
|
|
10871
|
+
() => {
|
|
10872
|
+
clearInterval(countdown);
|
|
10873
|
+
waitSpin.stop("Timed out");
|
|
10874
|
+
reject(new Error("Pairing timed out after 5 minutes. Run codeam link again."));
|
|
10875
|
+
}
|
|
10876
|
+
);
|
|
10877
|
+
process.once("SIGINT", sigint);
|
|
10878
|
+
});
|
|
10879
|
+
if (!paired.pluginAuthToken) {
|
|
10880
|
+
showError(
|
|
10881
|
+
"Backend did not return a pluginAuthToken \u2014 upgrade api-v2 (deploy includes the link endpoint)."
|
|
10882
|
+
);
|
|
10883
|
+
process.exit(1);
|
|
10884
|
+
}
|
|
10885
|
+
addSession({
|
|
10886
|
+
id: paired.sessionId,
|
|
10887
|
+
pluginId,
|
|
10888
|
+
userName: paired.userName,
|
|
10889
|
+
userEmail: paired.userEmail,
|
|
10890
|
+
plan: paired.plan,
|
|
10891
|
+
pairedAt: Date.now(),
|
|
10892
|
+
pluginAuthToken: paired.pluginAuthToken,
|
|
10893
|
+
agent: meta.internalId
|
|
10894
|
+
});
|
|
10895
|
+
saveCliConfig({ ...loadCliConfig(), preferredAgent: meta.internalId });
|
|
10896
|
+
let token = await meta.extract();
|
|
10897
|
+
if (token && reuseExisting) {
|
|
10898
|
+
showInfo(`Reusing existing ${meta.displayName} token at ${import_picocolors11.default.bold(meta.credentialsHint)}.`);
|
|
10899
|
+
} else {
|
|
10900
|
+
const beforeMtime = meta.mtime();
|
|
10901
|
+
showInfo(`Launching ${import_picocolors11.default.bold(`${meta.binary} ${meta.loginArgs.join(" ")}`)} \u2014 complete the sign-in in your browser, then return here.`);
|
|
10902
|
+
console.log("");
|
|
10903
|
+
const code = await runAgentLogin(meta);
|
|
10904
|
+
console.log("");
|
|
10905
|
+
if (code !== 0) {
|
|
10906
|
+
showError(
|
|
10907
|
+
`${meta.binary} ${meta.loginArgs.join(" ")} exited with code ${code}. Re-run when ready.`
|
|
10908
|
+
);
|
|
10909
|
+
process.exit(1);
|
|
10910
|
+
}
|
|
10911
|
+
const refreshed = await meta.extract();
|
|
10912
|
+
const afterMtime = meta.mtime();
|
|
10913
|
+
if (!refreshed) {
|
|
10914
|
+
showError(
|
|
10915
|
+
`${meta.displayName} login finished but no credential was found at ${meta.credentialsHint}. Re-run when ready.`
|
|
10916
|
+
);
|
|
10917
|
+
process.exit(1);
|
|
10918
|
+
}
|
|
10919
|
+
if (token && refreshed.credential === token.credential && beforeMtime !== null && afterMtime !== null && afterMtime <= beforeMtime) {
|
|
10920
|
+
showError(
|
|
10921
|
+
`${meta.displayName} login didn't produce a fresh token. Re-run when ready, or pass --reuse-existing to keep the current one.`
|
|
10922
|
+
);
|
|
10923
|
+
process.exit(1);
|
|
10924
|
+
}
|
|
10925
|
+
token = refreshed;
|
|
10926
|
+
}
|
|
10927
|
+
const uploadSpin = dist_exports.spinner();
|
|
10928
|
+
uploadSpin.start("Sealing credential in your vault...");
|
|
10929
|
+
const result = await postLinkCredential({
|
|
10930
|
+
agentId: meta.publicId,
|
|
10931
|
+
sessionId: paired.sessionId,
|
|
10932
|
+
pluginId,
|
|
10933
|
+
pluginAuthToken: paired.pluginAuthToken,
|
|
10934
|
+
method: token.method,
|
|
10935
|
+
credential: token.credential
|
|
10936
|
+
});
|
|
10937
|
+
if (!result.ok) {
|
|
10938
|
+
uploadSpin.stop("Failed");
|
|
10939
|
+
if (result.status === 401) {
|
|
10940
|
+
showError(
|
|
10941
|
+
"Pair token rejected by the backend (401). Re-run `codeam link` to start fresh."
|
|
10942
|
+
);
|
|
10943
|
+
} else if (result.status === 404) {
|
|
10944
|
+
showError(
|
|
10945
|
+
`${meta.displayName} link endpoint not available on this backend (404). The api-v2 deployment may not yet include the /api/plugin/agents/:agentId/link route.`
|
|
10946
|
+
);
|
|
10947
|
+
} else {
|
|
10948
|
+
showError(`Upload failed: ${result.message}`);
|
|
10949
|
+
}
|
|
10950
|
+
process.exit(1);
|
|
10951
|
+
}
|
|
10952
|
+
uploadSpin.stop("Linked");
|
|
10953
|
+
console.log("");
|
|
10954
|
+
showSuccess(`${meta.displayName} is now linked to ${paired.userEmail || paired.userName}.`);
|
|
10955
|
+
showInfo(
|
|
10956
|
+
`Your codespaces and @codeagent mentions can now use ${meta.displayName} without you signing in again.`
|
|
10957
|
+
);
|
|
10958
|
+
console.log("");
|
|
10959
|
+
}
|
|
10960
|
+
function runAgentLogin(meta) {
|
|
10961
|
+
return new Promise((resolve2) => {
|
|
10962
|
+
const child = (0, import_node_child_process3.spawn)(meta.binary, meta.loginArgs, { stdio: "inherit" });
|
|
10963
|
+
child.on("error", (err) => {
|
|
10964
|
+
if (err.code === "ENOENT") {
|
|
10965
|
+
showError(
|
|
10966
|
+
`${meta.binary} binary not found on PATH. Install ${meta.displayName} first, then re-run \`codeam link ${meta.internalId}\`.`
|
|
10967
|
+
);
|
|
10968
|
+
resolve2(127);
|
|
10969
|
+
return;
|
|
10970
|
+
}
|
|
10971
|
+
showError(`Failed to launch ${meta.binary}: ${err.message}`);
|
|
10972
|
+
resolve2(1);
|
|
10973
|
+
});
|
|
10974
|
+
child.on("exit", (code) => resolve2(code ?? 1));
|
|
10975
|
+
});
|
|
10976
|
+
}
|
|
10977
|
+
|
|
10978
|
+
// src/commands/version.ts
|
|
10979
|
+
var import_picocolors12 = __toESM(require("picocolors"));
|
|
10626
10980
|
function version() {
|
|
10627
|
-
const v = true ? "2.
|
|
10628
|
-
console.log(`${
|
|
10981
|
+
const v = true ? "2.16.0" : "unknown";
|
|
10982
|
+
console.log(`${import_picocolors12.default.bold("codeam-cli")} ${import_picocolors12.default.cyan(v)}`);
|
|
10629
10983
|
}
|
|
10630
10984
|
|
|
10631
10985
|
// src/commands/help.ts
|
|
10632
|
-
var
|
|
10986
|
+
var import_picocolors13 = __toESM(require("picocolors"));
|
|
10633
10987
|
function help() {
|
|
10634
10988
|
const lines = [
|
|
10635
10989
|
"",
|
|
10636
|
-
` ${
|
|
10990
|
+
` ${import_picocolors13.default.bold(import_picocolors13.default.magenta("codeam-cli"))} ${import_picocolors13.default.dim("\u2014 remote-control AI coding agents from your phone")}`,
|
|
10637
10991
|
"",
|
|
10638
|
-
` ${
|
|
10639
|
-
` ${
|
|
10992
|
+
` ${import_picocolors13.default.bold("Usage")}`,
|
|
10993
|
+
` ${import_picocolors13.default.cyan("codeam")} ${import_picocolors13.default.dim("[command]")}`,
|
|
10640
10994
|
"",
|
|
10641
|
-
` ${
|
|
10642
|
-
` ${
|
|
10643
|
-
` ${
|
|
10644
|
-
` ${
|
|
10645
|
-
` ${
|
|
10646
|
-
` ${
|
|
10647
|
-
` ${
|
|
10648
|
-
` ${
|
|
10649
|
-
` ${
|
|
10650
|
-
` ${
|
|
10651
|
-
` ${
|
|
10652
|
-
` ${
|
|
10653
|
-
` ${
|
|
10995
|
+
` ${import_picocolors13.default.bold("Commands")}`,
|
|
10996
|
+
` ${import_picocolors13.default.white("codeam")} ${import_picocolors13.default.dim("start the active agent with mobile control")}`,
|
|
10997
|
+
` ${import_picocolors13.default.white("codeam <agent>")} ${import_picocolors13.default.dim("start a specific agent \u2014 e.g. claude, codex")}`,
|
|
10998
|
+
` ${import_picocolors13.default.white("codeam pair")} ${import_picocolors13.default.dim("pair a new mobile device (interactive)")}`,
|
|
10999
|
+
` ${import_picocolors13.default.white("codeam pair --agent <id>")} ${import_picocolors13.default.dim("pair non-interactively for a specific agent")}`,
|
|
11000
|
+
` ${import_picocolors13.default.white("codeam sessions")} ${import_picocolors13.default.dim("list paired devices")}`,
|
|
11001
|
+
` ${import_picocolors13.default.white("codeam sessions switch")} ${import_picocolors13.default.dim("switch the active paired session")}`,
|
|
11002
|
+
` ${import_picocolors13.default.white("codeam sessions delete <id>")} ${import_picocolors13.default.dim("remove a specific paired session")}`,
|
|
11003
|
+
` ${import_picocolors13.default.white("codeam status")} ${import_picocolors13.default.dim("show connection info")}`,
|
|
11004
|
+
` ${import_picocolors13.default.white("codeam logout")} ${import_picocolors13.default.dim("remove all paired sessions")}`,
|
|
11005
|
+
` ${import_picocolors13.default.white("codeam link <agent>")} ${import_picocolors13.default.dim("link an agent (claude, codex) to your CodeAgent account")}`,
|
|
11006
|
+
` ${import_picocolors13.default.white("codeam deploy")} ${import_picocolors13.default.dim("provision a cloud workspace (Codespaces) and pair it")}`,
|
|
11007
|
+
` ${import_picocolors13.default.white("codeam deploy ls | list")} ${import_picocolors13.default.dim("list deployed cloud workspaces")}`,
|
|
11008
|
+
` ${import_picocolors13.default.white("codeam deploy stop | remove")} ${import_picocolors13.default.dim("stop a deployed workspace session")}`,
|
|
10654
11009
|
"",
|
|
10655
|
-
` ${
|
|
10656
|
-
` ${
|
|
10657
|
-
` ${
|
|
11010
|
+
` ${import_picocolors13.default.bold("Flags")}`,
|
|
11011
|
+
` ${import_picocolors13.default.white("-v, --version")} ${import_picocolors13.default.dim("print the CLI version")}`,
|
|
11012
|
+
` ${import_picocolors13.default.white("-h, --help")} ${import_picocolors13.default.dim("show this help")}`,
|
|
10658
11013
|
"",
|
|
10659
|
-
` ${
|
|
10660
|
-
` ${
|
|
10661
|
-
` ${
|
|
11014
|
+
` ${import_picocolors13.default.bold("Links")}`,
|
|
11015
|
+
` ${import_picocolors13.default.dim("Docs:")} ${import_picocolors13.default.green("https://www.codeagent-mobile.com")}`,
|
|
11016
|
+
` ${import_picocolors13.default.dim("Issues:")} ${import_picocolors13.default.green("https://github.com/edgar-durand/codeagent-mobile-clients/issues")}`,
|
|
10662
11017
|
""
|
|
10663
11018
|
];
|
|
10664
11019
|
process.stdout.write(lines.join("\n") + "\n");
|
|
10665
11020
|
}
|
|
10666
11021
|
|
|
10667
11022
|
// src/lib/updateNotifier.ts
|
|
10668
|
-
var
|
|
10669
|
-
var
|
|
10670
|
-
var
|
|
11023
|
+
var fs18 = __toESM(require("fs"));
|
|
11024
|
+
var os17 = __toESM(require("os"));
|
|
11025
|
+
var path26 = __toESM(require("path"));
|
|
10671
11026
|
var https7 = __toESM(require("https"));
|
|
10672
|
-
var
|
|
11027
|
+
var import_picocolors14 = __toESM(require("picocolors"));
|
|
10673
11028
|
var PKG_NAME = "codeam-cli";
|
|
10674
11029
|
var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
10675
11030
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
10676
11031
|
var REQUEST_TIMEOUT_MS = 1500;
|
|
10677
11032
|
function cachePath() {
|
|
10678
|
-
const dir =
|
|
10679
|
-
return
|
|
11033
|
+
const dir = path26.join(os17.homedir(), ".codeam");
|
|
11034
|
+
return path26.join(dir, "update-check.json");
|
|
10680
11035
|
}
|
|
10681
11036
|
function readCache() {
|
|
10682
11037
|
try {
|
|
10683
|
-
const raw =
|
|
11038
|
+
const raw = fs18.readFileSync(cachePath(), "utf8");
|
|
10684
11039
|
const parsed = JSON.parse(raw);
|
|
10685
11040
|
if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
|
|
10686
11041
|
return parsed;
|
|
@@ -10691,8 +11046,8 @@ function readCache() {
|
|
|
10691
11046
|
function writeCache(cache) {
|
|
10692
11047
|
try {
|
|
10693
11048
|
const file = cachePath();
|
|
10694
|
-
|
|
10695
|
-
|
|
11049
|
+
fs18.mkdirSync(path26.dirname(file), { recursive: true });
|
|
11050
|
+
fs18.writeFileSync(file, JSON.stringify(cache));
|
|
10696
11051
|
} catch {
|
|
10697
11052
|
}
|
|
10698
11053
|
}
|
|
@@ -10748,11 +11103,11 @@ function fetchLatest() {
|
|
|
10748
11103
|
}
|
|
10749
11104
|
function notifyIfStale(currentVersion, latest) {
|
|
10750
11105
|
if (compareSemver(latest, currentVersion) <= 0) return;
|
|
10751
|
-
const arrow =
|
|
10752
|
-
const cmd =
|
|
11106
|
+
const arrow = import_picocolors14.default.dim("\u2192");
|
|
11107
|
+
const cmd = import_picocolors14.default.cyan("npm install -g codeam-cli");
|
|
10753
11108
|
const lines = [
|
|
10754
11109
|
"",
|
|
10755
|
-
` ${
|
|
11110
|
+
` ${import_picocolors14.default.yellow("\u25CF")} ${import_picocolors14.default.bold("Update available")} ${import_picocolors14.default.dim(currentVersion)} ${arrow} ${import_picocolors14.default.green(latest)}`,
|
|
10756
11111
|
` Run ${cmd} to upgrade.`,
|
|
10757
11112
|
""
|
|
10758
11113
|
];
|
|
@@ -10763,7 +11118,7 @@ function checkForUpdates() {
|
|
|
10763
11118
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
10764
11119
|
if (process.env.CI) return;
|
|
10765
11120
|
if (!process.stdout.isTTY) return;
|
|
10766
|
-
const current = true ? "2.
|
|
11121
|
+
const current = true ? "2.16.0" : null;
|
|
10767
11122
|
if (!current) return;
|
|
10768
11123
|
const cache = readCache();
|
|
10769
11124
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -10801,6 +11156,8 @@ async function main() {
|
|
|
10801
11156
|
return status();
|
|
10802
11157
|
case "logout":
|
|
10803
11158
|
return logout();
|
|
11159
|
+
case "link":
|
|
11160
|
+
return link(args);
|
|
10804
11161
|
case "deploy":
|
|
10805
11162
|
if (args[0] === "ls" || args[0] === "list") return deployList();
|
|
10806
11163
|
if (args[0] === "stop" || args[0] === "remove") return deployStop();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.16.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",
|