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