codeam-cli 2.21.2 → 2.22.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 +6 -0
- package/dist/index.js +810 -755
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -87,7 +87,7 @@ var require_src = __commonJS({
|
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
// src/commands/start.ts
|
|
90
|
-
var
|
|
90
|
+
var import_picocolors3 = __toESM(require("picocolors"));
|
|
91
91
|
|
|
92
92
|
// ../../packages/shared/src/protocol/constants.ts
|
|
93
93
|
var PROTOCOL_VERSION = "2.0.0";
|
|
@@ -441,7 +441,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
441
441
|
// package.json
|
|
442
442
|
var package_default = {
|
|
443
443
|
name: "codeam-cli",
|
|
444
|
-
version: "2.
|
|
444
|
+
version: "2.22.0",
|
|
445
445
|
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.",
|
|
446
446
|
type: "commonjs",
|
|
447
447
|
main: "dist/index.js",
|
|
@@ -5740,7 +5740,7 @@ function readAnonId() {
|
|
|
5740
5740
|
}
|
|
5741
5741
|
function superProperties() {
|
|
5742
5742
|
return {
|
|
5743
|
-
cliVersion: true ? "2.
|
|
5743
|
+
cliVersion: true ? "2.22.0" : "0.0.0-dev",
|
|
5744
5744
|
nodeVersion: process.version,
|
|
5745
5745
|
platform: process.platform,
|
|
5746
5746
|
arch: process.arch,
|
|
@@ -6035,7 +6035,8 @@ var CommandRelayService = class {
|
|
|
6035
6035
|
async sendHeartbeat(online) {
|
|
6036
6036
|
await _postJson(`${API_BASE2}/api/plugin/heartbeat`, {
|
|
6037
6037
|
pluginId: this.pluginId,
|
|
6038
|
-
online
|
|
6038
|
+
online,
|
|
6039
|
+
agentId: this.agentMeta.id
|
|
6039
6040
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
6040
6041
|
}
|
|
6041
6042
|
reportAgents() {
|
|
@@ -13753,9 +13754,9 @@ function buildKeepAlive(ctx) {
|
|
|
13753
13754
|
}
|
|
13754
13755
|
|
|
13755
13756
|
// src/commands/start/handlers.ts
|
|
13756
|
-
var
|
|
13757
|
+
var fs27 = __toESM(require("fs"));
|
|
13757
13758
|
var os23 = __toESM(require("os"));
|
|
13758
|
-
var
|
|
13759
|
+
var path33 = __toESM(require("path"));
|
|
13759
13760
|
var import_crypto5 = require("crypto");
|
|
13760
13761
|
var import_child_process13 = require("child_process");
|
|
13761
13762
|
|
|
@@ -13813,7 +13814,14 @@ var startCommandSchema = import_zod2.z.object({
|
|
|
13813
13814
|
// `git restore` from there. `action='approved'` stages the edit,
|
|
13814
13815
|
// `action='rejected'` discards every worktree change on the file.
|
|
13815
13816
|
filePath: import_zod2.z.string().min(1).max(4096).optional(),
|
|
13816
|
-
action: import_zod2.z.enum(["approved", "rejected"]).optional()
|
|
13817
|
+
action: import_zod2.z.enum(["approved", "rejected"]).optional(),
|
|
13818
|
+
// `request_link_credentials` — backend fires this from the
|
|
13819
|
+
// heartbeat handler when it notices the user is running an agent
|
|
13820
|
+
// they haven't vaulted yet. We reuse the `codeam link` token-
|
|
13821
|
+
// capture path to push the credential up; if extraction fails
|
|
13822
|
+
// (no local auth, missing file), the handler no-ops silently —
|
|
13823
|
+
// no browser-login surprises during a normal `codeam pair`.
|
|
13824
|
+
agentId: import_zod2.z.enum(["claude_code", "codex", "cursor", "aider", "coderabbit"]).optional()
|
|
13817
13825
|
});
|
|
13818
13826
|
function parsePayload2(schema, raw) {
|
|
13819
13827
|
const result = schema.safeParse(raw);
|
|
@@ -14653,240 +14661,582 @@ function findGitRoot2(startDir) {
|
|
|
14653
14661
|
return null;
|
|
14654
14662
|
}
|
|
14655
14663
|
|
|
14656
|
-
// src/commands/
|
|
14657
|
-
var
|
|
14658
|
-
|
|
14659
|
-
|
|
14660
|
-
|
|
14661
|
-
|
|
14662
|
-
|
|
14663
|
-
|
|
14664
|
-
|
|
14665
|
-
|
|
14664
|
+
// src/commands/link.ts
|
|
14665
|
+
var import_node_crypto4 = require("crypto");
|
|
14666
|
+
var fs26 = __toESM(require("fs"));
|
|
14667
|
+
var path32 = __toESM(require("path"));
|
|
14668
|
+
var import_chokidar = __toESM(require("chokidar"));
|
|
14669
|
+
var import_picocolors2 = __toESM(require("picocolors"));
|
|
14670
|
+
|
|
14671
|
+
// src/ui/prompts.ts
|
|
14672
|
+
async function confirmAction(message) {
|
|
14673
|
+
const result = await ot2({ message });
|
|
14674
|
+
if (q(result)) return false;
|
|
14675
|
+
return result;
|
|
14666
14676
|
}
|
|
14667
|
-
function
|
|
14668
|
-
|
|
14669
|
-
|
|
14670
|
-
|
|
14671
|
-
|
|
14672
|
-
|
|
14673
|
-
|
|
14677
|
+
async function selectSession(sessions3, activeId) {
|
|
14678
|
+
const result = await _t({
|
|
14679
|
+
message: "Select active session:",
|
|
14680
|
+
options: sessions3.map((s) => ({
|
|
14681
|
+
value: s.id,
|
|
14682
|
+
label: `${s.userName} ${s.plan}`,
|
|
14683
|
+
hint: s.id === activeId ? "active" : `paired ${new Date(s.pairedAt).toLocaleDateString()}`
|
|
14684
|
+
})),
|
|
14685
|
+
initialValue: activeId ?? void 0
|
|
14674
14686
|
});
|
|
14687
|
+
if (q(result)) return null;
|
|
14688
|
+
return result;
|
|
14675
14689
|
}
|
|
14676
|
-
|
|
14677
|
-
|
|
14678
|
-
|
|
14690
|
+
|
|
14691
|
+
// src/commands/link.ts
|
|
14692
|
+
function buildLinkContext(agentId) {
|
|
14693
|
+
const runtime = createRuntimeStrategy(agentId);
|
|
14694
|
+
return {
|
|
14695
|
+
runtime,
|
|
14696
|
+
locator: runtime.credentialLocator(),
|
|
14697
|
+
launcher: runtime.loginLauncher(),
|
|
14698
|
+
displayName: runtime.meta.displayName,
|
|
14699
|
+
binary: runtime.meta.binaryName
|
|
14700
|
+
};
|
|
14679
14701
|
}
|
|
14680
|
-
|
|
14681
|
-
|
|
14682
|
-
|
|
14683
|
-
|
|
14684
|
-
|
|
14685
|
-
|
|
14686
|
-
|
|
14687
|
-
|
|
14688
|
-
|
|
14689
|
-
|
|
14690
|
-
|
|
14691
|
-
fs26.unlinkSync(p2);
|
|
14692
|
-
} catch {
|
|
14693
|
-
}
|
|
14694
|
-
pendingAttachmentFiles.delete(p2);
|
|
14695
|
-
}
|
|
14696
|
-
}, 12e4);
|
|
14697
|
-
} else if (effectivePrompt) {
|
|
14698
|
-
dispatchPrompt(ctx, effectivePrompt);
|
|
14702
|
+
function enabledLinkableAgents() {
|
|
14703
|
+
return Object.values(AGENT_REGISTRY).filter((m) => m.enabled).map((m) => m.id);
|
|
14704
|
+
}
|
|
14705
|
+
function parseLinkArgs(args2) {
|
|
14706
|
+
const positional = args2.find((a) => !a.startsWith("--"));
|
|
14707
|
+
const valid = enabledLinkableAgents();
|
|
14708
|
+
if (!positional) {
|
|
14709
|
+
throw new Error(
|
|
14710
|
+
`Usage: codeam link <agent>
|
|
14711
|
+
agent: ${valid.join(" | ")}`
|
|
14712
|
+
);
|
|
14699
14713
|
}
|
|
14700
|
-
|
|
14701
|
-
|
|
14702
|
-
|
|
14703
|
-
}
|
|
14704
|
-
|
|
14705
|
-
const index = parsed.index ?? 0;
|
|
14706
|
-
const from = parsed.from ?? 0;
|
|
14707
|
-
ctx.outputSvc.newTurn();
|
|
14708
|
-
ctx.agent.selectOption(index, from);
|
|
14709
|
-
};
|
|
14710
|
-
var escapeKey = (ctx) => {
|
|
14711
|
-
ctx.outputSvc.newTurn();
|
|
14712
|
-
ctx.agent.sendEscape();
|
|
14713
|
-
};
|
|
14714
|
-
var stopTask = (ctx) => {
|
|
14715
|
-
ctx.agent.interrupt();
|
|
14716
|
-
};
|
|
14717
|
-
var resumeSession = async (ctx, _cmd, parsed) => {
|
|
14718
|
-
const { id, auto } = parsed;
|
|
14719
|
-
if (!id) return;
|
|
14720
|
-
ctx.historySvc.setCurrentConversationId(id);
|
|
14721
|
-
await ctx.historySvc.loadConversation(id);
|
|
14722
|
-
await ctx.outputSvc.newTurnResume(id);
|
|
14723
|
-
ctx.agent.restart(id, auto ?? false);
|
|
14724
|
-
};
|
|
14725
|
-
var getContext = async (ctx, cmd) => {
|
|
14726
|
-
const usage = ctx.historySvc.getCurrentUsage();
|
|
14727
|
-
const monthlyCost = ctx.historySvc.getMonthlyEstimatedCost();
|
|
14728
|
-
const rateLimitReset = ctx.historySvc.getRateLimitReset();
|
|
14729
|
-
const quotaPercent = ctx.historySvc.getQuotaPercent();
|
|
14730
|
-
const base = usage ? { ...usage, monthlyCost } : { used: 0, total: 2e5, percent: 0, model: null, outputTokens: 0, cacheReadTokens: 0, monthlyCost, error: "No usage data found" };
|
|
14731
|
-
const result = {
|
|
14732
|
-
...base,
|
|
14733
|
-
...rateLimitReset ? { rateLimitReset } : {},
|
|
14734
|
-
...quotaPercent !== null ? { quotaPercent } : {}
|
|
14735
|
-
};
|
|
14736
|
-
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
14737
|
-
};
|
|
14738
|
-
var getConversation = async (ctx, cmd) => {
|
|
14739
|
-
const currentId = ctx.historySvc.getCurrentConversationId();
|
|
14740
|
-
if (!currentId) {
|
|
14741
|
-
await ctx.relay.sendResult(cmd.id, "completed", { conversationId: null });
|
|
14742
|
-
return;
|
|
14714
|
+
const normalised = positional === "claude_code" ? "claude" : positional;
|
|
14715
|
+
if (!isKnownAgentId(normalised) || !AGENT_REGISTRY[normalised].enabled) {
|
|
14716
|
+
throw new Error(
|
|
14717
|
+
`Unknown or unsupported agent "${positional}". Valid: ${valid.join(", ")}`
|
|
14718
|
+
);
|
|
14743
14719
|
}
|
|
14744
|
-
|
|
14745
|
-
|
|
14746
|
-
|
|
14747
|
-
|
|
14748
|
-
|
|
14720
|
+
const reuseExisting = args2.includes("--reuse-existing");
|
|
14721
|
+
const dryRun = args2.includes("--dry-run");
|
|
14722
|
+
const apiKeyFileArg = args2.find((a) => a.startsWith("--api-key-file="));
|
|
14723
|
+
let apiKey = null;
|
|
14724
|
+
if (apiKeyFileArg) {
|
|
14725
|
+
const filePath = apiKeyFileArg.slice("--api-key-file=".length);
|
|
14726
|
+
try {
|
|
14727
|
+
apiKey = fs26.readFileSync(path32.resolve(filePath), "utf8").trim();
|
|
14728
|
+
} catch (err) {
|
|
14729
|
+
throw new Error(`Could not read --api-key-file ${filePath}: ${err.message}`);
|
|
14730
|
+
}
|
|
14731
|
+
if (!apiKey) {
|
|
14732
|
+
throw new Error(`--api-key-file ${filePath} is empty.`);
|
|
14733
|
+
}
|
|
14734
|
+
} else {
|
|
14735
|
+
const apiKeyArg = args2.find((a) => a.startsWith("--api-key="));
|
|
14736
|
+
apiKey = apiKeyArg ? apiKeyArg.slice("--api-key=".length) : null;
|
|
14749
14737
|
}
|
|
14750
|
-
|
|
14751
|
-
|
|
14752
|
-
|
|
14753
|
-
|
|
14754
|
-
|
|
14755
|
-
|
|
14756
|
-
const
|
|
14757
|
-
|
|
14758
|
-
|
|
14738
|
+
const tokenFileArg = args2.find((a) => a.startsWith("--token-file="));
|
|
14739
|
+
const tokenFile = tokenFileArg ? tokenFileArg.slice("--token-file=".length) : null;
|
|
14740
|
+
return { agent: normalised, reuseExisting, apiKey, tokenFile, dryRun };
|
|
14741
|
+
}
|
|
14742
|
+
async function link(args2 = []) {
|
|
14743
|
+
const parsed = parseLinkArgs(args2);
|
|
14744
|
+
const ctx = buildLinkContext(parsed.agent);
|
|
14745
|
+
showIntro();
|
|
14746
|
+
console.log(
|
|
14747
|
+
import_picocolors2.default.bold(` Link ${ctx.displayName}`) + import_picocolors2.default.dim(` \xB7 ${ctx.locator.vendor}`)
|
|
14748
|
+
);
|
|
14749
|
+
console.log("");
|
|
14750
|
+
if (parsed.dryRun) {
|
|
14751
|
+
await linkDryRunPreflight(ctx);
|
|
14759
14752
|
return;
|
|
14760
14753
|
}
|
|
14761
|
-
const
|
|
14762
|
-
|
|
14763
|
-
|
|
14764
|
-
|
|
14765
|
-
|
|
14766
|
-
|
|
14767
|
-
|
|
14768
|
-
|
|
14769
|
-
await ctx.relay.sendResult(cmd.id, "failed", { error: "restart-mode change_model not supported in Phase 1" });
|
|
14770
|
-
return;
|
|
14754
|
+
const pluginId = (0, import_node_crypto4.randomUUID)();
|
|
14755
|
+
const spin = dist_exports.spinner();
|
|
14756
|
+
spin.start("Requesting pairing code...");
|
|
14757
|
+
const pairing = await requestCode(pluginId);
|
|
14758
|
+
if (!pairing) {
|
|
14759
|
+
spin.stop("Failed");
|
|
14760
|
+
showError("Could not reach the server. Check your connection and try again.");
|
|
14761
|
+
process.exit(1);
|
|
14771
14762
|
}
|
|
14772
|
-
|
|
14773
|
-
|
|
14774
|
-
|
|
14775
|
-
|
|
14776
|
-
const
|
|
14777
|
-
const
|
|
14778
|
-
|
|
14779
|
-
|
|
14780
|
-
|
|
14781
|
-
|
|
14782
|
-
|
|
14783
|
-
|
|
14784
|
-
|
|
14785
|
-
|
|
14786
|
-
|
|
14787
|
-
|
|
14788
|
-
|
|
14789
|
-
|
|
14790
|
-
|
|
14791
|
-
|
|
14763
|
+
spin.stop("Got pairing code");
|
|
14764
|
+
showPairingCode(pairing.code);
|
|
14765
|
+
console.log(import_picocolors2.default.dim(" Scan the QR or enter the code in CodeAgent Mobile."));
|
|
14766
|
+
console.log("");
|
|
14767
|
+
const waitSpin = dist_exports.spinner();
|
|
14768
|
+
const waitMsg = () => `Waiting for mobile pair... \xB7 expires in ${formatRemaining(pairing.expiresAt)}`;
|
|
14769
|
+
waitSpin.start(waitMsg());
|
|
14770
|
+
const countdown = setInterval(() => waitSpin.message(waitMsg()), 1e3);
|
|
14771
|
+
countdown.unref?.();
|
|
14772
|
+
const paired = await new Promise((resolve5, reject) => {
|
|
14773
|
+
let stopPoll = null;
|
|
14774
|
+
const sigint = () => {
|
|
14775
|
+
clearInterval(countdown);
|
|
14776
|
+
stopPoll?.();
|
|
14777
|
+
reject(new Error("cancelled"));
|
|
14778
|
+
};
|
|
14779
|
+
stopPoll = pollStatus(
|
|
14780
|
+
pluginId,
|
|
14781
|
+
(info) => {
|
|
14782
|
+
process.removeListener("SIGINT", sigint);
|
|
14783
|
+
clearInterval(countdown);
|
|
14784
|
+
waitSpin.stop("Paired");
|
|
14785
|
+
resolve5(info);
|
|
14786
|
+
},
|
|
14787
|
+
() => {
|
|
14788
|
+
clearInterval(countdown);
|
|
14789
|
+
waitSpin.stop("Timed out");
|
|
14790
|
+
reject(new Error("Pairing timed out after 5 minutes. Run codeam link again."));
|
|
14792
14791
|
}
|
|
14793
14792
|
);
|
|
14794
|
-
|
|
14793
|
+
process.once("SIGINT", sigint);
|
|
14794
|
+
});
|
|
14795
|
+
if (!paired.pluginAuthToken) {
|
|
14796
|
+
showError(
|
|
14797
|
+
"Backend did not return a pluginAuthToken \u2014 upgrade api-v2 (deploy includes the link endpoint)."
|
|
14798
|
+
);
|
|
14799
|
+
process.exit(1);
|
|
14795
14800
|
}
|
|
14796
|
-
|
|
14797
|
-
|
|
14798
|
-
|
|
14799
|
-
|
|
14800
|
-
|
|
14801
|
-
|
|
14801
|
+
addSession({
|
|
14802
|
+
id: paired.sessionId,
|
|
14803
|
+
pluginId,
|
|
14804
|
+
userName: paired.userName,
|
|
14805
|
+
userEmail: paired.userEmail,
|
|
14806
|
+
plan: paired.plan,
|
|
14807
|
+
pairedAt: Date.now(),
|
|
14808
|
+
pluginAuthToken: paired.pluginAuthToken,
|
|
14809
|
+
agent: ctx.runtime.id
|
|
14810
|
+
});
|
|
14811
|
+
saveCliConfig({ ...loadCliConfig(), preferredAgent: ctx.runtime.id });
|
|
14812
|
+
if (parsed.apiKey) {
|
|
14813
|
+
await uploadAndSucceed(ctx, paired, pluginId, {
|
|
14814
|
+
method: "api_key",
|
|
14815
|
+
credential: parsed.apiKey.trim(),
|
|
14816
|
+
source: "manual"
|
|
14817
|
+
});
|
|
14818
|
+
return;
|
|
14802
14819
|
}
|
|
14803
|
-
|
|
14804
|
-
const
|
|
14805
|
-
|
|
14806
|
-
|
|
14820
|
+
if (parsed.tokenFile) {
|
|
14821
|
+
const credential = fs26.readFileSync(path32.resolve(parsed.tokenFile), "utf8").trim();
|
|
14822
|
+
if (!credential) {
|
|
14823
|
+
showError(`--token-file ${parsed.tokenFile} is empty.`);
|
|
14824
|
+
process.exit(1);
|
|
14825
|
+
}
|
|
14826
|
+
await uploadAndSucceed(ctx, paired, pluginId, {
|
|
14827
|
+
method: "oauth",
|
|
14828
|
+
credential,
|
|
14829
|
+
source: "manual"
|
|
14807
14830
|
});
|
|
14808
|
-
|
|
14809
|
-
} catch {
|
|
14831
|
+
return;
|
|
14810
14832
|
}
|
|
14811
|
-
|
|
14812
|
-
ctx.
|
|
14813
|
-
|
|
14814
|
-
|
|
14815
|
-
|
|
14816
|
-
|
|
14817
|
-
|
|
14818
|
-
} catch {
|
|
14833
|
+
const installSpin = dist_exports.spinner();
|
|
14834
|
+
installSpin.start(`Checking that ${ctx.binary} is installed...`);
|
|
14835
|
+
const installed = await ctx.launcher.ensureInstalled();
|
|
14836
|
+
if (!installed) {
|
|
14837
|
+
installSpin.stop("Failed");
|
|
14838
|
+
showError(`Could not install ${ctx.displayName}. Install it manually then re-run.`);
|
|
14839
|
+
process.exit(1);
|
|
14819
14840
|
}
|
|
14820
|
-
|
|
14821
|
-
|
|
14822
|
-
|
|
14841
|
+
installSpin.stop(`${ctx.displayName} is installed`);
|
|
14842
|
+
const existing = await ctx.locator.extract();
|
|
14843
|
+
if (existing) {
|
|
14844
|
+
showInfo(`Found existing ${ctx.displayName} credentials at ${import_picocolors2.default.bold(existing.source)}.`);
|
|
14845
|
+
await uploadAndSucceed(ctx, paired, pluginId, existing);
|
|
14846
|
+
return;
|
|
14823
14847
|
}
|
|
14824
|
-
if (
|
|
14825
|
-
|
|
14826
|
-
|
|
14827
|
-
|
|
14828
|
-
|
|
14829
|
-
{ detached: true, stdio: "ignore" }
|
|
14830
|
-
);
|
|
14831
|
-
stopProc.unref();
|
|
14832
|
-
} catch {
|
|
14833
|
-
}
|
|
14848
|
+
if (parsed.reuseExisting) {
|
|
14849
|
+
showError(
|
|
14850
|
+
`--reuse-existing set, but no local ${ctx.displayName} credentials were found at ${ctx.locator.hint}.`
|
|
14851
|
+
);
|
|
14852
|
+
process.exit(1);
|
|
14834
14853
|
}
|
|
14854
|
+
showInfo(
|
|
14855
|
+
`No local ${ctx.displayName} credentials found. Launching the sign-in \u2014 complete it in your browser, the CLI will detect the new token and finish automatically.`
|
|
14856
|
+
);
|
|
14857
|
+
console.log("");
|
|
14858
|
+
const captured = await captureFreshCredentials(ctx);
|
|
14859
|
+
console.log("");
|
|
14860
|
+
await uploadAndSucceed(ctx, paired, pluginId, captured);
|
|
14861
|
+
}
|
|
14862
|
+
async function captureFreshCredentials(ctx) {
|
|
14863
|
+
const isWin = ctx.runtime.os.id === "win32";
|
|
14864
|
+
const watcher = import_chokidar.default.watch(ctx.locator.watchPaths(), {
|
|
14865
|
+
persistent: true,
|
|
14866
|
+
ignoreInitial: false,
|
|
14867
|
+
awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 },
|
|
14868
|
+
// Windows-only: when a credential file's ancestor is missing,
|
|
14869
|
+
// chokidar walks up to the closest existing parent and starts
|
|
14870
|
+
// traversing it. On a default Windows shell that ancestor is
|
|
14871
|
+
// `C:\Users\<u>`, which contains legacy junctions whose ACL makes
|
|
14872
|
+
// `fs.watch` throw EPERM (#43). These flags are no-ops on macOS,
|
|
14873
|
+
// where the user has read access to their entire home.
|
|
14874
|
+
...isWin ? { followSymlinks: false, ignorePermissionErrors: true } : {}
|
|
14875
|
+
});
|
|
14876
|
+
watcher.on("error", () => {
|
|
14877
|
+
});
|
|
14878
|
+
let child = null;
|
|
14879
|
+
let keychainPoll = null;
|
|
14880
|
+
const cleanup = () => {
|
|
14881
|
+
void watcher.close();
|
|
14882
|
+
if (keychainPoll) clearInterval(keychainPoll);
|
|
14883
|
+
if (child && !child.killed) {
|
|
14884
|
+
try {
|
|
14885
|
+
child.kill("SIGTERM");
|
|
14886
|
+
} catch {
|
|
14887
|
+
}
|
|
14888
|
+
}
|
|
14889
|
+
};
|
|
14835
14890
|
try {
|
|
14836
|
-
const
|
|
14837
|
-
|
|
14838
|
-
|
|
14891
|
+
const token = await new Promise((resolve5, reject) => {
|
|
14892
|
+
let settled = false;
|
|
14893
|
+
const tryExtract = async () => {
|
|
14894
|
+
if (settled) return;
|
|
14895
|
+
const t2 = await ctx.locator.extract();
|
|
14896
|
+
if (t2 && !settled) {
|
|
14897
|
+
settled = true;
|
|
14898
|
+
resolve5(t2);
|
|
14899
|
+
}
|
|
14900
|
+
};
|
|
14901
|
+
watcher.on("add", () => void tryExtract());
|
|
14902
|
+
watcher.on("change", () => void tryExtract());
|
|
14903
|
+
keychainPoll = setInterval(() => void tryExtract(), 2e3);
|
|
14904
|
+
keychainPoll.unref?.();
|
|
14905
|
+
const sigint = () => {
|
|
14906
|
+
if (settled) return;
|
|
14907
|
+
settled = true;
|
|
14908
|
+
reject(new Error("cancelled"));
|
|
14909
|
+
};
|
|
14910
|
+
process.once("SIGINT", sigint);
|
|
14911
|
+
setTimeout(() => {
|
|
14912
|
+
if (settled) return;
|
|
14913
|
+
settled = true;
|
|
14914
|
+
reject(new Error(`Timed out waiting for ${ctx.displayName} sign-in (5 minutes).`));
|
|
14915
|
+
}, 5 * 6e4);
|
|
14916
|
+
child = ctx.launcher.launch();
|
|
14917
|
+
child.on("exit", () => {
|
|
14918
|
+
void tryExtract().then(() => {
|
|
14919
|
+
if (!settled) {
|
|
14920
|
+
settled = true;
|
|
14921
|
+
reject(
|
|
14922
|
+
new Error(
|
|
14923
|
+
`${ctx.binary} exited but no credentials were written at ${ctx.locator.hint}.`
|
|
14924
|
+
)
|
|
14925
|
+
);
|
|
14926
|
+
}
|
|
14927
|
+
});
|
|
14928
|
+
});
|
|
14839
14929
|
});
|
|
14840
|
-
|
|
14841
|
-
|
|
14930
|
+
cleanup();
|
|
14931
|
+
return token;
|
|
14932
|
+
} catch (err) {
|
|
14933
|
+
cleanup();
|
|
14934
|
+
throw err;
|
|
14842
14935
|
}
|
|
14843
|
-
|
|
14844
|
-
|
|
14845
|
-
|
|
14846
|
-
|
|
14847
|
-
|
|
14848
|
-
if (!parsed.path) {
|
|
14849
|
-
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path" });
|
|
14850
|
-
return;
|
|
14936
|
+
}
|
|
14937
|
+
async function uploadAndSucceed(ctx, paired, pluginId, token) {
|
|
14938
|
+
if (!paired.pluginAuthToken) {
|
|
14939
|
+
showError("Missing pluginAuthToken; re-run codeam link.");
|
|
14940
|
+
process.exit(1);
|
|
14851
14941
|
}
|
|
14852
|
-
const
|
|
14853
|
-
|
|
14854
|
-
|
|
14855
|
-
|
|
14856
|
-
|
|
14857
|
-
|
|
14858
|
-
|
|
14942
|
+
const uploadSpin = dist_exports.spinner();
|
|
14943
|
+
uploadSpin.start("Sealing credential in your vault...");
|
|
14944
|
+
const result = await postLinkCredential({
|
|
14945
|
+
agentId: ctx.locator.publicId,
|
|
14946
|
+
sessionId: paired.sessionId,
|
|
14947
|
+
pluginId,
|
|
14948
|
+
pluginAuthToken: paired.pluginAuthToken,
|
|
14949
|
+
method: token.method,
|
|
14950
|
+
credential: token.credential
|
|
14951
|
+
});
|
|
14952
|
+
if (!result.ok) {
|
|
14953
|
+
uploadSpin.stop("Failed");
|
|
14954
|
+
if (result.status === 401) {
|
|
14955
|
+
showError("Pair token rejected by the backend (401). Re-run `codeam link`.");
|
|
14956
|
+
} else if (result.status === 404) {
|
|
14957
|
+
showError(
|
|
14958
|
+
`${ctx.displayName} link endpoint not available on this backend (404). The api-v2 deployment may not yet include the route.`
|
|
14959
|
+
);
|
|
14960
|
+
} else {
|
|
14961
|
+
showError(`Upload failed: ${result.message}`);
|
|
14962
|
+
}
|
|
14963
|
+
process.exit(1);
|
|
14859
14964
|
}
|
|
14860
|
-
|
|
14861
|
-
|
|
14862
|
-
};
|
|
14863
|
-
|
|
14864
|
-
|
|
14865
|
-
|
|
14866
|
-
|
|
14867
|
-
|
|
14868
|
-
|
|
14869
|
-
|
|
14870
|
-
|
|
14871
|
-
|
|
14965
|
+
uploadSpin.stop("Linked");
|
|
14966
|
+
console.log("");
|
|
14967
|
+
showSuccess(`${ctx.displayName} is now linked to ${paired.userEmail || paired.userName}.`);
|
|
14968
|
+
showInfo(
|
|
14969
|
+
`Your codespaces and @codeagent mentions can now use ${ctx.displayName} without you signing in again.`
|
|
14970
|
+
);
|
|
14971
|
+
console.log("");
|
|
14972
|
+
}
|
|
14973
|
+
async function linkDryRunPreflight(ctx) {
|
|
14974
|
+
const publicId = ctx.locator.publicId;
|
|
14975
|
+
const spin = dist_exports.spinner();
|
|
14976
|
+
spin.start(`Probing ${publicId} link endpoint...`);
|
|
14977
|
+
const result = await postLinkCredential({
|
|
14978
|
+
agentId: publicId,
|
|
14979
|
+
sessionId: "dryrun-session",
|
|
14980
|
+
pluginId: "dryrun-plugin",
|
|
14981
|
+
pluginAuthToken: "dryrun-token",
|
|
14982
|
+
method: "oauth",
|
|
14983
|
+
credential: "dryrun-credential"
|
|
14872
14984
|
});
|
|
14873
|
-
if (
|
|
14874
|
-
|
|
14875
|
-
|
|
14985
|
+
if (result.ok) {
|
|
14986
|
+
spin.stop("Unexpected 2xx");
|
|
14987
|
+
showError(
|
|
14988
|
+
"Link dry-run: backend accepted a stub credential (2xx). PluginAuthGuard appears to be disabled \u2014 investigate api-v2."
|
|
14989
|
+
);
|
|
14990
|
+
process.exit(1);
|
|
14876
14991
|
}
|
|
14877
|
-
|
|
14878
|
-
|
|
14879
|
-
|
|
14880
|
-
|
|
14881
|
-
|
|
14882
|
-
|
|
14992
|
+
if (result.status === 401) {
|
|
14993
|
+
spin.stop("Endpoint OK");
|
|
14994
|
+
showSuccess(
|
|
14995
|
+
`Link dry-run OK \u2014 /api/plugin/agents/${publicId}/link reachable and auth-gated (401 as expected).`
|
|
14996
|
+
);
|
|
14997
|
+
process.exit(0);
|
|
14883
14998
|
}
|
|
14884
|
-
|
|
14885
|
-
|
|
14886
|
-
}
|
|
14887
|
-
|
|
14888
|
-
|
|
14889
|
-
|
|
14999
|
+
spin.stop("Failed");
|
|
15000
|
+
showError(
|
|
15001
|
+
`Link dry-run: unexpected response from /api/plugin/agents/${publicId}/link (status=${result.status}, message=${result.message}). Expected 401.`
|
|
15002
|
+
);
|
|
15003
|
+
process.exit(1);
|
|
15004
|
+
}
|
|
15005
|
+
|
|
15006
|
+
// src/commands/start/handlers.ts
|
|
15007
|
+
var pendingAttachmentFiles = /* @__PURE__ */ new Set();
|
|
15008
|
+
function cleanupAttachmentTempFiles() {
|
|
15009
|
+
for (const p2 of pendingAttachmentFiles) {
|
|
15010
|
+
try {
|
|
15011
|
+
fs27.unlinkSync(p2);
|
|
15012
|
+
} catch {
|
|
15013
|
+
}
|
|
15014
|
+
}
|
|
15015
|
+
pendingAttachmentFiles.clear();
|
|
15016
|
+
}
|
|
15017
|
+
function saveFilesTemp(files) {
|
|
15018
|
+
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
15019
|
+
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
15020
|
+
const tmpPath = path33.join(os23.tmpdir(), `codeam-${(0, import_crypto5.randomUUID)()}-${safeName}`);
|
|
15021
|
+
fs27.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
15022
|
+
pendingAttachmentFiles.add(tmpPath);
|
|
15023
|
+
return tmpPath;
|
|
15024
|
+
});
|
|
15025
|
+
}
|
|
15026
|
+
function dispatchPrompt(ctx, prompt) {
|
|
15027
|
+
ctx.outputSvc.newTurn();
|
|
15028
|
+
ctx.agent.sendCommand(prompt);
|
|
15029
|
+
}
|
|
15030
|
+
var startTask = (ctx, _cmd, parsed) => {
|
|
15031
|
+
const { prompt, files } = parsed;
|
|
15032
|
+
const effectivePrompt = prompt ?? "";
|
|
15033
|
+
if (files && files.length > 0) {
|
|
15034
|
+
const paths = saveFilesTemp(files);
|
|
15035
|
+
const atRefs = paths.map((p2) => `@${p2}`).join(" ");
|
|
15036
|
+
ctx.outputSvc.newTurn();
|
|
15037
|
+
ctx.agent.sendCommand(`${atRefs} ${effectivePrompt}`.trim());
|
|
15038
|
+
setTimeout(() => {
|
|
15039
|
+
for (const p2 of paths) {
|
|
15040
|
+
try {
|
|
15041
|
+
fs27.unlinkSync(p2);
|
|
15042
|
+
} catch {
|
|
15043
|
+
}
|
|
15044
|
+
pendingAttachmentFiles.delete(p2);
|
|
15045
|
+
}
|
|
15046
|
+
}, 12e4);
|
|
15047
|
+
} else if (effectivePrompt) {
|
|
15048
|
+
dispatchPrompt(ctx, effectivePrompt);
|
|
15049
|
+
}
|
|
15050
|
+
};
|
|
15051
|
+
var provideInput = (ctx, _cmd, parsed) => {
|
|
15052
|
+
if (parsed.input) dispatchPrompt(ctx, parsed.input);
|
|
15053
|
+
};
|
|
15054
|
+
var selectOption = (ctx, _cmd, parsed) => {
|
|
15055
|
+
const index = parsed.index ?? 0;
|
|
15056
|
+
const from = parsed.from ?? 0;
|
|
15057
|
+
ctx.outputSvc.newTurn();
|
|
15058
|
+
ctx.agent.selectOption(index, from);
|
|
15059
|
+
};
|
|
15060
|
+
var escapeKey = (ctx) => {
|
|
15061
|
+
ctx.outputSvc.newTurn();
|
|
15062
|
+
ctx.agent.sendEscape();
|
|
15063
|
+
};
|
|
15064
|
+
var stopTask = (ctx) => {
|
|
15065
|
+
ctx.agent.interrupt();
|
|
15066
|
+
};
|
|
15067
|
+
var resumeSession = async (ctx, _cmd, parsed) => {
|
|
15068
|
+
const { id, auto } = parsed;
|
|
15069
|
+
if (!id) return;
|
|
15070
|
+
ctx.historySvc.setCurrentConversationId(id);
|
|
15071
|
+
await ctx.historySvc.loadConversation(id);
|
|
15072
|
+
await ctx.outputSvc.newTurnResume(id);
|
|
15073
|
+
ctx.agent.restart(id, auto ?? false);
|
|
15074
|
+
};
|
|
15075
|
+
var getContext = async (ctx, cmd) => {
|
|
15076
|
+
const usage = ctx.historySvc.getCurrentUsage();
|
|
15077
|
+
const monthlyCost = ctx.historySvc.getMonthlyEstimatedCost();
|
|
15078
|
+
const rateLimitReset = ctx.historySvc.getRateLimitReset();
|
|
15079
|
+
const quotaPercent = ctx.historySvc.getQuotaPercent();
|
|
15080
|
+
const base = usage ? { ...usage, monthlyCost } : { used: 0, total: 2e5, percent: 0, model: null, outputTokens: 0, cacheReadTokens: 0, monthlyCost, error: "No usage data found" };
|
|
15081
|
+
const result = {
|
|
15082
|
+
...base,
|
|
15083
|
+
...rateLimitReset ? { rateLimitReset } : {},
|
|
15084
|
+
...quotaPercent !== null ? { quotaPercent } : {}
|
|
15085
|
+
};
|
|
15086
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
15087
|
+
};
|
|
15088
|
+
var getConversation = async (ctx, cmd) => {
|
|
15089
|
+
const currentId = ctx.historySvc.getCurrentConversationId();
|
|
15090
|
+
if (!currentId) {
|
|
15091
|
+
await ctx.relay.sendResult(cmd.id, "completed", { conversationId: null });
|
|
15092
|
+
return;
|
|
15093
|
+
}
|
|
15094
|
+
try {
|
|
15095
|
+
await ctx.historySvc.loadConversation(currentId);
|
|
15096
|
+
await ctx.relay.sendResult(cmd.id, "completed", { conversationId: currentId });
|
|
15097
|
+
} catch {
|
|
15098
|
+
await ctx.relay.sendResult(cmd.id, "failed", {});
|
|
15099
|
+
}
|
|
15100
|
+
};
|
|
15101
|
+
var listModels = async (ctx, cmd) => {
|
|
15102
|
+
const models = await ctx.runtime.listModels();
|
|
15103
|
+
await ctx.relay.sendResult(cmd.id, "completed", { models });
|
|
15104
|
+
};
|
|
15105
|
+
var changeModel = async (ctx, cmd) => {
|
|
15106
|
+
const params = cmd.payload;
|
|
15107
|
+
if (typeof params.modelId !== "string" || !params.modelId) {
|
|
15108
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "modelId required" });
|
|
15109
|
+
return;
|
|
15110
|
+
}
|
|
15111
|
+
const instr = ctx.runtime.changeModelInstruction(params.modelId);
|
|
15112
|
+
if (instr.type === "pty") {
|
|
15113
|
+
if (!instr.ptyInput) {
|
|
15114
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "no pty input for this agent" });
|
|
15115
|
+
return;
|
|
15116
|
+
}
|
|
15117
|
+
ctx.agent.sendRawPtyInput(instr.ptyInput);
|
|
15118
|
+
} else if (instr.type === "restart") {
|
|
15119
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "restart-mode change_model not supported in Phase 1" });
|
|
15120
|
+
return;
|
|
15121
|
+
}
|
|
15122
|
+
await ctx.relay.sendResult(cmd.id, "completed", {});
|
|
15123
|
+
};
|
|
15124
|
+
var summarize = async (ctx, cmd) => {
|
|
15125
|
+
const params = cmd.payload;
|
|
15126
|
+
const mode = params.mode === "auto" ? "auto" : "normal";
|
|
15127
|
+
const instr = ctx.runtime.summarizeInstruction(mode);
|
|
15128
|
+
ctx.agent.sendRawPtyInput(instr.ptyInput);
|
|
15129
|
+
await ctx.relay.sendResult(cmd.id, "completed", {});
|
|
15130
|
+
};
|
|
15131
|
+
var setKeepAlive = async (ctx, cmd) => {
|
|
15132
|
+
const enabled = !!cmd.payload.enabled;
|
|
15133
|
+
ctx.setKeepAlive(enabled);
|
|
15134
|
+
try {
|
|
15135
|
+
await ctx.relay.sendResult(
|
|
15136
|
+
cmd.id,
|
|
15137
|
+
"success",
|
|
15138
|
+
{
|
|
15139
|
+
enabled,
|
|
15140
|
+
applied: enabled && ctx.keepAliveCtx.inCodespace,
|
|
15141
|
+
runtime: ctx.keepAliveCtx.inCodespace ? "github-codespaces" : "local"
|
|
15142
|
+
}
|
|
15143
|
+
);
|
|
15144
|
+
} catch {
|
|
15145
|
+
}
|
|
15146
|
+
};
|
|
15147
|
+
var sessionTerminated = (ctx) => {
|
|
15148
|
+
showInfo("Session was deleted from the app \u2014 exiting.");
|
|
15149
|
+
try {
|
|
15150
|
+
ctx.agent.kill();
|
|
15151
|
+
} catch {
|
|
15152
|
+
}
|
|
15153
|
+
try {
|
|
15154
|
+
const proc = (0, import_child_process13.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
15155
|
+
detached: true,
|
|
15156
|
+
stdio: "ignore"
|
|
15157
|
+
});
|
|
15158
|
+
proc.unref();
|
|
15159
|
+
} catch {
|
|
15160
|
+
}
|
|
15161
|
+
ctx.outputSvc.dispose();
|
|
15162
|
+
ctx.relay.stop();
|
|
15163
|
+
process.exit(0);
|
|
15164
|
+
};
|
|
15165
|
+
var shutdownSession = async (ctx, cmd) => {
|
|
15166
|
+
try {
|
|
15167
|
+
await ctx.relay.sendResult(cmd.id, "success", { ok: true });
|
|
15168
|
+
} catch {
|
|
15169
|
+
}
|
|
15170
|
+
try {
|
|
15171
|
+
ctx.agent.kill();
|
|
15172
|
+
} catch {
|
|
15173
|
+
}
|
|
15174
|
+
if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
|
|
15175
|
+
try {
|
|
15176
|
+
const stopProc = (0, import_child_process13.spawn)(
|
|
15177
|
+
"bash",
|
|
15178
|
+
["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
|
|
15179
|
+
{ detached: true, stdio: "ignore" }
|
|
15180
|
+
);
|
|
15181
|
+
stopProc.unref();
|
|
15182
|
+
} catch {
|
|
15183
|
+
}
|
|
15184
|
+
}
|
|
15185
|
+
try {
|
|
15186
|
+
const proc = (0, import_child_process13.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
15187
|
+
detached: true,
|
|
15188
|
+
stdio: "ignore"
|
|
15189
|
+
});
|
|
15190
|
+
proc.unref();
|
|
15191
|
+
} catch {
|
|
15192
|
+
}
|
|
15193
|
+
ctx.outputSvc.dispose();
|
|
15194
|
+
ctx.relay.stop();
|
|
15195
|
+
process.exit(0);
|
|
15196
|
+
};
|
|
15197
|
+
var readFile4 = async (ctx, cmd, parsed) => {
|
|
15198
|
+
if (!parsed.path) {
|
|
15199
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path" });
|
|
15200
|
+
return;
|
|
15201
|
+
}
|
|
15202
|
+
const result = await readProjectFile(parsed.path);
|
|
15203
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
15204
|
+
};
|
|
15205
|
+
var writeFile3 = async (ctx, cmd, parsed) => {
|
|
15206
|
+
if (!parsed.path || typeof parsed.content !== "string") {
|
|
15207
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path or content" });
|
|
15208
|
+
return;
|
|
15209
|
+
}
|
|
15210
|
+
const result = await writeProjectFile(parsed.path, parsed.content);
|
|
15211
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
15212
|
+
};
|
|
15213
|
+
var listFiles = async (ctx, cmd, parsed) => {
|
|
15214
|
+
const result = await listProjectFiles({ query: parsed.query });
|
|
15215
|
+
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
15216
|
+
};
|
|
15217
|
+
var terminalOpenH = async (ctx, cmd, parsed) => {
|
|
15218
|
+
const r = openTerminal({
|
|
15219
|
+
cols: typeof parsed.cols === "number" ? parsed.cols : void 0,
|
|
15220
|
+
rows: typeof parsed.rows === "number" ? parsed.rows : void 0,
|
|
15221
|
+
cwd: typeof parsed.cwd === "string" ? parsed.cwd : void 0
|
|
15222
|
+
});
|
|
15223
|
+
if ("error" in r) {
|
|
15224
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: r.error });
|
|
15225
|
+
return;
|
|
15226
|
+
}
|
|
15227
|
+
await ctx.relay.sendResult(cmd.id, "completed", r);
|
|
15228
|
+
};
|
|
15229
|
+
var terminalWriteH = async (ctx, cmd, parsed) => {
|
|
15230
|
+
if (typeof parsed.sessionId !== "string" || typeof parsed.data !== "string") {
|
|
15231
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing sessionId or data" });
|
|
15232
|
+
return;
|
|
15233
|
+
}
|
|
15234
|
+
const r = writeTerminal(parsed.sessionId, parsed.data);
|
|
15235
|
+
await ctx.relay.sendResult(cmd.id, r.ok ? "completed" : "failed", r);
|
|
15236
|
+
};
|
|
15237
|
+
var terminalResizeH = async (ctx, cmd, parsed) => {
|
|
15238
|
+
if (typeof parsed.sessionId !== "string" || typeof parsed.cols !== "number" || typeof parsed.rows !== "number") {
|
|
15239
|
+
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing sessionId / cols / rows" });
|
|
14890
15240
|
return;
|
|
14891
15241
|
}
|
|
14892
15242
|
const r = resizeTerminal(parsed.sessionId, parsed.cols, parsed.rows);
|
|
@@ -14974,25 +15324,66 @@ var applyFileReviewH = async (ctx, cmd, parsed) => {
|
|
|
14974
15324
|
result
|
|
14975
15325
|
);
|
|
14976
15326
|
};
|
|
14977
|
-
var
|
|
14978
|
-
|
|
14979
|
-
|
|
14980
|
-
|
|
14981
|
-
|
|
14982
|
-
|
|
14983
|
-
|
|
14984
|
-
|
|
14985
|
-
|
|
14986
|
-
|
|
14987
|
-
|
|
14988
|
-
|
|
14989
|
-
|
|
14990
|
-
|
|
14991
|
-
|
|
14992
|
-
|
|
14993
|
-
|
|
14994
|
-
|
|
14995
|
-
|
|
15327
|
+
var requestLinkCredentialsH = async (ctx, _cmd, parsed) => {
|
|
15328
|
+
const publicId = parsed.agentId;
|
|
15329
|
+
if (!publicId) return;
|
|
15330
|
+
if (!ctx.pluginAuthToken) {
|
|
15331
|
+
log.trace("auto-link", "skipped \u2014 no pluginAuthToken on this paired session");
|
|
15332
|
+
return;
|
|
15333
|
+
}
|
|
15334
|
+
const internalId = publicId === "claude_code" ? "claude" : publicId;
|
|
15335
|
+
if (!isKnownAgentId(internalId) || !AGENT_REGISTRY[internalId].enabled) {
|
|
15336
|
+
log.trace("auto-link", `unknown / disabled agent: ${internalId}`);
|
|
15337
|
+
return;
|
|
15338
|
+
}
|
|
15339
|
+
let linkCtx;
|
|
15340
|
+
try {
|
|
15341
|
+
linkCtx = buildLinkContext(internalId);
|
|
15342
|
+
} catch (err) {
|
|
15343
|
+
log.trace("auto-link", "buildLinkContext threw", err);
|
|
15344
|
+
return;
|
|
15345
|
+
}
|
|
15346
|
+
const token = await linkCtx.locator.extract().catch((err) => {
|
|
15347
|
+
log.trace("auto-link", `locator.extract failed for ${publicId}`, err);
|
|
15348
|
+
return null;
|
|
15349
|
+
});
|
|
15350
|
+
if (!token) {
|
|
15351
|
+
log.trace("auto-link", `no local ${linkCtx.displayName} credentials \u2014 skipping`);
|
|
15352
|
+
return;
|
|
15353
|
+
}
|
|
15354
|
+
const result = await postLinkCredential({
|
|
15355
|
+
agentId: publicId,
|
|
15356
|
+
sessionId: ctx.sessionId,
|
|
15357
|
+
pluginId: ctx.pluginId,
|
|
15358
|
+
pluginAuthToken: ctx.pluginAuthToken,
|
|
15359
|
+
method: token.method,
|
|
15360
|
+
credential: token.credential
|
|
15361
|
+
});
|
|
15362
|
+
if (result.ok) {
|
|
15363
|
+
log.trace("auto-link", `vaulted ${publicId} from ${token.source}`);
|
|
15364
|
+
} else {
|
|
15365
|
+
log.trace("auto-link", `upload failed (${result.status}): ${result.message}`);
|
|
15366
|
+
}
|
|
15367
|
+
};
|
|
15368
|
+
var handlers = {
|
|
15369
|
+
start_task: startTask,
|
|
15370
|
+
provide_input: provideInput,
|
|
15371
|
+
select_option: selectOption,
|
|
15372
|
+
escape_key: escapeKey,
|
|
15373
|
+
stop_task: stopTask,
|
|
15374
|
+
resume_session: resumeSession,
|
|
15375
|
+
get_context: getContext,
|
|
15376
|
+
get_conversation: getConversation,
|
|
15377
|
+
list_models: listModels,
|
|
15378
|
+
change_model: changeModel,
|
|
15379
|
+
summarize,
|
|
15380
|
+
set_keep_alive: setKeepAlive,
|
|
15381
|
+
session_terminated: sessionTerminated,
|
|
15382
|
+
shutdown_session: shutdownSession,
|
|
15383
|
+
read_file: readFile4,
|
|
15384
|
+
write_file: writeFile3,
|
|
15385
|
+
list_files: listFiles,
|
|
15386
|
+
search_files: searchFilesH,
|
|
14996
15387
|
terminal_open: terminalOpenH,
|
|
14997
15388
|
terminal_write: terminalWriteH,
|
|
14998
15389
|
terminal_resize: terminalResizeH,
|
|
@@ -15005,7 +15396,8 @@ var handlers = {
|
|
|
15005
15396
|
git_push: gitPushH,
|
|
15006
15397
|
git_pull: gitPullH,
|
|
15007
15398
|
git_resolve: gitResolveH,
|
|
15008
|
-
apply_file_review: applyFileReviewH
|
|
15399
|
+
apply_file_review: applyFileReviewH,
|
|
15400
|
+
request_link_credentials: requestLinkCredentialsH
|
|
15009
15401
|
};
|
|
15010
15402
|
async function dispatchCommand(ctx, cmd) {
|
|
15011
15403
|
const parsed = parsePayload2(startCommandSchema, cmd.payload);
|
|
@@ -15025,14 +15417,14 @@ async function start(requestedAgent) {
|
|
|
15025
15417
|
if (!session) {
|
|
15026
15418
|
if (requestedAgent) {
|
|
15027
15419
|
const displayName = AGENT_REGISTRY[requestedAgent]?.displayName ?? requestedAgent;
|
|
15028
|
-
console.log(` ${
|
|
15420
|
+
console.log(` ${import_picocolors3.default.dim(`No paired ${displayName} session found.`)}`);
|
|
15029
15421
|
console.log(
|
|
15030
|
-
` ${
|
|
15422
|
+
` ${import_picocolors3.default.dim(`Run ${import_picocolors3.default.white("codeam pair")} from a ${displayName} setup to connect your mobile app.`)}
|
|
15031
15423
|
`
|
|
15032
15424
|
);
|
|
15033
15425
|
} else {
|
|
15034
|
-
console.log(` ${
|
|
15035
|
-
console.log(` ${
|
|
15426
|
+
console.log(` ${import_picocolors3.default.dim("No paired session found.")}`);
|
|
15427
|
+
console.log(` ${import_picocolors3.default.dim(`Run ${import_picocolors3.default.white("codeam pair")} to connect your mobile app.`)}
|
|
15036
15428
|
`);
|
|
15037
15429
|
}
|
|
15038
15430
|
process.exit(0);
|
|
@@ -15041,7 +15433,7 @@ async function start(requestedAgent) {
|
|
|
15041
15433
|
throw new Error("Active session has no agent \u2014 re-pair with `codeam pair`.");
|
|
15042
15434
|
}
|
|
15043
15435
|
const pluginId = session.pluginId ?? ensurePluginId();
|
|
15044
|
-
showInfo(`${session.userName} \xB7 ${
|
|
15436
|
+
showInfo(`${session.userName} \xB7 ${import_picocolors3.default.cyan(session.plan)}`);
|
|
15045
15437
|
showInfo(`Launching ${AGENT_REGISTRY[session.agent].displayName}...
|
|
15046
15438
|
`);
|
|
15047
15439
|
identifyUser({
|
|
@@ -15142,7 +15534,10 @@ async function start(requestedAgent) {
|
|
|
15142
15534
|
runtime,
|
|
15143
15535
|
relay: void 0,
|
|
15144
15536
|
setKeepAlive: setKeepAlive2,
|
|
15145
|
-
keepAliveCtx
|
|
15537
|
+
keepAliveCtx,
|
|
15538
|
+
pluginId,
|
|
15539
|
+
sessionId: session.id,
|
|
15540
|
+
pluginAuthToken: session.pluginAuthToken ?? void 0
|
|
15146
15541
|
};
|
|
15147
15542
|
const relay = new CommandRelayService(pluginId, async (cmd) => {
|
|
15148
15543
|
await dispatchCommand(ctx, cmd);
|
|
@@ -15187,27 +15582,7 @@ async function start(requestedAgent) {
|
|
|
15187
15582
|
|
|
15188
15583
|
// src/commands/pair.ts
|
|
15189
15584
|
var import_crypto6 = require("crypto");
|
|
15190
|
-
var
|
|
15191
|
-
|
|
15192
|
-
// src/ui/prompts.ts
|
|
15193
|
-
async function confirmAction(message) {
|
|
15194
|
-
const result = await ot2({ message });
|
|
15195
|
-
if (q(result)) return false;
|
|
15196
|
-
return result;
|
|
15197
|
-
}
|
|
15198
|
-
async function selectSession(sessions3, activeId) {
|
|
15199
|
-
const result = await _t({
|
|
15200
|
-
message: "Select active session:",
|
|
15201
|
-
options: sessions3.map((s) => ({
|
|
15202
|
-
value: s.id,
|
|
15203
|
-
label: `${s.userName} ${s.plan}`,
|
|
15204
|
-
hint: s.id === activeId ? "active" : `paired ${new Date(s.pairedAt).toLocaleDateString()}`
|
|
15205
|
-
})),
|
|
15206
|
-
initialValue: activeId ?? void 0
|
|
15207
|
-
});
|
|
15208
|
-
if (q(result)) return null;
|
|
15209
|
-
return result;
|
|
15210
|
-
}
|
|
15585
|
+
var import_picocolors4 = __toESM(require("picocolors"));
|
|
15211
15586
|
|
|
15212
15587
|
// src/utils/agent-prompt.ts
|
|
15213
15588
|
function parseAgentFlag(args2) {
|
|
@@ -15276,7 +15651,7 @@ async function pair(args2 = []) {
|
|
|
15276
15651
|
process.exit(0);
|
|
15277
15652
|
}
|
|
15278
15653
|
showPairingCode(result.code);
|
|
15279
|
-
console.log(
|
|
15654
|
+
console.log(import_picocolors4.default.dim(" Scan the QR code or enter the code in CodeAgent Mobile."));
|
|
15280
15655
|
console.log("");
|
|
15281
15656
|
const waitSpin = dist_exports.spinner();
|
|
15282
15657
|
const waitMessage = () => `Waiting for mobile app... \xB7 expires in ${formatRemaining(result.expiresAt)}`;
|
|
@@ -15341,7 +15716,7 @@ async function pair(args2 = []) {
|
|
|
15341
15716
|
}
|
|
15342
15717
|
|
|
15343
15718
|
// src/commands/pair-auto.ts
|
|
15344
|
-
var
|
|
15719
|
+
var fs28 = __toESM(require("fs"));
|
|
15345
15720
|
var os24 = __toESM(require("os"));
|
|
15346
15721
|
var import_crypto7 = require("crypto");
|
|
15347
15722
|
var API_BASE8 = resolveApiBaseUrl();
|
|
@@ -15361,10 +15736,10 @@ function readTokenFromArgs(args2) {
|
|
|
15361
15736
|
if (fileFlag) {
|
|
15362
15737
|
const path40 = fileFlag.slice("--token-file=".length);
|
|
15363
15738
|
try {
|
|
15364
|
-
const content =
|
|
15739
|
+
const content = fs28.readFileSync(path40, "utf8").trim();
|
|
15365
15740
|
if (content.length === 0) fail(`--token-file ${path40} is empty`);
|
|
15366
15741
|
try {
|
|
15367
|
-
|
|
15742
|
+
fs28.unlinkSync(path40);
|
|
15368
15743
|
} catch {
|
|
15369
15744
|
}
|
|
15370
15745
|
return content;
|
|
@@ -15487,7 +15862,7 @@ async function pairAuto(args2) {
|
|
|
15487
15862
|
}
|
|
15488
15863
|
|
|
15489
15864
|
// src/commands/sessions.ts
|
|
15490
|
-
var
|
|
15865
|
+
var import_picocolors5 = __toESM(require("picocolors"));
|
|
15491
15866
|
async function sessions2(args2) {
|
|
15492
15867
|
const [sub, id] = args2;
|
|
15493
15868
|
if (sub === "switch") return switchSession();
|
|
@@ -15504,18 +15879,18 @@ function listSessions() {
|
|
|
15504
15879
|
showIntro();
|
|
15505
15880
|
const config = getConfig();
|
|
15506
15881
|
if (config.sessions.length === 0) {
|
|
15507
|
-
console.log(
|
|
15882
|
+
console.log(import_picocolors5.default.dim(" No paired sessions. Run codeam pair to connect.\n"));
|
|
15508
15883
|
return;
|
|
15509
15884
|
}
|
|
15510
|
-
console.log(
|
|
15885
|
+
console.log(import_picocolors5.default.bold(" Paired sessions:\n"));
|
|
15511
15886
|
for (const s of config.sessions) {
|
|
15512
15887
|
const isActive = s.id === config.activeSessionId;
|
|
15513
|
-
const bullet = isActive ?
|
|
15514
|
-
const name = isActive ?
|
|
15515
|
-
const plan =
|
|
15516
|
-
const date =
|
|
15888
|
+
const bullet = isActive ? import_picocolors5.default.green(" \u25CF") : import_picocolors5.default.dim(" \u25CB");
|
|
15889
|
+
const name = isActive ? import_picocolors5.default.bold(s.userName) : s.userName;
|
|
15890
|
+
const plan = import_picocolors5.default.cyan(s.plan);
|
|
15891
|
+
const date = import_picocolors5.default.dim(new Date(s.pairedAt).toLocaleDateString());
|
|
15517
15892
|
console.log(`${bullet} ${name} ${plan} ${date}`);
|
|
15518
|
-
console.log(
|
|
15893
|
+
console.log(import_picocolors5.default.dim(` ${s.id}`));
|
|
15519
15894
|
}
|
|
15520
15895
|
console.log("");
|
|
15521
15896
|
}
|
|
@@ -15533,7 +15908,7 @@ async function switchSession() {
|
|
|
15533
15908
|
}
|
|
15534
15909
|
setActiveSession(chosen);
|
|
15535
15910
|
const s = config.sessions.find((x) => x.id === chosen);
|
|
15536
|
-
console.log(
|
|
15911
|
+
console.log(import_picocolors5.default.green(`
|
|
15537
15912
|
\u2713 Switched to ${s?.userName ?? chosen}
|
|
15538
15913
|
`));
|
|
15539
15914
|
}
|
|
@@ -15551,29 +15926,29 @@ async function deleteSession(id) {
|
|
|
15551
15926
|
return;
|
|
15552
15927
|
}
|
|
15553
15928
|
removeSession(id);
|
|
15554
|
-
console.log(
|
|
15929
|
+
console.log(import_picocolors5.default.green("\n \u2713 Session deleted\n"));
|
|
15555
15930
|
}
|
|
15556
15931
|
|
|
15557
15932
|
// src/commands/status.ts
|
|
15558
|
-
var
|
|
15933
|
+
var import_picocolors6 = __toESM(require("picocolors"));
|
|
15559
15934
|
function status() {
|
|
15560
15935
|
showIntro();
|
|
15561
15936
|
const config = getConfig();
|
|
15562
15937
|
const active = config.sessions.find((s) => s.id === config.activeSessionId) ?? null;
|
|
15563
|
-
console.log(
|
|
15564
|
-
console.log(` Plugin ID ${
|
|
15938
|
+
console.log(import_picocolors6.default.bold(" Status\n"));
|
|
15939
|
+
console.log(` Plugin ID ${import_picocolors6.default.dim(config.pluginId || "not generated yet")}`);
|
|
15565
15940
|
console.log(` Sessions ${config.sessions.length} paired`);
|
|
15566
15941
|
if (active) {
|
|
15567
|
-
console.log(` Active ${
|
|
15568
|
-
console.log(` Session ID ${
|
|
15942
|
+
console.log(` Active ${import_picocolors6.default.bold(active.userName)} ${import_picocolors6.default.cyan(active.plan)}`);
|
|
15943
|
+
console.log(` Session ID ${import_picocolors6.default.dim(active.id)}`);
|
|
15569
15944
|
} else {
|
|
15570
|
-
console.log(` Active ${
|
|
15945
|
+
console.log(` Active ${import_picocolors6.default.yellow("none")} ${import_picocolors6.default.dim("run codeam pair to connect")}`);
|
|
15571
15946
|
}
|
|
15572
15947
|
console.log("");
|
|
15573
15948
|
}
|
|
15574
15949
|
|
|
15575
15950
|
// src/commands/logout.ts
|
|
15576
|
-
var
|
|
15951
|
+
var import_picocolors7 = __toESM(require("picocolors"));
|
|
15577
15952
|
var API_BASE9 = resolveApiBaseUrl();
|
|
15578
15953
|
async function notifyBackendOffline() {
|
|
15579
15954
|
const cfg = loadCliConfig();
|
|
@@ -15607,17 +15982,17 @@ async function logout() {
|
|
|
15607
15982
|
}
|
|
15608
15983
|
await notifyBackendOffline();
|
|
15609
15984
|
clearAll();
|
|
15610
|
-
console.log(
|
|
15985
|
+
console.log(import_picocolors7.default.green("\n \u2713 Done. All sessions removed.\n"));
|
|
15611
15986
|
}
|
|
15612
15987
|
|
|
15613
15988
|
// src/commands/deploy.ts
|
|
15614
|
-
var
|
|
15989
|
+
var import_picocolors10 = __toESM(require("picocolors"));
|
|
15615
15990
|
|
|
15616
15991
|
// src/services/providers/github-codespaces.ts
|
|
15617
15992
|
var import_child_process14 = require("child_process");
|
|
15618
15993
|
var import_util3 = require("util");
|
|
15619
|
-
var
|
|
15620
|
-
var
|
|
15994
|
+
var import_picocolors8 = __toESM(require("picocolors"));
|
|
15995
|
+
var path34 = __toESM(require("path"));
|
|
15621
15996
|
var execFileP4 = (0, import_util3.promisify)(import_child_process14.execFile);
|
|
15622
15997
|
var MAX_BUFFER = 8 * 1024 * 1024;
|
|
15623
15998
|
function resetStdinForChild() {
|
|
@@ -15684,7 +16059,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15684
16059
|
if (expectedUser) {
|
|
15685
16060
|
noteLines.push("");
|
|
15686
16061
|
noteLines.push(
|
|
15687
|
-
`${
|
|
16062
|
+
`${import_picocolors8.default.yellow("\u26A0")} Sign in as ${import_picocolors8.default.cyan(expectedUser)} in the browser.`
|
|
15688
16063
|
);
|
|
15689
16064
|
noteLines.push(
|
|
15690
16065
|
" If a different GitHub account is already signed in, sign out"
|
|
@@ -15707,7 +16082,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15707
16082
|
if (refreshCode !== 0) {
|
|
15708
16083
|
const lines = [
|
|
15709
16084
|
"The browser approval came back for a different GitHub account",
|
|
15710
|
-
`than the one gh is configured for${expectedUser ? ` (${
|
|
16085
|
+
`than the one gh is configured for${expectedUser ? ` (${import_picocolors8.default.cyan(expectedUser)})` : ""}.`,
|
|
15711
16086
|
"",
|
|
15712
16087
|
"To recover:",
|
|
15713
16088
|
" 1. Open https://github.com and sign out of any non-target",
|
|
@@ -15716,7 +16091,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15716
16091
|
"",
|
|
15717
16092
|
"You can also grant the scope manually first and skip this step",
|
|
15718
16093
|
"on the next run:",
|
|
15719
|
-
` ${
|
|
16094
|
+
` ${import_picocolors8.default.cyan("gh auth refresh -h github.com -s codespace")}`
|
|
15720
16095
|
];
|
|
15721
16096
|
throw new Error(lines.join("\n"));
|
|
15722
16097
|
}
|
|
@@ -15783,7 +16158,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15783
16158
|
async tryInstallGh() {
|
|
15784
16159
|
const platform2 = process.platform;
|
|
15785
16160
|
wt(
|
|
15786
|
-
`GitHub CLI (${
|
|
16161
|
+
`GitHub CLI (${import_picocolors8.default.cyan("gh")}) is required for Codespaces deploys but isn't on your PATH.`,
|
|
15787
16162
|
"Heads up"
|
|
15788
16163
|
);
|
|
15789
16164
|
if (platform2 === "linux") {
|
|
@@ -15839,7 +16214,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15839
16214
|
return;
|
|
15840
16215
|
}
|
|
15841
16216
|
const proceed = await ot2({
|
|
15842
|
-
message: `Run ${
|
|
16217
|
+
message: `Run ${import_picocolors8.default.cyan(installCmd.describe)} now?`,
|
|
15843
16218
|
initialValue: true
|
|
15844
16219
|
});
|
|
15845
16220
|
if (q(proceed) || !proceed) return;
|
|
@@ -16106,7 +16481,7 @@ var GitHubCodespacesProvider = class {
|
|
|
16106
16481
|
});
|
|
16107
16482
|
}
|
|
16108
16483
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
16109
|
-
const remoteDir =
|
|
16484
|
+
const remoteDir = path34.posix.dirname(remotePath);
|
|
16110
16485
|
const parts = [
|
|
16111
16486
|
`mkdir -p ${shellQuote(remoteDir)}`,
|
|
16112
16487
|
`cat > ${shellQuote(remotePath)}`
|
|
@@ -16176,8 +16551,8 @@ function shellQuote(s) {
|
|
|
16176
16551
|
// src/services/providers/gitpod.ts
|
|
16177
16552
|
var import_child_process15 = require("child_process");
|
|
16178
16553
|
var import_util4 = require("util");
|
|
16179
|
-
var
|
|
16180
|
-
var
|
|
16554
|
+
var path35 = __toESM(require("path"));
|
|
16555
|
+
var import_picocolors9 = __toESM(require("picocolors"));
|
|
16181
16556
|
var execFileP5 = (0, import_util4.promisify)(import_child_process15.execFile);
|
|
16182
16557
|
var MAX_BUFFER2 = 8 * 1024 * 1024;
|
|
16183
16558
|
function resetStdinForChild2() {
|
|
@@ -16416,7 +16791,7 @@ var GitpodProvider = class {
|
|
|
16416
16791
|
});
|
|
16417
16792
|
}
|
|
16418
16793
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
16419
|
-
const remoteDir =
|
|
16794
|
+
const remoteDir = path35.posix.dirname(remotePath);
|
|
16420
16795
|
const parts = [
|
|
16421
16796
|
`mkdir -p ${shellQuote2(remoteDir)}`,
|
|
16422
16797
|
`cat > ${shellQuote2(remotePath)}`
|
|
@@ -16452,7 +16827,7 @@ function shellQuote2(s) {
|
|
|
16452
16827
|
// src/services/providers/gitlab-workspaces.ts
|
|
16453
16828
|
var import_child_process16 = require("child_process");
|
|
16454
16829
|
var import_util5 = require("util");
|
|
16455
|
-
var
|
|
16830
|
+
var path36 = __toESM(require("path"));
|
|
16456
16831
|
var execFileP6 = (0, import_util5.promisify)(import_child_process16.execFile);
|
|
16457
16832
|
var MAX_BUFFER3 = 8 * 1024 * 1024;
|
|
16458
16833
|
var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
|
|
@@ -16712,7 +17087,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
16712
17087
|
}
|
|
16713
17088
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
16714
17089
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
16715
|
-
const remoteDir =
|
|
17090
|
+
const remoteDir = path36.posix.dirname(remotePath);
|
|
16716
17091
|
const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
|
|
16717
17092
|
if (options.mode != null) {
|
|
16718
17093
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
|
|
@@ -16780,7 +17155,7 @@ function shellQuote3(s) {
|
|
|
16780
17155
|
// src/services/providers/railway.ts
|
|
16781
17156
|
var import_child_process17 = require("child_process");
|
|
16782
17157
|
var import_util6 = require("util");
|
|
16783
|
-
var
|
|
17158
|
+
var path37 = __toESM(require("path"));
|
|
16784
17159
|
var execFileP7 = (0, import_util6.promisify)(import_child_process17.execFile);
|
|
16785
17160
|
var MAX_BUFFER4 = 8 * 1024 * 1024;
|
|
16786
17161
|
function resetStdinForChild4() {
|
|
@@ -17016,7 +17391,7 @@ var RailwayProvider = class {
|
|
|
17016
17391
|
if (!projectId || !serviceId) {
|
|
17017
17392
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
17018
17393
|
}
|
|
17019
|
-
const remoteDir =
|
|
17394
|
+
const remoteDir = path37.posix.dirname(remotePath);
|
|
17020
17395
|
const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
|
|
17021
17396
|
if (options.mode != null) {
|
|
17022
17397
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
|
|
@@ -17057,7 +17432,7 @@ var PROVIDERS = [
|
|
|
17057
17432
|
// src/commands/deploy.ts
|
|
17058
17433
|
async function deploy(args2 = []) {
|
|
17059
17434
|
console.log();
|
|
17060
|
-
mt(
|
|
17435
|
+
mt(import_picocolors10.default.bgMagenta(import_picocolors10.default.white(" codeam deploy ")));
|
|
17061
17436
|
const provider = await pickProvider();
|
|
17062
17437
|
if (!provider) {
|
|
17063
17438
|
pt("No provider selected.");
|
|
@@ -17094,7 +17469,7 @@ async function deploy(args2 = []) {
|
|
|
17094
17469
|
if (provider.expandListScopes) {
|
|
17095
17470
|
options.push({
|
|
17096
17471
|
value: EXPAND_SCOPES,
|
|
17097
|
-
label:
|
|
17472
|
+
label: import_picocolors10.default.cyan("+ Don't see your project? Expand scopes\u2026"),
|
|
17098
17473
|
hint: "Re-authorize with broader scopes (org / team repos)"
|
|
17099
17474
|
});
|
|
17100
17475
|
}
|
|
@@ -17142,7 +17517,7 @@ async function deploy(args2 = []) {
|
|
|
17142
17517
|
label: w3.displayName ?? w3.id,
|
|
17143
17518
|
hint: [w3.state, formatLastUsed(w3.lastUsedAt)].filter(Boolean).join(" \xB7 ")
|
|
17144
17519
|
})),
|
|
17145
|
-
{ value: "__new__", label:
|
|
17520
|
+
{ value: "__new__", label: import_picocolors10.default.green("+ Create a new workspace"), hint: "fresh codespace" }
|
|
17146
17521
|
]
|
|
17147
17522
|
});
|
|
17148
17523
|
if (q(choice)) {
|
|
@@ -17243,12 +17618,12 @@ async function deploy(args2 = []) {
|
|
|
17243
17618
|
cliStep.stop("\u2713 codeam-cli installed");
|
|
17244
17619
|
wt(
|
|
17245
17620
|
[
|
|
17246
|
-
`Workspace: ${
|
|
17247
|
-
workspace.webUrl ? `Web: ${
|
|
17621
|
+
`Workspace: ${import_picocolors10.default.cyan(workspace.displayName ?? workspace.id)}`,
|
|
17622
|
+
workspace.webUrl ? `Web: ${import_picocolors10.default.cyan(workspace.webUrl)}` : "",
|
|
17248
17623
|
"",
|
|
17249
17624
|
`Starting \`codeam pair\` on the workspace (agent: ${AGENT_REGISTRY[agentId].displayName}).`,
|
|
17250
17625
|
"Scan the QR code from your phone to pair.",
|
|
17251
|
-
|
|
17626
|
+
import_picocolors10.default.dim("(Once paired, this terminal disconnects automatically; the session stays alive on the codespace.)")
|
|
17252
17627
|
].filter(Boolean).join("\n"),
|
|
17253
17628
|
"Almost there"
|
|
17254
17629
|
);
|
|
@@ -17363,11 +17738,11 @@ async function deploy(args2 = []) {
|
|
|
17363
17738
|
].join("\n");
|
|
17364
17739
|
const code = (await provider.streamCommand(workspace.id, `bash -lc ${shellQuoteSingle(wrapper)}`)).code;
|
|
17365
17740
|
if (code === 0) {
|
|
17366
|
-
gt(
|
|
17741
|
+
gt(import_picocolors10.default.green("\u2713 Workspace deployed and paired. Drive from your phone, anywhere."));
|
|
17367
17742
|
} else if (code === 130) {
|
|
17368
|
-
gt(
|
|
17743
|
+
gt(import_picocolors10.default.yellow("Disconnected from local terminal. Mobile session keeps running on the codespace."));
|
|
17369
17744
|
} else {
|
|
17370
|
-
gt(
|
|
17745
|
+
gt(import_picocolors10.default.yellow('Pairing did not complete. Run "codeam pair" inside the codespace if needed.'));
|
|
17371
17746
|
}
|
|
17372
17747
|
}
|
|
17373
17748
|
function shellQuoteSingle(s) {
|
|
@@ -17401,7 +17776,7 @@ async function pickProvider() {
|
|
|
17401
17776
|
message: "Where do you want to deploy?",
|
|
17402
17777
|
options: PROVIDERS.map((prov) => ({
|
|
17403
17778
|
value: prov.id,
|
|
17404
|
-
label: prov.available ? prov.displayName : `${prov.displayName} ${
|
|
17779
|
+
label: prov.available ? prov.displayName : `${prov.displayName} ${import_picocolors10.default.dim("(coming soon)")}`,
|
|
17405
17780
|
hint: prov.tagline
|
|
17406
17781
|
}))
|
|
17407
17782
|
});
|
|
@@ -17415,464 +17790,144 @@ async function pickProvider() {
|
|
|
17415
17790
|
return null;
|
|
17416
17791
|
}
|
|
17417
17792
|
return found;
|
|
17418
|
-
}
|
|
17419
|
-
|
|
17420
|
-
// src/commands/deploy-manage.ts
|
|
17421
|
-
var
|
|
17422
|
-
async function deployList() {
|
|
17423
|
-
console.log();
|
|
17424
|
-
mt(
|
|
17425
|
-
const workspaces = await collectWorkspacesWithStatus();
|
|
17426
|
-
if (workspaces.length === 0) {
|
|
17427
|
-
gt(
|
|
17428
|
-
return;
|
|
17429
|
-
}
|
|
17430
|
-
for (const w3 of workspaces) {
|
|
17431
|
-
const tag = w3.codeamRunning ? import_picocolors10.default.green("\u25CF running") : w3.state === "Available" ? import_picocolors10.default.dim("\u25CB idle") : import_picocolors10.default.dim(`\u25CB ${w3.state ?? "stopped"}`);
|
|
17432
|
-
console.log(` ${tag} ${import_picocolors10.default.cyan(w3.displayName ?? w3.id)} ${import_picocolors10.default.dim("(" + w3.providerName + ")")}`);
|
|
17433
|
-
}
|
|
17434
|
-
gt(import_picocolors10.default.dim("Use `codeam deploy stop` to terminate a session."));
|
|
17435
|
-
}
|
|
17436
|
-
async function deployStop() {
|
|
17437
|
-
console.log();
|
|
17438
|
-
mt(import_picocolors10.default.bgMagenta(import_picocolors10.default.white(" codeam deploy stop ")));
|
|
17439
|
-
const workspaces = await collectWorkspacesWithStatus();
|
|
17440
|
-
if (workspaces.length === 0) {
|
|
17441
|
-
gt(import_picocolors10.default.dim("No deployed workspaces found."));
|
|
17442
|
-
return;
|
|
17443
|
-
}
|
|
17444
|
-
const choice = await _t({
|
|
17445
|
-
message: "Pick a workspace to stop:",
|
|
17446
|
-
options: workspaces.map((w3) => ({
|
|
17447
|
-
value: w3.id,
|
|
17448
|
-
label: w3.displayName ?? w3.id,
|
|
17449
|
-
hint: [
|
|
17450
|
-
w3.providerName,
|
|
17451
|
-
w3.codeamRunning ? import_picocolors10.default.green("\u25CF codeam-pair running") : import_picocolors10.default.dim("\u25CB no codeam-pair"),
|
|
17452
|
-
w3.state ?? ""
|
|
17453
|
-
].filter(Boolean).join(" \xB7 ")
|
|
17454
|
-
}))
|
|
17455
|
-
});
|
|
17456
|
-
if (q(choice)) {
|
|
17457
|
-
pt("Cancelled.");
|
|
17458
|
-
process.exit(0);
|
|
17459
|
-
}
|
|
17460
|
-
const target = workspaces.find((w3) => w3.id === choice);
|
|
17461
|
-
if (target.codeamRunning) {
|
|
17462
|
-
const stopStep = fe();
|
|
17463
|
-
stopStep.start("Stopping codeam-pair on the workspace\u2026");
|
|
17464
|
-
try {
|
|
17465
|
-
const result = await target.provider.exec(
|
|
17466
|
-
target.id,
|
|
17467
|
-
"pm2 delete codeam-pair >/dev/null 2>&1; pm2 list 2>/dev/null | grep -c codeam-pair || true"
|
|
17468
|
-
);
|
|
17469
|
-
void result;
|
|
17470
|
-
stopStep.stop("\u2713 codeam-pair stopped \u2014 your phone is now disconnected from this workspace");
|
|
17471
|
-
} catch (err) {
|
|
17472
|
-
stopStep.stop("\u26A0 Could not reach the workspace to stop codeam-pair");
|
|
17473
|
-
void err;
|
|
17474
|
-
}
|
|
17475
|
-
} else {
|
|
17476
|
-
O2.info("No codeam-pair process to stop on this workspace.");
|
|
17477
|
-
}
|
|
17478
|
-
const alsoStop = await ot2({
|
|
17479
|
-
message: `Also stop the workspace ${import_picocolors10.default.cyan(target.displayName ?? target.id)} to save compute hours?`,
|
|
17480
|
-
initialValue: true
|
|
17481
|
-
});
|
|
17482
|
-
if (!q(alsoStop) && alsoStop) {
|
|
17483
|
-
const cs = fe();
|
|
17484
|
-
cs.start("Stopping workspace\u2026");
|
|
17485
|
-
try {
|
|
17486
|
-
const result = await target.provider.exec(
|
|
17487
|
-
target.id,
|
|
17488
|
-
// We'd ideally use a provider method; for now do it inline
|
|
17489
|
-
// — works for the github-codespaces provider, no-op-ish for
|
|
17490
|
-
// others (the command will fail and we fall back gracefully).
|
|
17491
|
-
"echo stopping"
|
|
17492
|
-
);
|
|
17493
|
-
void result;
|
|
17494
|
-
await stopWorkspaceFromLocal(target);
|
|
17495
|
-
cs.stop(`\u2713 Workspace ${target.displayName ?? target.id} is stopping`);
|
|
17496
|
-
} catch (err) {
|
|
17497
|
-
cs.stop("\u26A0 Could not stop the workspace");
|
|
17498
|
-
O2.warn(err instanceof Error ? err.message : String(err));
|
|
17499
|
-
}
|
|
17500
|
-
}
|
|
17501
|
-
gt(import_picocolors10.default.green("\u2713 Done."));
|
|
17502
|
-
}
|
|
17503
|
-
async function collectWorkspacesWithStatus() {
|
|
17504
|
-
const out2 = [];
|
|
17505
|
-
const ready = PROVIDERS.filter((prov) => prov.available);
|
|
17506
|
-
for (const provider of ready) {
|
|
17507
|
-
if (!provider.listExistingWorkspaces) continue;
|
|
17508
|
-
const probeStep = fe();
|
|
17509
|
-
probeStep.start(`Listing ${provider.displayName} workspaces\u2026`);
|
|
17510
|
-
let workspaces = [];
|
|
17511
|
-
try {
|
|
17512
|
-
await provider.authorize();
|
|
17513
|
-
workspaces = await provider.listExistingWorkspaces();
|
|
17514
|
-
probeStep.stop(`\u2713 ${workspaces.length} workspace${workspaces.length === 1 ? "" : "s"} on ${provider.displayName}`);
|
|
17515
|
-
} catch (err) {
|
|
17516
|
-
probeStep.stop(`\u2717 Could not list ${provider.displayName} workspaces`);
|
|
17517
|
-
O2.warn(err instanceof Error ? err.message : String(err));
|
|
17518
|
-
continue;
|
|
17519
|
-
}
|
|
17520
|
-
for (const w3 of workspaces) {
|
|
17521
|
-
const codeamRunning = await probeCodeamPair(provider, w3);
|
|
17522
|
-
out2.push({
|
|
17523
|
-
...w3,
|
|
17524
|
-
provider,
|
|
17525
|
-
providerName: provider.displayName,
|
|
17526
|
-
codeamRunning
|
|
17527
|
-
});
|
|
17528
|
-
}
|
|
17529
|
-
}
|
|
17530
|
-
return out2;
|
|
17531
|
-
}
|
|
17532
|
-
async function probeCodeamPair(provider, workspace) {
|
|
17533
|
-
if (workspace.state && workspace.state !== "Available") return false;
|
|
17534
|
-
try {
|
|
17535
|
-
const result = await provider.exec(
|
|
17536
|
-
workspace.id,
|
|
17537
|
-
// `online` is the only state we care about — `errored`, `stopped`,
|
|
17538
|
-
// `stopping` all mean it's not actively serving the user's phone.
|
|
17539
|
-
`pm2 jlist 2>/dev/null | grep -c '"name":"codeam-pair"[^}]*"status":"online"' || echo 0`
|
|
17540
|
-
);
|
|
17541
|
-
if (result.code !== 0) return false;
|
|
17542
|
-
const n = parseInt(result.stdout.trim(), 10);
|
|
17543
|
-
return Number.isFinite(n) && n > 0;
|
|
17544
|
-
} catch {
|
|
17545
|
-
return false;
|
|
17546
|
-
}
|
|
17547
|
-
}
|
|
17548
|
-
async function stopWorkspaceFromLocal(target) {
|
|
17549
|
-
if (target.provider.id === "github-codespaces") {
|
|
17550
|
-
const { execFile: execFile8 } = await import("child_process");
|
|
17551
|
-
const { promisify: promisify9 } = await import("util");
|
|
17552
|
-
const execFileP8 = promisify9(execFile8);
|
|
17553
|
-
await execFileP8("gh", ["codespace", "stop", "-c", target.id], { maxBuffer: 8 * 1024 * 1024 });
|
|
17554
|
-
return;
|
|
17555
|
-
}
|
|
17556
|
-
}
|
|
17557
|
-
|
|
17558
|
-
// src/commands/link.ts
|
|
17559
|
-
var import_node_crypto4 = require("crypto");
|
|
17560
|
-
var fs28 = __toESM(require("fs"));
|
|
17561
|
-
var path37 = __toESM(require("path"));
|
|
17562
|
-
var import_chokidar = __toESM(require("chokidar"));
|
|
17563
|
-
var import_picocolors11 = __toESM(require("picocolors"));
|
|
17564
|
-
function buildLinkContext(agentId) {
|
|
17565
|
-
const runtime = createRuntimeStrategy(agentId);
|
|
17566
|
-
return {
|
|
17567
|
-
runtime,
|
|
17568
|
-
locator: runtime.credentialLocator(),
|
|
17569
|
-
launcher: runtime.loginLauncher(),
|
|
17570
|
-
displayName: runtime.meta.displayName,
|
|
17571
|
-
binary: runtime.meta.binaryName
|
|
17572
|
-
};
|
|
17573
|
-
}
|
|
17574
|
-
function enabledLinkableAgents() {
|
|
17575
|
-
return Object.values(AGENT_REGISTRY).filter((m) => m.enabled).map((m) => m.id);
|
|
17576
|
-
}
|
|
17577
|
-
function parseLinkArgs(args2) {
|
|
17578
|
-
const positional = args2.find((a) => !a.startsWith("--"));
|
|
17579
|
-
const valid = enabledLinkableAgents();
|
|
17580
|
-
if (!positional) {
|
|
17581
|
-
throw new Error(
|
|
17582
|
-
`Usage: codeam link <agent>
|
|
17583
|
-
agent: ${valid.join(" | ")}`
|
|
17584
|
-
);
|
|
17585
|
-
}
|
|
17586
|
-
const normalised = positional === "claude_code" ? "claude" : positional;
|
|
17587
|
-
if (!isKnownAgentId(normalised) || !AGENT_REGISTRY[normalised].enabled) {
|
|
17588
|
-
throw new Error(
|
|
17589
|
-
`Unknown or unsupported agent "${positional}". Valid: ${valid.join(", ")}`
|
|
17590
|
-
);
|
|
17591
|
-
}
|
|
17592
|
-
const reuseExisting = args2.includes("--reuse-existing");
|
|
17593
|
-
const dryRun = args2.includes("--dry-run");
|
|
17594
|
-
const apiKeyFileArg = args2.find((a) => a.startsWith("--api-key-file="));
|
|
17595
|
-
let apiKey = null;
|
|
17596
|
-
if (apiKeyFileArg) {
|
|
17597
|
-
const filePath = apiKeyFileArg.slice("--api-key-file=".length);
|
|
17598
|
-
try {
|
|
17599
|
-
apiKey = fs28.readFileSync(path37.resolve(filePath), "utf8").trim();
|
|
17600
|
-
} catch (err) {
|
|
17601
|
-
throw new Error(`Could not read --api-key-file ${filePath}: ${err.message}`);
|
|
17602
|
-
}
|
|
17603
|
-
if (!apiKey) {
|
|
17604
|
-
throw new Error(`--api-key-file ${filePath} is empty.`);
|
|
17605
|
-
}
|
|
17606
|
-
} else {
|
|
17607
|
-
const apiKeyArg = args2.find((a) => a.startsWith("--api-key="));
|
|
17608
|
-
apiKey = apiKeyArg ? apiKeyArg.slice("--api-key=".length) : null;
|
|
17609
|
-
}
|
|
17610
|
-
const tokenFileArg = args2.find((a) => a.startsWith("--token-file="));
|
|
17611
|
-
const tokenFile = tokenFileArg ? tokenFileArg.slice("--token-file=".length) : null;
|
|
17612
|
-
return { agent: normalised, reuseExisting, apiKey, tokenFile, dryRun };
|
|
17613
|
-
}
|
|
17614
|
-
async function link(args2 = []) {
|
|
17615
|
-
const parsed = parseLinkArgs(args2);
|
|
17616
|
-
const ctx = buildLinkContext(parsed.agent);
|
|
17617
|
-
showIntro();
|
|
17618
|
-
console.log(
|
|
17619
|
-
import_picocolors11.default.bold(` Link ${ctx.displayName}`) + import_picocolors11.default.dim(` \xB7 ${ctx.locator.vendor}`)
|
|
17620
|
-
);
|
|
17621
|
-
console.log("");
|
|
17622
|
-
if (parsed.dryRun) {
|
|
17623
|
-
await linkDryRunPreflight(ctx);
|
|
17624
|
-
return;
|
|
17625
|
-
}
|
|
17626
|
-
const pluginId = (0, import_node_crypto4.randomUUID)();
|
|
17627
|
-
const spin = dist_exports.spinner();
|
|
17628
|
-
spin.start("Requesting pairing code...");
|
|
17629
|
-
const pairing = await requestCode(pluginId);
|
|
17630
|
-
if (!pairing) {
|
|
17631
|
-
spin.stop("Failed");
|
|
17632
|
-
showError("Could not reach the server. Check your connection and try again.");
|
|
17633
|
-
process.exit(1);
|
|
17634
|
-
}
|
|
17635
|
-
spin.stop("Got pairing code");
|
|
17636
|
-
showPairingCode(pairing.code);
|
|
17637
|
-
console.log(import_picocolors11.default.dim(" Scan the QR or enter the code in CodeAgent Mobile."));
|
|
17638
|
-
console.log("");
|
|
17639
|
-
const waitSpin = dist_exports.spinner();
|
|
17640
|
-
const waitMsg = () => `Waiting for mobile pair... \xB7 expires in ${formatRemaining(pairing.expiresAt)}`;
|
|
17641
|
-
waitSpin.start(waitMsg());
|
|
17642
|
-
const countdown = setInterval(() => waitSpin.message(waitMsg()), 1e3);
|
|
17643
|
-
countdown.unref?.();
|
|
17644
|
-
const paired = await new Promise((resolve5, reject) => {
|
|
17645
|
-
let stopPoll = null;
|
|
17646
|
-
const sigint = () => {
|
|
17647
|
-
clearInterval(countdown);
|
|
17648
|
-
stopPoll?.();
|
|
17649
|
-
reject(new Error("cancelled"));
|
|
17650
|
-
};
|
|
17651
|
-
stopPoll = pollStatus(
|
|
17652
|
-
pluginId,
|
|
17653
|
-
(info) => {
|
|
17654
|
-
process.removeListener("SIGINT", sigint);
|
|
17655
|
-
clearInterval(countdown);
|
|
17656
|
-
waitSpin.stop("Paired");
|
|
17657
|
-
resolve5(info);
|
|
17658
|
-
},
|
|
17659
|
-
() => {
|
|
17660
|
-
clearInterval(countdown);
|
|
17661
|
-
waitSpin.stop("Timed out");
|
|
17662
|
-
reject(new Error("Pairing timed out after 5 minutes. Run codeam link again."));
|
|
17663
|
-
}
|
|
17664
|
-
);
|
|
17665
|
-
process.once("SIGINT", sigint);
|
|
17666
|
-
});
|
|
17667
|
-
if (!paired.pluginAuthToken) {
|
|
17668
|
-
showError(
|
|
17669
|
-
"Backend did not return a pluginAuthToken \u2014 upgrade api-v2 (deploy includes the link endpoint)."
|
|
17670
|
-
);
|
|
17671
|
-
process.exit(1);
|
|
17672
|
-
}
|
|
17673
|
-
addSession({
|
|
17674
|
-
id: paired.sessionId,
|
|
17675
|
-
pluginId,
|
|
17676
|
-
userName: paired.userName,
|
|
17677
|
-
userEmail: paired.userEmail,
|
|
17678
|
-
plan: paired.plan,
|
|
17679
|
-
pairedAt: Date.now(),
|
|
17680
|
-
pluginAuthToken: paired.pluginAuthToken,
|
|
17681
|
-
agent: ctx.runtime.id
|
|
17682
|
-
});
|
|
17683
|
-
saveCliConfig({ ...loadCliConfig(), preferredAgent: ctx.runtime.id });
|
|
17684
|
-
if (parsed.apiKey) {
|
|
17685
|
-
await uploadAndSucceed(ctx, paired, pluginId, {
|
|
17686
|
-
method: "api_key",
|
|
17687
|
-
credential: parsed.apiKey.trim(),
|
|
17688
|
-
source: "manual"
|
|
17689
|
-
});
|
|
17690
|
-
return;
|
|
17691
|
-
}
|
|
17692
|
-
if (parsed.tokenFile) {
|
|
17693
|
-
const credential = fs28.readFileSync(path37.resolve(parsed.tokenFile), "utf8").trim();
|
|
17694
|
-
if (!credential) {
|
|
17695
|
-
showError(`--token-file ${parsed.tokenFile} is empty.`);
|
|
17696
|
-
process.exit(1);
|
|
17697
|
-
}
|
|
17698
|
-
await uploadAndSucceed(ctx, paired, pluginId, {
|
|
17699
|
-
method: "oauth",
|
|
17700
|
-
credential,
|
|
17701
|
-
source: "manual"
|
|
17702
|
-
});
|
|
17703
|
-
return;
|
|
17704
|
-
}
|
|
17705
|
-
const installSpin = dist_exports.spinner();
|
|
17706
|
-
installSpin.start(`Checking that ${ctx.binary} is installed...`);
|
|
17707
|
-
const installed = await ctx.launcher.ensureInstalled();
|
|
17708
|
-
if (!installed) {
|
|
17709
|
-
installSpin.stop("Failed");
|
|
17710
|
-
showError(`Could not install ${ctx.displayName}. Install it manually then re-run.`);
|
|
17711
|
-
process.exit(1);
|
|
17712
|
-
}
|
|
17713
|
-
installSpin.stop(`${ctx.displayName} is installed`);
|
|
17714
|
-
const existing = await ctx.locator.extract();
|
|
17715
|
-
if (existing) {
|
|
17716
|
-
showInfo(`Found existing ${ctx.displayName} credentials at ${import_picocolors11.default.bold(existing.source)}.`);
|
|
17717
|
-
await uploadAndSucceed(ctx, paired, pluginId, existing);
|
|
17793
|
+
}
|
|
17794
|
+
|
|
17795
|
+
// src/commands/deploy-manage.ts
|
|
17796
|
+
var import_picocolors11 = __toESM(require("picocolors"));
|
|
17797
|
+
async function deployList() {
|
|
17798
|
+
console.log();
|
|
17799
|
+
mt(import_picocolors11.default.bgMagenta(import_picocolors11.default.white(" codeam deploy ls ")));
|
|
17800
|
+
const workspaces = await collectWorkspacesWithStatus();
|
|
17801
|
+
if (workspaces.length === 0) {
|
|
17802
|
+
gt(import_picocolors11.default.dim("No deployed workspaces found."));
|
|
17718
17803
|
return;
|
|
17719
17804
|
}
|
|
17720
|
-
|
|
17721
|
-
|
|
17722
|
-
|
|
17723
|
-
);
|
|
17724
|
-
process.exit(1);
|
|
17805
|
+
for (const w3 of workspaces) {
|
|
17806
|
+
const tag = w3.codeamRunning ? import_picocolors11.default.green("\u25CF running") : w3.state === "Available" ? import_picocolors11.default.dim("\u25CB idle") : import_picocolors11.default.dim(`\u25CB ${w3.state ?? "stopped"}`);
|
|
17807
|
+
console.log(` ${tag} ${import_picocolors11.default.cyan(w3.displayName ?? w3.id)} ${import_picocolors11.default.dim("(" + w3.providerName + ")")}`);
|
|
17725
17808
|
}
|
|
17726
|
-
|
|
17727
|
-
`No local ${ctx.displayName} credentials found. Launching the sign-in \u2014 complete it in your browser, the CLI will detect the new token and finish automatically.`
|
|
17728
|
-
);
|
|
17729
|
-
console.log("");
|
|
17730
|
-
const captured = await captureFreshCredentials(ctx);
|
|
17731
|
-
console.log("");
|
|
17732
|
-
await uploadAndSucceed(ctx, paired, pluginId, captured);
|
|
17809
|
+
gt(import_picocolors11.default.dim("Use `codeam deploy stop` to terminate a session."));
|
|
17733
17810
|
}
|
|
17734
|
-
async function
|
|
17735
|
-
|
|
17736
|
-
|
|
17737
|
-
|
|
17738
|
-
|
|
17739
|
-
|
|
17740
|
-
|
|
17741
|
-
|
|
17742
|
-
|
|
17743
|
-
|
|
17744
|
-
|
|
17745
|
-
|
|
17746
|
-
|
|
17747
|
-
|
|
17748
|
-
|
|
17811
|
+
async function deployStop() {
|
|
17812
|
+
console.log();
|
|
17813
|
+
mt(import_picocolors11.default.bgMagenta(import_picocolors11.default.white(" codeam deploy stop ")));
|
|
17814
|
+
const workspaces = await collectWorkspacesWithStatus();
|
|
17815
|
+
if (workspaces.length === 0) {
|
|
17816
|
+
gt(import_picocolors11.default.dim("No deployed workspaces found."));
|
|
17817
|
+
return;
|
|
17818
|
+
}
|
|
17819
|
+
const choice = await _t({
|
|
17820
|
+
message: "Pick a workspace to stop:",
|
|
17821
|
+
options: workspaces.map((w3) => ({
|
|
17822
|
+
value: w3.id,
|
|
17823
|
+
label: w3.displayName ?? w3.id,
|
|
17824
|
+
hint: [
|
|
17825
|
+
w3.providerName,
|
|
17826
|
+
w3.codeamRunning ? import_picocolors11.default.green("\u25CF codeam-pair running") : import_picocolors11.default.dim("\u25CB no codeam-pair"),
|
|
17827
|
+
w3.state ?? ""
|
|
17828
|
+
].filter(Boolean).join(" \xB7 ")
|
|
17829
|
+
}))
|
|
17749
17830
|
});
|
|
17750
|
-
|
|
17751
|
-
|
|
17752
|
-
|
|
17753
|
-
void watcher.close();
|
|
17754
|
-
if (keychainPoll) clearInterval(keychainPoll);
|
|
17755
|
-
if (child && !child.killed) {
|
|
17756
|
-
try {
|
|
17757
|
-
child.kill("SIGTERM");
|
|
17758
|
-
} catch {
|
|
17759
|
-
}
|
|
17760
|
-
}
|
|
17761
|
-
};
|
|
17762
|
-
try {
|
|
17763
|
-
const token = await new Promise((resolve5, reject) => {
|
|
17764
|
-
let settled = false;
|
|
17765
|
-
const tryExtract = async () => {
|
|
17766
|
-
if (settled) return;
|
|
17767
|
-
const t2 = await ctx.locator.extract();
|
|
17768
|
-
if (t2 && !settled) {
|
|
17769
|
-
settled = true;
|
|
17770
|
-
resolve5(t2);
|
|
17771
|
-
}
|
|
17772
|
-
};
|
|
17773
|
-
watcher.on("add", () => void tryExtract());
|
|
17774
|
-
watcher.on("change", () => void tryExtract());
|
|
17775
|
-
keychainPoll = setInterval(() => void tryExtract(), 2e3);
|
|
17776
|
-
keychainPoll.unref?.();
|
|
17777
|
-
const sigint = () => {
|
|
17778
|
-
if (settled) return;
|
|
17779
|
-
settled = true;
|
|
17780
|
-
reject(new Error("cancelled"));
|
|
17781
|
-
};
|
|
17782
|
-
process.once("SIGINT", sigint);
|
|
17783
|
-
setTimeout(() => {
|
|
17784
|
-
if (settled) return;
|
|
17785
|
-
settled = true;
|
|
17786
|
-
reject(new Error(`Timed out waiting for ${ctx.displayName} sign-in (5 minutes).`));
|
|
17787
|
-
}, 5 * 6e4);
|
|
17788
|
-
child = ctx.launcher.launch();
|
|
17789
|
-
child.on("exit", () => {
|
|
17790
|
-
void tryExtract().then(() => {
|
|
17791
|
-
if (!settled) {
|
|
17792
|
-
settled = true;
|
|
17793
|
-
reject(
|
|
17794
|
-
new Error(
|
|
17795
|
-
`${ctx.binary} exited but no credentials were written at ${ctx.locator.hint}.`
|
|
17796
|
-
)
|
|
17797
|
-
);
|
|
17798
|
-
}
|
|
17799
|
-
});
|
|
17800
|
-
});
|
|
17801
|
-
});
|
|
17802
|
-
cleanup();
|
|
17803
|
-
return token;
|
|
17804
|
-
} catch (err) {
|
|
17805
|
-
cleanup();
|
|
17806
|
-
throw err;
|
|
17831
|
+
if (q(choice)) {
|
|
17832
|
+
pt("Cancelled.");
|
|
17833
|
+
process.exit(0);
|
|
17807
17834
|
}
|
|
17808
|
-
|
|
17809
|
-
|
|
17810
|
-
|
|
17811
|
-
|
|
17812
|
-
|
|
17835
|
+
const target = workspaces.find((w3) => w3.id === choice);
|
|
17836
|
+
if (target.codeamRunning) {
|
|
17837
|
+
const stopStep = fe();
|
|
17838
|
+
stopStep.start("Stopping codeam-pair on the workspace\u2026");
|
|
17839
|
+
try {
|
|
17840
|
+
const result = await target.provider.exec(
|
|
17841
|
+
target.id,
|
|
17842
|
+
"pm2 delete codeam-pair >/dev/null 2>&1; pm2 list 2>/dev/null | grep -c codeam-pair || true"
|
|
17843
|
+
);
|
|
17844
|
+
void result;
|
|
17845
|
+
stopStep.stop("\u2713 codeam-pair stopped \u2014 your phone is now disconnected from this workspace");
|
|
17846
|
+
} catch (err) {
|
|
17847
|
+
stopStep.stop("\u26A0 Could not reach the workspace to stop codeam-pair");
|
|
17848
|
+
void err;
|
|
17849
|
+
}
|
|
17850
|
+
} else {
|
|
17851
|
+
O2.info("No codeam-pair process to stop on this workspace.");
|
|
17813
17852
|
}
|
|
17814
|
-
const
|
|
17815
|
-
|
|
17816
|
-
|
|
17817
|
-
agentId: ctx.locator.publicId,
|
|
17818
|
-
sessionId: paired.sessionId,
|
|
17819
|
-
pluginId,
|
|
17820
|
-
pluginAuthToken: paired.pluginAuthToken,
|
|
17821
|
-
method: token.method,
|
|
17822
|
-
credential: token.credential
|
|
17853
|
+
const alsoStop = await ot2({
|
|
17854
|
+
message: `Also stop the workspace ${import_picocolors11.default.cyan(target.displayName ?? target.id)} to save compute hours?`,
|
|
17855
|
+
initialValue: true
|
|
17823
17856
|
});
|
|
17824
|
-
if (!
|
|
17825
|
-
|
|
17826
|
-
|
|
17827
|
-
|
|
17828
|
-
|
|
17829
|
-
|
|
17830
|
-
|
|
17857
|
+
if (!q(alsoStop) && alsoStop) {
|
|
17858
|
+
const cs = fe();
|
|
17859
|
+
cs.start("Stopping workspace\u2026");
|
|
17860
|
+
try {
|
|
17861
|
+
const result = await target.provider.exec(
|
|
17862
|
+
target.id,
|
|
17863
|
+
// We'd ideally use a provider method; for now do it inline
|
|
17864
|
+
// — works for the github-codespaces provider, no-op-ish for
|
|
17865
|
+
// others (the command will fail and we fall back gracefully).
|
|
17866
|
+
"echo stopping"
|
|
17831
17867
|
);
|
|
17832
|
-
|
|
17833
|
-
|
|
17868
|
+
void result;
|
|
17869
|
+
await stopWorkspaceFromLocal(target);
|
|
17870
|
+
cs.stop(`\u2713 Workspace ${target.displayName ?? target.id} is stopping`);
|
|
17871
|
+
} catch (err) {
|
|
17872
|
+
cs.stop("\u26A0 Could not stop the workspace");
|
|
17873
|
+
O2.warn(err instanceof Error ? err.message : String(err));
|
|
17834
17874
|
}
|
|
17835
|
-
process.exit(1);
|
|
17836
17875
|
}
|
|
17837
|
-
|
|
17838
|
-
console.log("");
|
|
17839
|
-
showSuccess(`${ctx.displayName} is now linked to ${paired.userEmail || paired.userName}.`);
|
|
17840
|
-
showInfo(
|
|
17841
|
-
`Your codespaces and @codeagent mentions can now use ${ctx.displayName} without you signing in again.`
|
|
17842
|
-
);
|
|
17843
|
-
console.log("");
|
|
17876
|
+
gt(import_picocolors11.default.green("\u2713 Done."));
|
|
17844
17877
|
}
|
|
17845
|
-
async function
|
|
17846
|
-
const
|
|
17847
|
-
const
|
|
17848
|
-
|
|
17849
|
-
|
|
17850
|
-
|
|
17851
|
-
|
|
17852
|
-
|
|
17853
|
-
|
|
17854
|
-
|
|
17855
|
-
|
|
17856
|
-
|
|
17857
|
-
|
|
17858
|
-
|
|
17859
|
-
|
|
17860
|
-
|
|
17861
|
-
|
|
17862
|
-
|
|
17878
|
+
async function collectWorkspacesWithStatus() {
|
|
17879
|
+
const out2 = [];
|
|
17880
|
+
const ready = PROVIDERS.filter((prov) => prov.available);
|
|
17881
|
+
for (const provider of ready) {
|
|
17882
|
+
if (!provider.listExistingWorkspaces) continue;
|
|
17883
|
+
const probeStep = fe();
|
|
17884
|
+
probeStep.start(`Listing ${provider.displayName} workspaces\u2026`);
|
|
17885
|
+
let workspaces = [];
|
|
17886
|
+
try {
|
|
17887
|
+
await provider.authorize();
|
|
17888
|
+
workspaces = await provider.listExistingWorkspaces();
|
|
17889
|
+
probeStep.stop(`\u2713 ${workspaces.length} workspace${workspaces.length === 1 ? "" : "s"} on ${provider.displayName}`);
|
|
17890
|
+
} catch (err) {
|
|
17891
|
+
probeStep.stop(`\u2717 Could not list ${provider.displayName} workspaces`);
|
|
17892
|
+
O2.warn(err instanceof Error ? err.message : String(err));
|
|
17893
|
+
continue;
|
|
17894
|
+
}
|
|
17895
|
+
for (const w3 of workspaces) {
|
|
17896
|
+
const codeamRunning = await probeCodeamPair(provider, w3);
|
|
17897
|
+
out2.push({
|
|
17898
|
+
...w3,
|
|
17899
|
+
provider,
|
|
17900
|
+
providerName: provider.displayName,
|
|
17901
|
+
codeamRunning
|
|
17902
|
+
});
|
|
17903
|
+
}
|
|
17863
17904
|
}
|
|
17864
|
-
|
|
17865
|
-
|
|
17866
|
-
|
|
17867
|
-
|
|
17905
|
+
return out2;
|
|
17906
|
+
}
|
|
17907
|
+
async function probeCodeamPair(provider, workspace) {
|
|
17908
|
+
if (workspace.state && workspace.state !== "Available") return false;
|
|
17909
|
+
try {
|
|
17910
|
+
const result = await provider.exec(
|
|
17911
|
+
workspace.id,
|
|
17912
|
+
// `online` is the only state we care about — `errored`, `stopped`,
|
|
17913
|
+
// `stopping` all mean it's not actively serving the user's phone.
|
|
17914
|
+
`pm2 jlist 2>/dev/null | grep -c '"name":"codeam-pair"[^}]*"status":"online"' || echo 0`
|
|
17868
17915
|
);
|
|
17869
|
-
|
|
17916
|
+
if (result.code !== 0) return false;
|
|
17917
|
+
const n = parseInt(result.stdout.trim(), 10);
|
|
17918
|
+
return Number.isFinite(n) && n > 0;
|
|
17919
|
+
} catch {
|
|
17920
|
+
return false;
|
|
17921
|
+
}
|
|
17922
|
+
}
|
|
17923
|
+
async function stopWorkspaceFromLocal(target) {
|
|
17924
|
+
if (target.provider.id === "github-codespaces") {
|
|
17925
|
+
const { execFile: execFile8 } = await import("child_process");
|
|
17926
|
+
const { promisify: promisify9 } = await import("util");
|
|
17927
|
+
const execFileP8 = promisify9(execFile8);
|
|
17928
|
+
await execFileP8("gh", ["codespace", "stop", "-c", target.id], { maxBuffer: 8 * 1024 * 1024 });
|
|
17929
|
+
return;
|
|
17870
17930
|
}
|
|
17871
|
-
spin.stop("Failed");
|
|
17872
|
-
showError(
|
|
17873
|
-
`Link dry-run: unexpected response from /api/plugin/agents/${publicId}/link (status=${result.status}, message=${result.message}). Expected 401.`
|
|
17874
|
-
);
|
|
17875
|
-
process.exit(1);
|
|
17876
17931
|
}
|
|
17877
17932
|
|
|
17878
17933
|
// src/commands/doctor.ts
|
|
@@ -18048,7 +18103,7 @@ function checkChokidar() {
|
|
|
18048
18103
|
}
|
|
18049
18104
|
async function doctor(args2 = []) {
|
|
18050
18105
|
const json = args2.includes("--json");
|
|
18051
|
-
const cliVersion = true ? "2.
|
|
18106
|
+
const cliVersion = true ? "2.22.0" : "0.0.0-dev";
|
|
18052
18107
|
const apiBase = resolveApiBaseUrl();
|
|
18053
18108
|
const diagnosticId = (0, import_node_crypto5.randomUUID)();
|
|
18054
18109
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -18247,7 +18302,7 @@ async function completion(args2) {
|
|
|
18247
18302
|
// src/commands/version.ts
|
|
18248
18303
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
18249
18304
|
function version2() {
|
|
18250
|
-
const v = true ? "2.
|
|
18305
|
+
const v = true ? "2.22.0" : "unknown";
|
|
18251
18306
|
console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
|
|
18252
18307
|
}
|
|
18253
18308
|
|
|
@@ -18475,7 +18530,7 @@ function checkForUpdates() {
|
|
|
18475
18530
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
18476
18531
|
if (process.env.CI) return;
|
|
18477
18532
|
if (!process.stdout.isTTY) return;
|
|
18478
|
-
const current = true ? "2.
|
|
18533
|
+
const current = true ? "2.22.0" : null;
|
|
18479
18534
|
if (!current) return;
|
|
18480
18535
|
const cache = readCache();
|
|
18481
18536
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|