codeam-cli 2.24.0 → 2.26.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 +42 -0
- package/dist/index.js +810 -187
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -326,6 +326,37 @@ function resolveApiBaseUrl() {
|
|
|
326
326
|
return DEFAULT_API_BASE_URL;
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
+
// ../../packages/shared/src/preview-prompts.ts
|
|
330
|
+
var PREVIEW_DETECT_PROMPT = `
|
|
331
|
+
Analyze the project in the current working directory and return how to start
|
|
332
|
+
its development server for in-app preview.
|
|
333
|
+
|
|
334
|
+
Read package.json, Procfile, Dockerfile, docker-compose.yml, manage.py, app.json,
|
|
335
|
+
mix.exs, Cargo.toml, go.mod, requirements.txt, Gemfile, and any other framework
|
|
336
|
+
markers you find at depth <= 2.
|
|
337
|
+
|
|
338
|
+
Return ONLY a JSON object on stdout (no prose, no markdown fences):
|
|
339
|
+
|
|
340
|
+
{
|
|
341
|
+
"framework": "<name, or 'unsupported'>",
|
|
342
|
+
"command": "<executable>",
|
|
343
|
+
"args": ["..."],
|
|
344
|
+
"port": <number>,
|
|
345
|
+
"ready_pattern": "<regex matching the server-ready stdout line>",
|
|
346
|
+
"env": { "HOST": "0.0.0.0" },
|
|
347
|
+
"setup_commands": [{"cmd":"npm","args":["install"]}],
|
|
348
|
+
"notes": "<one-line caveat or null>"
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
Rules:
|
|
352
|
+
- Pick the script the developer would run locally to see the app (typically "dev", "start", "serve").
|
|
353
|
+
- Prefer binding to 0.0.0.0 \u2014 most frameworks default to localhost which the tunnel cannot reach.
|
|
354
|
+
- For Expo: framework="Expo", command="npx", args=["expo","start","--tunnel"], port=8081, notes="Scan QR with Expo Go".
|
|
355
|
+
- If no dev server applies (CLI library, lambda, batch script): {"framework":"unsupported","notes":"<reason>"}.
|
|
356
|
+
|
|
357
|
+
OUTPUT JSON ONLY. NO MARKDOWN. NO COMMENTARY.
|
|
358
|
+
`.trim();
|
|
359
|
+
|
|
329
360
|
// src/config.ts
|
|
330
361
|
var fs = __toESM(require("fs"));
|
|
331
362
|
var os = __toESM(require("os"));
|
|
@@ -441,7 +472,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
441
472
|
// package.json
|
|
442
473
|
var package_default = {
|
|
443
474
|
name: "codeam-cli",
|
|
444
|
-
version: "2.
|
|
475
|
+
version: "2.26.0",
|
|
445
476
|
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
477
|
type: "commonjs",
|
|
447
478
|
main: "dist/index.js",
|
|
@@ -515,6 +546,7 @@ var package_default = {
|
|
|
515
546
|
chokidar: "^3.6.0",
|
|
516
547
|
picocolors: "^1.1.0",
|
|
517
548
|
"qrcode-terminal": "^0.12.0",
|
|
549
|
+
which: "^2.0.2",
|
|
518
550
|
ws: "^8.18.0",
|
|
519
551
|
zod: "^4.3.6"
|
|
520
552
|
},
|
|
@@ -522,6 +554,7 @@ var package_default = {
|
|
|
522
554
|
"@codeagent/shared": "*",
|
|
523
555
|
"@types/node": "^22.0.0",
|
|
524
556
|
"@types/qrcode-terminal": "^0.12.0",
|
|
557
|
+
"@types/which": "^3.0.4",
|
|
525
558
|
"@types/ws": "^8.5.0",
|
|
526
559
|
"@vitest/coverage-v8": "^4.1.7",
|
|
527
560
|
"node-pty": "^1.1.0",
|
|
@@ -768,6 +801,28 @@ async function postAiResult(input) {
|
|
|
768
801
|
};
|
|
769
802
|
}
|
|
770
803
|
}
|
|
804
|
+
async function postPreviewEvent(input) {
|
|
805
|
+
try {
|
|
806
|
+
await _transport.postJsonAuthed(
|
|
807
|
+
`${API_BASE}/api/preview/events`,
|
|
808
|
+
{
|
|
809
|
+
sessionId: input.sessionId,
|
|
810
|
+
pluginId: input.pluginId,
|
|
811
|
+
type: input.type,
|
|
812
|
+
payload: input.payload ?? {}
|
|
813
|
+
},
|
|
814
|
+
input.pluginAuthToken
|
|
815
|
+
);
|
|
816
|
+
return { ok: true };
|
|
817
|
+
} catch (err) {
|
|
818
|
+
const e = err;
|
|
819
|
+
return {
|
|
820
|
+
ok: false,
|
|
821
|
+
status: typeof e.statusCode === "number" ? e.statusCode : 0,
|
|
822
|
+
message: e.message || "unknown"
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
}
|
|
771
826
|
async function _postJsonAuthed(url, body, pluginAuthToken) {
|
|
772
827
|
return new Promise((resolve5, reject) => {
|
|
773
828
|
const data = JSON.stringify(body);
|
|
@@ -1060,8 +1115,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
|
|
|
1060
1115
|
return decodedFile;
|
|
1061
1116
|
};
|
|
1062
1117
|
}
|
|
1063
|
-
function normalizeWindowsPath(
|
|
1064
|
-
return
|
|
1118
|
+
function normalizeWindowsPath(path42) {
|
|
1119
|
+
return path42.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
|
|
1065
1120
|
}
|
|
1066
1121
|
|
|
1067
1122
|
// ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
|
|
@@ -3541,9 +3596,9 @@ async function addSourceContext(frames) {
|
|
|
3541
3596
|
LRU_FILE_CONTENTS_CACHE.reduce();
|
|
3542
3597
|
return frames;
|
|
3543
3598
|
}
|
|
3544
|
-
function getContextLinesFromFile(
|
|
3599
|
+
function getContextLinesFromFile(path42, ranges, output) {
|
|
3545
3600
|
return new Promise((resolve5) => {
|
|
3546
|
-
const stream = (0, import_node_fs.createReadStream)(
|
|
3601
|
+
const stream = (0, import_node_fs.createReadStream)(path42);
|
|
3547
3602
|
const lineReaded = (0, import_node_readline.createInterface)({
|
|
3548
3603
|
input: stream
|
|
3549
3604
|
});
|
|
@@ -3558,7 +3613,7 @@ function getContextLinesFromFile(path40, ranges, output) {
|
|
|
3558
3613
|
let rangeStart = range[0];
|
|
3559
3614
|
let rangeEnd = range[1];
|
|
3560
3615
|
function onStreamError() {
|
|
3561
|
-
LRU_FILE_CONTENTS_FS_READ_FAILED.set(
|
|
3616
|
+
LRU_FILE_CONTENTS_FS_READ_FAILED.set(path42, 1);
|
|
3562
3617
|
lineReaded.close();
|
|
3563
3618
|
lineReaded.removeAllListeners();
|
|
3564
3619
|
destroyStreamAndResolve();
|
|
@@ -3619,8 +3674,8 @@ function clearLineContext(frame) {
|
|
|
3619
3674
|
delete frame.context_line;
|
|
3620
3675
|
delete frame.post_context;
|
|
3621
3676
|
}
|
|
3622
|
-
function shouldSkipContextLinesForFile(
|
|
3623
|
-
return
|
|
3677
|
+
function shouldSkipContextLinesForFile(path42) {
|
|
3678
|
+
return path42.startsWith("node:") || path42.endsWith(".min.js") || path42.endsWith(".min.cjs") || path42.endsWith(".min.mjs") || path42.startsWith("data:");
|
|
3624
3679
|
}
|
|
3625
3680
|
function shouldSkipContextLinesForFrame(frame) {
|
|
3626
3681
|
if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
|
|
@@ -5774,7 +5829,7 @@ function readAnonId() {
|
|
|
5774
5829
|
}
|
|
5775
5830
|
function superProperties() {
|
|
5776
5831
|
return {
|
|
5777
|
-
cliVersion: true ? "2.
|
|
5832
|
+
cliVersion: true ? "2.26.0" : "0.0.0-dev",
|
|
5778
5833
|
nodeVersion: process.version,
|
|
5779
5834
|
platform: process.platform,
|
|
5780
5835
|
arch: process.arch,
|
|
@@ -7116,10 +7171,10 @@ function buildForPlatform(platform2) {
|
|
|
7116
7171
|
var import_node_crypto4 = require("crypto");
|
|
7117
7172
|
|
|
7118
7173
|
// src/agents/claude/resolver.ts
|
|
7119
|
-
function buildClaudeLaunch(extraArgs = [],
|
|
7120
|
-
const found =
|
|
7174
|
+
function buildClaudeLaunch(extraArgs = [], os27 = createOsStrategy()) {
|
|
7175
|
+
const found = os27.findInPath("claude") ?? os27.findInPath("claude-code");
|
|
7121
7176
|
if (!found) return null;
|
|
7122
|
-
return
|
|
7177
|
+
return os27.buildLaunch(found, extraArgs);
|
|
7123
7178
|
}
|
|
7124
7179
|
|
|
7125
7180
|
// src/agents/claude/installer.ts
|
|
@@ -9802,13 +9857,13 @@ function detectStartupBanner(lines) {
|
|
|
9802
9857
|
while (artStart > 0 && BANNER_ART_RE.test(lines[artStart - 1])) artStart--;
|
|
9803
9858
|
if (metaIdx - artStart < 2) return null;
|
|
9804
9859
|
const pathLine = (lines[metaIdx + 1] ?? "").trim();
|
|
9805
|
-
const
|
|
9860
|
+
const path42 = pathLine && !BANNER_ART_RE.test(pathLine) ? pathLine : "";
|
|
9806
9861
|
return {
|
|
9807
9862
|
title: "",
|
|
9808
9863
|
subtitle: lines[metaIdx].trim(),
|
|
9809
|
-
path:
|
|
9864
|
+
path: path42,
|
|
9810
9865
|
startIdx: artStart,
|
|
9811
|
-
endIdx: metaIdx + (
|
|
9866
|
+
endIdx: metaIdx + (path42 ? 1 : 0)
|
|
9812
9867
|
};
|
|
9813
9868
|
}
|
|
9814
9869
|
|
|
@@ -9818,8 +9873,8 @@ var ClaudeRuntimeStrategy = class {
|
|
|
9818
9873
|
meta = getAgent("claude");
|
|
9819
9874
|
mode = "interactive";
|
|
9820
9875
|
os;
|
|
9821
|
-
constructor(
|
|
9822
|
-
this.os =
|
|
9876
|
+
constructor(os27) {
|
|
9877
|
+
this.os = os27;
|
|
9823
9878
|
}
|
|
9824
9879
|
/**
|
|
9825
9880
|
* Claude Code's react-ink TUI enables bracketed-paste mode at
|
|
@@ -10708,8 +10763,8 @@ function codexCredentialLocator() {
|
|
|
10708
10763
|
function codexLoginLauncher() {
|
|
10709
10764
|
return {
|
|
10710
10765
|
async ensureInstalled() {
|
|
10711
|
-
const
|
|
10712
|
-
return
|
|
10766
|
+
const os27 = createOsStrategy();
|
|
10767
|
+
return os27.findInPath("codex") !== null;
|
|
10713
10768
|
},
|
|
10714
10769
|
launch() {
|
|
10715
10770
|
return (0, import_node_child_process3.spawn)("codex", ["login"], { stdio: "inherit" });
|
|
@@ -10732,8 +10787,8 @@ var CodexRuntimeStrategy = class {
|
|
|
10732
10787
|
meta = getAgent("codex");
|
|
10733
10788
|
mode = "interactive";
|
|
10734
10789
|
os;
|
|
10735
|
-
constructor(
|
|
10736
|
-
this.os =
|
|
10790
|
+
constructor(os27) {
|
|
10791
|
+
this.os = os27;
|
|
10737
10792
|
}
|
|
10738
10793
|
async prepareLaunch() {
|
|
10739
10794
|
let binary = this.os.findInPath("codex");
|
|
@@ -10836,12 +10891,12 @@ var CodexRuntimeStrategy = class {
|
|
|
10836
10891
|
});
|
|
10837
10892
|
}
|
|
10838
10893
|
};
|
|
10839
|
-
function resolveNpm(
|
|
10840
|
-
return
|
|
10894
|
+
function resolveNpm(os27) {
|
|
10895
|
+
return os27.id === "win32" ? "npm.cmd" : "npm";
|
|
10841
10896
|
}
|
|
10842
|
-
async function installCodexViaNpm(
|
|
10897
|
+
async function installCodexViaNpm(os27) {
|
|
10843
10898
|
return new Promise((resolve5, reject) => {
|
|
10844
|
-
const proc = (0, import_node_child_process4.spawn)(resolveNpm(
|
|
10899
|
+
const proc = (0, import_node_child_process4.spawn)(resolveNpm(os27), ["install", "-g", "@openai/codex"], {
|
|
10845
10900
|
stdio: "inherit"
|
|
10846
10901
|
});
|
|
10847
10902
|
proc.on("close", (code) => {
|
|
@@ -10858,16 +10913,16 @@ async function installCodexViaNpm(os26) {
|
|
|
10858
10913
|
});
|
|
10859
10914
|
});
|
|
10860
10915
|
}
|
|
10861
|
-
function augmentNpmGlobalBin(
|
|
10916
|
+
function augmentNpmGlobalBin(os27) {
|
|
10862
10917
|
try {
|
|
10863
|
-
const result = (0, import_node_child_process4.spawnSync)(resolveNpm(
|
|
10918
|
+
const result = (0, import_node_child_process4.spawnSync)(resolveNpm(os27), ["prefix", "-g"], {
|
|
10864
10919
|
stdio: ["ignore", "pipe", "ignore"]
|
|
10865
10920
|
});
|
|
10866
10921
|
if (result.status !== 0) return;
|
|
10867
10922
|
const prefix = result.stdout.toString().trim();
|
|
10868
10923
|
if (!prefix) return;
|
|
10869
|
-
const binDir =
|
|
10870
|
-
|
|
10924
|
+
const binDir = os27.id === "win32" ? prefix : path17.join(prefix, "bin");
|
|
10925
|
+
os27.augmentPath([binDir]);
|
|
10871
10926
|
} catch {
|
|
10872
10927
|
}
|
|
10873
10928
|
}
|
|
@@ -10951,9 +11006,9 @@ var import_node_child_process7 = require("child_process");
|
|
|
10951
11006
|
// src/agents/coderabbit/installer.ts
|
|
10952
11007
|
var import_node_child_process5 = require("child_process");
|
|
10953
11008
|
var INSTALL_URL = "https://cli.coderabbit.ai/install.sh";
|
|
10954
|
-
async function ensureCoderabbitInstalled(
|
|
10955
|
-
if (
|
|
10956
|
-
if (
|
|
11009
|
+
async function ensureCoderabbitInstalled(os27) {
|
|
11010
|
+
if (os27.findInPath("coderabbit")) return true;
|
|
11011
|
+
if (os27.id === "win32") {
|
|
10957
11012
|
console.error(
|
|
10958
11013
|
"\n \u2717 CodeRabbit on Windows requires WSL.\n Install the CLI inside your WSL distribution\n (curl -fsSL https://cli.coderabbit.ai/install.sh | sh)\n then re-run `codeam link coderabbit` from WSL.\n"
|
|
10959
11014
|
);
|
|
@@ -10968,8 +11023,8 @@ async function ensureCoderabbitInstalled(os26) {
|
|
|
10968
11023
|
proc.on("error", () => resolve5(false));
|
|
10969
11024
|
});
|
|
10970
11025
|
if (!ok) return false;
|
|
10971
|
-
|
|
10972
|
-
return
|
|
11026
|
+
os27.augmentPath([`${os27.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
|
|
11027
|
+
return os27.findInPath("coderabbit") !== null;
|
|
10973
11028
|
}
|
|
10974
11029
|
|
|
10975
11030
|
// src/agents/coderabbit/link.ts
|
|
@@ -10996,10 +11051,10 @@ function coderabbitCredentialLocator() {
|
|
|
10996
11051
|
extract: extractLocalCoderabbitToken
|
|
10997
11052
|
};
|
|
10998
11053
|
}
|
|
10999
|
-
function coderabbitLoginLauncher(
|
|
11054
|
+
function coderabbitLoginLauncher(os27) {
|
|
11000
11055
|
return {
|
|
11001
11056
|
async ensureInstalled() {
|
|
11002
|
-
return ensureCoderabbitInstalled(
|
|
11057
|
+
return ensureCoderabbitInstalled(os27);
|
|
11003
11058
|
},
|
|
11004
11059
|
launch() {
|
|
11005
11060
|
return (0, import_node_child_process6.spawn)("coderabbit", ["login"], { stdio: "inherit" });
|
|
@@ -11022,11 +11077,11 @@ function parseReview(stdout) {
|
|
|
11022
11077
|
for (const line of lines) {
|
|
11023
11078
|
const m = line.match(HUNK_LINE_RE);
|
|
11024
11079
|
if (!m) continue;
|
|
11025
|
-
const [,
|
|
11026
|
-
if (!
|
|
11080
|
+
const [, path42, lineNo, sevToken, message] = m;
|
|
11081
|
+
if (!path42 || !lineNo || !message) continue;
|
|
11027
11082
|
const cleanedMessage = message.trim().replace(/^[*-]\s+/, "");
|
|
11028
11083
|
hunks.push({
|
|
11029
|
-
path:
|
|
11084
|
+
path: path42.trim(),
|
|
11030
11085
|
line: Number(lineNo),
|
|
11031
11086
|
severity: sevToken ? SEVERITY_MAP[sevToken.toLowerCase()] : void 0,
|
|
11032
11087
|
message: cleanedMessage
|
|
@@ -11045,8 +11100,8 @@ var CoderabbitRuntimeStrategy = class {
|
|
|
11045
11100
|
meta = getAgent("coderabbit");
|
|
11046
11101
|
mode = "batch";
|
|
11047
11102
|
os;
|
|
11048
|
-
constructor(
|
|
11049
|
-
this.os =
|
|
11103
|
+
constructor(os27) {
|
|
11104
|
+
this.os = os27;
|
|
11050
11105
|
}
|
|
11051
11106
|
getDefaultArgs() {
|
|
11052
11107
|
return ["review"];
|
|
@@ -11161,10 +11216,10 @@ function cursorCredentialLocator() {
|
|
|
11161
11216
|
extract: extractLocalCursorToken
|
|
11162
11217
|
};
|
|
11163
11218
|
}
|
|
11164
|
-
function cursorLoginLauncher(
|
|
11219
|
+
function cursorLoginLauncher(os27) {
|
|
11165
11220
|
return {
|
|
11166
11221
|
async ensureInstalled() {
|
|
11167
|
-
if (
|
|
11222
|
+
if (os27.findInPath("cursor-agent")) return true;
|
|
11168
11223
|
console.error(
|
|
11169
11224
|
"\n \u2717 cursor-agent binary not on PATH.\n Install Cursor (https://cursor.com/) and ensure the CLI\n plugin is enabled, then re-run `codeam link cursor`.\n"
|
|
11170
11225
|
);
|
|
@@ -11226,8 +11281,8 @@ var CursorRuntimeStrategy = class {
|
|
|
11226
11281
|
meta = getAgent("cursor");
|
|
11227
11282
|
mode = "interactive";
|
|
11228
11283
|
os;
|
|
11229
|
-
constructor(
|
|
11230
|
-
this.os =
|
|
11284
|
+
constructor(os27) {
|
|
11285
|
+
this.os = os27;
|
|
11231
11286
|
}
|
|
11232
11287
|
async prepareLaunch() {
|
|
11233
11288
|
const binary = this.os.findInPath("cursor-agent");
|
|
@@ -11347,10 +11402,10 @@ function aiderCredentialLocator() {
|
|
|
11347
11402
|
extract: extractLocalAiderToken
|
|
11348
11403
|
};
|
|
11349
11404
|
}
|
|
11350
|
-
function aiderLoginLauncher(
|
|
11405
|
+
function aiderLoginLauncher(os27) {
|
|
11351
11406
|
return {
|
|
11352
11407
|
async ensureInstalled() {
|
|
11353
|
-
if (
|
|
11408
|
+
if (os27.findInPath("aider")) return true;
|
|
11354
11409
|
console.error(
|
|
11355
11410
|
"\n \u2717 aider binary not on PATH.\n Install Aider:\n pip install aider-chat\n then re-run `codeam link aider`.\n"
|
|
11356
11411
|
);
|
|
@@ -11360,7 +11415,7 @@ function aiderLoginLauncher(os26) {
|
|
|
11360
11415
|
console.error(
|
|
11361
11416
|
"\n Aider has no interactive login flow.\n Set ANTHROPIC_API_KEY or OPENAI_API_KEY in your shell,\n or re-run `codeam link aider --api-key=<your-key>`.\n"
|
|
11362
11417
|
);
|
|
11363
|
-
return (0, import_node_child_process9.spawn)(
|
|
11418
|
+
return (0, import_node_child_process9.spawn)(os27.id === "win32" ? "cmd.exe" : "sh", os27.id === "win32" ? ["/c", "exit", "0"] : ["-c", "exit 0"], {
|
|
11364
11419
|
stdio: "ignore"
|
|
11365
11420
|
});
|
|
11366
11421
|
}
|
|
@@ -11432,8 +11487,8 @@ var AiderRuntimeStrategy = class {
|
|
|
11432
11487
|
meta = getAgent("aider");
|
|
11433
11488
|
mode = "interactive";
|
|
11434
11489
|
os;
|
|
11435
|
-
constructor(
|
|
11436
|
-
this.os =
|
|
11490
|
+
constructor(os27) {
|
|
11491
|
+
this.os = os27;
|
|
11437
11492
|
}
|
|
11438
11493
|
async prepareLaunch() {
|
|
11439
11494
|
const binary = this.os.findInPath("aider");
|
|
@@ -11502,17 +11557,17 @@ var AiderRuntimeStrategy = class {
|
|
|
11502
11557
|
|
|
11503
11558
|
// src/agents/registry.ts
|
|
11504
11559
|
var runtimeBuilders = {
|
|
11505
|
-
claude: (
|
|
11506
|
-
codex: (
|
|
11507
|
-
coderabbit: (
|
|
11508
|
-
cursor: (
|
|
11509
|
-
aider: (
|
|
11560
|
+
claude: (os27) => new ClaudeRuntimeStrategy(os27),
|
|
11561
|
+
codex: (os27) => new CodexRuntimeStrategy(os27),
|
|
11562
|
+
coderabbit: (os27) => new CoderabbitRuntimeStrategy(os27),
|
|
11563
|
+
cursor: (os27) => new CursorRuntimeStrategy(os27),
|
|
11564
|
+
aider: (os27) => new AiderRuntimeStrategy(os27)
|
|
11510
11565
|
};
|
|
11511
11566
|
var deployBuilders = {
|
|
11512
11567
|
claude: () => new ClaudeDeployStrategy(),
|
|
11513
11568
|
codex: () => new CodexDeployStrategy()
|
|
11514
11569
|
};
|
|
11515
|
-
function createAgentStrategy(agent,
|
|
11570
|
+
function createAgentStrategy(agent, os27 = createOsStrategy()) {
|
|
11516
11571
|
if (!AGENT_REGISTRY[agent]?.enabled) {
|
|
11517
11572
|
throw new Error(
|
|
11518
11573
|
`Agent "${agent}" is not supported in this codeam-cli version. Upgrade with 'npm i -g codeam-cli@latest'.`
|
|
@@ -11522,10 +11577,10 @@ function createAgentStrategy(agent, os26 = createOsStrategy()) {
|
|
|
11522
11577
|
if (!build) {
|
|
11523
11578
|
throw new Error(`No runtime strategy registered for agent "${agent}"`);
|
|
11524
11579
|
}
|
|
11525
|
-
return build(
|
|
11580
|
+
return build(os27);
|
|
11526
11581
|
}
|
|
11527
|
-
function createInteractiveAgentStrategy(agent,
|
|
11528
|
-
const s = createAgentStrategy(agent,
|
|
11582
|
+
function createInteractiveAgentStrategy(agent, os27 = createOsStrategy()) {
|
|
11583
|
+
const s = createAgentStrategy(agent, os27);
|
|
11529
11584
|
if (s.mode !== "interactive") {
|
|
11530
11585
|
throw new Error(
|
|
11531
11586
|
`Agent "${agent}" is a batch agent; use createAgentStrategy + .runOneShot for one-shot reviews.`
|
|
@@ -13622,7 +13677,7 @@ function defaultRunGit(cwd, args2) {
|
|
|
13622
13677
|
});
|
|
13623
13678
|
}
|
|
13624
13679
|
async function discoverRepos(workingDir, maxDepth = 4) {
|
|
13625
|
-
const
|
|
13680
|
+
const fs33 = await import("fs/promises");
|
|
13626
13681
|
const out2 = [];
|
|
13627
13682
|
await walk(workingDir, 0);
|
|
13628
13683
|
return out2;
|
|
@@ -13630,7 +13685,7 @@ async function discoverRepos(workingDir, maxDepth = 4) {
|
|
|
13630
13685
|
if (depth > maxDepth) return;
|
|
13631
13686
|
let entries = [];
|
|
13632
13687
|
try {
|
|
13633
|
-
const dirents = await
|
|
13688
|
+
const dirents = await fs33.readdir(dir, { withFileTypes: true });
|
|
13634
13689
|
entries = dirents.filter((d3) => !d3.name.startsWith(".") || d3.name === ".git").map((d3) => ({ name: d3.name, isDirectory: d3.isDirectory() }));
|
|
13635
13690
|
} catch {
|
|
13636
13691
|
return;
|
|
@@ -14532,11 +14587,11 @@ function buildKeepAlive(ctx) {
|
|
|
14532
14587
|
}
|
|
14533
14588
|
|
|
14534
14589
|
// src/commands/start/handlers.ts
|
|
14535
|
-
var
|
|
14536
|
-
var
|
|
14537
|
-
var
|
|
14590
|
+
var fs29 = __toESM(require("fs"));
|
|
14591
|
+
var os24 = __toESM(require("os"));
|
|
14592
|
+
var path35 = __toESM(require("path"));
|
|
14538
14593
|
var import_crypto5 = require("crypto");
|
|
14539
|
-
var
|
|
14594
|
+
var import_child_process15 = require("child_process");
|
|
14540
14595
|
|
|
14541
14596
|
// src/lib/payload.ts
|
|
14542
14597
|
var import_zod2 = require("zod");
|
|
@@ -14628,6 +14683,28 @@ var startCommandSchema = import_zod2.z.object({
|
|
|
14628
14683
|
added: import_zod2.z.number().int(),
|
|
14629
14684
|
removed: import_zod2.z.number().int(),
|
|
14630
14685
|
complexityShift: import_zod2.z.number().int()
|
|
14686
|
+
}).optional(),
|
|
14687
|
+
// `preview_start` carries the agent-detected `PreviewDetection`
|
|
14688
|
+
// shape from the mobile / web confirmation sheet. Mirrors
|
|
14689
|
+
// `@codeagent/shared`'s `PreviewDetection` byte-for-byte. Kept
|
|
14690
|
+
// loose (`unknown` for env / setup_commands) so a CLI version
|
|
14691
|
+
// running against a newer backend that adds optional fields still
|
|
14692
|
+
// accepts the payload; the handler validates the shape it needs
|
|
14693
|
+
// before spawning.
|
|
14694
|
+
detection: import_zod2.z.object({
|
|
14695
|
+
framework: import_zod2.z.string().max(64),
|
|
14696
|
+
command: import_zod2.z.string().min(1).max(256),
|
|
14697
|
+
args: import_zod2.z.array(import_zod2.z.string().max(1024)).max(64),
|
|
14698
|
+
port: import_zod2.z.number().int().min(1).max(65535),
|
|
14699
|
+
ready_pattern: import_zod2.z.string().min(1).max(4096),
|
|
14700
|
+
env: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.string().max(8192)).optional(),
|
|
14701
|
+
setup_commands: import_zod2.z.array(
|
|
14702
|
+
import_zod2.z.object({
|
|
14703
|
+
cmd: import_zod2.z.string().min(1).max(256),
|
|
14704
|
+
args: import_zod2.z.array(import_zod2.z.string().max(1024)).max(64)
|
|
14705
|
+
})
|
|
14706
|
+
).max(32).optional(),
|
|
14707
|
+
notes: import_zod2.z.string().max(4096).nullable().optional()
|
|
14631
14708
|
}).optional()
|
|
14632
14709
|
});
|
|
14633
14710
|
function parsePayload2(schema, raw) {
|
|
@@ -15811,12 +15888,212 @@ async function linkDryRunPreflight(ctx) {
|
|
|
15811
15888
|
process.exit(1);
|
|
15812
15889
|
}
|
|
15813
15890
|
|
|
15891
|
+
// src/services/preview/cloudflared.ts
|
|
15892
|
+
var import_fs = require("fs");
|
|
15893
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
15894
|
+
var import_os7 = __toESM(require("os"));
|
|
15895
|
+
var import_path4 = __toESM(require("path"));
|
|
15896
|
+
var import_promises2 = require("stream/promises");
|
|
15897
|
+
var import_which = __toESM(require("which"));
|
|
15898
|
+
var CACHED_BINARY = import_path4.default.join(import_os7.default.homedir(), ".codeam", "bin", "cloudflared");
|
|
15899
|
+
async function resolveCloudflared(opts = {}) {
|
|
15900
|
+
try {
|
|
15901
|
+
return await (0, import_which.default)("cloudflared");
|
|
15902
|
+
} catch {
|
|
15903
|
+
}
|
|
15904
|
+
try {
|
|
15905
|
+
await import_promises.default.access(CACHED_BINARY);
|
|
15906
|
+
return CACHED_BINARY;
|
|
15907
|
+
} catch {
|
|
15908
|
+
}
|
|
15909
|
+
if (opts.skipDownload) {
|
|
15910
|
+
throw new Error(
|
|
15911
|
+
"cloudflared not installed. Install via `brew install cloudflared` (macOS) or download from https://github.com/cloudflare/cloudflared/releases."
|
|
15912
|
+
);
|
|
15913
|
+
}
|
|
15914
|
+
await downloadCloudflared(CACHED_BINARY);
|
|
15915
|
+
return CACHED_BINARY;
|
|
15916
|
+
}
|
|
15917
|
+
async function downloadCloudflared(target) {
|
|
15918
|
+
const url = downloadUrlForPlatform();
|
|
15919
|
+
await import_promises.default.mkdir(import_path4.default.dirname(target), { recursive: true });
|
|
15920
|
+
const response = await fetch(url);
|
|
15921
|
+
if (!response.ok || !response.body) {
|
|
15922
|
+
throw new Error(
|
|
15923
|
+
`Failed to download cloudflared from ${url}: HTTP ${response.status}. Install manually from https://github.com/cloudflare/cloudflared/releases.`
|
|
15924
|
+
);
|
|
15925
|
+
}
|
|
15926
|
+
await (0, import_promises2.pipeline)(
|
|
15927
|
+
response.body,
|
|
15928
|
+
(0, import_fs.createWriteStream)(target, { mode: 493 })
|
|
15929
|
+
);
|
|
15930
|
+
}
|
|
15931
|
+
function downloadUrlForPlatform() {
|
|
15932
|
+
const platform2 = process.platform;
|
|
15933
|
+
const arch = process.arch;
|
|
15934
|
+
const base = "https://github.com/cloudflare/cloudflared/releases/latest/download";
|
|
15935
|
+
if (platform2 === "darwin" && arch === "arm64") return `${base}/cloudflared-darwin-arm64.tgz`;
|
|
15936
|
+
if (platform2 === "darwin" && arch === "x64") return `${base}/cloudflared-darwin-amd64.tgz`;
|
|
15937
|
+
if (platform2 === "linux" && arch === "x64") return `${base}/cloudflared-linux-amd64`;
|
|
15938
|
+
if (platform2 === "linux" && arch === "arm64") return `${base}/cloudflared-linux-arm64`;
|
|
15939
|
+
if (platform2 === "win32") return `${base}/cloudflared-windows-amd64.exe`;
|
|
15940
|
+
throw new Error(
|
|
15941
|
+
`cloudflared auto-install not supported on ${platform2}/${arch}. Install manually from https://github.com/cloudflare/cloudflared/releases.`
|
|
15942
|
+
);
|
|
15943
|
+
}
|
|
15944
|
+
|
|
15945
|
+
// src/services/preview/codespace.ts
|
|
15946
|
+
var import_child_process14 = require("child_process");
|
|
15947
|
+
var import_util3 = require("util");
|
|
15948
|
+
var execFileP4 = (0, import_util3.promisify)(import_child_process14.execFile);
|
|
15949
|
+
function isCodespaceSession(env = process.env) {
|
|
15950
|
+
return Boolean(env.CODESPACE_NAME);
|
|
15951
|
+
}
|
|
15952
|
+
function buildCodespaceUrl(codespaceName, port) {
|
|
15953
|
+
return `https://${codespaceName}-${port}.app.github.dev`;
|
|
15954
|
+
}
|
|
15955
|
+
async function setPortPublic(codespaceName, port) {
|
|
15956
|
+
await execFileP4("gh", [
|
|
15957
|
+
"codespace",
|
|
15958
|
+
"ports",
|
|
15959
|
+
"visibility",
|
|
15960
|
+
`${port}:public`,
|
|
15961
|
+
"-c",
|
|
15962
|
+
codespaceName
|
|
15963
|
+
]);
|
|
15964
|
+
}
|
|
15965
|
+
async function waitForCodespacePortReady(url, timeoutMs = 15e3) {
|
|
15966
|
+
const start2 = Date.now();
|
|
15967
|
+
while (Date.now() - start2 < timeoutMs) {
|
|
15968
|
+
try {
|
|
15969
|
+
const res = await fetch(url, { method: "HEAD" });
|
|
15970
|
+
if (res.status < 500) return;
|
|
15971
|
+
} catch {
|
|
15972
|
+
}
|
|
15973
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
15974
|
+
}
|
|
15975
|
+
throw new Error(`Codespace forwarded URL ${url} not reachable after ${timeoutMs}ms.`);
|
|
15976
|
+
}
|
|
15977
|
+
|
|
15978
|
+
// src/services/preview/config-file.ts
|
|
15979
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
15980
|
+
var import_path5 = __toESM(require("path"));
|
|
15981
|
+
var CONFIG_DIR = ".codeam";
|
|
15982
|
+
var CONFIG_FILE = "preview.json";
|
|
15983
|
+
function configPath(cwd) {
|
|
15984
|
+
return import_path5.default.join(cwd, CONFIG_DIR, CONFIG_FILE);
|
|
15985
|
+
}
|
|
15986
|
+
var REQUIRED_FIELDS = [
|
|
15987
|
+
"framework",
|
|
15988
|
+
"command",
|
|
15989
|
+
"args",
|
|
15990
|
+
"port",
|
|
15991
|
+
"ready_pattern"
|
|
15992
|
+
];
|
|
15993
|
+
async function readPreviewConfig(cwd) {
|
|
15994
|
+
let raw;
|
|
15995
|
+
try {
|
|
15996
|
+
raw = await import_promises3.default.readFile(configPath(cwd), "utf-8");
|
|
15997
|
+
} catch {
|
|
15998
|
+
return null;
|
|
15999
|
+
}
|
|
16000
|
+
let parsed;
|
|
16001
|
+
try {
|
|
16002
|
+
parsed = JSON.parse(raw);
|
|
16003
|
+
} catch {
|
|
16004
|
+
return null;
|
|
16005
|
+
}
|
|
16006
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
16007
|
+
const obj = parsed;
|
|
16008
|
+
for (const field of REQUIRED_FIELDS) {
|
|
16009
|
+
if (!(field in obj)) return null;
|
|
16010
|
+
}
|
|
16011
|
+
return obj;
|
|
16012
|
+
}
|
|
16013
|
+
async function writePreviewConfig(cwd, detection) {
|
|
16014
|
+
const filePath = configPath(cwd);
|
|
16015
|
+
await import_promises3.default.mkdir(import_path5.default.dirname(filePath), { recursive: true });
|
|
16016
|
+
await import_promises3.default.writeFile(filePath, JSON.stringify(detection, null, 2) + "\n", "utf-8");
|
|
16017
|
+
}
|
|
16018
|
+
|
|
16019
|
+
// src/services/preview/parser.ts
|
|
16020
|
+
var REQUIRED_FIELDS2 = [
|
|
16021
|
+
"framework",
|
|
16022
|
+
"command",
|
|
16023
|
+
"args",
|
|
16024
|
+
"port",
|
|
16025
|
+
"ready_pattern"
|
|
16026
|
+
];
|
|
16027
|
+
function safeParseDetection(raw) {
|
|
16028
|
+
if (!raw) return null;
|
|
16029
|
+
const stripped = raw.replace(/^```(?:json)?\s*/m, "").replace(/\s*```\s*$/m, "").trim();
|
|
16030
|
+
let parsed;
|
|
16031
|
+
try {
|
|
16032
|
+
parsed = JSON.parse(stripped);
|
|
16033
|
+
} catch {
|
|
16034
|
+
return null;
|
|
16035
|
+
}
|
|
16036
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
16037
|
+
const obj = parsed;
|
|
16038
|
+
for (const field of REQUIRED_FIELDS2) {
|
|
16039
|
+
if (!(field in obj)) return null;
|
|
16040
|
+
}
|
|
16041
|
+
return obj;
|
|
16042
|
+
}
|
|
16043
|
+
var CLOUDFLARED_URL_RE = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/i;
|
|
16044
|
+
function parseCloudflaredUrl(stderr) {
|
|
16045
|
+
const match = stderr.match(CLOUDFLARED_URL_RE);
|
|
16046
|
+
return match ? match[0] : null;
|
|
16047
|
+
}
|
|
16048
|
+
var EXPO_URL_RE = /exp:\/\/[^\s]+\.exp\.host/;
|
|
16049
|
+
function parseExpoUrl(stdout) {
|
|
16050
|
+
const match = stdout.match(EXPO_URL_RE);
|
|
16051
|
+
return match ? match[0] : null;
|
|
16052
|
+
}
|
|
16053
|
+
|
|
16054
|
+
// src/services/preview/index.ts
|
|
16055
|
+
var activePreviews = /* @__PURE__ */ new Map();
|
|
16056
|
+
function registerPreview(sessionId, preview) {
|
|
16057
|
+
activePreviews.set(sessionId, preview);
|
|
16058
|
+
}
|
|
16059
|
+
async function killPreview(sessionId) {
|
|
16060
|
+
const preview = activePreviews.get(sessionId);
|
|
16061
|
+
if (!preview) return;
|
|
16062
|
+
if (preview.tunnel) {
|
|
16063
|
+
try {
|
|
16064
|
+
preview.tunnel.kill("SIGTERM");
|
|
16065
|
+
} catch {
|
|
16066
|
+
}
|
|
16067
|
+
}
|
|
16068
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
16069
|
+
try {
|
|
16070
|
+
preview.devServer.kill("SIGTERM");
|
|
16071
|
+
} catch {
|
|
16072
|
+
}
|
|
16073
|
+
const sigkillTimer = setTimeout(() => {
|
|
16074
|
+
try {
|
|
16075
|
+
preview.devServer.kill("SIGKILL");
|
|
16076
|
+
} catch {
|
|
16077
|
+
}
|
|
16078
|
+
try {
|
|
16079
|
+
preview.tunnel?.kill("SIGKILL");
|
|
16080
|
+
} catch {
|
|
16081
|
+
}
|
|
16082
|
+
}, 250);
|
|
16083
|
+
sigkillTimer.unref?.();
|
|
16084
|
+
activePreviews.delete(sessionId);
|
|
16085
|
+
}
|
|
16086
|
+
async function killAllPreviews() {
|
|
16087
|
+
const ids = Array.from(activePreviews.keys());
|
|
16088
|
+
await Promise.all(ids.map((id) => killPreview(id)));
|
|
16089
|
+
}
|
|
16090
|
+
|
|
15814
16091
|
// src/commands/start/handlers.ts
|
|
15815
16092
|
var pendingAttachmentFiles = /* @__PURE__ */ new Set();
|
|
15816
16093
|
function cleanupAttachmentTempFiles() {
|
|
15817
16094
|
for (const p2 of pendingAttachmentFiles) {
|
|
15818
16095
|
try {
|
|
15819
|
-
|
|
16096
|
+
fs29.unlinkSync(p2);
|
|
15820
16097
|
} catch {
|
|
15821
16098
|
}
|
|
15822
16099
|
}
|
|
@@ -15825,8 +16102,8 @@ function cleanupAttachmentTempFiles() {
|
|
|
15825
16102
|
function saveFilesTemp(files) {
|
|
15826
16103
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
15827
16104
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
15828
|
-
const tmpPath =
|
|
15829
|
-
|
|
16105
|
+
const tmpPath = path35.join(os24.tmpdir(), `codeam-${(0, import_crypto5.randomUUID)()}-${safeName}`);
|
|
16106
|
+
fs29.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
15830
16107
|
pendingAttachmentFiles.add(tmpPath);
|
|
15831
16108
|
return tmpPath;
|
|
15832
16109
|
});
|
|
@@ -15846,7 +16123,7 @@ var startTask = (ctx, _cmd, parsed) => {
|
|
|
15846
16123
|
setTimeout(() => {
|
|
15847
16124
|
for (const p2 of paths) {
|
|
15848
16125
|
try {
|
|
15849
|
-
|
|
16126
|
+
fs29.unlinkSync(p2);
|
|
15850
16127
|
} catch {
|
|
15851
16128
|
}
|
|
15852
16129
|
pendingAttachmentFiles.delete(p2);
|
|
@@ -15956,14 +16233,22 @@ var setKeepAlive = async (ctx, cmd) => {
|
|
|
15956
16233
|
} catch {
|
|
15957
16234
|
}
|
|
15958
16235
|
};
|
|
15959
|
-
var sessionTerminated = (ctx) => {
|
|
16236
|
+
var sessionTerminated = async (ctx, cmd) => {
|
|
15960
16237
|
showInfo("Session was deleted from the app \u2014 exiting.");
|
|
16238
|
+
try {
|
|
16239
|
+
await ctx.relay.sendResult(cmd.id, "success", { ok: true });
|
|
16240
|
+
} catch {
|
|
16241
|
+
}
|
|
16242
|
+
try {
|
|
16243
|
+
removeSession(ctx.sessionId);
|
|
16244
|
+
} catch {
|
|
16245
|
+
}
|
|
15961
16246
|
try {
|
|
15962
16247
|
ctx.agent.kill();
|
|
15963
16248
|
} catch {
|
|
15964
16249
|
}
|
|
15965
16250
|
try {
|
|
15966
|
-
const proc = (0,
|
|
16251
|
+
const proc = (0, import_child_process15.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
15967
16252
|
detached: true,
|
|
15968
16253
|
stdio: "ignore"
|
|
15969
16254
|
});
|
|
@@ -15985,7 +16270,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
15985
16270
|
}
|
|
15986
16271
|
if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
|
|
15987
16272
|
try {
|
|
15988
|
-
const stopProc = (0,
|
|
16273
|
+
const stopProc = (0, import_child_process15.spawn)(
|
|
15989
16274
|
"bash",
|
|
15990
16275
|
["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
|
|
15991
16276
|
{ detached: true, stdio: "ignore" }
|
|
@@ -15995,7 +16280,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
15995
16280
|
}
|
|
15996
16281
|
}
|
|
15997
16282
|
try {
|
|
15998
|
-
const proc = (0,
|
|
16283
|
+
const proc = (0, import_child_process15.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
15999
16284
|
detached: true,
|
|
16000
16285
|
stdio: "ignore"
|
|
16001
16286
|
});
|
|
@@ -16289,6 +16574,339 @@ function parseInsightText(text) {
|
|
|
16289
16574
|
securityNote: securityMatch?.[1]?.trim() || void 0
|
|
16290
16575
|
};
|
|
16291
16576
|
}
|
|
16577
|
+
var requestPreviewDetectH = (ctx) => {
|
|
16578
|
+
if (!ctx.pluginAuthToken) {
|
|
16579
|
+
log.info("preview", "no pluginAuthToken \u2014 skipping detect");
|
|
16580
|
+
return;
|
|
16581
|
+
}
|
|
16582
|
+
if (typeof ctx.runtime.generateOneShot !== "function") {
|
|
16583
|
+
log.info("preview", `runtime ${ctx.runtime.id} has no generateOneShot \u2014 emitting unsupported`);
|
|
16584
|
+
void postPreviewEvent({
|
|
16585
|
+
sessionId: ctx.sessionId,
|
|
16586
|
+
pluginId: ctx.pluginId,
|
|
16587
|
+
pluginAuthToken: ctx.pluginAuthToken,
|
|
16588
|
+
type: "preview_error",
|
|
16589
|
+
payload: {
|
|
16590
|
+
stage: "detection",
|
|
16591
|
+
message: `Preview detection isn't available on ${ctx.runtime.id} sessions yet \u2014 link a Claude or Codex agent.`
|
|
16592
|
+
}
|
|
16593
|
+
});
|
|
16594
|
+
return;
|
|
16595
|
+
}
|
|
16596
|
+
const pluginAuthToken = ctx.pluginAuthToken;
|
|
16597
|
+
void (async () => {
|
|
16598
|
+
const fromFile = await readPreviewConfig(process.cwd());
|
|
16599
|
+
if (fromFile) {
|
|
16600
|
+
log.info("preview", `detect: using .codeam/preview.json (${fromFile.framework})`);
|
|
16601
|
+
void postPreviewEvent({
|
|
16602
|
+
sessionId: ctx.sessionId,
|
|
16603
|
+
pluginId: ctx.pluginId,
|
|
16604
|
+
pluginAuthToken,
|
|
16605
|
+
type: "preview_detection_ready",
|
|
16606
|
+
payload: { detection: fromFile }
|
|
16607
|
+
});
|
|
16608
|
+
return;
|
|
16609
|
+
}
|
|
16610
|
+
void postPreviewEvent({
|
|
16611
|
+
sessionId: ctx.sessionId,
|
|
16612
|
+
pluginId: ctx.pluginId,
|
|
16613
|
+
pluginAuthToken,
|
|
16614
|
+
type: "preview_detection_pending"
|
|
16615
|
+
});
|
|
16616
|
+
log.info("preview", "detect: invoking generateOneShot");
|
|
16617
|
+
const startedAt = Date.now();
|
|
16618
|
+
const raw = await ctx.runtime.generateOneShot(PREVIEW_DETECT_PROMPT).catch((err) => {
|
|
16619
|
+
log.info("preview", `detect: generateOneShot threw: ${String(err)}`);
|
|
16620
|
+
return null;
|
|
16621
|
+
});
|
|
16622
|
+
const tookMs = Date.now() - startedAt;
|
|
16623
|
+
const detection = safeParseDetection(raw);
|
|
16624
|
+
if (!detection) {
|
|
16625
|
+
log.info("preview", `detect: invalid agent output after ${tookMs}ms`);
|
|
16626
|
+
void postPreviewEvent({
|
|
16627
|
+
sessionId: ctx.sessionId,
|
|
16628
|
+
pluginId: ctx.pluginId,
|
|
16629
|
+
pluginAuthToken,
|
|
16630
|
+
type: "preview_error",
|
|
16631
|
+
payload: {
|
|
16632
|
+
stage: "detection",
|
|
16633
|
+
message: "Agent returned invalid JSON. Try again, or add a .codeam/preview.json override."
|
|
16634
|
+
}
|
|
16635
|
+
});
|
|
16636
|
+
return;
|
|
16637
|
+
}
|
|
16638
|
+
if (detection.framework === "unsupported") {
|
|
16639
|
+
log.info("preview", "detect: framework=unsupported");
|
|
16640
|
+
void postPreviewEvent({
|
|
16641
|
+
sessionId: ctx.sessionId,
|
|
16642
|
+
pluginId: ctx.pluginId,
|
|
16643
|
+
pluginAuthToken,
|
|
16644
|
+
type: "preview_error",
|
|
16645
|
+
payload: {
|
|
16646
|
+
stage: "unsupported",
|
|
16647
|
+
message: detection.notes ?? "No dev server applies to this project."
|
|
16648
|
+
}
|
|
16649
|
+
});
|
|
16650
|
+
return;
|
|
16651
|
+
}
|
|
16652
|
+
log.info("preview", `detect: ${detection.framework} on :${detection.port} (took ${tookMs}ms)`);
|
|
16653
|
+
void postPreviewEvent({
|
|
16654
|
+
sessionId: ctx.sessionId,
|
|
16655
|
+
pluginId: ctx.pluginId,
|
|
16656
|
+
pluginAuthToken,
|
|
16657
|
+
type: "preview_detection_ready",
|
|
16658
|
+
payload: { detection }
|
|
16659
|
+
});
|
|
16660
|
+
})();
|
|
16661
|
+
};
|
|
16662
|
+
var previewStartH = (ctx, _cmd, parsed) => {
|
|
16663
|
+
if (!ctx.pluginAuthToken) {
|
|
16664
|
+
log.info("preview", "no pluginAuthToken \u2014 skipping start");
|
|
16665
|
+
return;
|
|
16666
|
+
}
|
|
16667
|
+
const detection = parsed.detection;
|
|
16668
|
+
if (!detection) {
|
|
16669
|
+
log.info("preview", "start: no detection in payload");
|
|
16670
|
+
return;
|
|
16671
|
+
}
|
|
16672
|
+
const pluginAuthToken = ctx.pluginAuthToken;
|
|
16673
|
+
void (async () => {
|
|
16674
|
+
void postPreviewEvent({
|
|
16675
|
+
sessionId: ctx.sessionId,
|
|
16676
|
+
pluginId: ctx.pluginId,
|
|
16677
|
+
pluginAuthToken,
|
|
16678
|
+
type: "preview_starting",
|
|
16679
|
+
payload: { framework: detection.framework, port: detection.port }
|
|
16680
|
+
});
|
|
16681
|
+
for (const setup of detection.setup_commands ?? []) {
|
|
16682
|
+
const exitCode = await runOnce(setup.cmd, setup.args, process.cwd(), detection.env);
|
|
16683
|
+
if (exitCode !== 0) {
|
|
16684
|
+
void postPreviewEvent({
|
|
16685
|
+
sessionId: ctx.sessionId,
|
|
16686
|
+
pluginId: ctx.pluginId,
|
|
16687
|
+
pluginAuthToken,
|
|
16688
|
+
type: "preview_error",
|
|
16689
|
+
payload: {
|
|
16690
|
+
stage: "spawn",
|
|
16691
|
+
message: `Setup failed (${setup.cmd} ${setup.args.join(" ")}, exit ${exitCode}).`
|
|
16692
|
+
}
|
|
16693
|
+
});
|
|
16694
|
+
return;
|
|
16695
|
+
}
|
|
16696
|
+
}
|
|
16697
|
+
const devServer = (0, import_child_process15.spawn)(detection.command, detection.args, {
|
|
16698
|
+
cwd: process.cwd(),
|
|
16699
|
+
env: { ...process.env, ...detection.env ?? {} },
|
|
16700
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
16701
|
+
});
|
|
16702
|
+
let readyMatched = false;
|
|
16703
|
+
let expoUrl = null;
|
|
16704
|
+
const readyRe = new RegExp(detection.ready_pattern);
|
|
16705
|
+
const onChunk = (chunk) => {
|
|
16706
|
+
const s = chunk.toString();
|
|
16707
|
+
if (!readyMatched && readyRe.test(s)) readyMatched = true;
|
|
16708
|
+
if (!expoUrl && detection.framework === "Expo") expoUrl = parseExpoUrl(s);
|
|
16709
|
+
};
|
|
16710
|
+
devServer.stdout.on("data", onChunk);
|
|
16711
|
+
devServer.stderr.on("data", onChunk);
|
|
16712
|
+
const readyDeadline = Date.now() + 12e4;
|
|
16713
|
+
while (!readyMatched && Date.now() < readyDeadline) {
|
|
16714
|
+
if (devServer.exitCode !== null) {
|
|
16715
|
+
void postPreviewEvent({
|
|
16716
|
+
sessionId: ctx.sessionId,
|
|
16717
|
+
pluginId: ctx.pluginId,
|
|
16718
|
+
pluginAuthToken,
|
|
16719
|
+
type: "preview_error",
|
|
16720
|
+
payload: {
|
|
16721
|
+
stage: "spawn",
|
|
16722
|
+
message: `Dev server exited (code ${devServer.exitCode}).`
|
|
16723
|
+
}
|
|
16724
|
+
});
|
|
16725
|
+
return;
|
|
16726
|
+
}
|
|
16727
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
16728
|
+
}
|
|
16729
|
+
if (!readyMatched) {
|
|
16730
|
+
try {
|
|
16731
|
+
devServer.kill("SIGTERM");
|
|
16732
|
+
} catch {
|
|
16733
|
+
}
|
|
16734
|
+
void postPreviewEvent({
|
|
16735
|
+
sessionId: ctx.sessionId,
|
|
16736
|
+
pluginId: ctx.pluginId,
|
|
16737
|
+
pluginAuthToken,
|
|
16738
|
+
type: "preview_error",
|
|
16739
|
+
payload: { stage: "ready_timeout", message: "Server didn't signal ready in 120s." }
|
|
16740
|
+
});
|
|
16741
|
+
return;
|
|
16742
|
+
}
|
|
16743
|
+
let tunnel = null;
|
|
16744
|
+
let url;
|
|
16745
|
+
if (detection.framework === "Expo") {
|
|
16746
|
+
if (!expoUrl) {
|
|
16747
|
+
const expoDeadline = Date.now() + 15e3;
|
|
16748
|
+
while (!expoUrl && Date.now() < expoDeadline) {
|
|
16749
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
16750
|
+
}
|
|
16751
|
+
}
|
|
16752
|
+
if (!expoUrl) {
|
|
16753
|
+
try {
|
|
16754
|
+
devServer.kill("SIGTERM");
|
|
16755
|
+
} catch {
|
|
16756
|
+
}
|
|
16757
|
+
void postPreviewEvent({
|
|
16758
|
+
sessionId: ctx.sessionId,
|
|
16759
|
+
pluginId: ctx.pluginId,
|
|
16760
|
+
pluginAuthToken,
|
|
16761
|
+
type: "preview_error",
|
|
16762
|
+
payload: { stage: "tunnel", message: "Expo did not report a tunnel URL." }
|
|
16763
|
+
});
|
|
16764
|
+
return;
|
|
16765
|
+
}
|
|
16766
|
+
url = expoUrl;
|
|
16767
|
+
} else if (isCodespaceSession()) {
|
|
16768
|
+
const codespaceName = process.env.CODESPACE_NAME;
|
|
16769
|
+
try {
|
|
16770
|
+
await setPortPublic(codespaceName, detection.port);
|
|
16771
|
+
} catch (e) {
|
|
16772
|
+
try {
|
|
16773
|
+
devServer.kill("SIGTERM");
|
|
16774
|
+
} catch {
|
|
16775
|
+
}
|
|
16776
|
+
void postPreviewEvent({
|
|
16777
|
+
sessionId: ctx.sessionId,
|
|
16778
|
+
pluginId: ctx.pluginId,
|
|
16779
|
+
pluginAuthToken,
|
|
16780
|
+
type: "preview_error",
|
|
16781
|
+
payload: { stage: "tunnel", message: `Failed to flip port public: ${e.message}` }
|
|
16782
|
+
});
|
|
16783
|
+
return;
|
|
16784
|
+
}
|
|
16785
|
+
url = buildCodespaceUrl(codespaceName, detection.port);
|
|
16786
|
+
try {
|
|
16787
|
+
await waitForCodespacePortReady(url, 15e3);
|
|
16788
|
+
} catch (e) {
|
|
16789
|
+
try {
|
|
16790
|
+
devServer.kill("SIGTERM");
|
|
16791
|
+
} catch {
|
|
16792
|
+
}
|
|
16793
|
+
void postPreviewEvent({
|
|
16794
|
+
sessionId: ctx.sessionId,
|
|
16795
|
+
pluginId: ctx.pluginId,
|
|
16796
|
+
pluginAuthToken,
|
|
16797
|
+
type: "preview_error",
|
|
16798
|
+
payload: { stage: "tunnel", message: e.message }
|
|
16799
|
+
});
|
|
16800
|
+
return;
|
|
16801
|
+
}
|
|
16802
|
+
} else {
|
|
16803
|
+
let bin;
|
|
16804
|
+
try {
|
|
16805
|
+
bin = await resolveCloudflared();
|
|
16806
|
+
} catch (e) {
|
|
16807
|
+
try {
|
|
16808
|
+
devServer.kill("SIGTERM");
|
|
16809
|
+
} catch {
|
|
16810
|
+
}
|
|
16811
|
+
void postPreviewEvent({
|
|
16812
|
+
sessionId: ctx.sessionId,
|
|
16813
|
+
pluginId: ctx.pluginId,
|
|
16814
|
+
pluginAuthToken,
|
|
16815
|
+
type: "preview_error",
|
|
16816
|
+
payload: { stage: "tunnel", message: e.message }
|
|
16817
|
+
});
|
|
16818
|
+
return;
|
|
16819
|
+
}
|
|
16820
|
+
tunnel = (0, import_child_process15.spawn)(bin, ["tunnel", "--url", `http://localhost:${detection.port}`], {
|
|
16821
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
16822
|
+
});
|
|
16823
|
+
let parsedUrl = null;
|
|
16824
|
+
const onTunnelChunk = (chunk) => {
|
|
16825
|
+
const s = chunk.toString();
|
|
16826
|
+
if (!parsedUrl) parsedUrl = parseCloudflaredUrl(s);
|
|
16827
|
+
};
|
|
16828
|
+
tunnel.stderr.on("data", onTunnelChunk);
|
|
16829
|
+
tunnel.stdout.on("data", onTunnelChunk);
|
|
16830
|
+
const tunnelDeadline = Date.now() + 15e3;
|
|
16831
|
+
while (!parsedUrl && Date.now() < tunnelDeadline) {
|
|
16832
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
16833
|
+
}
|
|
16834
|
+
if (!parsedUrl) {
|
|
16835
|
+
try {
|
|
16836
|
+
tunnel.kill("SIGTERM");
|
|
16837
|
+
} catch {
|
|
16838
|
+
}
|
|
16839
|
+
try {
|
|
16840
|
+
devServer.kill("SIGTERM");
|
|
16841
|
+
} catch {
|
|
16842
|
+
}
|
|
16843
|
+
void postPreviewEvent({
|
|
16844
|
+
sessionId: ctx.sessionId,
|
|
16845
|
+
pluginId: ctx.pluginId,
|
|
16846
|
+
pluginAuthToken,
|
|
16847
|
+
type: "preview_error",
|
|
16848
|
+
payload: { stage: "tunnel", message: "cloudflared did not emit a URL within 15s." }
|
|
16849
|
+
});
|
|
16850
|
+
return;
|
|
16851
|
+
}
|
|
16852
|
+
url = parsedUrl;
|
|
16853
|
+
}
|
|
16854
|
+
registerPreview(ctx.sessionId, {
|
|
16855
|
+
sessionId: ctx.sessionId,
|
|
16856
|
+
devServer,
|
|
16857
|
+
tunnel,
|
|
16858
|
+
url,
|
|
16859
|
+
framework: detection.framework
|
|
16860
|
+
});
|
|
16861
|
+
log.info("preview", `ready: ${detection.framework} at ${url}`);
|
|
16862
|
+
void postPreviewEvent({
|
|
16863
|
+
sessionId: ctx.sessionId,
|
|
16864
|
+
pluginId: ctx.pluginId,
|
|
16865
|
+
pluginAuthToken,
|
|
16866
|
+
type: "preview_ready",
|
|
16867
|
+
payload: { url, framework: detection.framework, port: detection.port }
|
|
16868
|
+
});
|
|
16869
|
+
})();
|
|
16870
|
+
};
|
|
16871
|
+
var previewStopH = (ctx) => {
|
|
16872
|
+
if (!ctx.pluginAuthToken) {
|
|
16873
|
+
log.info("preview", "no pluginAuthToken \u2014 skipping stop");
|
|
16874
|
+
return;
|
|
16875
|
+
}
|
|
16876
|
+
const pluginAuthToken = ctx.pluginAuthToken;
|
|
16877
|
+
void (async () => {
|
|
16878
|
+
await killPreview(ctx.sessionId);
|
|
16879
|
+
log.info("preview", `stopped session=${ctx.sessionId}`);
|
|
16880
|
+
void postPreviewEvent({
|
|
16881
|
+
sessionId: ctx.sessionId,
|
|
16882
|
+
pluginId: ctx.pluginId,
|
|
16883
|
+
pluginAuthToken,
|
|
16884
|
+
type: "preview_stopped",
|
|
16885
|
+
payload: { reason: "user" }
|
|
16886
|
+
});
|
|
16887
|
+
})();
|
|
16888
|
+
};
|
|
16889
|
+
function runOnce(cmd, args2, cwd, env) {
|
|
16890
|
+
return new Promise((resolve5) => {
|
|
16891
|
+
const child = (0, import_child_process15.spawn)(cmd, args2, {
|
|
16892
|
+
cwd,
|
|
16893
|
+
env: { ...process.env, ...env ?? {} },
|
|
16894
|
+
stdio: "inherit"
|
|
16895
|
+
});
|
|
16896
|
+
child.once("exit", (code) => resolve5(code));
|
|
16897
|
+
child.once("error", () => resolve5(-1));
|
|
16898
|
+
});
|
|
16899
|
+
}
|
|
16900
|
+
var savePreviewConfigH = (_ctx, _cmd, parsed) => {
|
|
16901
|
+
const detection = parsed.detection;
|
|
16902
|
+
if (!detection) {
|
|
16903
|
+
log.info("preview", "save_preview_config: no detection in payload");
|
|
16904
|
+
return;
|
|
16905
|
+
}
|
|
16906
|
+
void writePreviewConfig(process.cwd(), detection).catch((err) => {
|
|
16907
|
+
log.info("preview", `save_preview_config failed: ${String(err)}`);
|
|
16908
|
+
});
|
|
16909
|
+
};
|
|
16292
16910
|
var handlers = {
|
|
16293
16911
|
start_task: startTask,
|
|
16294
16912
|
provide_input: provideInput,
|
|
@@ -16323,7 +16941,11 @@ var handlers = {
|
|
|
16323
16941
|
apply_file_review: applyFileReviewH,
|
|
16324
16942
|
request_link_credentials: requestLinkCredentialsH,
|
|
16325
16943
|
request_ai_summary: requestAiSummaryH,
|
|
16326
|
-
request_ai_insight: requestAiInsightH
|
|
16944
|
+
request_ai_insight: requestAiInsightH,
|
|
16945
|
+
request_preview_detect: requestPreviewDetectH,
|
|
16946
|
+
preview_start: previewStartH,
|
|
16947
|
+
preview_stop: previewStopH,
|
|
16948
|
+
save_preview_config: savePreviewConfigH
|
|
16327
16949
|
};
|
|
16328
16950
|
async function dispatchCommand(ctx, cmd) {
|
|
16329
16951
|
const parsed = parsePayload2(startCommandSchema, cmd.payload);
|
|
@@ -16497,6 +17119,7 @@ async function start(requestedAgent) {
|
|
|
16497
17119
|
closeAllTerminals();
|
|
16498
17120
|
cleanupAttachmentTempFiles();
|
|
16499
17121
|
killActiveSpawnAndCaptureChildren();
|
|
17122
|
+
void killAllPreviews();
|
|
16500
17123
|
void shutdownTelemetry();
|
|
16501
17124
|
process.exit(0);
|
|
16502
17125
|
}
|
|
@@ -16702,8 +17325,8 @@ async function autoLinkAfterPair(opts) {
|
|
|
16702
17325
|
}
|
|
16703
17326
|
|
|
16704
17327
|
// src/commands/pair-auto.ts
|
|
16705
|
-
var
|
|
16706
|
-
var
|
|
17328
|
+
var fs30 = __toESM(require("fs"));
|
|
17329
|
+
var os25 = __toESM(require("os"));
|
|
16707
17330
|
var import_crypto7 = require("crypto");
|
|
16708
17331
|
|
|
16709
17332
|
// src/commands/start-infra-only.ts
|
|
@@ -16871,12 +17494,12 @@ function readTokenFromArgs(args2) {
|
|
|
16871
17494
|
}
|
|
16872
17495
|
const fileFlag = args2.find((a) => a.startsWith("--token-file="));
|
|
16873
17496
|
if (fileFlag) {
|
|
16874
|
-
const
|
|
17497
|
+
const path42 = fileFlag.slice("--token-file=".length);
|
|
16875
17498
|
try {
|
|
16876
|
-
const content =
|
|
16877
|
-
if (content.length === 0) fail(`--token-file ${
|
|
17499
|
+
const content = fs30.readFileSync(path42, "utf8").trim();
|
|
17500
|
+
if (content.length === 0) fail(`--token-file ${path42} is empty`);
|
|
16878
17501
|
try {
|
|
16879
|
-
|
|
17502
|
+
fs30.unlinkSync(path42);
|
|
16880
17503
|
} catch {
|
|
16881
17504
|
}
|
|
16882
17505
|
return content;
|
|
@@ -16902,7 +17525,7 @@ async function claimOnce(token, pluginId) {
|
|
|
16902
17525
|
pluginId,
|
|
16903
17526
|
ideName: "codeam-cli (codespace)",
|
|
16904
17527
|
ideVersion: process.env.npm_package_version ?? "unknown",
|
|
16905
|
-
hostname:
|
|
17528
|
+
hostname: os25.hostname(),
|
|
16906
17529
|
codespaceName: process.env.CODESPACE_NAME ?? "",
|
|
16907
17530
|
// Current git branch of the codespace's working directory, so the
|
|
16908
17531
|
// backend can populate `PairedSession.branch` for the codespace pair.
|
|
@@ -17139,11 +17762,11 @@ async function logout() {
|
|
|
17139
17762
|
var import_picocolors10 = __toESM(require("picocolors"));
|
|
17140
17763
|
|
|
17141
17764
|
// src/services/providers/github-codespaces.ts
|
|
17142
|
-
var
|
|
17143
|
-
var
|
|
17765
|
+
var import_child_process16 = require("child_process");
|
|
17766
|
+
var import_util4 = require("util");
|
|
17144
17767
|
var import_picocolors8 = __toESM(require("picocolors"));
|
|
17145
|
-
var
|
|
17146
|
-
var
|
|
17768
|
+
var path36 = __toESM(require("path"));
|
|
17769
|
+
var execFileP5 = (0, import_util4.promisify)(import_child_process16.execFile);
|
|
17147
17770
|
var MAX_BUFFER = 8 * 1024 * 1024;
|
|
17148
17771
|
function resetStdinForChild() {
|
|
17149
17772
|
if (process.stdin.isTTY) {
|
|
@@ -17160,11 +17783,11 @@ var GitHubCodespacesProvider = class {
|
|
|
17160
17783
|
available = true;
|
|
17161
17784
|
async authorize() {
|
|
17162
17785
|
try {
|
|
17163
|
-
await
|
|
17786
|
+
await execFileP5("gh", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
17164
17787
|
} catch {
|
|
17165
17788
|
await this.tryInstallGh();
|
|
17166
17789
|
try {
|
|
17167
|
-
await
|
|
17790
|
+
await execFileP5("gh", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
17168
17791
|
} catch {
|
|
17169
17792
|
throw new Error(
|
|
17170
17793
|
[
|
|
@@ -17180,14 +17803,14 @@ var GitHubCodespacesProvider = class {
|
|
|
17180
17803
|
}
|
|
17181
17804
|
let isAuthed = false;
|
|
17182
17805
|
try {
|
|
17183
|
-
await
|
|
17806
|
+
await execFileP5("gh", ["auth", "status"], { maxBuffer: MAX_BUFFER });
|
|
17184
17807
|
isAuthed = true;
|
|
17185
17808
|
} catch {
|
|
17186
17809
|
}
|
|
17187
17810
|
if (!isAuthed) {
|
|
17188
17811
|
resetStdinForChild();
|
|
17189
17812
|
await new Promise((resolve5, reject) => {
|
|
17190
|
-
const proc = (0,
|
|
17813
|
+
const proc = (0, import_child_process16.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
|
|
17191
17814
|
stdio: "inherit"
|
|
17192
17815
|
});
|
|
17193
17816
|
proc.on("exit", (code) => {
|
|
@@ -17221,7 +17844,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17221
17844
|
wt(noteLines.join("\n"), "One more permission needed");
|
|
17222
17845
|
resetStdinForChild();
|
|
17223
17846
|
const refreshCode = await new Promise((resolve5, reject) => {
|
|
17224
|
-
const proc = (0,
|
|
17847
|
+
const proc = (0, import_child_process16.spawn)(
|
|
17225
17848
|
"gh",
|
|
17226
17849
|
["auth", "refresh", "-h", "github.com", "-s", "codespace"],
|
|
17227
17850
|
{ stdio: "inherit" }
|
|
@@ -17256,7 +17879,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17256
17879
|
*/
|
|
17257
17880
|
async getActiveGhUser() {
|
|
17258
17881
|
try {
|
|
17259
|
-
const { stdout } = await
|
|
17882
|
+
const { stdout } = await execFileP5(
|
|
17260
17883
|
"gh",
|
|
17261
17884
|
["api", "user", "--jq", ".login"],
|
|
17262
17885
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -17276,7 +17899,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17276
17899
|
*/
|
|
17277
17900
|
async hasCodespaceScope() {
|
|
17278
17901
|
try {
|
|
17279
|
-
const { stdout } = await
|
|
17902
|
+
const { stdout } = await execFileP5(
|
|
17280
17903
|
"gh",
|
|
17281
17904
|
["api", "-i", "user"],
|
|
17282
17905
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -17325,7 +17948,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17325
17948
|
let installCmd = null;
|
|
17326
17949
|
if (platform2 === "darwin") {
|
|
17327
17950
|
try {
|
|
17328
|
-
await
|
|
17951
|
+
await execFileP5("brew", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
17329
17952
|
} catch {
|
|
17330
17953
|
wt(
|
|
17331
17954
|
[
|
|
@@ -17344,7 +17967,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17344
17967
|
};
|
|
17345
17968
|
} else if (platform2 === "win32") {
|
|
17346
17969
|
try {
|
|
17347
|
-
await
|
|
17970
|
+
await execFileP5("winget", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
17348
17971
|
} catch {
|
|
17349
17972
|
wt(
|
|
17350
17973
|
[
|
|
@@ -17371,7 +17994,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17371
17994
|
O2.step(`Installing gh via ${installCmd.describe}\u2026`);
|
|
17372
17995
|
resetStdinForChild();
|
|
17373
17996
|
const ok = await new Promise((resolve5) => {
|
|
17374
|
-
const proc = (0,
|
|
17997
|
+
const proc = (0, import_child_process16.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
|
|
17375
17998
|
proc.on("exit", (code) => resolve5(code === 0));
|
|
17376
17999
|
proc.on("error", () => resolve5(false));
|
|
17377
18000
|
});
|
|
@@ -17398,7 +18021,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17398
18021
|
);
|
|
17399
18022
|
resetStdinForChild();
|
|
17400
18023
|
await new Promise((resolve5, reject) => {
|
|
17401
|
-
const proc = (0,
|
|
18024
|
+
const proc = (0, import_child_process16.spawn)(
|
|
17402
18025
|
"gh",
|
|
17403
18026
|
["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
|
|
17404
18027
|
{ stdio: "inherit" }
|
|
@@ -17423,7 +18046,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17423
18046
|
"200"
|
|
17424
18047
|
);
|
|
17425
18048
|
try {
|
|
17426
|
-
const { stdout } = await
|
|
18049
|
+
const { stdout } = await execFileP5("gh", args2, { maxBuffer: MAX_BUFFER });
|
|
17427
18050
|
return JSON.parse(stdout);
|
|
17428
18051
|
} catch {
|
|
17429
18052
|
return [];
|
|
@@ -17432,7 +18055,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17432
18055
|
const own = await fetchRepos();
|
|
17433
18056
|
let orgRepos = [];
|
|
17434
18057
|
try {
|
|
17435
|
-
const { stdout } = await
|
|
18058
|
+
const { stdout } = await execFileP5(
|
|
17436
18059
|
"gh",
|
|
17437
18060
|
["api", "--paginate", "user/orgs", "--jq", ".[].login"],
|
|
17438
18061
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -17469,7 +18092,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17469
18092
|
*/
|
|
17470
18093
|
async listMachineTypes(projectId) {
|
|
17471
18094
|
try {
|
|
17472
|
-
const { stdout } = await
|
|
18095
|
+
const { stdout } = await execFileP5(
|
|
17473
18096
|
"gh",
|
|
17474
18097
|
["api", `/repos/${projectId}/codespaces/machines`],
|
|
17475
18098
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -17500,7 +18123,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17500
18123
|
const machine = machineTypeId ?? await this.pickDefaultMachine(projectId);
|
|
17501
18124
|
const args2 = ["codespace", "create", "-R", projectId, "--default-permissions"];
|
|
17502
18125
|
if (machine) args2.push("-m", machine);
|
|
17503
|
-
const { stdout } = await
|
|
18126
|
+
const { stdout } = await execFileP5(
|
|
17504
18127
|
"gh",
|
|
17505
18128
|
args2,
|
|
17506
18129
|
{ maxBuffer: MAX_BUFFER, timeout: 12e4 }
|
|
@@ -17540,7 +18163,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17540
18163
|
async waitUntilAvailable(name) {
|
|
17541
18164
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
17542
18165
|
while (Date.now() < deadline) {
|
|
17543
|
-
const { stdout } = await
|
|
18166
|
+
const { stdout } = await execFileP5(
|
|
17544
18167
|
"gh",
|
|
17545
18168
|
["codespace", "list", "--json", "name,state"],
|
|
17546
18169
|
{ maxBuffer: MAX_BUFFER }
|
|
@@ -17558,7 +18181,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17558
18181
|
}
|
|
17559
18182
|
async exec(workspaceId, command2) {
|
|
17560
18183
|
try {
|
|
17561
|
-
const { stdout, stderr } = await
|
|
18184
|
+
const { stdout, stderr } = await execFileP5(
|
|
17562
18185
|
"gh",
|
|
17563
18186
|
["codespace", "ssh", "-c", workspaceId, "--", command2],
|
|
17564
18187
|
{ maxBuffer: MAX_BUFFER, timeout: 6e5 }
|
|
@@ -17576,7 +18199,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17576
18199
|
async streamCommand(workspaceId, command2) {
|
|
17577
18200
|
resetStdinForChild();
|
|
17578
18201
|
return new Promise((resolve5, reject) => {
|
|
17579
|
-
const proc = (0,
|
|
18202
|
+
const proc = (0, import_child_process16.spawn)(
|
|
17580
18203
|
"gh",
|
|
17581
18204
|
["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
|
|
17582
18205
|
{ stdio: "inherit" }
|
|
@@ -17603,11 +18226,11 @@ var GitHubCodespacesProvider = class {
|
|
|
17603
18226
|
`mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
|
|
17604
18227
|
];
|
|
17605
18228
|
await new Promise((resolve5, reject) => {
|
|
17606
|
-
const tar = (0,
|
|
18229
|
+
const tar = (0, import_child_process16.spawn)("tar", tarArgs, {
|
|
17607
18230
|
stdio: ["ignore", "pipe", "pipe"],
|
|
17608
18231
|
env: tarEnv
|
|
17609
18232
|
});
|
|
17610
|
-
const ssh = (0,
|
|
18233
|
+
const ssh = (0, import_child_process16.spawn)("gh", sshArgs, {
|
|
17611
18234
|
stdio: [tar.stdout, "pipe", "pipe"]
|
|
17612
18235
|
});
|
|
17613
18236
|
let tarErr = "";
|
|
@@ -17631,7 +18254,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17631
18254
|
});
|
|
17632
18255
|
}
|
|
17633
18256
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
17634
|
-
const remoteDir =
|
|
18257
|
+
const remoteDir = path36.posix.dirname(remotePath);
|
|
17635
18258
|
const parts = [
|
|
17636
18259
|
`mkdir -p ${shellQuote(remoteDir)}`,
|
|
17637
18260
|
`cat > ${shellQuote(remotePath)}`
|
|
@@ -17641,7 +18264,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17641
18264
|
}
|
|
17642
18265
|
const cmd = parts.join(" && ");
|
|
17643
18266
|
await new Promise((resolve5, reject) => {
|
|
17644
|
-
const proc = (0,
|
|
18267
|
+
const proc = (0, import_child_process16.spawn)(
|
|
17645
18268
|
"gh",
|
|
17646
18269
|
["codespace", "ssh", "-c", workspaceId, "--", cmd],
|
|
17647
18270
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -17663,7 +18286,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17663
18286
|
try {
|
|
17664
18287
|
const args2 = ["codespace", "list", "--json", "name,displayName,state,lastUsedAt"];
|
|
17665
18288
|
if (projectId) args2.push("--repo", projectId);
|
|
17666
|
-
const { stdout } = await
|
|
18289
|
+
const { stdout } = await execFileP5("gh", args2, { maxBuffer: MAX_BUFFER });
|
|
17667
18290
|
const list = JSON.parse(stdout);
|
|
17668
18291
|
return list.map((c2) => ({
|
|
17669
18292
|
id: c2.name,
|
|
@@ -17678,7 +18301,7 @@ var GitHubCodespacesProvider = class {
|
|
|
17678
18301
|
}
|
|
17679
18302
|
async startWorkspace(workspaceId) {
|
|
17680
18303
|
try {
|
|
17681
|
-
await
|
|
18304
|
+
await execFileP5(
|
|
17682
18305
|
"gh",
|
|
17683
18306
|
["api", "-X", "POST", `/user/codespaces/${workspaceId}/start`],
|
|
17684
18307
|
{ maxBuffer: MAX_BUFFER, timeout: 6e4 }
|
|
@@ -17699,11 +18322,11 @@ function shellQuote(s) {
|
|
|
17699
18322
|
}
|
|
17700
18323
|
|
|
17701
18324
|
// src/services/providers/gitpod.ts
|
|
17702
|
-
var
|
|
17703
|
-
var
|
|
17704
|
-
var
|
|
18325
|
+
var import_child_process17 = require("child_process");
|
|
18326
|
+
var import_util5 = require("util");
|
|
18327
|
+
var path37 = __toESM(require("path"));
|
|
17705
18328
|
var import_picocolors9 = __toESM(require("picocolors"));
|
|
17706
|
-
var
|
|
18329
|
+
var execFileP6 = (0, import_util5.promisify)(import_child_process17.execFile);
|
|
17707
18330
|
var MAX_BUFFER2 = 8 * 1024 * 1024;
|
|
17708
18331
|
function resetStdinForChild2() {
|
|
17709
18332
|
if (process.stdin.isTTY) {
|
|
@@ -17720,7 +18343,7 @@ var GitpodProvider = class {
|
|
|
17720
18343
|
available = true;
|
|
17721
18344
|
async authorize() {
|
|
17722
18345
|
try {
|
|
17723
|
-
await
|
|
18346
|
+
await execFileP6("gitpod", ["--version"], { maxBuffer: MAX_BUFFER2 });
|
|
17724
18347
|
} catch {
|
|
17725
18348
|
throw new Error(
|
|
17726
18349
|
[
|
|
@@ -17733,7 +18356,7 @@ var GitpodProvider = class {
|
|
|
17733
18356
|
);
|
|
17734
18357
|
}
|
|
17735
18358
|
try {
|
|
17736
|
-
await
|
|
18359
|
+
await execFileP6("gitpod", ["whoami"], { maxBuffer: MAX_BUFFER2 });
|
|
17737
18360
|
return;
|
|
17738
18361
|
} catch {
|
|
17739
18362
|
}
|
|
@@ -17743,7 +18366,7 @@ var GitpodProvider = class {
|
|
|
17743
18366
|
);
|
|
17744
18367
|
resetStdinForChild2();
|
|
17745
18368
|
await new Promise((resolve5, reject) => {
|
|
17746
|
-
const proc = (0,
|
|
18369
|
+
const proc = (0, import_child_process17.spawn)("gitpod", ["login"], { stdio: "inherit" });
|
|
17747
18370
|
proc.on("exit", (code) => {
|
|
17748
18371
|
if (code === 0) resolve5();
|
|
17749
18372
|
else reject(new Error("gitpod login failed."));
|
|
@@ -17753,7 +18376,7 @@ var GitpodProvider = class {
|
|
|
17753
18376
|
}
|
|
17754
18377
|
async listProjects() {
|
|
17755
18378
|
try {
|
|
17756
|
-
const { stdout } = await
|
|
18379
|
+
const { stdout } = await execFileP6(
|
|
17757
18380
|
"gitpod",
|
|
17758
18381
|
["workspace", "list", "--output", "json", "--limit", "200"],
|
|
17759
18382
|
{ maxBuffer: MAX_BUFFER2 }
|
|
@@ -17786,7 +18409,7 @@ var GitpodProvider = class {
|
|
|
17786
18409
|
async createWorkspace(projectId, machineTypeId) {
|
|
17787
18410
|
const args2 = ["workspace", "create", projectId, "--start", "--output", "json"];
|
|
17788
18411
|
if (machineTypeId) args2.push("--class", machineTypeId);
|
|
17789
|
-
const { stdout } = await
|
|
18412
|
+
const { stdout } = await execFileP6("gitpod", args2, {
|
|
17790
18413
|
maxBuffer: MAX_BUFFER2,
|
|
17791
18414
|
timeout: 3e5
|
|
17792
18415
|
});
|
|
@@ -17810,7 +18433,7 @@ var GitpodProvider = class {
|
|
|
17810
18433
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
17811
18434
|
while (Date.now() < deadline) {
|
|
17812
18435
|
try {
|
|
17813
|
-
const { stdout } = await
|
|
18436
|
+
const { stdout } = await execFileP6(
|
|
17814
18437
|
"gitpod",
|
|
17815
18438
|
["workspace", "get", workspaceId, "--output", "json"],
|
|
17816
18439
|
{ maxBuffer: MAX_BUFFER2 }
|
|
@@ -17828,7 +18451,7 @@ var GitpodProvider = class {
|
|
|
17828
18451
|
}
|
|
17829
18452
|
async listMachineTypes(_projectId) {
|
|
17830
18453
|
try {
|
|
17831
|
-
const { stdout } = await
|
|
18454
|
+
const { stdout } = await execFileP6(
|
|
17832
18455
|
"gitpod",
|
|
17833
18456
|
["organization", "list-classes", "--output", "json"],
|
|
17834
18457
|
{ maxBuffer: MAX_BUFFER2 }
|
|
@@ -17846,7 +18469,7 @@ var GitpodProvider = class {
|
|
|
17846
18469
|
async listExistingWorkspaces(projectId) {
|
|
17847
18470
|
try {
|
|
17848
18471
|
const args2 = ["workspace", "list", "--output", "json", "--limit", "200"];
|
|
17849
|
-
const { stdout } = await
|
|
18472
|
+
const { stdout } = await execFileP6("gitpod", args2, { maxBuffer: MAX_BUFFER2 });
|
|
17850
18473
|
const list = JSON.parse(stdout);
|
|
17851
18474
|
return list.filter((w3) => !projectId || w3.contextUrl === projectId).map((w3) => ({
|
|
17852
18475
|
id: w3.id,
|
|
@@ -17861,7 +18484,7 @@ var GitpodProvider = class {
|
|
|
17861
18484
|
}
|
|
17862
18485
|
async startWorkspace(workspaceId) {
|
|
17863
18486
|
try {
|
|
17864
|
-
await
|
|
18487
|
+
await execFileP6(
|
|
17865
18488
|
"gitpod",
|
|
17866
18489
|
["workspace", "start", workspaceId],
|
|
17867
18490
|
{ maxBuffer: MAX_BUFFER2, timeout: 6e4 }
|
|
@@ -17877,7 +18500,7 @@ var GitpodProvider = class {
|
|
|
17877
18500
|
}
|
|
17878
18501
|
async exec(workspaceId, command2) {
|
|
17879
18502
|
try {
|
|
17880
|
-
const { stdout, stderr } = await
|
|
18503
|
+
const { stdout, stderr } = await execFileP6(
|
|
17881
18504
|
"gitpod",
|
|
17882
18505
|
["workspace", "ssh", workspaceId, "--", command2],
|
|
17883
18506
|
{ maxBuffer: MAX_BUFFER2, timeout: 6e5 }
|
|
@@ -17895,7 +18518,7 @@ var GitpodProvider = class {
|
|
|
17895
18518
|
async streamCommand(workspaceId, command2) {
|
|
17896
18519
|
resetStdinForChild2();
|
|
17897
18520
|
return new Promise((resolve5, reject) => {
|
|
17898
|
-
const proc = (0,
|
|
18521
|
+
const proc = (0, import_child_process17.spawn)(
|
|
17899
18522
|
"gitpod",
|
|
17900
18523
|
["workspace", "ssh", workspaceId, "--", "-tt", command2],
|
|
17901
18524
|
{ stdio: "inherit" }
|
|
@@ -17915,11 +18538,11 @@ var GitpodProvider = class {
|
|
|
17915
18538
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
17916
18539
|
const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
|
|
17917
18540
|
await new Promise((resolve5, reject) => {
|
|
17918
|
-
const tar = (0,
|
|
18541
|
+
const tar = (0, import_child_process17.spawn)("tar", tarArgs, {
|
|
17919
18542
|
stdio: ["ignore", "pipe", "pipe"],
|
|
17920
18543
|
env: tarEnv
|
|
17921
18544
|
});
|
|
17922
|
-
const ssh = (0,
|
|
18545
|
+
const ssh = (0, import_child_process17.spawn)(
|
|
17923
18546
|
"gitpod",
|
|
17924
18547
|
["workspace", "ssh", workspaceId, "--", remoteCmd],
|
|
17925
18548
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -17941,7 +18564,7 @@ var GitpodProvider = class {
|
|
|
17941
18564
|
});
|
|
17942
18565
|
}
|
|
17943
18566
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
17944
|
-
const remoteDir =
|
|
18567
|
+
const remoteDir = path37.posix.dirname(remotePath);
|
|
17945
18568
|
const parts = [
|
|
17946
18569
|
`mkdir -p ${shellQuote2(remoteDir)}`,
|
|
17947
18570
|
`cat > ${shellQuote2(remotePath)}`
|
|
@@ -17951,7 +18574,7 @@ var GitpodProvider = class {
|
|
|
17951
18574
|
}
|
|
17952
18575
|
const cmd = parts.join(" && ");
|
|
17953
18576
|
await new Promise((resolve5, reject) => {
|
|
17954
|
-
const proc = (0,
|
|
18577
|
+
const proc = (0, import_child_process17.spawn)(
|
|
17955
18578
|
"gitpod",
|
|
17956
18579
|
["workspace", "ssh", workspaceId, "--", cmd],
|
|
17957
18580
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -17975,10 +18598,10 @@ function shellQuote2(s) {
|
|
|
17975
18598
|
}
|
|
17976
18599
|
|
|
17977
18600
|
// src/services/providers/gitlab-workspaces.ts
|
|
17978
|
-
var
|
|
17979
|
-
var
|
|
17980
|
-
var
|
|
17981
|
-
var
|
|
18601
|
+
var import_child_process18 = require("child_process");
|
|
18602
|
+
var import_util6 = require("util");
|
|
18603
|
+
var path38 = __toESM(require("path"));
|
|
18604
|
+
var execFileP7 = (0, import_util6.promisify)(import_child_process18.execFile);
|
|
17982
18605
|
var MAX_BUFFER3 = 8 * 1024 * 1024;
|
|
17983
18606
|
var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
|
|
17984
18607
|
function resetStdinForChild3() {
|
|
@@ -17996,7 +18619,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
17996
18619
|
available = true;
|
|
17997
18620
|
async authorize() {
|
|
17998
18621
|
try {
|
|
17999
|
-
await
|
|
18622
|
+
await execFileP7("glab", ["--version"], { maxBuffer: MAX_BUFFER3 });
|
|
18000
18623
|
} catch {
|
|
18001
18624
|
throw new Error(
|
|
18002
18625
|
[
|
|
@@ -18010,7 +18633,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
18010
18633
|
);
|
|
18011
18634
|
}
|
|
18012
18635
|
try {
|
|
18013
|
-
await
|
|
18636
|
+
await execFileP7("glab", ["auth", "status"], { maxBuffer: MAX_BUFFER3 });
|
|
18014
18637
|
return;
|
|
18015
18638
|
} catch {
|
|
18016
18639
|
}
|
|
@@ -18020,7 +18643,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
18020
18643
|
);
|
|
18021
18644
|
resetStdinForChild3();
|
|
18022
18645
|
await new Promise((resolve5, reject) => {
|
|
18023
|
-
const proc = (0,
|
|
18646
|
+
const proc = (0, import_child_process18.spawn)(
|
|
18024
18647
|
"glab",
|
|
18025
18648
|
["auth", "login", "--scopes", "api,read_user,read_repository"],
|
|
18026
18649
|
{ stdio: "inherit" }
|
|
@@ -18034,7 +18657,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
18034
18657
|
}
|
|
18035
18658
|
async listProjects() {
|
|
18036
18659
|
try {
|
|
18037
|
-
const { stdout } = await
|
|
18660
|
+
const { stdout } = await execFileP7(
|
|
18038
18661
|
"glab",
|
|
18039
18662
|
["repo", "list", "--per-page", "200", "--output", "json"],
|
|
18040
18663
|
{ maxBuffer: MAX_BUFFER3 }
|
|
@@ -18166,7 +18789,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
18166
18789
|
async exec(workspaceId, command2) {
|
|
18167
18790
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
18168
18791
|
try {
|
|
18169
|
-
const { stdout, stderr } = await
|
|
18792
|
+
const { stdout, stderr } = await execFileP7(
|
|
18170
18793
|
"ssh",
|
|
18171
18794
|
[
|
|
18172
18795
|
"-o",
|
|
@@ -18192,7 +18815,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
18192
18815
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
18193
18816
|
resetStdinForChild3();
|
|
18194
18817
|
return new Promise((resolve5, reject) => {
|
|
18195
|
-
const proc = (0,
|
|
18818
|
+
const proc = (0, import_child_process18.spawn)(
|
|
18196
18819
|
"ssh",
|
|
18197
18820
|
["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
|
|
18198
18821
|
{ stdio: "inherit" }
|
|
@@ -18213,8 +18836,8 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
18213
18836
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
18214
18837
|
const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
|
|
18215
18838
|
await new Promise((resolve5, reject) => {
|
|
18216
|
-
const tar = (0,
|
|
18217
|
-
const ssh = (0,
|
|
18839
|
+
const tar = (0, import_child_process18.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
18840
|
+
const ssh = (0, import_child_process18.spawn)(
|
|
18218
18841
|
"ssh",
|
|
18219
18842
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, remoteCmd],
|
|
18220
18843
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -18237,14 +18860,14 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
18237
18860
|
}
|
|
18238
18861
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
18239
18862
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
18240
|
-
const remoteDir =
|
|
18863
|
+
const remoteDir = path38.posix.dirname(remotePath);
|
|
18241
18864
|
const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
|
|
18242
18865
|
if (options.mode != null) {
|
|
18243
18866
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
|
|
18244
18867
|
}
|
|
18245
18868
|
const cmd = parts.join(" && ");
|
|
18246
18869
|
await new Promise((resolve5, reject) => {
|
|
18247
|
-
const proc = (0,
|
|
18870
|
+
const proc = (0, import_child_process18.spawn)(
|
|
18248
18871
|
"ssh",
|
|
18249
18872
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
|
|
18250
18873
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -18269,7 +18892,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
18269
18892
|
*/
|
|
18270
18893
|
async getGlabToken() {
|
|
18271
18894
|
try {
|
|
18272
|
-
const { stdout, stderr } = await
|
|
18895
|
+
const { stdout, stderr } = await execFileP7(
|
|
18273
18896
|
"glab",
|
|
18274
18897
|
["auth", "status", "--show-token"],
|
|
18275
18898
|
{ maxBuffer: MAX_BUFFER3 }
|
|
@@ -18303,10 +18926,10 @@ function shellQuote3(s) {
|
|
|
18303
18926
|
}
|
|
18304
18927
|
|
|
18305
18928
|
// src/services/providers/railway.ts
|
|
18306
|
-
var
|
|
18307
|
-
var
|
|
18308
|
-
var
|
|
18309
|
-
var
|
|
18929
|
+
var import_child_process19 = require("child_process");
|
|
18930
|
+
var import_util7 = require("util");
|
|
18931
|
+
var path39 = __toESM(require("path"));
|
|
18932
|
+
var execFileP8 = (0, import_util7.promisify)(import_child_process19.execFile);
|
|
18310
18933
|
var MAX_BUFFER4 = 8 * 1024 * 1024;
|
|
18311
18934
|
function resetStdinForChild4() {
|
|
18312
18935
|
if (process.stdin.isTTY) {
|
|
@@ -18323,7 +18946,7 @@ var RailwayProvider = class {
|
|
|
18323
18946
|
available = true;
|
|
18324
18947
|
async authorize() {
|
|
18325
18948
|
try {
|
|
18326
|
-
await
|
|
18949
|
+
await execFileP8("railway", ["--version"], { maxBuffer: MAX_BUFFER4 });
|
|
18327
18950
|
} catch {
|
|
18328
18951
|
throw new Error(
|
|
18329
18952
|
[
|
|
@@ -18337,7 +18960,7 @@ var RailwayProvider = class {
|
|
|
18337
18960
|
);
|
|
18338
18961
|
}
|
|
18339
18962
|
try {
|
|
18340
|
-
await
|
|
18963
|
+
await execFileP8("railway", ["whoami"], { maxBuffer: MAX_BUFFER4 });
|
|
18341
18964
|
return;
|
|
18342
18965
|
} catch {
|
|
18343
18966
|
}
|
|
@@ -18347,7 +18970,7 @@ var RailwayProvider = class {
|
|
|
18347
18970
|
);
|
|
18348
18971
|
resetStdinForChild4();
|
|
18349
18972
|
await new Promise((resolve5, reject) => {
|
|
18350
|
-
const proc = (0,
|
|
18973
|
+
const proc = (0, import_child_process19.spawn)("railway", ["login"], { stdio: "inherit" });
|
|
18351
18974
|
proc.on("exit", (code) => {
|
|
18352
18975
|
if (code === 0) resolve5();
|
|
18353
18976
|
else reject(new Error("railway login failed."));
|
|
@@ -18357,7 +18980,7 @@ var RailwayProvider = class {
|
|
|
18357
18980
|
}
|
|
18358
18981
|
async listProjects() {
|
|
18359
18982
|
try {
|
|
18360
|
-
const { stdout } = await
|
|
18983
|
+
const { stdout } = await execFileP8(
|
|
18361
18984
|
"railway",
|
|
18362
18985
|
["list", "--json"],
|
|
18363
18986
|
{ maxBuffer: MAX_BUFFER4 }
|
|
@@ -18373,7 +18996,7 @@ var RailwayProvider = class {
|
|
|
18373
18996
|
}));
|
|
18374
18997
|
} catch {
|
|
18375
18998
|
try {
|
|
18376
|
-
const { stdout } = await
|
|
18999
|
+
const { stdout } = await execFileP8("railway", ["list"], { maxBuffer: MAX_BUFFER4 });
|
|
18377
19000
|
const projects = [];
|
|
18378
19001
|
for (const line of stdout.split("\n")) {
|
|
18379
19002
|
const trimmed = line.trim();
|
|
@@ -18424,7 +19047,7 @@ var RailwayProvider = class {
|
|
|
18424
19047
|
async listExistingWorkspaces(projectId) {
|
|
18425
19048
|
if (!projectId) return [];
|
|
18426
19049
|
try {
|
|
18427
|
-
const { stdout } = await
|
|
19050
|
+
const { stdout } = await execFileP8(
|
|
18428
19051
|
"railway",
|
|
18429
19052
|
["service", "list", "--project", projectId, "--json"],
|
|
18430
19053
|
{ maxBuffer: MAX_BUFFER4 }
|
|
@@ -18449,7 +19072,7 @@ var RailwayProvider = class {
|
|
|
18449
19072
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
18450
19073
|
}
|
|
18451
19074
|
try {
|
|
18452
|
-
await
|
|
19075
|
+
await execFileP8(
|
|
18453
19076
|
"railway",
|
|
18454
19077
|
["service", "restart", "--service", serviceId, "--project", projectId],
|
|
18455
19078
|
{ maxBuffer: MAX_BUFFER4, timeout: 6e4 }
|
|
@@ -18468,7 +19091,7 @@ var RailwayProvider = class {
|
|
|
18468
19091
|
};
|
|
18469
19092
|
}
|
|
18470
19093
|
try {
|
|
18471
|
-
const { stdout, stderr } = await
|
|
19094
|
+
const { stdout, stderr } = await execFileP8(
|
|
18472
19095
|
"railway",
|
|
18473
19096
|
["run", "--project", projectId, "--service", serviceId, "--", "bash", "-lc", command2],
|
|
18474
19097
|
{ maxBuffer: MAX_BUFFER4, timeout: 6e5 }
|
|
@@ -18490,7 +19113,7 @@ var RailwayProvider = class {
|
|
|
18490
19113
|
}
|
|
18491
19114
|
resetStdinForChild4();
|
|
18492
19115
|
return new Promise((resolve5, reject) => {
|
|
18493
|
-
const proc = (0,
|
|
19116
|
+
const proc = (0, import_child_process19.spawn)(
|
|
18494
19117
|
"railway",
|
|
18495
19118
|
["shell", "--project", projectId, "--service", serviceId, "--command", command2],
|
|
18496
19119
|
{ stdio: "inherit" }
|
|
@@ -18514,8 +19137,8 @@ var RailwayProvider = class {
|
|
|
18514
19137
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
18515
19138
|
const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
|
|
18516
19139
|
await new Promise((resolve5, reject) => {
|
|
18517
|
-
const tar = (0,
|
|
18518
|
-
const sh = (0,
|
|
19140
|
+
const tar = (0, import_child_process19.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
19141
|
+
const sh = (0, import_child_process19.spawn)(
|
|
18519
19142
|
"railway",
|
|
18520
19143
|
["shell", "--project", projectId, "--service", serviceId, "--command", remoteCmd],
|
|
18521
19144
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -18541,14 +19164,14 @@ var RailwayProvider = class {
|
|
|
18541
19164
|
if (!projectId || !serviceId) {
|
|
18542
19165
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
18543
19166
|
}
|
|
18544
|
-
const remoteDir =
|
|
19167
|
+
const remoteDir = path39.posix.dirname(remotePath);
|
|
18545
19168
|
const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
|
|
18546
19169
|
if (options.mode != null) {
|
|
18547
19170
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
|
|
18548
19171
|
}
|
|
18549
19172
|
const cmd = parts.join(" && ");
|
|
18550
19173
|
await new Promise((resolve5, reject) => {
|
|
18551
|
-
const proc = (0,
|
|
19174
|
+
const proc = (0, import_child_process19.spawn)(
|
|
18552
19175
|
"railway",
|
|
18553
19176
|
["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
|
|
18554
19177
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -19072,10 +19695,10 @@ async function probeCodeamPair(provider, workspace) {
|
|
|
19072
19695
|
}
|
|
19073
19696
|
async function stopWorkspaceFromLocal(target) {
|
|
19074
19697
|
if (target.provider.id === "github-codespaces") {
|
|
19075
|
-
const { execFile:
|
|
19076
|
-
const { promisify:
|
|
19077
|
-
const
|
|
19078
|
-
await
|
|
19698
|
+
const { execFile: execFile9 } = await import("child_process");
|
|
19699
|
+
const { promisify: promisify10 } = await import("util");
|
|
19700
|
+
const execFileP9 = promisify10(execFile9);
|
|
19701
|
+
await execFileP9("gh", ["codespace", "stop", "-c", target.id], { maxBuffer: 8 * 1024 * 1024 });
|
|
19079
19702
|
return;
|
|
19080
19703
|
}
|
|
19081
19704
|
}
|
|
@@ -19084,8 +19707,8 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
19084
19707
|
var import_node_dns = require("dns");
|
|
19085
19708
|
var import_node_util4 = require("util");
|
|
19086
19709
|
var import_node_crypto6 = require("crypto");
|
|
19087
|
-
var
|
|
19088
|
-
var
|
|
19710
|
+
var fs31 = __toESM(require("fs"));
|
|
19711
|
+
var path40 = __toESM(require("path"));
|
|
19089
19712
|
var import_picocolors12 = __toESM(require("picocolors"));
|
|
19090
19713
|
var dnsResolveP = (0, import_node_util4.promisify)(import_node_dns.resolve);
|
|
19091
19714
|
async function checkDns(apiBase) {
|
|
@@ -19141,13 +19764,13 @@ async function checkHealth(apiBase) {
|
|
|
19141
19764
|
}
|
|
19142
19765
|
}
|
|
19143
19766
|
function checkConfigDir() {
|
|
19144
|
-
const dir =
|
|
19767
|
+
const dir = path40.join(require("os").homedir(), ".codeam");
|
|
19145
19768
|
try {
|
|
19146
|
-
|
|
19147
|
-
const probe =
|
|
19148
|
-
|
|
19149
|
-
const read =
|
|
19150
|
-
|
|
19769
|
+
fs31.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
19770
|
+
const probe = path40.join(dir, ".doctor-probe");
|
|
19771
|
+
fs31.writeFileSync(probe, "ok", { mode: 384 });
|
|
19772
|
+
const read = fs31.readFileSync(probe, "utf8");
|
|
19773
|
+
fs31.unlinkSync(probe);
|
|
19151
19774
|
if (read !== "ok") throw new Error("write/read round-trip mismatch");
|
|
19152
19775
|
return {
|
|
19153
19776
|
id: "config-dir",
|
|
@@ -19187,9 +19810,9 @@ function checkSessions() {
|
|
|
19187
19810
|
}
|
|
19188
19811
|
}
|
|
19189
19812
|
function checkAgentBinaries() {
|
|
19190
|
-
const
|
|
19813
|
+
const os27 = createOsStrategy();
|
|
19191
19814
|
return getEnabledAgents().map((meta) => {
|
|
19192
|
-
const found =
|
|
19815
|
+
const found = os27.findInPath(meta.binaryName);
|
|
19193
19816
|
return {
|
|
19194
19817
|
id: `agent-${meta.id}`,
|
|
19195
19818
|
label: `Agent binary: ${meta.displayName} (${meta.binaryName})`,
|
|
@@ -19211,7 +19834,7 @@ function checkNodePty() {
|
|
|
19211
19834
|
detail: "not required on this platform"
|
|
19212
19835
|
};
|
|
19213
19836
|
}
|
|
19214
|
-
const vendoredPath =
|
|
19837
|
+
const vendoredPath = path40.join(__dirname, "vendor", "node-pty");
|
|
19215
19838
|
for (const target of [vendoredPath, "node-pty"]) {
|
|
19216
19839
|
try {
|
|
19217
19840
|
require(target);
|
|
@@ -19253,7 +19876,7 @@ function checkChokidar() {
|
|
|
19253
19876
|
}
|
|
19254
19877
|
async function doctor(args2 = []) {
|
|
19255
19878
|
const json = args2.includes("--json");
|
|
19256
|
-
const cliVersion = true ? "2.
|
|
19879
|
+
const cliVersion = true ? "2.26.0" : "0.0.0-dev";
|
|
19257
19880
|
const apiBase = resolveApiBaseUrl();
|
|
19258
19881
|
const diagnosticId = (0, import_node_crypto6.randomUUID)();
|
|
19259
19882
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -19452,7 +20075,7 @@ async function completion(args2) {
|
|
|
19452
20075
|
// src/commands/version.ts
|
|
19453
20076
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
19454
20077
|
function version2() {
|
|
19455
|
-
const v = true ? "2.
|
|
20078
|
+
const v = true ? "2.26.0" : "unknown";
|
|
19456
20079
|
console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
|
|
19457
20080
|
}
|
|
19458
20081
|
|
|
@@ -19580,9 +20203,9 @@ function tryShowSubcommandHelp(cmd, args2) {
|
|
|
19580
20203
|
var _subcommandHelpKeys = Object.keys(HELPS);
|
|
19581
20204
|
|
|
19582
20205
|
// src/lib/updateNotifier.ts
|
|
19583
|
-
var
|
|
19584
|
-
var
|
|
19585
|
-
var
|
|
20206
|
+
var fs32 = __toESM(require("fs"));
|
|
20207
|
+
var os26 = __toESM(require("os"));
|
|
20208
|
+
var path41 = __toESM(require("path"));
|
|
19586
20209
|
var https7 = __toESM(require("https"));
|
|
19587
20210
|
var import_picocolors16 = __toESM(require("picocolors"));
|
|
19588
20211
|
var PKG_NAME = "codeam-cli";
|
|
@@ -19590,12 +20213,12 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
|
19590
20213
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
19591
20214
|
var REQUEST_TIMEOUT_MS = 1500;
|
|
19592
20215
|
function cachePath() {
|
|
19593
|
-
const dir =
|
|
19594
|
-
return
|
|
20216
|
+
const dir = path41.join(os26.homedir(), ".codeam");
|
|
20217
|
+
return path41.join(dir, "update-check.json");
|
|
19595
20218
|
}
|
|
19596
20219
|
function readCache() {
|
|
19597
20220
|
try {
|
|
19598
|
-
const raw =
|
|
20221
|
+
const raw = fs32.readFileSync(cachePath(), "utf8");
|
|
19599
20222
|
const parsed = JSON.parse(raw);
|
|
19600
20223
|
if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
|
|
19601
20224
|
return parsed;
|
|
@@ -19606,10 +20229,10 @@ function readCache() {
|
|
|
19606
20229
|
function writeCache(cache) {
|
|
19607
20230
|
try {
|
|
19608
20231
|
const file = cachePath();
|
|
19609
|
-
|
|
20232
|
+
fs32.mkdirSync(path41.dirname(file), { recursive: true });
|
|
19610
20233
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
19611
|
-
|
|
19612
|
-
|
|
20234
|
+
fs32.writeFileSync(tmp, JSON.stringify(cache));
|
|
20235
|
+
fs32.renameSync(tmp, file);
|
|
19613
20236
|
} catch {
|
|
19614
20237
|
}
|
|
19615
20238
|
}
|
|
@@ -19680,7 +20303,7 @@ function checkForUpdates() {
|
|
|
19680
20303
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
19681
20304
|
if (process.env.CI) return;
|
|
19682
20305
|
if (!process.stdout.isTTY) return;
|
|
19683
|
-
const current = true ? "2.
|
|
20306
|
+
const current = true ? "2.26.0" : null;
|
|
19684
20307
|
if (!current) return;
|
|
19685
20308
|
const cache = readCache();
|
|
19686
20309
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|