codeam-cli 2.20.3 → 2.21.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 +13 -0
- package/dist/index.js +867 -258
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -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.21.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",
|
|
@@ -735,7 +735,7 @@ async function postLinkCredential(input) {
|
|
|
735
735
|
}
|
|
736
736
|
}
|
|
737
737
|
async function _postJsonAuthed(url, body, pluginAuthToken) {
|
|
738
|
-
return new Promise((
|
|
738
|
+
return new Promise((resolve5, reject) => {
|
|
739
739
|
const data = JSON.stringify(body);
|
|
740
740
|
const u2 = new URL(url);
|
|
741
741
|
const transport = u2.protocol === "https:" ? https : http;
|
|
@@ -767,9 +767,9 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
|
|
|
767
767
|
return;
|
|
768
768
|
}
|
|
769
769
|
try {
|
|
770
|
-
|
|
770
|
+
resolve5(JSON.parse(responseBody));
|
|
771
771
|
} catch {
|
|
772
|
-
|
|
772
|
+
resolve5(null);
|
|
773
773
|
}
|
|
774
774
|
});
|
|
775
775
|
}
|
|
@@ -784,7 +784,7 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
|
|
|
784
784
|
});
|
|
785
785
|
}
|
|
786
786
|
async function _postJson(url, body) {
|
|
787
|
-
return new Promise((
|
|
787
|
+
return new Promise((resolve5, reject) => {
|
|
788
788
|
const data = JSON.stringify(body);
|
|
789
789
|
const u2 = new URL(url);
|
|
790
790
|
const transport = u2.protocol === "https:" ? https : http;
|
|
@@ -813,9 +813,9 @@ async function _postJson(url, body) {
|
|
|
813
813
|
return;
|
|
814
814
|
}
|
|
815
815
|
try {
|
|
816
|
-
|
|
816
|
+
resolve5(JSON.parse(body2));
|
|
817
817
|
} catch {
|
|
818
|
-
|
|
818
|
+
resolve5(null);
|
|
819
819
|
}
|
|
820
820
|
});
|
|
821
821
|
}
|
|
@@ -830,7 +830,7 @@ async function _postJson(url, body) {
|
|
|
830
830
|
});
|
|
831
831
|
}
|
|
832
832
|
async function _getJson(url) {
|
|
833
|
-
return new Promise((
|
|
833
|
+
return new Promise((resolve5, reject) => {
|
|
834
834
|
const u2 = new URL(url);
|
|
835
835
|
const transport = u2.protocol === "https:" ? https : http;
|
|
836
836
|
const req = transport.request(
|
|
@@ -854,9 +854,9 @@ async function _getJson(url) {
|
|
|
854
854
|
return;
|
|
855
855
|
}
|
|
856
856
|
try {
|
|
857
|
-
|
|
857
|
+
resolve5(JSON.parse(body));
|
|
858
858
|
} catch {
|
|
859
|
-
|
|
859
|
+
resolve5(null);
|
|
860
860
|
}
|
|
861
861
|
});
|
|
862
862
|
}
|
|
@@ -1026,8 +1026,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
|
|
|
1026
1026
|
return decodedFile;
|
|
1027
1027
|
};
|
|
1028
1028
|
}
|
|
1029
|
-
function normalizeWindowsPath(
|
|
1030
|
-
return
|
|
1029
|
+
function normalizeWindowsPath(path40) {
|
|
1030
|
+
return path40.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
|
|
1031
1031
|
}
|
|
1032
1032
|
|
|
1033
1033
|
// ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
|
|
@@ -3507,15 +3507,15 @@ async function addSourceContext(frames) {
|
|
|
3507
3507
|
LRU_FILE_CONTENTS_CACHE.reduce();
|
|
3508
3508
|
return frames;
|
|
3509
3509
|
}
|
|
3510
|
-
function getContextLinesFromFile(
|
|
3511
|
-
return new Promise((
|
|
3512
|
-
const stream = (0, import_node_fs.createReadStream)(
|
|
3510
|
+
function getContextLinesFromFile(path40, ranges, output) {
|
|
3511
|
+
return new Promise((resolve5) => {
|
|
3512
|
+
const stream = (0, import_node_fs.createReadStream)(path40);
|
|
3513
3513
|
const lineReaded = (0, import_node_readline.createInterface)({
|
|
3514
3514
|
input: stream
|
|
3515
3515
|
});
|
|
3516
3516
|
function destroyStreamAndResolve() {
|
|
3517
3517
|
stream.destroy();
|
|
3518
|
-
|
|
3518
|
+
resolve5();
|
|
3519
3519
|
}
|
|
3520
3520
|
let lineNumber = 0;
|
|
3521
3521
|
let currentRangeIndex = 0;
|
|
@@ -3524,7 +3524,7 @@ function getContextLinesFromFile(path37, ranges, output) {
|
|
|
3524
3524
|
let rangeStart = range[0];
|
|
3525
3525
|
let rangeEnd = range[1];
|
|
3526
3526
|
function onStreamError() {
|
|
3527
|
-
LRU_FILE_CONTENTS_FS_READ_FAILED.set(
|
|
3527
|
+
LRU_FILE_CONTENTS_FS_READ_FAILED.set(path40, 1);
|
|
3528
3528
|
lineReaded.close();
|
|
3529
3529
|
lineReaded.removeAllListeners();
|
|
3530
3530
|
destroyStreamAndResolve();
|
|
@@ -3585,8 +3585,8 @@ function clearLineContext(frame) {
|
|
|
3585
3585
|
delete frame.context_line;
|
|
3586
3586
|
delete frame.post_context;
|
|
3587
3587
|
}
|
|
3588
|
-
function shouldSkipContextLinesForFile(
|
|
3589
|
-
return
|
|
3588
|
+
function shouldSkipContextLinesForFile(path40) {
|
|
3589
|
+
return path40.startsWith("node:") || path40.endsWith(".min.js") || path40.endsWith(".min.cjs") || path40.endsWith(".min.mjs") || path40.startsWith("data:");
|
|
3590
3590
|
}
|
|
3591
3591
|
function shouldSkipContextLinesForFrame(frame) {
|
|
3592
3592
|
if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
|
|
@@ -4802,9 +4802,9 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
|
|
|
4802
4802
|
if (!waitUntil) return;
|
|
4803
4803
|
if (this.disabled || this.optedOut) return;
|
|
4804
4804
|
if (!this._waitUntilCycle) {
|
|
4805
|
-
let
|
|
4805
|
+
let resolve5;
|
|
4806
4806
|
const promise = new Promise((r) => {
|
|
4807
|
-
|
|
4807
|
+
resolve5 = r;
|
|
4808
4808
|
});
|
|
4809
4809
|
try {
|
|
4810
4810
|
waitUntil(promise);
|
|
@@ -4812,7 +4812,7 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
|
|
|
4812
4812
|
return;
|
|
4813
4813
|
}
|
|
4814
4814
|
this._waitUntilCycle = {
|
|
4815
|
-
resolve:
|
|
4815
|
+
resolve: resolve5,
|
|
4816
4816
|
startedAt: Date.now(),
|
|
4817
4817
|
timer: void 0
|
|
4818
4818
|
};
|
|
@@ -4836,12 +4836,12 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
|
|
|
4836
4836
|
return cycle?.resolve;
|
|
4837
4837
|
}
|
|
4838
4838
|
async resolveWaitUntilFlush() {
|
|
4839
|
-
const
|
|
4839
|
+
const resolve5 = this._consumeWaitUntilCycle();
|
|
4840
4840
|
try {
|
|
4841
4841
|
await super.flush();
|
|
4842
4842
|
} catch {
|
|
4843
4843
|
} finally {
|
|
4844
|
-
|
|
4844
|
+
resolve5?.();
|
|
4845
4845
|
}
|
|
4846
4846
|
}
|
|
4847
4847
|
getPersistedProperty(key) {
|
|
@@ -4933,15 +4933,15 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
|
|
|
4933
4933
|
async waitForLocalEvaluationReady(timeoutMs = THIRTY_SECONDS) {
|
|
4934
4934
|
if (this.isLocalEvaluationReady()) return true;
|
|
4935
4935
|
if (void 0 === this.featureFlagsPoller) return false;
|
|
4936
|
-
return new Promise((
|
|
4936
|
+
return new Promise((resolve5) => {
|
|
4937
4937
|
const timeout = setTimeout(() => {
|
|
4938
4938
|
cleanup();
|
|
4939
|
-
|
|
4939
|
+
resolve5(false);
|
|
4940
4940
|
}, timeoutMs);
|
|
4941
4941
|
const cleanup = this._events.on("localEvaluationFlagsLoaded", (count) => {
|
|
4942
4942
|
clearTimeout(timeout);
|
|
4943
4943
|
cleanup();
|
|
4944
|
-
|
|
4944
|
+
resolve5(count > 0);
|
|
4945
4945
|
});
|
|
4946
4946
|
});
|
|
4947
4947
|
}
|
|
@@ -5378,13 +5378,13 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
|
|
|
5378
5378
|
this.context?.enter(data, options);
|
|
5379
5379
|
}
|
|
5380
5380
|
async _shutdown(shutdownTimeoutMs) {
|
|
5381
|
-
const
|
|
5381
|
+
const resolve5 = this._consumeWaitUntilCycle();
|
|
5382
5382
|
await this.featureFlagsPoller?.stopPoller(shutdownTimeoutMs);
|
|
5383
5383
|
this.errorTracking.shutdown();
|
|
5384
5384
|
try {
|
|
5385
5385
|
return await super._shutdown(shutdownTimeoutMs);
|
|
5386
5386
|
} finally {
|
|
5387
|
-
|
|
5387
|
+
resolve5?.();
|
|
5388
5388
|
}
|
|
5389
5389
|
}
|
|
5390
5390
|
async _requestRemoteConfigPayload(flagKey) {
|
|
@@ -5740,7 +5740,7 @@ function readAnonId() {
|
|
|
5740
5740
|
}
|
|
5741
5741
|
function superProperties() {
|
|
5742
5742
|
return {
|
|
5743
|
-
cliVersion: true ? "2.
|
|
5743
|
+
cliVersion: true ? "2.21.1" : "0.0.0-dev",
|
|
5744
5744
|
nodeVersion: process.version,
|
|
5745
5745
|
platform: process.platform,
|
|
5746
5746
|
arch: process.arch,
|
|
@@ -8874,15 +8874,15 @@ function runInstaller() {
|
|
|
8874
8874
|
"-Command",
|
|
8875
8875
|
"irm https://claude.ai/install.ps1 | iex"
|
|
8876
8876
|
] : ["-c", "curl -fsSL https://claude.ai/install.sh | bash"];
|
|
8877
|
-
return new Promise((
|
|
8877
|
+
return new Promise((resolve5) => {
|
|
8878
8878
|
const proc = (0, import_child_process4.spawn)(cmd, args2, { stdio: "inherit" });
|
|
8879
8879
|
proc.on("error", (err) => {
|
|
8880
8880
|
console.error(`
|
|
8881
8881
|
\u2717 Installer failed to launch: ${err.message}`);
|
|
8882
|
-
|
|
8882
|
+
resolve5(false);
|
|
8883
8883
|
});
|
|
8884
8884
|
proc.on("exit", (code) => {
|
|
8885
|
-
|
|
8885
|
+
resolve5(code === 0);
|
|
8886
8886
|
});
|
|
8887
8887
|
});
|
|
8888
8888
|
}
|
|
@@ -9045,17 +9045,17 @@ function parseUsageOutput(raw) {
|
|
|
9045
9045
|
return { percent, resetAt };
|
|
9046
9046
|
}
|
|
9047
9047
|
async function fetchClaudeQuota() {
|
|
9048
|
-
return new Promise((
|
|
9048
|
+
return new Promise((resolve5) => {
|
|
9049
9049
|
const claudeCmd = findInPath("claude") ? "claude" : "claude-code";
|
|
9050
9050
|
if (!claudeCmd) {
|
|
9051
|
-
|
|
9051
|
+
resolve5(null);
|
|
9052
9052
|
return;
|
|
9053
9053
|
}
|
|
9054
9054
|
const helperPath = path11.join(os10.tmpdir(), "codeam-quota-helper.py");
|
|
9055
9055
|
fs8.writeFileSync(helperPath, HELPER_SCRIPT, { mode: 420 });
|
|
9056
9056
|
const python = findInPath("python3") ?? findInPath("python");
|
|
9057
9057
|
if (!python) {
|
|
9058
|
-
|
|
9058
|
+
resolve5(null);
|
|
9059
9059
|
return;
|
|
9060
9060
|
}
|
|
9061
9061
|
const proc = (0, import_child_process5.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
|
|
@@ -9082,13 +9082,13 @@ async function fetchClaudeQuota() {
|
|
|
9082
9082
|
fs8.unlinkSync(helperPath);
|
|
9083
9083
|
} catch {
|
|
9084
9084
|
}
|
|
9085
|
-
|
|
9085
|
+
resolve5(result);
|
|
9086
9086
|
}, 5e3);
|
|
9087
9087
|
}, 8e3);
|
|
9088
9088
|
setTimeout(() => {
|
|
9089
9089
|
if (!resolved) {
|
|
9090
9090
|
resolved = true;
|
|
9091
|
-
|
|
9091
|
+
resolve5(null);
|
|
9092
9092
|
}
|
|
9093
9093
|
try {
|
|
9094
9094
|
proc.kill();
|
|
@@ -10428,12 +10428,12 @@ function resolveNpm(os26) {
|
|
|
10428
10428
|
return os26.id === "win32" ? "npm.cmd" : "npm";
|
|
10429
10429
|
}
|
|
10430
10430
|
async function installCodexViaNpm(os26) {
|
|
10431
|
-
return new Promise((
|
|
10431
|
+
return new Promise((resolve5, reject) => {
|
|
10432
10432
|
const proc = (0, import_node_child_process4.spawn)(resolveNpm(os26), ["install", "-g", "@openai/codex"], {
|
|
10433
10433
|
stdio: "inherit"
|
|
10434
10434
|
});
|
|
10435
10435
|
proc.on("close", (code) => {
|
|
10436
|
-
if (code === 0)
|
|
10436
|
+
if (code === 0) resolve5();
|
|
10437
10437
|
else reject(new Error(`npm install -g @openai/codex exited ${code}`));
|
|
10438
10438
|
});
|
|
10439
10439
|
proc.on("error", (err) => {
|
|
@@ -10548,12 +10548,12 @@ async function ensureCoderabbitInstalled(os26) {
|
|
|
10548
10548
|
return false;
|
|
10549
10549
|
}
|
|
10550
10550
|
console.log("\n CodeRabbit CLI not found \u2014 installing via the official script\u2026\n");
|
|
10551
|
-
const ok = await new Promise((
|
|
10551
|
+
const ok = await new Promise((resolve5) => {
|
|
10552
10552
|
const proc = (0, import_node_child_process5.spawn)("sh", ["-c", `curl -fsSL ${INSTALL_URL} | sh`], {
|
|
10553
10553
|
stdio: "inherit"
|
|
10554
10554
|
});
|
|
10555
|
-
proc.on("close", (code) =>
|
|
10556
|
-
proc.on("error", () =>
|
|
10555
|
+
proc.on("close", (code) => resolve5(code === 0));
|
|
10556
|
+
proc.on("error", () => resolve5(false));
|
|
10557
10557
|
});
|
|
10558
10558
|
if (!ok) return false;
|
|
10559
10559
|
os26.augmentPath([`${os26.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
|
|
@@ -10610,11 +10610,11 @@ function parseReview(stdout) {
|
|
|
10610
10610
|
for (const line of lines) {
|
|
10611
10611
|
const m = line.match(HUNK_LINE_RE);
|
|
10612
10612
|
if (!m) continue;
|
|
10613
|
-
const [,
|
|
10614
|
-
if (!
|
|
10613
|
+
const [, path40, lineNo, sevToken, message] = m;
|
|
10614
|
+
if (!path40 || !lineNo || !message) continue;
|
|
10615
10615
|
const cleanedMessage = message.trim().replace(/^[*-]\s+/, "");
|
|
10616
10616
|
hunks.push({
|
|
10617
|
-
path:
|
|
10617
|
+
path: path40.trim(),
|
|
10618
10618
|
line: Number(lineNo),
|
|
10619
10619
|
severity: sevToken ? SEVERITY_MAP[sevToken.toLowerCase()] : void 0,
|
|
10620
10620
|
message: cleanedMessage
|
|
@@ -10672,7 +10672,7 @@ var CoderabbitRuntimeStrategy = class {
|
|
|
10672
10672
|
}
|
|
10673
10673
|
async runOneShot(input) {
|
|
10674
10674
|
const launch = await this.prepareInvocation(input);
|
|
10675
|
-
return new Promise((
|
|
10675
|
+
return new Promise((resolve5, reject) => {
|
|
10676
10676
|
const stdoutBuf = [];
|
|
10677
10677
|
const stderrBuf = [];
|
|
10678
10678
|
const proc = (0, import_node_child_process7.spawn)(launch.cmd, launch.args, {
|
|
@@ -10683,7 +10683,7 @@ var CoderabbitRuntimeStrategy = class {
|
|
|
10683
10683
|
proc.stderr?.on("data", (b) => stderrBuf.push(b));
|
|
10684
10684
|
proc.on("error", (err) => reject(err));
|
|
10685
10685
|
proc.on("close", (code) => {
|
|
10686
|
-
|
|
10686
|
+
resolve5(
|
|
10687
10687
|
this.parseOutput({
|
|
10688
10688
|
exitCode: code ?? 0,
|
|
10689
10689
|
stdout: Buffer.concat(stdoutBuf).toString("utf8"),
|
|
@@ -11216,14 +11216,14 @@ var ChunkEmitter = class {
|
|
|
11216
11216
|
"chunkEmitter",
|
|
11217
11217
|
`send type=${body.type ?? "(clear)"} bytes=${payload.length} done=${body.done === true}`
|
|
11218
11218
|
);
|
|
11219
|
-
return new Promise((
|
|
11219
|
+
return new Promise((resolve5) => {
|
|
11220
11220
|
const attempt = (attemptsLeft) => {
|
|
11221
11221
|
_transport2.post(this.url, this.headers, payload).then(({ statusCode, body: resBody }) => {
|
|
11222
11222
|
const tookMs = Date.now() - t0;
|
|
11223
11223
|
if (statusCode === 410 || statusCode === 404 && /SESSION_NOT_FOUND|SESSION_GONE/.test(resBody)) {
|
|
11224
11224
|
process.stderr.write("[codeam] session was deleted/disconnected \u2014 stopping output stream.\n");
|
|
11225
11225
|
log.info("chunkEmitter", `dead status=${statusCode} took=${tookMs}ms`);
|
|
11226
|
-
|
|
11226
|
+
resolve5({ dead: true });
|
|
11227
11227
|
return;
|
|
11228
11228
|
}
|
|
11229
11229
|
if (statusCode >= 400) {
|
|
@@ -11233,7 +11233,7 @@ var ChunkEmitter = class {
|
|
|
11233
11233
|
} else {
|
|
11234
11234
|
log.info("chunkEmitter", `ok status=${statusCode} took=${tookMs}ms`);
|
|
11235
11235
|
}
|
|
11236
|
-
|
|
11236
|
+
resolve5({ dead: false });
|
|
11237
11237
|
}).catch((err) => {
|
|
11238
11238
|
log.warn(
|
|
11239
11239
|
"chunkEmitter",
|
|
@@ -11244,7 +11244,7 @@ var ChunkEmitter = class {
|
|
|
11244
11244
|
const delay = 200 * (maxRetries - attemptsLeft + 1);
|
|
11245
11245
|
setTimeout(() => attempt(attemptsLeft - 1), delay);
|
|
11246
11246
|
} else {
|
|
11247
|
-
|
|
11247
|
+
resolve5({ dead: false });
|
|
11248
11248
|
}
|
|
11249
11249
|
});
|
|
11250
11250
|
};
|
|
@@ -11256,7 +11256,7 @@ var _transport2 = {
|
|
|
11256
11256
|
post: _post
|
|
11257
11257
|
};
|
|
11258
11258
|
function _post(url, headers, payload) {
|
|
11259
|
-
return new Promise((
|
|
11259
|
+
return new Promise((resolve5, reject) => {
|
|
11260
11260
|
let settled = false;
|
|
11261
11261
|
const u2 = new URL(url);
|
|
11262
11262
|
const transport = u2.protocol === "https:" ? https3 : http3;
|
|
@@ -11280,7 +11280,7 @@ function _post(url, headers, payload) {
|
|
|
11280
11280
|
res.on("end", () => {
|
|
11281
11281
|
if (settled) return;
|
|
11282
11282
|
settled = true;
|
|
11283
|
-
|
|
11283
|
+
resolve5({ statusCode: res.statusCode ?? 0, body: resData });
|
|
11284
11284
|
});
|
|
11285
11285
|
}
|
|
11286
11286
|
);
|
|
@@ -11781,7 +11781,7 @@ function parseJsonl(filePath) {
|
|
|
11781
11781
|
return messages;
|
|
11782
11782
|
}
|
|
11783
11783
|
function post(endpoint, body) {
|
|
11784
|
-
return new Promise((
|
|
11784
|
+
return new Promise((resolve5) => {
|
|
11785
11785
|
const payload = JSON.stringify(body);
|
|
11786
11786
|
const u2 = new URL(`${API_BASE4}${endpoint}`);
|
|
11787
11787
|
const transport = u2.protocol === "https:" ? https4 : http4;
|
|
@@ -11802,17 +11802,17 @@ function post(endpoint, body) {
|
|
|
11802
11802
|
res.resume();
|
|
11803
11803
|
const ok = res.statusCode !== void 0 && res.statusCode >= 200 && res.statusCode < 300;
|
|
11804
11804
|
if (!ok) log.warn("history:post", `${endpoint} \u2192 HTTP ${res.statusCode}`);
|
|
11805
|
-
|
|
11805
|
+
resolve5(ok);
|
|
11806
11806
|
}
|
|
11807
11807
|
);
|
|
11808
11808
|
req.on("error", (err) => {
|
|
11809
11809
|
log.warn("history:post", `${endpoint} network error`, err);
|
|
11810
|
-
|
|
11810
|
+
resolve5(false);
|
|
11811
11811
|
});
|
|
11812
11812
|
req.on("timeout", () => {
|
|
11813
11813
|
log.warn("history:post", `${endpoint} timeout after 15s`);
|
|
11814
11814
|
req.destroy();
|
|
11815
|
-
|
|
11815
|
+
resolve5(false);
|
|
11816
11816
|
});
|
|
11817
11817
|
req.write(payload);
|
|
11818
11818
|
req.end();
|
|
@@ -12296,7 +12296,7 @@ var _transport3 = {
|
|
|
12296
12296
|
post: _post2
|
|
12297
12297
|
};
|
|
12298
12298
|
function _post2(url, headers, payload) {
|
|
12299
|
-
return new Promise((
|
|
12299
|
+
return new Promise((resolve5, reject) => {
|
|
12300
12300
|
let settled = false;
|
|
12301
12301
|
const u2 = new URL(url);
|
|
12302
12302
|
const lib = u2.protocol === "https:" ? https5 : http5;
|
|
@@ -12321,7 +12321,7 @@ function _post2(url, headers, payload) {
|
|
|
12321
12321
|
res.on("end", () => {
|
|
12322
12322
|
if (settled) return;
|
|
12323
12323
|
settled = true;
|
|
12324
|
-
|
|
12324
|
+
resolve5({ statusCode: res.statusCode ?? 0, body });
|
|
12325
12325
|
});
|
|
12326
12326
|
}
|
|
12327
12327
|
);
|
|
@@ -12576,6 +12576,7 @@ var FileWatcherService = class {
|
|
|
12576
12576
|
);
|
|
12577
12577
|
return;
|
|
12578
12578
|
}
|
|
12579
|
+
this.opts.onRepoDirty?.(gitRoot);
|
|
12579
12580
|
const relPathInRepo = path25.relative(gitRoot, absPath);
|
|
12580
12581
|
if (!relPathInRepo || relPathInRepo.startsWith("..")) return;
|
|
12581
12582
|
const repoPath = path25.relative(this.opts.workingDir, gitRoot);
|
|
@@ -12690,6 +12691,7 @@ var FileWatcherService = class {
|
|
|
12690
12691
|
"X-Plugin-Auth-Token": this.opts.pluginAuthToken
|
|
12691
12692
|
};
|
|
12692
12693
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt += 1) {
|
|
12694
|
+
if (this.stopped) return;
|
|
12693
12695
|
try {
|
|
12694
12696
|
const { statusCode, body: resBody } = await _transport3.post(url, headers, payload);
|
|
12695
12697
|
if (statusCode >= 200 && statusCode < 300) {
|
|
@@ -12738,12 +12740,12 @@ var _gitSeam = {
|
|
|
12738
12740
|
run: _runGitImpl
|
|
12739
12741
|
};
|
|
12740
12742
|
async function _runGitImpl(cwd, args2, opts = {}) {
|
|
12741
|
-
return new Promise((
|
|
12743
|
+
return new Promise((resolve5) => {
|
|
12742
12744
|
let proc;
|
|
12743
12745
|
try {
|
|
12744
12746
|
proc = (0, import_child_process7.spawn)("git", args2, { cwd, env: process.env });
|
|
12745
12747
|
} catch {
|
|
12746
|
-
|
|
12748
|
+
resolve5(null);
|
|
12747
12749
|
return;
|
|
12748
12750
|
}
|
|
12749
12751
|
let stdout = "";
|
|
@@ -12754,13 +12756,13 @@ async function _runGitImpl(cwd, args2, opts = {}) {
|
|
|
12754
12756
|
proc.stderr?.on("data", (c2) => {
|
|
12755
12757
|
stderr += c2.toString();
|
|
12756
12758
|
});
|
|
12757
|
-
proc.on("error", () =>
|
|
12759
|
+
proc.on("error", () => resolve5(null));
|
|
12758
12760
|
proc.on("close", (code) => {
|
|
12759
12761
|
if (code === 0 || opts.allowNonZeroExit) {
|
|
12760
|
-
|
|
12762
|
+
resolve5(stdout);
|
|
12761
12763
|
} else {
|
|
12762
12764
|
log.trace("fileWatcher", `git ${args2.join(" ")} exited ${code} stderr=${stderr.slice(0, 200)}`);
|
|
12763
|
-
|
|
12765
|
+
resolve5(null);
|
|
12764
12766
|
}
|
|
12765
12767
|
});
|
|
12766
12768
|
});
|
|
@@ -12769,9 +12771,493 @@ function _runGit(cwd, args2, opts = {}) {
|
|
|
12769
12771
|
return _gitSeam.run(cwd, args2, opts);
|
|
12770
12772
|
}
|
|
12771
12773
|
|
|
12772
|
-
// src/services/
|
|
12774
|
+
// src/services/turn-files/turn-file-aggregator.ts
|
|
12773
12775
|
var import_crypto2 = require("crypto");
|
|
12774
12776
|
|
|
12777
|
+
// src/services/turn-files/git-changeset.ts
|
|
12778
|
+
var import_child_process8 = require("child_process");
|
|
12779
|
+
var path26 = __toESM(require("path"));
|
|
12780
|
+
async function collectRepoChangeset(opts) {
|
|
12781
|
+
const status2 = await runGit2(opts.repoRoot, ["status", "--porcelain=v1", "-z"]);
|
|
12782
|
+
if (status2 === null) return null;
|
|
12783
|
+
const numstatRaw = await runGit2(opts.repoRoot, [
|
|
12784
|
+
"diff",
|
|
12785
|
+
"--numstat",
|
|
12786
|
+
"-z",
|
|
12787
|
+
"HEAD"
|
|
12788
|
+
]).catch(() => null);
|
|
12789
|
+
const numstat = parseNumstat(numstatRaw ?? "");
|
|
12790
|
+
const entries = [];
|
|
12791
|
+
for (const row of parseStatus(status2)) {
|
|
12792
|
+
const stats = numstat.get(row.filePath) ?? { added: 0, removed: 0 };
|
|
12793
|
+
entries.push({
|
|
12794
|
+
filePath: row.filePath,
|
|
12795
|
+
fileStatus: row.fileStatus,
|
|
12796
|
+
linesAdded: stats.added,
|
|
12797
|
+
linesRemoved: stats.removed,
|
|
12798
|
+
// hunkCount isn't surfaced by --numstat. For the rail / drawer
|
|
12799
|
+
// it's only a count badge; defaulting to 1 when the file has
|
|
12800
|
+
// any non-zero stat is good enough until we wire a follow-up
|
|
12801
|
+
// per-file `git diff --shortstat` if we ever want exact hunks.
|
|
12802
|
+
hunkCount: stats.added + stats.removed > 0 ? 1 : 0,
|
|
12803
|
+
repoPath: opts.repoPath,
|
|
12804
|
+
repoName: opts.repoName
|
|
12805
|
+
});
|
|
12806
|
+
}
|
|
12807
|
+
return entries;
|
|
12808
|
+
}
|
|
12809
|
+
function parseStatus(raw) {
|
|
12810
|
+
const tokens = raw.split("\0");
|
|
12811
|
+
const rows = [];
|
|
12812
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
12813
|
+
const token = tokens[i];
|
|
12814
|
+
if (!token || token.length < 3) continue;
|
|
12815
|
+
const code = token.slice(0, 2);
|
|
12816
|
+
const filePath = token.slice(3);
|
|
12817
|
+
if (!filePath) continue;
|
|
12818
|
+
const indexCode = code[0];
|
|
12819
|
+
const worktreeCode = code[1];
|
|
12820
|
+
if (indexCode === "R" || worktreeCode === "R") {
|
|
12821
|
+
rows.push({ filePath, fileStatus: "renamed" });
|
|
12822
|
+
i += 1;
|
|
12823
|
+
continue;
|
|
12824
|
+
}
|
|
12825
|
+
if (code === "??" || indexCode === "A" || worktreeCode === "A") {
|
|
12826
|
+
rows.push({ filePath, fileStatus: "added" });
|
|
12827
|
+
continue;
|
|
12828
|
+
}
|
|
12829
|
+
if (indexCode === "D" || worktreeCode === "D") {
|
|
12830
|
+
rows.push({ filePath, fileStatus: "deleted" });
|
|
12831
|
+
continue;
|
|
12832
|
+
}
|
|
12833
|
+
rows.push({ filePath, fileStatus: "modified" });
|
|
12834
|
+
}
|
|
12835
|
+
return rows;
|
|
12836
|
+
}
|
|
12837
|
+
function parseNumstat(raw) {
|
|
12838
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
12839
|
+
for (const record of raw.split("\0")) {
|
|
12840
|
+
if (!record) continue;
|
|
12841
|
+
const parts = record.split(" ");
|
|
12842
|
+
if (parts.length < 3) continue;
|
|
12843
|
+
const added = parts[0] === "-" ? 0 : parseInt(parts[0], 10) || 0;
|
|
12844
|
+
const removed = parts[1] === "-" ? 0 : parseInt(parts[1], 10) || 0;
|
|
12845
|
+
const filePath = parts.slice(2).join(" ");
|
|
12846
|
+
if (!filePath) continue;
|
|
12847
|
+
out2.set(filePath, { added, removed });
|
|
12848
|
+
}
|
|
12849
|
+
return out2;
|
|
12850
|
+
}
|
|
12851
|
+
var _runGitImpl2 = {
|
|
12852
|
+
run: defaultRunGit
|
|
12853
|
+
};
|
|
12854
|
+
function runGit2(cwd, args2) {
|
|
12855
|
+
return _runGitImpl2.run(cwd, args2);
|
|
12856
|
+
}
|
|
12857
|
+
function defaultRunGit(cwd, args2) {
|
|
12858
|
+
return new Promise((resolve5) => {
|
|
12859
|
+
let proc;
|
|
12860
|
+
try {
|
|
12861
|
+
proc = (0, import_child_process8.spawn)("git", args2, { cwd, env: process.env });
|
|
12862
|
+
} catch {
|
|
12863
|
+
resolve5(null);
|
|
12864
|
+
return;
|
|
12865
|
+
}
|
|
12866
|
+
let stdout = "";
|
|
12867
|
+
let stderr = "";
|
|
12868
|
+
proc.stdout?.on("data", (c2) => {
|
|
12869
|
+
stdout += c2.toString();
|
|
12870
|
+
});
|
|
12871
|
+
proc.stderr?.on("data", (c2) => {
|
|
12872
|
+
stderr += c2.toString();
|
|
12873
|
+
});
|
|
12874
|
+
proc.on("error", () => resolve5(null));
|
|
12875
|
+
proc.on("close", (code) => {
|
|
12876
|
+
if (code === 0) {
|
|
12877
|
+
resolve5(stdout);
|
|
12878
|
+
} else {
|
|
12879
|
+
log.trace(
|
|
12880
|
+
"turnFiles",
|
|
12881
|
+
`git ${args2.join(" ")} exited ${code} stderr=${stderr.slice(0, 200)}`
|
|
12882
|
+
);
|
|
12883
|
+
resolve5(null);
|
|
12884
|
+
}
|
|
12885
|
+
});
|
|
12886
|
+
});
|
|
12887
|
+
}
|
|
12888
|
+
async function discoverRepos(workingDir, maxDepth = 4) {
|
|
12889
|
+
const fs31 = await import("fs/promises");
|
|
12890
|
+
const out2 = [];
|
|
12891
|
+
await walk(workingDir, 0);
|
|
12892
|
+
return out2;
|
|
12893
|
+
async function walk(dir, depth) {
|
|
12894
|
+
if (depth > maxDepth) return;
|
|
12895
|
+
let entries = [];
|
|
12896
|
+
try {
|
|
12897
|
+
const dirents = await fs31.readdir(dir, { withFileTypes: true });
|
|
12898
|
+
entries = dirents.filter((d3) => !d3.name.startsWith(".") || d3.name === ".git").map((d3) => ({ name: d3.name, isDirectory: d3.isDirectory() }));
|
|
12899
|
+
} catch {
|
|
12900
|
+
return;
|
|
12901
|
+
}
|
|
12902
|
+
const hasGit = entries.some(
|
|
12903
|
+
(e) => e.name === ".git" && (e.isDirectory || true)
|
|
12904
|
+
);
|
|
12905
|
+
if (hasGit) {
|
|
12906
|
+
out2.push({
|
|
12907
|
+
repoRoot: dir,
|
|
12908
|
+
repoPath: path26.relative(workingDir, dir),
|
|
12909
|
+
repoName: path26.basename(dir)
|
|
12910
|
+
});
|
|
12911
|
+
return;
|
|
12912
|
+
}
|
|
12913
|
+
for (const entry of entries) {
|
|
12914
|
+
if (!entry.isDirectory) continue;
|
|
12915
|
+
if (entry.name === "node_modules") continue;
|
|
12916
|
+
if (entry.name === "dist" || entry.name === "build") continue;
|
|
12917
|
+
await walk(path26.join(dir, entry.name), depth + 1);
|
|
12918
|
+
}
|
|
12919
|
+
}
|
|
12920
|
+
}
|
|
12921
|
+
|
|
12922
|
+
// src/services/turn-files/files-outbox.ts
|
|
12923
|
+
var fs22 = __toESM(require("fs/promises"));
|
|
12924
|
+
var path27 = __toESM(require("path"));
|
|
12925
|
+
var import_os6 = require("os");
|
|
12926
|
+
var HOME_OUTBOX_DIR = ".codeam/outbox";
|
|
12927
|
+
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
12928
|
+
var BACKOFF_STEPS_MS = [
|
|
12929
|
+
1e3,
|
|
12930
|
+
// 1 s
|
|
12931
|
+
2e3,
|
|
12932
|
+
// 2 s
|
|
12933
|
+
4e3,
|
|
12934
|
+
// 4 s
|
|
12935
|
+
8e3,
|
|
12936
|
+
// 8 s
|
|
12937
|
+
16e3,
|
|
12938
|
+
// 16 s
|
|
12939
|
+
32e3,
|
|
12940
|
+
// 32 s
|
|
12941
|
+
6e4,
|
|
12942
|
+
// 1 min
|
|
12943
|
+
12e4,
|
|
12944
|
+
// 2 min
|
|
12945
|
+
3e5
|
|
12946
|
+
// 5 min — cap
|
|
12947
|
+
];
|
|
12948
|
+
var FilesOutbox = class {
|
|
12949
|
+
filePath;
|
|
12950
|
+
post;
|
|
12951
|
+
autoSchedule;
|
|
12952
|
+
flushTimer = null;
|
|
12953
|
+
flushing = false;
|
|
12954
|
+
backoffIndex = 0;
|
|
12955
|
+
stopped = false;
|
|
12956
|
+
constructor(opts) {
|
|
12957
|
+
const base = opts.baseDir ?? path27.join(homeDir(), HOME_OUTBOX_DIR);
|
|
12958
|
+
this.filePath = path27.join(base, `${opts.sessionId}.jsonl`);
|
|
12959
|
+
this.post = opts.post;
|
|
12960
|
+
this.autoSchedule = opts.autoSchedule !== false;
|
|
12961
|
+
}
|
|
12962
|
+
/** Persist the entry to disk and trigger a flush. Returns once the
|
|
12963
|
+
* line is durable on disk (not once the POST succeeds). */
|
|
12964
|
+
async enqueue(entry) {
|
|
12965
|
+
await fs22.mkdir(path27.dirname(this.filePath), { recursive: true });
|
|
12966
|
+
await fs22.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
|
|
12967
|
+
this.backoffIndex = 0;
|
|
12968
|
+
if (this.autoSchedule) this.scheduleFlush(0);
|
|
12969
|
+
}
|
|
12970
|
+
/** Stop the scheduler. Idempotent. The on-disk file is left alone
|
|
12971
|
+
* so the next process pickup can flush whatever's pending. */
|
|
12972
|
+
stop() {
|
|
12973
|
+
this.stopped = true;
|
|
12974
|
+
if (this.flushTimer) {
|
|
12975
|
+
clearTimeout(this.flushTimer);
|
|
12976
|
+
this.flushTimer = null;
|
|
12977
|
+
}
|
|
12978
|
+
}
|
|
12979
|
+
/** Visible for tests. Forces a flush attempt right now. */
|
|
12980
|
+
async _flushNow() {
|
|
12981
|
+
return this.flush();
|
|
12982
|
+
}
|
|
12983
|
+
scheduleFlush(delayMs) {
|
|
12984
|
+
if (this.stopped) return;
|
|
12985
|
+
if (this.flushTimer) clearTimeout(this.flushTimer);
|
|
12986
|
+
const jittered = delayMs === 0 ? 0 : applyJitter(delayMs);
|
|
12987
|
+
this.flushTimer = setTimeout(() => {
|
|
12988
|
+
this.flushTimer = null;
|
|
12989
|
+
void this.flush();
|
|
12990
|
+
}, jittered);
|
|
12991
|
+
}
|
|
12992
|
+
async flush() {
|
|
12993
|
+
if (this.stopped) return;
|
|
12994
|
+
if (this.flushing) {
|
|
12995
|
+
return;
|
|
12996
|
+
}
|
|
12997
|
+
this.flushing = true;
|
|
12998
|
+
try {
|
|
12999
|
+
const entries = await this.readAll();
|
|
13000
|
+
if (entries.length === 0) return;
|
|
13001
|
+
const now = Date.now();
|
|
13002
|
+
const fresh = entries.filter((e) => now - e.enqueuedAt <= MAX_AGE_MS);
|
|
13003
|
+
if (fresh.length < entries.length) {
|
|
13004
|
+
log.warn(
|
|
13005
|
+
"turnFiles",
|
|
13006
|
+
`dropping ${entries.length - fresh.length} outbox entries older than ${MAX_AGE_MS / 1e3}s`
|
|
13007
|
+
);
|
|
13008
|
+
}
|
|
13009
|
+
const stillPending = [];
|
|
13010
|
+
let anyFailed = false;
|
|
13011
|
+
for (const entry of fresh) {
|
|
13012
|
+
if (this.stopped) {
|
|
13013
|
+
stillPending.push(entry);
|
|
13014
|
+
continue;
|
|
13015
|
+
}
|
|
13016
|
+
try {
|
|
13017
|
+
const res = await this.post(entry);
|
|
13018
|
+
if (res.ok) continue;
|
|
13019
|
+
if (res.statusCode === 404 || res.statusCode === 410) {
|
|
13020
|
+
log.warn(
|
|
13021
|
+
"turnFiles",
|
|
13022
|
+
`session dead (status=${res.statusCode}); dropping turnId=${entry.turnId.slice(0, 8)}`
|
|
13023
|
+
);
|
|
13024
|
+
continue;
|
|
13025
|
+
}
|
|
13026
|
+
anyFailed = true;
|
|
13027
|
+
stillPending.push(entry);
|
|
13028
|
+
} catch (err) {
|
|
13029
|
+
anyFailed = true;
|
|
13030
|
+
stillPending.push(entry);
|
|
13031
|
+
log.trace(
|
|
13032
|
+
"turnFiles",
|
|
13033
|
+
`outbox post threw for turnId=${entry.turnId.slice(0, 8)}: ${err.message}`
|
|
13034
|
+
);
|
|
13035
|
+
}
|
|
13036
|
+
}
|
|
13037
|
+
await this.rewrite(stillPending);
|
|
13038
|
+
if (anyFailed) {
|
|
13039
|
+
const delay = BACKOFF_STEPS_MS[Math.min(this.backoffIndex, BACKOFF_STEPS_MS.length - 1)];
|
|
13040
|
+
this.backoffIndex = Math.min(
|
|
13041
|
+
this.backoffIndex + 1,
|
|
13042
|
+
BACKOFF_STEPS_MS.length - 1
|
|
13043
|
+
);
|
|
13044
|
+
this.scheduleFlush(delay);
|
|
13045
|
+
} else {
|
|
13046
|
+
this.backoffIndex = 0;
|
|
13047
|
+
}
|
|
13048
|
+
} finally {
|
|
13049
|
+
this.flushing = false;
|
|
13050
|
+
}
|
|
13051
|
+
}
|
|
13052
|
+
async readAll() {
|
|
13053
|
+
let raw = "";
|
|
13054
|
+
try {
|
|
13055
|
+
raw = await fs22.readFile(this.filePath, "utf8");
|
|
13056
|
+
} catch {
|
|
13057
|
+
return [];
|
|
13058
|
+
}
|
|
13059
|
+
const out2 = [];
|
|
13060
|
+
for (const line of raw.split("\n")) {
|
|
13061
|
+
const trimmed = line.trim();
|
|
13062
|
+
if (!trimmed) continue;
|
|
13063
|
+
try {
|
|
13064
|
+
out2.push(JSON.parse(trimmed));
|
|
13065
|
+
} catch {
|
|
13066
|
+
}
|
|
13067
|
+
}
|
|
13068
|
+
return out2;
|
|
13069
|
+
}
|
|
13070
|
+
/**
|
|
13071
|
+
* Atomic compaction: write to `<file>.tmp`, fsync, rename over the
|
|
13072
|
+
* original. A crash between any of these steps leaves either the
|
|
13073
|
+
* original (no progress) or the new file (clean compaction) — never
|
|
13074
|
+
* a torn write.
|
|
13075
|
+
*/
|
|
13076
|
+
async rewrite(entries) {
|
|
13077
|
+
const tmpPath = `${this.filePath}.${process.pid}.tmp`;
|
|
13078
|
+
if (entries.length === 0) {
|
|
13079
|
+
await fs22.unlink(this.filePath).catch(() => void 0);
|
|
13080
|
+
return;
|
|
13081
|
+
}
|
|
13082
|
+
const payload = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
13083
|
+
await fs22.writeFile(tmpPath, payload, "utf8");
|
|
13084
|
+
await fs22.rename(tmpPath, this.filePath);
|
|
13085
|
+
}
|
|
13086
|
+
};
|
|
13087
|
+
function applyJitter(ms) {
|
|
13088
|
+
const factor = 0.8 + Math.random() * 0.4;
|
|
13089
|
+
return Math.round(ms * factor);
|
|
13090
|
+
}
|
|
13091
|
+
function homeDir() {
|
|
13092
|
+
return process.env.HOME ?? process.env.USERPROFILE ?? (0, import_os6.tmpdir)();
|
|
13093
|
+
}
|
|
13094
|
+
|
|
13095
|
+
// src/services/turn-files/turn-file-aggregator.ts
|
|
13096
|
+
var API_BASE6 = resolveApiBaseUrl();
|
|
13097
|
+
var ENDPOINT = "/api/files/batch";
|
|
13098
|
+
var MAX_BATCH_SIZE = 1e3;
|
|
13099
|
+
var TurnFileAggregator = class {
|
|
13100
|
+
constructor(opts) {
|
|
13101
|
+
this.opts = opts;
|
|
13102
|
+
this.apiBase = opts.apiBaseUrl ?? API_BASE6;
|
|
13103
|
+
this.outbox = new FilesOutbox({
|
|
13104
|
+
sessionId: opts.sessionId,
|
|
13105
|
+
baseDir: opts.outboxDir,
|
|
13106
|
+
post: (entry) => this.postEntry(entry),
|
|
13107
|
+
autoSchedule: opts.outboxAutoSchedule
|
|
13108
|
+
});
|
|
13109
|
+
this.discovering = discoverRepos(opts.workingDir).then((repos) => {
|
|
13110
|
+
this.repos = repos;
|
|
13111
|
+
opts.dirtyTracker?.markAllDirty(repos);
|
|
13112
|
+
log.info(
|
|
13113
|
+
"turnFiles",
|
|
13114
|
+
`discovered ${repos.length} repo(s) under ${opts.workingDir}: ${repos.map((r) => r.repoName || "<root>").join(", ")}`
|
|
13115
|
+
);
|
|
13116
|
+
});
|
|
13117
|
+
}
|
|
13118
|
+
opts;
|
|
13119
|
+
apiBase;
|
|
13120
|
+
outbox;
|
|
13121
|
+
repos = [];
|
|
13122
|
+
discovering = null;
|
|
13123
|
+
stopped = false;
|
|
13124
|
+
/** Stop the outbox scheduler. Idempotent. */
|
|
13125
|
+
stop() {
|
|
13126
|
+
this.stopped = true;
|
|
13127
|
+
this.outbox.stop();
|
|
13128
|
+
}
|
|
13129
|
+
/**
|
|
13130
|
+
* Run the discovery + git collection + POST pipeline for one
|
|
13131
|
+
* turn. Errors are swallowed (logged) so an agent never blocks on a
|
|
13132
|
+
* file-changeset failure; the outbox covers the network half.
|
|
13133
|
+
*/
|
|
13134
|
+
async flushTurn() {
|
|
13135
|
+
if (this.stopped) return;
|
|
13136
|
+
try {
|
|
13137
|
+
if (this.discovering) {
|
|
13138
|
+
await this.discovering;
|
|
13139
|
+
this.discovering = null;
|
|
13140
|
+
}
|
|
13141
|
+
if (this.repos.length === 0) {
|
|
13142
|
+
log.trace("turnFiles", "no repos discovered \u2014 skipping flush");
|
|
13143
|
+
return;
|
|
13144
|
+
}
|
|
13145
|
+
const reposToScan = this.opts.dirtyTracker ? this.filterByDirty(this.opts.dirtyTracker) : this.repos;
|
|
13146
|
+
if (reposToScan.length === 0) {
|
|
13147
|
+
log.trace("turnFiles", "dirty set empty \u2014 skipping flush");
|
|
13148
|
+
return;
|
|
13149
|
+
}
|
|
13150
|
+
const files = [];
|
|
13151
|
+
for (const repo of reposToScan) {
|
|
13152
|
+
const entries = await collectRepoChangeset(repo);
|
|
13153
|
+
if (entries) files.push(...entries);
|
|
13154
|
+
}
|
|
13155
|
+
if (files.length === 0) {
|
|
13156
|
+
log.trace("turnFiles", "no changes detected this turn \u2014 skipping POST");
|
|
13157
|
+
return;
|
|
13158
|
+
}
|
|
13159
|
+
const chunks = chunkArray(files, MAX_BATCH_SIZE);
|
|
13160
|
+
for (const chunk of chunks) {
|
|
13161
|
+
const entry = {
|
|
13162
|
+
turnId: (0, import_crypto2.randomUUID)(),
|
|
13163
|
+
sessionId: this.opts.sessionId,
|
|
13164
|
+
pluginId: this.opts.pluginId,
|
|
13165
|
+
enqueuedAt: Date.now(),
|
|
13166
|
+
files: chunk
|
|
13167
|
+
};
|
|
13168
|
+
await this.outbox.enqueue(entry);
|
|
13169
|
+
}
|
|
13170
|
+
} catch (err) {
|
|
13171
|
+
log.warn(
|
|
13172
|
+
"turnFiles",
|
|
13173
|
+
`flushTurn failed: ${err.message ?? String(err)}`
|
|
13174
|
+
);
|
|
13175
|
+
}
|
|
13176
|
+
}
|
|
13177
|
+
/**
|
|
13178
|
+
* Consume the tracker's dirty set and intersect with the
|
|
13179
|
+
* discovered repos so we never spawn git for a path the watcher
|
|
13180
|
+
* marked outside our discovered set (e.g. a sibling repo that
|
|
13181
|
+
* appeared after construction). Unknown roots get dropped on the
|
|
13182
|
+
* floor — they'll re-mark themselves on the next event if they're
|
|
13183
|
+
* inside `workingDir`.
|
|
13184
|
+
*/
|
|
13185
|
+
filterByDirty(tracker) {
|
|
13186
|
+
const dirty = tracker.consume();
|
|
13187
|
+
if (dirty.size === 0) return [];
|
|
13188
|
+
return this.repos.filter((repo) => dirty.has(repo.repoRoot));
|
|
13189
|
+
}
|
|
13190
|
+
async postEntry(entry) {
|
|
13191
|
+
const url = `${this.apiBase}${ENDPOINT}`;
|
|
13192
|
+
const headers = {
|
|
13193
|
+
"Content-Type": "application/json",
|
|
13194
|
+
"X-Codeam-Protocol-Version": PROTOCOL_VERSION,
|
|
13195
|
+
"X-Plugin-Auth-Token": this.opts.pluginAuthToken
|
|
13196
|
+
};
|
|
13197
|
+
const body = JSON.stringify({
|
|
13198
|
+
sessionId: entry.sessionId,
|
|
13199
|
+
pluginId: entry.pluginId,
|
|
13200
|
+
turnId: entry.turnId,
|
|
13201
|
+
files: entry.files
|
|
13202
|
+
});
|
|
13203
|
+
try {
|
|
13204
|
+
const res = await _transport3.post(url, headers, body);
|
|
13205
|
+
return { ok: res.statusCode >= 200 && res.statusCode < 300, statusCode: res.statusCode };
|
|
13206
|
+
} catch (err) {
|
|
13207
|
+
log.trace(
|
|
13208
|
+
"turnFiles",
|
|
13209
|
+
`batch POST threw turnId=${entry.turnId.slice(0, 8)}: ${err.message}`
|
|
13210
|
+
);
|
|
13211
|
+
return { ok: false, statusCode: 0 };
|
|
13212
|
+
}
|
|
13213
|
+
}
|
|
13214
|
+
};
|
|
13215
|
+
function chunkArray(arr, size) {
|
|
13216
|
+
if (arr.length <= size) return [arr];
|
|
13217
|
+
const out2 = [];
|
|
13218
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
13219
|
+
out2.push(arr.slice(i, i + size));
|
|
13220
|
+
}
|
|
13221
|
+
return out2;
|
|
13222
|
+
}
|
|
13223
|
+
|
|
13224
|
+
// src/services/turn-files/repo-dirty-tracker.ts
|
|
13225
|
+
var RepoDirtyTracker = class {
|
|
13226
|
+
dirty = /* @__PURE__ */ new Set();
|
|
13227
|
+
/** Add a repo root to the dirty set. Idempotent. */
|
|
13228
|
+
markDirty(repoRoot) {
|
|
13229
|
+
this.dirty.add(repoRoot);
|
|
13230
|
+
}
|
|
13231
|
+
/** Seed every known repo as dirty — used right after `discoverRepos`
|
|
13232
|
+
* returns so the first end-of-turn flush captures worktree state
|
|
13233
|
+
* that predates the pairing. */
|
|
13234
|
+
markAllDirty(repoRoots) {
|
|
13235
|
+
for (const r of repoRoots) this.dirty.add(r.repoRoot);
|
|
13236
|
+
}
|
|
13237
|
+
/** Snapshot the current dirty set without clearing — useful for
|
|
13238
|
+
* diagnostic logs / tests. */
|
|
13239
|
+
peek() {
|
|
13240
|
+
return new Set(this.dirty);
|
|
13241
|
+
}
|
|
13242
|
+
/** Atomically read AND clear the dirty set. The aggregator calls
|
|
13243
|
+
* this on each `done:true`; subsequent filesystem events
|
|
13244
|
+
* re-populate it for the next turn. */
|
|
13245
|
+
consume() {
|
|
13246
|
+
const snapshot = new Set(this.dirty);
|
|
13247
|
+
this.dirty.clear();
|
|
13248
|
+
return snapshot;
|
|
13249
|
+
}
|
|
13250
|
+
/** True when the set is non-empty. Cheap pre-flight gate so the
|
|
13251
|
+
* aggregator can early-return on chat-only turns without
|
|
13252
|
+
* touching the dirty set. */
|
|
13253
|
+
hasDirty() {
|
|
13254
|
+
return this.dirty.size > 0;
|
|
13255
|
+
}
|
|
13256
|
+
};
|
|
13257
|
+
|
|
13258
|
+
// src/services/streaming-emitter.service.ts
|
|
13259
|
+
var import_crypto3 = require("crypto");
|
|
13260
|
+
|
|
12775
13261
|
// src/services/streaming/transport.ts
|
|
12776
13262
|
var http6 = __toESM(require("http"));
|
|
12777
13263
|
var https6 = __toESM(require("https"));
|
|
@@ -12780,7 +13266,7 @@ var _transport4 = {
|
|
|
12780
13266
|
get: _get
|
|
12781
13267
|
};
|
|
12782
13268
|
function _post3(url, headers, payload) {
|
|
12783
|
-
return new Promise((
|
|
13269
|
+
return new Promise((resolve5, reject) => {
|
|
12784
13270
|
let settled = false;
|
|
12785
13271
|
const u2 = new URL(url);
|
|
12786
13272
|
const lib = u2.protocol === "https:" ? https6 : http6;
|
|
@@ -12805,7 +13291,7 @@ function _post3(url, headers, payload) {
|
|
|
12805
13291
|
res.on("end", () => {
|
|
12806
13292
|
if (settled) return;
|
|
12807
13293
|
settled = true;
|
|
12808
|
-
|
|
13294
|
+
resolve5({ statusCode: res.statusCode ?? 0, body });
|
|
12809
13295
|
});
|
|
12810
13296
|
}
|
|
12811
13297
|
);
|
|
@@ -12822,7 +13308,7 @@ function _post3(url, headers, payload) {
|
|
|
12822
13308
|
});
|
|
12823
13309
|
}
|
|
12824
13310
|
function _get(url, headers) {
|
|
12825
|
-
return new Promise((
|
|
13311
|
+
return new Promise((resolve5, reject) => {
|
|
12826
13312
|
let settled = false;
|
|
12827
13313
|
const u2 = new URL(url);
|
|
12828
13314
|
const lib = u2.protocol === "https:" ? https6 : http6;
|
|
@@ -12846,7 +13332,7 @@ function _get(url, headers) {
|
|
|
12846
13332
|
res.on("end", () => {
|
|
12847
13333
|
if (settled) return;
|
|
12848
13334
|
settled = true;
|
|
12849
|
-
|
|
13335
|
+
resolve5({ statusCode: res.statusCode ?? 0, body });
|
|
12850
13336
|
});
|
|
12851
13337
|
}
|
|
12852
13338
|
);
|
|
@@ -12863,7 +13349,7 @@ function _get(url, headers) {
|
|
|
12863
13349
|
}
|
|
12864
13350
|
|
|
12865
13351
|
// src/services/streaming-emitter.service.ts
|
|
12866
|
-
var
|
|
13352
|
+
var API_BASE7 = resolveApiBaseUrl();
|
|
12867
13353
|
var TICK_MS = 50;
|
|
12868
13354
|
var ANSWER_POLL_MS = 1500;
|
|
12869
13355
|
var SELECTOR_STABLE_MS = 800;
|
|
@@ -12874,7 +13360,7 @@ var TAIL_KEEP_BYTES2 = 1.5 * 1024 * 1024;
|
|
|
12874
13360
|
var StreamingEmitterService = class {
|
|
12875
13361
|
constructor(opts) {
|
|
12876
13362
|
this.opts = opts;
|
|
12877
|
-
this.apiBase = opts.apiBaseUrl ??
|
|
13363
|
+
this.apiBase = opts.apiBaseUrl ?? API_BASE7;
|
|
12878
13364
|
this.headers = {
|
|
12879
13365
|
"Content-Type": "application/json",
|
|
12880
13366
|
"X-Codeam-Protocol-Version": "2.0.0",
|
|
@@ -12993,7 +13479,7 @@ var StreamingEmitterService = class {
|
|
|
12993
13479
|
});
|
|
12994
13480
|
}
|
|
12995
13481
|
this.activeChunk = {
|
|
12996
|
-
chunkId: (0,
|
|
13482
|
+
chunkId: (0, import_crypto3.randomUUID)(),
|
|
12997
13483
|
kind: last.kind,
|
|
12998
13484
|
emittedContent: "",
|
|
12999
13485
|
currentContent: last.content,
|
|
@@ -13061,7 +13547,7 @@ var StreamingEmitterService = class {
|
|
|
13061
13547
|
}
|
|
13062
13548
|
if (this.pendingAnswer) return;
|
|
13063
13549
|
if (now - this.selectorFirstSeenAt < SELECTOR_STABLE_MS) return;
|
|
13064
|
-
const questionId = (0,
|
|
13550
|
+
const questionId = (0, import_crypto3.randomUUID)();
|
|
13065
13551
|
this.pendingAnswer = {
|
|
13066
13552
|
questionId,
|
|
13067
13553
|
options: selector.options.length > 0 ? selector.options : void 0,
|
|
@@ -13224,13 +13710,13 @@ function fetchQuotaUsage(runtime, historySvc) {
|
|
|
13224
13710
|
}
|
|
13225
13711
|
|
|
13226
13712
|
// src/commands/start/keep-alive.ts
|
|
13227
|
-
var
|
|
13713
|
+
var import_child_process9 = require("child_process");
|
|
13228
13714
|
function buildKeepAlive(ctx) {
|
|
13229
13715
|
let timer = null;
|
|
13230
13716
|
async function setIdleTimeout(minutes) {
|
|
13231
13717
|
if (!ctx.inCodespace || !ctx.codespaceName) return;
|
|
13232
|
-
await new Promise((
|
|
13233
|
-
const proc = (0,
|
|
13718
|
+
await new Promise((resolve5) => {
|
|
13719
|
+
const proc = (0, import_child_process9.spawn)(
|
|
13234
13720
|
"gh",
|
|
13235
13721
|
[
|
|
13236
13722
|
"api",
|
|
@@ -13243,8 +13729,8 @@ function buildKeepAlive(ctx) {
|
|
|
13243
13729
|
{ stdio: "ignore", detached: true }
|
|
13244
13730
|
);
|
|
13245
13731
|
proc.unref();
|
|
13246
|
-
proc.on("exit", () =>
|
|
13247
|
-
proc.on("error", () =>
|
|
13732
|
+
proc.on("exit", () => resolve5());
|
|
13733
|
+
proc.on("error", () => resolve5());
|
|
13248
13734
|
});
|
|
13249
13735
|
}
|
|
13250
13736
|
return {
|
|
@@ -13267,11 +13753,11 @@ function buildKeepAlive(ctx) {
|
|
|
13267
13753
|
}
|
|
13268
13754
|
|
|
13269
13755
|
// src/commands/start/handlers.ts
|
|
13270
|
-
var
|
|
13756
|
+
var fs26 = __toESM(require("fs"));
|
|
13271
13757
|
var os23 = __toESM(require("os"));
|
|
13272
|
-
var
|
|
13273
|
-
var
|
|
13274
|
-
var
|
|
13758
|
+
var path32 = __toESM(require("path"));
|
|
13759
|
+
var import_crypto5 = require("crypto");
|
|
13760
|
+
var import_child_process13 = require("child_process");
|
|
13275
13761
|
|
|
13276
13762
|
// src/lib/payload.ts
|
|
13277
13763
|
var import_zod2 = require("zod");
|
|
@@ -13319,7 +13805,15 @@ var startCommandSchema = import_zod2.z.object({
|
|
|
13319
13805
|
data: import_zod2.z.string().max(64 * 1024).optional(),
|
|
13320
13806
|
cwd: import_zod2.z.string().max(4096).optional(),
|
|
13321
13807
|
cols: import_zod2.z.number().int().min(1).max(500).optional(),
|
|
13322
|
-
rows: import_zod2.z.number().int().min(1).max(200).optional()
|
|
13808
|
+
rows: import_zod2.z.number().int().min(1).max(200).optional(),
|
|
13809
|
+
// `apply_file_review` (Epic B follow-up — backend pushes this when
|
|
13810
|
+
// the user clicks APPROVE_CHANGES / REJECT_CHANGES in the diff
|
|
13811
|
+
// drawer). `filePath` is relative to the enclosing git repo; the
|
|
13812
|
+
// handler walks up from it to find `.git/` and runs `git add` or
|
|
13813
|
+
// `git restore` from there. `action='approved'` stages the edit,
|
|
13814
|
+
// `action='rejected'` discards every worktree change on the file.
|
|
13815
|
+
filePath: import_zod2.z.string().min(1).max(4096).optional(),
|
|
13816
|
+
action: import_zod2.z.enum(["approved", "rejected"]).optional()
|
|
13323
13817
|
});
|
|
13324
13818
|
function parsePayload2(schema, raw) {
|
|
13325
13819
|
const result = schema.safeParse(raw);
|
|
@@ -13327,8 +13821,8 @@ function parsePayload2(schema, raw) {
|
|
|
13327
13821
|
}
|
|
13328
13822
|
|
|
13329
13823
|
// src/services/file-ops.service.ts
|
|
13330
|
-
var
|
|
13331
|
-
var
|
|
13824
|
+
var fs23 = __toESM(require("fs/promises"));
|
|
13825
|
+
var path28 = __toESM(require("path"));
|
|
13332
13826
|
var MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
13333
13827
|
var MAX_WALK_DEPTH = 6;
|
|
13334
13828
|
var MAX_VISITED_DIRS = 5e3;
|
|
@@ -13363,12 +13857,12 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
|
|
|
13363
13857
|
"__pycache__"
|
|
13364
13858
|
]);
|
|
13365
13859
|
function isUnder(parent, candidate) {
|
|
13366
|
-
const rel =
|
|
13367
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
13860
|
+
const rel = path28.relative(parent, candidate);
|
|
13861
|
+
return rel === "" || !rel.startsWith("..") && !path28.isAbsolute(rel);
|
|
13368
13862
|
}
|
|
13369
13863
|
async function isExistingFile(absPath) {
|
|
13370
13864
|
try {
|
|
13371
|
-
const stat3 = await
|
|
13865
|
+
const stat3 = await fs23.stat(absPath);
|
|
13372
13866
|
return stat3.isFile();
|
|
13373
13867
|
} catch {
|
|
13374
13868
|
return false;
|
|
@@ -13381,13 +13875,13 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
13381
13875
|
ctx.visited++;
|
|
13382
13876
|
let entries = [];
|
|
13383
13877
|
try {
|
|
13384
|
-
entries = await
|
|
13878
|
+
entries = await fs23.readdir(dir, { withFileTypes: true });
|
|
13385
13879
|
} catch {
|
|
13386
13880
|
return;
|
|
13387
13881
|
}
|
|
13388
13882
|
for (const e of entries) {
|
|
13389
13883
|
if (!e.isFile()) continue;
|
|
13390
|
-
const full =
|
|
13884
|
+
const full = path28.join(dir, e.name);
|
|
13391
13885
|
if (needleVariants.some((needle) => full.endsWith(needle))) {
|
|
13392
13886
|
ctx.matches.push(full);
|
|
13393
13887
|
if (ctx.matches.length >= ctx.cap) return;
|
|
@@ -13397,21 +13891,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
13397
13891
|
if (!e.isDirectory()) continue;
|
|
13398
13892
|
if (SUBDIR_IGNORE.has(e.name)) continue;
|
|
13399
13893
|
if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
|
|
13400
|
-
await walkForSuffix(
|
|
13894
|
+
await walkForSuffix(path28.join(dir, e.name), needleVariants, depth + 1, ctx);
|
|
13401
13895
|
if (ctx.matches.length >= ctx.cap) return;
|
|
13402
13896
|
}
|
|
13403
13897
|
}
|
|
13404
13898
|
async function findFile(rawPath) {
|
|
13405
13899
|
const cwd = process.cwd();
|
|
13406
|
-
if (
|
|
13407
|
-
const abs =
|
|
13900
|
+
if (path28.isAbsolute(rawPath)) {
|
|
13901
|
+
const abs = path28.normalize(rawPath);
|
|
13408
13902
|
if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
|
|
13409
13903
|
}
|
|
13410
|
-
const direct =
|
|
13904
|
+
const direct = path28.resolve(cwd, rawPath);
|
|
13411
13905
|
if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
|
|
13412
|
-
const normalized =
|
|
13906
|
+
const normalized = path28.normalize(rawPath).replace(/^[./\\]+/, "");
|
|
13413
13907
|
const needles = [
|
|
13414
|
-
`${
|
|
13908
|
+
`${path28.sep}${normalized}`,
|
|
13415
13909
|
`/${normalized}`
|
|
13416
13910
|
].filter((v, i, a) => a.indexOf(v) === i);
|
|
13417
13911
|
const ctx = { visited: 0, matches: [], cap: 16 };
|
|
@@ -13425,7 +13919,7 @@ async function findWriteTarget(rawPath) {
|
|
|
13425
13919
|
const found = await findFile(rawPath);
|
|
13426
13920
|
if (found) return found;
|
|
13427
13921
|
const cwd = process.cwd();
|
|
13428
|
-
const fallback =
|
|
13922
|
+
const fallback = path28.isAbsolute(rawPath) ? path28.normalize(rawPath) : path28.resolve(cwd, rawPath);
|
|
13429
13923
|
if (!isUnder(cwd, fallback)) return null;
|
|
13430
13924
|
return fallback;
|
|
13431
13925
|
}
|
|
@@ -13442,11 +13936,11 @@ async function readProjectFile(rawPath) {
|
|
|
13442
13936
|
if (!abs) {
|
|
13443
13937
|
return { error: `File not found in the project tree: ${rawPath}` };
|
|
13444
13938
|
}
|
|
13445
|
-
const stat3 = await
|
|
13939
|
+
const stat3 = await fs23.stat(abs);
|
|
13446
13940
|
if (stat3.size > MAX_FILE_BYTES) {
|
|
13447
13941
|
return { error: `File too large (${(stat3.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
|
|
13448
13942
|
}
|
|
13449
|
-
const buf = await
|
|
13943
|
+
const buf = await fs23.readFile(abs);
|
|
13450
13944
|
if (looksBinary(buf)) {
|
|
13451
13945
|
return { error: "Binary file \u2014 refusing to open in a code editor." };
|
|
13452
13946
|
}
|
|
@@ -13465,8 +13959,8 @@ async function writeProjectFile(rawPath, content) {
|
|
|
13465
13959
|
if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
|
|
13466
13960
|
return { error: "Content too large." };
|
|
13467
13961
|
}
|
|
13468
|
-
await
|
|
13469
|
-
await
|
|
13962
|
+
await fs23.mkdir(path28.dirname(abs), { recursive: true });
|
|
13963
|
+
await fs23.writeFile(abs, content, "utf-8");
|
|
13470
13964
|
return { ok: true };
|
|
13471
13965
|
} catch (e) {
|
|
13472
13966
|
const msg = e instanceof Error ? e.message : "Write failed";
|
|
@@ -13475,11 +13969,11 @@ async function writeProjectFile(rawPath, content) {
|
|
|
13475
13969
|
}
|
|
13476
13970
|
|
|
13477
13971
|
// src/services/project-ops.service.ts
|
|
13478
|
-
var
|
|
13972
|
+
var import_child_process10 = require("child_process");
|
|
13479
13973
|
var import_util2 = require("util");
|
|
13480
|
-
var
|
|
13481
|
-
var
|
|
13482
|
-
var execFileP3 = (0, import_util2.promisify)(
|
|
13974
|
+
var fs24 = __toESM(require("fs/promises"));
|
|
13975
|
+
var path29 = __toESM(require("path"));
|
|
13976
|
+
var execFileP3 = (0, import_util2.promisify)(import_child_process10.execFile);
|
|
13483
13977
|
var PROJECT_IGNORE = /* @__PURE__ */ new Set([
|
|
13484
13978
|
"node_modules",
|
|
13485
13979
|
".git",
|
|
@@ -13526,7 +14020,7 @@ async function listProjectFiles(opts = {}) {
|
|
|
13526
14020
|
}
|
|
13527
14021
|
let entries = [];
|
|
13528
14022
|
try {
|
|
13529
|
-
entries = await
|
|
14023
|
+
entries = await fs24.readdir(dir, { withFileTypes: true });
|
|
13530
14024
|
} catch {
|
|
13531
14025
|
return;
|
|
13532
14026
|
}
|
|
@@ -13536,18 +14030,18 @@ async function listProjectFiles(opts = {}) {
|
|
|
13536
14030
|
return;
|
|
13537
14031
|
}
|
|
13538
14032
|
if (PROJECT_IGNORE.has(e.name)) continue;
|
|
13539
|
-
const full =
|
|
14033
|
+
const full = path29.join(dir, e.name);
|
|
13540
14034
|
if (e.isDirectory()) {
|
|
13541
14035
|
if (depth >= 12) continue;
|
|
13542
14036
|
await walk(full, depth + 1);
|
|
13543
14037
|
} else if (e.isFile()) {
|
|
13544
|
-
const rel =
|
|
14038
|
+
const rel = path29.relative(root, full);
|
|
13545
14039
|
if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
|
|
13546
14040
|
continue;
|
|
13547
14041
|
}
|
|
13548
14042
|
let size = 0;
|
|
13549
14043
|
try {
|
|
13550
|
-
const st3 = await
|
|
14044
|
+
const st3 = await fs24.stat(full);
|
|
13551
14045
|
size = st3.size;
|
|
13552
14046
|
} catch {
|
|
13553
14047
|
}
|
|
@@ -13649,8 +14143,8 @@ async function gitStatus(cwd) {
|
|
|
13649
14143
|
let hasMergeInProgress = false;
|
|
13650
14144
|
try {
|
|
13651
14145
|
const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
|
|
13652
|
-
const mergeHead =
|
|
13653
|
-
await
|
|
14146
|
+
const mergeHead = path29.isAbsolute(gitDir) ? path29.join(gitDir, "MERGE_HEAD") : path29.join(root, gitDir, "MERGE_HEAD");
|
|
14147
|
+
await fs24.access(mergeHead);
|
|
13654
14148
|
hasMergeInProgress = true;
|
|
13655
14149
|
} catch {
|
|
13656
14150
|
}
|
|
@@ -13796,7 +14290,7 @@ async function jsSearchFiles(opts, cwd, cap) {
|
|
|
13796
14290
|
}
|
|
13797
14291
|
let content = "";
|
|
13798
14292
|
try {
|
|
13799
|
-
content = await
|
|
14293
|
+
content = await fs24.readFile(path29.join(cwd, f.path), "utf8");
|
|
13800
14294
|
} catch {
|
|
13801
14295
|
continue;
|
|
13802
14296
|
}
|
|
@@ -13819,8 +14313,8 @@ async function jsSearchFiles(opts, cwd, cap) {
|
|
|
13819
14313
|
}
|
|
13820
14314
|
|
|
13821
14315
|
// src/services/terminal-ops.service.ts
|
|
13822
|
-
var
|
|
13823
|
-
var
|
|
14316
|
+
var import_child_process11 = require("child_process");
|
|
14317
|
+
var import_crypto4 = require("crypto");
|
|
13824
14318
|
var import_path3 = __toESM(require("path"));
|
|
13825
14319
|
var MAX_CONCURRENT_SESSIONS = 4;
|
|
13826
14320
|
var nodePtyModule;
|
|
@@ -13941,7 +14435,7 @@ function createPythonSession(id, shell, cwd, env, cols, rows) {
|
|
|
13941
14435
|
}
|
|
13942
14436
|
let child;
|
|
13943
14437
|
try {
|
|
13944
|
-
child = (0,
|
|
14438
|
+
child = (0, import_child_process11.spawn)(python, ["-c", PYTHON_TERMINAL_HELPER, shell], {
|
|
13945
14439
|
cwd,
|
|
13946
14440
|
env: { ...env, COLUMNS: String(cols), LINES: String(rows) },
|
|
13947
14441
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -13996,7 +14490,7 @@ function openTerminal(opts) {
|
|
|
13996
14490
|
};
|
|
13997
14491
|
const cols = Math.max(1, Math.min(opts.cols ?? 80, 500));
|
|
13998
14492
|
const rows = Math.max(1, Math.min(opts.rows ?? 24, 200));
|
|
13999
|
-
const id = (0,
|
|
14493
|
+
const id = (0, import_crypto4.randomUUID)();
|
|
14000
14494
|
const ptyMod = loadNodePty2();
|
|
14001
14495
|
if (ptyMod) {
|
|
14002
14496
|
try {
|
|
@@ -14075,12 +14569,96 @@ function closeAllTerminals() {
|
|
|
14075
14569
|
for (const id of Array.from(sessions.keys())) closeTerminal(id);
|
|
14076
14570
|
}
|
|
14077
14571
|
|
|
14572
|
+
// src/services/apply-file-review.service.ts
|
|
14573
|
+
var import_child_process12 = require("child_process");
|
|
14574
|
+
var fs25 = __toESM(require("fs"));
|
|
14575
|
+
var path31 = __toESM(require("path"));
|
|
14576
|
+
async function applyFileReview(workingDir, filePath, action) {
|
|
14577
|
+
if (filePath.includes("..") || path31.isAbsolute(filePath)) {
|
|
14578
|
+
return { ok: false, action, filePath, error: "invalid file path" };
|
|
14579
|
+
}
|
|
14580
|
+
const absFile = path31.resolve(workingDir, filePath);
|
|
14581
|
+
const repoRoot = findGitRoot2(path31.dirname(absFile));
|
|
14582
|
+
if (!repoRoot) {
|
|
14583
|
+
return {
|
|
14584
|
+
ok: false,
|
|
14585
|
+
action,
|
|
14586
|
+
filePath,
|
|
14587
|
+
error: `no enclosing git repo for ${filePath}`
|
|
14588
|
+
};
|
|
14589
|
+
}
|
|
14590
|
+
const relInRepo = path31.relative(repoRoot, absFile);
|
|
14591
|
+
if (!relInRepo || relInRepo.startsWith("..")) {
|
|
14592
|
+
return { ok: false, action, filePath, error: "path escapes repo root" };
|
|
14593
|
+
}
|
|
14594
|
+
const args2 = action === "approved" ? ["add", "--", relInRepo] : ["restore", "--", relInRepo];
|
|
14595
|
+
const result = await runGit3(repoRoot, args2);
|
|
14596
|
+
if (!result.ok) {
|
|
14597
|
+
return {
|
|
14598
|
+
ok: false,
|
|
14599
|
+
action,
|
|
14600
|
+
filePath,
|
|
14601
|
+
repoRoot,
|
|
14602
|
+
error: result.stderr.slice(0, 500) || `git ${args2[0]} exited ${result.code}`
|
|
14603
|
+
};
|
|
14604
|
+
}
|
|
14605
|
+
log.info(
|
|
14606
|
+
"reviewApply",
|
|
14607
|
+
`git ${args2[0]} ${relInRepo} in ${repoRoot} \u2014 ok`
|
|
14608
|
+
);
|
|
14609
|
+
return { ok: true, action, filePath, repoRoot };
|
|
14610
|
+
}
|
|
14611
|
+
function runGit3(cwd, args2) {
|
|
14612
|
+
return new Promise((resolve5) => {
|
|
14613
|
+
let proc;
|
|
14614
|
+
try {
|
|
14615
|
+
proc = (0, import_child_process12.spawn)("git", args2, { cwd, env: process.env });
|
|
14616
|
+
} catch (err) {
|
|
14617
|
+
resolve5({ ok: false, code: -1, stdout: "", stderr: err.message });
|
|
14618
|
+
return;
|
|
14619
|
+
}
|
|
14620
|
+
let stdout = "";
|
|
14621
|
+
let stderr = "";
|
|
14622
|
+
proc.stdout?.on("data", (c2) => {
|
|
14623
|
+
stdout += c2.toString();
|
|
14624
|
+
});
|
|
14625
|
+
proc.stderr?.on("data", (c2) => {
|
|
14626
|
+
stderr += c2.toString();
|
|
14627
|
+
});
|
|
14628
|
+
proc.on(
|
|
14629
|
+
"error",
|
|
14630
|
+
(err) => resolve5({ ok: false, code: -1, stdout, stderr: stderr + err.message })
|
|
14631
|
+
);
|
|
14632
|
+
proc.on(
|
|
14633
|
+
"close",
|
|
14634
|
+
(code) => resolve5({ ok: code === 0, code: code ?? -1, stdout, stderr })
|
|
14635
|
+
);
|
|
14636
|
+
});
|
|
14637
|
+
}
|
|
14638
|
+
function findGitRoot2(startDir) {
|
|
14639
|
+
let dir = path31.resolve(startDir);
|
|
14640
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14641
|
+
for (let i = 0; i < 256; i++) {
|
|
14642
|
+
if (seen.has(dir)) return null;
|
|
14643
|
+
seen.add(dir);
|
|
14644
|
+
try {
|
|
14645
|
+
const stat3 = fs25.statSync(path31.join(dir, ".git"), { throwIfNoEntry: false });
|
|
14646
|
+
if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
|
|
14647
|
+
} catch {
|
|
14648
|
+
}
|
|
14649
|
+
const parent = path31.dirname(dir);
|
|
14650
|
+
if (parent === dir) return null;
|
|
14651
|
+
dir = parent;
|
|
14652
|
+
}
|
|
14653
|
+
return null;
|
|
14654
|
+
}
|
|
14655
|
+
|
|
14078
14656
|
// src/commands/start/handlers.ts
|
|
14079
14657
|
var pendingAttachmentFiles = /* @__PURE__ */ new Set();
|
|
14080
14658
|
function cleanupAttachmentTempFiles() {
|
|
14081
14659
|
for (const p2 of pendingAttachmentFiles) {
|
|
14082
14660
|
try {
|
|
14083
|
-
|
|
14661
|
+
fs26.unlinkSync(p2);
|
|
14084
14662
|
} catch {
|
|
14085
14663
|
}
|
|
14086
14664
|
}
|
|
@@ -14089,8 +14667,8 @@ function cleanupAttachmentTempFiles() {
|
|
|
14089
14667
|
function saveFilesTemp(files) {
|
|
14090
14668
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
14091
14669
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
14092
|
-
const tmpPath =
|
|
14093
|
-
|
|
14670
|
+
const tmpPath = path32.join(os23.tmpdir(), `codeam-${(0, import_crypto5.randomUUID)()}-${safeName}`);
|
|
14671
|
+
fs26.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
14094
14672
|
pendingAttachmentFiles.add(tmpPath);
|
|
14095
14673
|
return tmpPath;
|
|
14096
14674
|
});
|
|
@@ -14110,7 +14688,7 @@ var startTask = (ctx, _cmd, parsed) => {
|
|
|
14110
14688
|
setTimeout(() => {
|
|
14111
14689
|
for (const p2 of paths) {
|
|
14112
14690
|
try {
|
|
14113
|
-
|
|
14691
|
+
fs26.unlinkSync(p2);
|
|
14114
14692
|
} catch {
|
|
14115
14693
|
}
|
|
14116
14694
|
pendingAttachmentFiles.delete(p2);
|
|
@@ -14223,7 +14801,7 @@ var sessionTerminated = (ctx) => {
|
|
|
14223
14801
|
} catch {
|
|
14224
14802
|
}
|
|
14225
14803
|
try {
|
|
14226
|
-
const proc = (0,
|
|
14804
|
+
const proc = (0, import_child_process13.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
14227
14805
|
detached: true,
|
|
14228
14806
|
stdio: "ignore"
|
|
14229
14807
|
});
|
|
@@ -14245,7 +14823,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
14245
14823
|
}
|
|
14246
14824
|
if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
|
|
14247
14825
|
try {
|
|
14248
|
-
const stopProc = (0,
|
|
14826
|
+
const stopProc = (0, import_child_process13.spawn)(
|
|
14249
14827
|
"bash",
|
|
14250
14828
|
["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
|
|
14251
14829
|
{ detached: true, stdio: "ignore" }
|
|
@@ -14255,7 +14833,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
14255
14833
|
}
|
|
14256
14834
|
}
|
|
14257
14835
|
try {
|
|
14258
|
-
const proc = (0,
|
|
14836
|
+
const proc = (0, import_child_process13.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
14259
14837
|
detached: true,
|
|
14260
14838
|
stdio: "ignore"
|
|
14261
14839
|
});
|
|
@@ -14266,7 +14844,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
14266
14844
|
ctx.relay.stop();
|
|
14267
14845
|
process.exit(0);
|
|
14268
14846
|
};
|
|
14269
|
-
var
|
|
14847
|
+
var readFile4 = async (ctx, cmd, parsed) => {
|
|
14270
14848
|
if (!parsed.path) {
|
|
14271
14849
|
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path" });
|
|
14272
14850
|
return;
|
|
@@ -14274,7 +14852,7 @@ var readFile3 = async (ctx, cmd, parsed) => {
|
|
|
14274
14852
|
const result = await readProjectFile(parsed.path);
|
|
14275
14853
|
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
14276
14854
|
};
|
|
14277
|
-
var
|
|
14855
|
+
var writeFile3 = async (ctx, cmd, parsed) => {
|
|
14278
14856
|
if (!parsed.path || typeof parsed.content !== "string") {
|
|
14279
14857
|
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path or content" });
|
|
14280
14858
|
return;
|
|
@@ -14378,6 +14956,24 @@ var gitResolveH = async (ctx, cmd, parsed) => {
|
|
|
14378
14956
|
const result = await gitResolve(parsed.path, parsed.side);
|
|
14379
14957
|
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
14380
14958
|
};
|
|
14959
|
+
var applyFileReviewH = async (ctx, cmd, parsed) => {
|
|
14960
|
+
if (!parsed.filePath || !parsed.action) {
|
|
14961
|
+
await ctx.relay.sendResult(cmd.id, "failed", {
|
|
14962
|
+
error: "Missing filePath or action"
|
|
14963
|
+
});
|
|
14964
|
+
return;
|
|
14965
|
+
}
|
|
14966
|
+
const result = await applyFileReview(
|
|
14967
|
+
process.cwd(),
|
|
14968
|
+
parsed.filePath,
|
|
14969
|
+
parsed.action
|
|
14970
|
+
);
|
|
14971
|
+
await ctx.relay.sendResult(
|
|
14972
|
+
cmd.id,
|
|
14973
|
+
result.ok ? "completed" : "failed",
|
|
14974
|
+
result
|
|
14975
|
+
);
|
|
14976
|
+
};
|
|
14381
14977
|
var handlers = {
|
|
14382
14978
|
start_task: startTask,
|
|
14383
14979
|
provide_input: provideInput,
|
|
@@ -14393,8 +14989,8 @@ var handlers = {
|
|
|
14393
14989
|
set_keep_alive: setKeepAlive,
|
|
14394
14990
|
session_terminated: sessionTerminated,
|
|
14395
14991
|
shutdown_session: shutdownSession,
|
|
14396
|
-
read_file:
|
|
14397
|
-
write_file:
|
|
14992
|
+
read_file: readFile4,
|
|
14993
|
+
write_file: writeFile3,
|
|
14398
14994
|
list_files: listFiles,
|
|
14399
14995
|
search_files: searchFilesH,
|
|
14400
14996
|
terminal_open: terminalOpenH,
|
|
@@ -14408,7 +15004,8 @@ var handlers = {
|
|
|
14408
15004
|
git_commit: gitCommitH,
|
|
14409
15005
|
git_push: gitPushH,
|
|
14410
15006
|
git_pull: gitPullH,
|
|
14411
|
-
git_resolve: gitResolveH
|
|
15007
|
+
git_resolve: gitResolveH,
|
|
15008
|
+
apply_file_review: applyFileReviewH
|
|
14412
15009
|
};
|
|
14413
15010
|
async function dispatchCommand(ctx, cmd) {
|
|
14414
15011
|
const parsed = parsePayload2(startCommandSchema, cmd.payload);
|
|
@@ -14480,6 +15077,8 @@ async function start(requestedAgent) {
|
|
|
14480
15077
|
historySvc.uploadDelta().catch(() => {
|
|
14481
15078
|
});
|
|
14482
15079
|
}, 400);
|
|
15080
|
+
turnFiles?.flushTurn().catch(() => {
|
|
15081
|
+
});
|
|
14483
15082
|
},
|
|
14484
15083
|
() => {
|
|
14485
15084
|
const prevCount = historySvc.getCurrentMessageCount();
|
|
@@ -14488,11 +15087,20 @@ async function start(requestedAgent) {
|
|
|
14488
15087
|
session.pluginAuthToken,
|
|
14489
15088
|
runtime
|
|
14490
15089
|
);
|
|
15090
|
+
const dirtyTracker = session.pluginAuthToken ? new RepoDirtyTracker() : null;
|
|
14491
15091
|
const fileWatcher = session.pluginAuthToken ? new FileWatcherService({
|
|
14492
15092
|
workingDir: cwd,
|
|
14493
15093
|
sessionId: session.id,
|
|
14494
15094
|
pluginId,
|
|
14495
|
-
pluginAuthToken: session.pluginAuthToken
|
|
15095
|
+
pluginAuthToken: session.pluginAuthToken,
|
|
15096
|
+
onRepoDirty: dirtyTracker ? (repoRoot) => dirtyTracker.markDirty(repoRoot) : void 0
|
|
15097
|
+
}) : null;
|
|
15098
|
+
const turnFiles = session.pluginAuthToken ? new TurnFileAggregator({
|
|
15099
|
+
workingDir: cwd,
|
|
15100
|
+
sessionId: session.id,
|
|
15101
|
+
pluginId,
|
|
15102
|
+
pluginAuthToken: session.pluginAuthToken,
|
|
15103
|
+
dirtyTracker: dirtyTracker ?? void 0
|
|
14496
15104
|
}) : null;
|
|
14497
15105
|
let streamingEmitter = null;
|
|
14498
15106
|
const agent = new AgentService(
|
|
@@ -14510,6 +15118,7 @@ async function start(requestedAgent) {
|
|
|
14510
15118
|
outputSvc.dispose();
|
|
14511
15119
|
relay.stop();
|
|
14512
15120
|
void fileWatcher?.stop();
|
|
15121
|
+
turnFiles?.stop();
|
|
14513
15122
|
void streamingEmitter?.stop();
|
|
14514
15123
|
closeAllTerminals();
|
|
14515
15124
|
cleanupAttachmentTempFiles();
|
|
@@ -14577,7 +15186,7 @@ async function start(requestedAgent) {
|
|
|
14577
15186
|
}
|
|
14578
15187
|
|
|
14579
15188
|
// src/commands/pair.ts
|
|
14580
|
-
var
|
|
15189
|
+
var import_crypto6 = require("crypto");
|
|
14581
15190
|
var import_picocolors3 = __toESM(require("picocolors"));
|
|
14582
15191
|
|
|
14583
15192
|
// src/ui/prompts.ts
|
|
@@ -14641,7 +15250,7 @@ async function pair(args2 = []) {
|
|
|
14641
15250
|
const flagAgent = parseAgentFlag(args2);
|
|
14642
15251
|
const agentId = dryRun ? flagAgent ?? config.preferredAgent ?? "claude" : flagAgent ?? await promptForAgent(config.preferredAgent ?? "claude");
|
|
14643
15252
|
showIntro();
|
|
14644
|
-
const pluginId = (0,
|
|
15253
|
+
const pluginId = (0, import_crypto6.randomUUID)();
|
|
14645
15254
|
capture("pair_started", { agentId, pluginId, dryRun });
|
|
14646
15255
|
const spin = dist_exports.spinner();
|
|
14647
15256
|
spin.start("Requesting pairing code...");
|
|
@@ -14676,7 +15285,7 @@ async function pair(args2 = []) {
|
|
|
14676
15285
|
waitSpin.message(waitMessage());
|
|
14677
15286
|
}, 1e3);
|
|
14678
15287
|
countdownInterval.unref?.();
|
|
14679
|
-
await new Promise((
|
|
15288
|
+
await new Promise((resolve5) => {
|
|
14680
15289
|
let stopPolling = null;
|
|
14681
15290
|
function sigintHandler() {
|
|
14682
15291
|
clearInterval(countdownInterval);
|
|
@@ -14716,7 +15325,7 @@ async function pair(args2 = []) {
|
|
|
14716
15325
|
});
|
|
14717
15326
|
showSuccess(`Paired with ${info.userName} (${info.plan})`);
|
|
14718
15327
|
console.log("");
|
|
14719
|
-
|
|
15328
|
+
resolve5();
|
|
14720
15329
|
},
|
|
14721
15330
|
() => {
|
|
14722
15331
|
clearInterval(countdownInterval);
|
|
@@ -14732,10 +15341,10 @@ async function pair(args2 = []) {
|
|
|
14732
15341
|
}
|
|
14733
15342
|
|
|
14734
15343
|
// src/commands/pair-auto.ts
|
|
14735
|
-
var
|
|
15344
|
+
var fs27 = __toESM(require("fs"));
|
|
14736
15345
|
var os24 = __toESM(require("os"));
|
|
14737
|
-
var
|
|
14738
|
-
var
|
|
15346
|
+
var import_crypto7 = require("crypto");
|
|
15347
|
+
var API_BASE8 = resolveApiBaseUrl();
|
|
14739
15348
|
function fail(msg) {
|
|
14740
15349
|
console.error(`
|
|
14741
15350
|
${msg}
|
|
@@ -14750,12 +15359,12 @@ function readTokenFromArgs(args2) {
|
|
|
14750
15359
|
}
|
|
14751
15360
|
const fileFlag = args2.find((a) => a.startsWith("--token-file="));
|
|
14752
15361
|
if (fileFlag) {
|
|
14753
|
-
const
|
|
15362
|
+
const path40 = fileFlag.slice("--token-file=".length);
|
|
14754
15363
|
try {
|
|
14755
|
-
const content =
|
|
14756
|
-
if (content.length === 0) fail(`--token-file ${
|
|
15364
|
+
const content = fs27.readFileSync(path40, "utf8").trim();
|
|
15365
|
+
if (content.length === 0) fail(`--token-file ${path40} is empty`);
|
|
14757
15366
|
try {
|
|
14758
|
-
|
|
15367
|
+
fs27.unlinkSync(path40);
|
|
14759
15368
|
} catch {
|
|
14760
15369
|
}
|
|
14761
15370
|
return content;
|
|
@@ -14775,7 +15384,7 @@ function networkError(msg, cause) {
|
|
|
14775
15384
|
return err;
|
|
14776
15385
|
}
|
|
14777
15386
|
async function claimOnce(token, pluginId) {
|
|
14778
|
-
const url = `${
|
|
15387
|
+
const url = `${API_BASE8}/api/pairing/claim-auto-token`;
|
|
14779
15388
|
const body = {
|
|
14780
15389
|
token,
|
|
14781
15390
|
pluginId,
|
|
@@ -14839,7 +15448,7 @@ async function claim(token, pluginId) {
|
|
|
14839
15448
|
}
|
|
14840
15449
|
async function pairAuto(args2) {
|
|
14841
15450
|
const token = readTokenFromArgs(args2);
|
|
14842
|
-
const pluginId = (0,
|
|
15451
|
+
const pluginId = (0, import_crypto7.randomUUID)();
|
|
14843
15452
|
capture("pair_auto_started", { pluginId });
|
|
14844
15453
|
console.log(" Claiming pairing token\u2026");
|
|
14845
15454
|
const claimed = await claim(token, pluginId);
|
|
@@ -14965,7 +15574,7 @@ function status() {
|
|
|
14965
15574
|
|
|
14966
15575
|
// src/commands/logout.ts
|
|
14967
15576
|
var import_picocolors6 = __toESM(require("picocolors"));
|
|
14968
|
-
var
|
|
15577
|
+
var API_BASE9 = resolveApiBaseUrl();
|
|
14969
15578
|
async function notifyBackendOffline() {
|
|
14970
15579
|
const cfg = loadCliConfig();
|
|
14971
15580
|
const pluginIds = /* @__PURE__ */ new Set([
|
|
@@ -14977,7 +15586,7 @@ async function notifyBackendOffline() {
|
|
|
14977
15586
|
try {
|
|
14978
15587
|
await Promise.all(
|
|
14979
15588
|
Array.from(pluginIds).map(
|
|
14980
|
-
(pluginId) => _postJson(`${
|
|
15589
|
+
(pluginId) => _postJson(`${API_BASE9}/api/plugin/heartbeat`, {
|
|
14981
15590
|
pluginId,
|
|
14982
15591
|
online: false
|
|
14983
15592
|
}).catch((err) => {
|
|
@@ -15005,11 +15614,11 @@ async function logout() {
|
|
|
15005
15614
|
var import_picocolors9 = __toESM(require("picocolors"));
|
|
15006
15615
|
|
|
15007
15616
|
// src/services/providers/github-codespaces.ts
|
|
15008
|
-
var
|
|
15617
|
+
var import_child_process14 = require("child_process");
|
|
15009
15618
|
var import_util3 = require("util");
|
|
15010
15619
|
var import_picocolors7 = __toESM(require("picocolors"));
|
|
15011
|
-
var
|
|
15012
|
-
var execFileP4 = (0, import_util3.promisify)(
|
|
15620
|
+
var path33 = __toESM(require("path"));
|
|
15621
|
+
var execFileP4 = (0, import_util3.promisify)(import_child_process14.execFile);
|
|
15013
15622
|
var MAX_BUFFER = 8 * 1024 * 1024;
|
|
15014
15623
|
function resetStdinForChild() {
|
|
15015
15624
|
if (process.stdin.isTTY) {
|
|
@@ -15052,12 +15661,12 @@ var GitHubCodespacesProvider = class {
|
|
|
15052
15661
|
}
|
|
15053
15662
|
if (!isAuthed) {
|
|
15054
15663
|
resetStdinForChild();
|
|
15055
|
-
await new Promise((
|
|
15056
|
-
const proc = (0,
|
|
15664
|
+
await new Promise((resolve5, reject) => {
|
|
15665
|
+
const proc = (0, import_child_process14.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
|
|
15057
15666
|
stdio: "inherit"
|
|
15058
15667
|
});
|
|
15059
15668
|
proc.on("exit", (code) => {
|
|
15060
|
-
if (code === 0)
|
|
15669
|
+
if (code === 0) resolve5();
|
|
15061
15670
|
else reject(new Error("gh auth login failed."));
|
|
15062
15671
|
});
|
|
15063
15672
|
proc.on("error", reject);
|
|
@@ -15086,13 +15695,13 @@ var GitHubCodespacesProvider = class {
|
|
|
15086
15695
|
}
|
|
15087
15696
|
wt(noteLines.join("\n"), "One more permission needed");
|
|
15088
15697
|
resetStdinForChild();
|
|
15089
|
-
const refreshCode = await new Promise((
|
|
15090
|
-
const proc = (0,
|
|
15698
|
+
const refreshCode = await new Promise((resolve5, reject) => {
|
|
15699
|
+
const proc = (0, import_child_process14.spawn)(
|
|
15091
15700
|
"gh",
|
|
15092
15701
|
["auth", "refresh", "-h", "github.com", "-s", "codespace"],
|
|
15093
15702
|
{ stdio: "inherit" }
|
|
15094
15703
|
);
|
|
15095
|
-
proc.on("exit", (code) =>
|
|
15704
|
+
proc.on("exit", (code) => resolve5(code ?? 1));
|
|
15096
15705
|
proc.on("error", reject);
|
|
15097
15706
|
});
|
|
15098
15707
|
if (refreshCode !== 0) {
|
|
@@ -15236,10 +15845,10 @@ var GitHubCodespacesProvider = class {
|
|
|
15236
15845
|
if (q(proceed) || !proceed) return;
|
|
15237
15846
|
O2.step(`Installing gh via ${installCmd.describe}\u2026`);
|
|
15238
15847
|
resetStdinForChild();
|
|
15239
|
-
const ok = await new Promise((
|
|
15240
|
-
const proc = (0,
|
|
15241
|
-
proc.on("exit", (code) =>
|
|
15242
|
-
proc.on("error", () =>
|
|
15848
|
+
const ok = await new Promise((resolve5) => {
|
|
15849
|
+
const proc = (0, import_child_process14.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
|
|
15850
|
+
proc.on("exit", (code) => resolve5(code === 0));
|
|
15851
|
+
proc.on("error", () => resolve5(false));
|
|
15243
15852
|
});
|
|
15244
15853
|
if (ok) O2.success("gh installed");
|
|
15245
15854
|
else O2.error("gh install failed");
|
|
@@ -15263,14 +15872,14 @@ var GitHubCodespacesProvider = class {
|
|
|
15263
15872
|
"Expanding GitHub scopes"
|
|
15264
15873
|
);
|
|
15265
15874
|
resetStdinForChild();
|
|
15266
|
-
await new Promise((
|
|
15267
|
-
const proc = (0,
|
|
15875
|
+
await new Promise((resolve5, reject) => {
|
|
15876
|
+
const proc = (0, import_child_process14.spawn)(
|
|
15268
15877
|
"gh",
|
|
15269
15878
|
["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
|
|
15270
15879
|
{ stdio: "inherit" }
|
|
15271
15880
|
);
|
|
15272
15881
|
proc.on("exit", (code) => {
|
|
15273
|
-
if (code === 0)
|
|
15882
|
+
if (code === 0) resolve5();
|
|
15274
15883
|
else reject(new Error(
|
|
15275
15884
|
"gh auth refresh failed. Re-run `gh auth refresh -h github.com -s repo,read:org` manually."
|
|
15276
15885
|
));
|
|
@@ -15441,13 +16050,13 @@ var GitHubCodespacesProvider = class {
|
|
|
15441
16050
|
}
|
|
15442
16051
|
async streamCommand(workspaceId, command2) {
|
|
15443
16052
|
resetStdinForChild();
|
|
15444
|
-
return new Promise((
|
|
15445
|
-
const proc = (0,
|
|
16053
|
+
return new Promise((resolve5, reject) => {
|
|
16054
|
+
const proc = (0, import_child_process14.spawn)(
|
|
15446
16055
|
"gh",
|
|
15447
16056
|
["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
|
|
15448
16057
|
{ stdio: "inherit" }
|
|
15449
16058
|
);
|
|
15450
|
-
proc.on("exit", (code) =>
|
|
16059
|
+
proc.on("exit", (code) => resolve5({ code: code ?? 0 }));
|
|
15451
16060
|
proc.on("error", reject);
|
|
15452
16061
|
});
|
|
15453
16062
|
}
|
|
@@ -15468,12 +16077,12 @@ var GitHubCodespacesProvider = class {
|
|
|
15468
16077
|
"--",
|
|
15469
16078
|
`mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
|
|
15470
16079
|
];
|
|
15471
|
-
await new Promise((
|
|
15472
|
-
const tar = (0,
|
|
16080
|
+
await new Promise((resolve5, reject) => {
|
|
16081
|
+
const tar = (0, import_child_process14.spawn)("tar", tarArgs, {
|
|
15473
16082
|
stdio: ["ignore", "pipe", "pipe"],
|
|
15474
16083
|
env: tarEnv
|
|
15475
16084
|
});
|
|
15476
|
-
const ssh = (0,
|
|
16085
|
+
const ssh = (0, import_child_process14.spawn)("gh", sshArgs, {
|
|
15477
16086
|
stdio: [tar.stdout, "pipe", "pipe"]
|
|
15478
16087
|
});
|
|
15479
16088
|
let tarErr = "";
|
|
@@ -15488,7 +16097,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15488
16097
|
ssh.on("error", reject);
|
|
15489
16098
|
ssh.on("exit", (code) => {
|
|
15490
16099
|
if (code === 0) {
|
|
15491
|
-
|
|
16100
|
+
resolve5();
|
|
15492
16101
|
} else {
|
|
15493
16102
|
const reason = (sshErr || tarErr || `exit ${code}`).trim().slice(0, 500);
|
|
15494
16103
|
reject(new Error(`Remote tar failed: ${reason}`));
|
|
@@ -15497,7 +16106,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15497
16106
|
});
|
|
15498
16107
|
}
|
|
15499
16108
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
15500
|
-
const remoteDir =
|
|
16109
|
+
const remoteDir = path33.posix.dirname(remotePath);
|
|
15501
16110
|
const parts = [
|
|
15502
16111
|
`mkdir -p ${shellQuote(remoteDir)}`,
|
|
15503
16112
|
`cat > ${shellQuote(remotePath)}`
|
|
@@ -15506,8 +16115,8 @@ var GitHubCodespacesProvider = class {
|
|
|
15506
16115
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote(remotePath)}`);
|
|
15507
16116
|
}
|
|
15508
16117
|
const cmd = parts.join(" && ");
|
|
15509
|
-
await new Promise((
|
|
15510
|
-
const proc = (0,
|
|
16118
|
+
await new Promise((resolve5, reject) => {
|
|
16119
|
+
const proc = (0, import_child_process14.spawn)(
|
|
15511
16120
|
"gh",
|
|
15512
16121
|
["codespace", "ssh", "-c", workspaceId, "--", cmd],
|
|
15513
16122
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -15518,7 +16127,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15518
16127
|
});
|
|
15519
16128
|
proc.on("error", reject);
|
|
15520
16129
|
proc.on("exit", (code) => {
|
|
15521
|
-
if (code === 0)
|
|
16130
|
+
if (code === 0) resolve5();
|
|
15522
16131
|
else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
|
|
15523
16132
|
});
|
|
15524
16133
|
proc.stdin?.write(contents);
|
|
@@ -15565,11 +16174,11 @@ function shellQuote(s) {
|
|
|
15565
16174
|
}
|
|
15566
16175
|
|
|
15567
16176
|
// src/services/providers/gitpod.ts
|
|
15568
|
-
var
|
|
16177
|
+
var import_child_process15 = require("child_process");
|
|
15569
16178
|
var import_util4 = require("util");
|
|
15570
|
-
var
|
|
16179
|
+
var path34 = __toESM(require("path"));
|
|
15571
16180
|
var import_picocolors8 = __toESM(require("picocolors"));
|
|
15572
|
-
var execFileP5 = (0, import_util4.promisify)(
|
|
16181
|
+
var execFileP5 = (0, import_util4.promisify)(import_child_process15.execFile);
|
|
15573
16182
|
var MAX_BUFFER2 = 8 * 1024 * 1024;
|
|
15574
16183
|
function resetStdinForChild2() {
|
|
15575
16184
|
if (process.stdin.isTTY) {
|
|
@@ -15608,10 +16217,10 @@ var GitpodProvider = class {
|
|
|
15608
16217
|
"Authenticating Gitpod"
|
|
15609
16218
|
);
|
|
15610
16219
|
resetStdinForChild2();
|
|
15611
|
-
await new Promise((
|
|
15612
|
-
const proc = (0,
|
|
16220
|
+
await new Promise((resolve5, reject) => {
|
|
16221
|
+
const proc = (0, import_child_process15.spawn)("gitpod", ["login"], { stdio: "inherit" });
|
|
15613
16222
|
proc.on("exit", (code) => {
|
|
15614
|
-
if (code === 0)
|
|
16223
|
+
if (code === 0) resolve5();
|
|
15615
16224
|
else reject(new Error("gitpod login failed."));
|
|
15616
16225
|
});
|
|
15617
16226
|
proc.on("error", reject);
|
|
@@ -15760,13 +16369,13 @@ var GitpodProvider = class {
|
|
|
15760
16369
|
}
|
|
15761
16370
|
async streamCommand(workspaceId, command2) {
|
|
15762
16371
|
resetStdinForChild2();
|
|
15763
|
-
return new Promise((
|
|
15764
|
-
const proc = (0,
|
|
16372
|
+
return new Promise((resolve5, reject) => {
|
|
16373
|
+
const proc = (0, import_child_process15.spawn)(
|
|
15765
16374
|
"gitpod",
|
|
15766
16375
|
["workspace", "ssh", workspaceId, "--", "-tt", command2],
|
|
15767
16376
|
{ stdio: "inherit" }
|
|
15768
16377
|
);
|
|
15769
|
-
proc.on("exit", (code) =>
|
|
16378
|
+
proc.on("exit", (code) => resolve5({ code: code ?? 0 }));
|
|
15770
16379
|
proc.on("error", reject);
|
|
15771
16380
|
});
|
|
15772
16381
|
}
|
|
@@ -15780,12 +16389,12 @@ var GitpodProvider = class {
|
|
|
15780
16389
|
tarArgs.push(".");
|
|
15781
16390
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
15782
16391
|
const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
|
|
15783
|
-
await new Promise((
|
|
15784
|
-
const tar = (0,
|
|
16392
|
+
await new Promise((resolve5, reject) => {
|
|
16393
|
+
const tar = (0, import_child_process15.spawn)("tar", tarArgs, {
|
|
15785
16394
|
stdio: ["ignore", "pipe", "pipe"],
|
|
15786
16395
|
env: tarEnv
|
|
15787
16396
|
});
|
|
15788
|
-
const ssh = (0,
|
|
16397
|
+
const ssh = (0, import_child_process15.spawn)(
|
|
15789
16398
|
"gitpod",
|
|
15790
16399
|
["workspace", "ssh", workspaceId, "--", remoteCmd],
|
|
15791
16400
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -15801,13 +16410,13 @@ var GitpodProvider = class {
|
|
|
15801
16410
|
tar.on("error", reject);
|
|
15802
16411
|
ssh.on("error", reject);
|
|
15803
16412
|
ssh.on("exit", (code) => {
|
|
15804
|
-
if (code === 0)
|
|
16413
|
+
if (code === 0) resolve5();
|
|
15805
16414
|
else reject(new Error(`Remote tar failed: ${(sshErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
|
|
15806
16415
|
});
|
|
15807
16416
|
});
|
|
15808
16417
|
}
|
|
15809
16418
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
15810
|
-
const remoteDir =
|
|
16419
|
+
const remoteDir = path34.posix.dirname(remotePath);
|
|
15811
16420
|
const parts = [
|
|
15812
16421
|
`mkdir -p ${shellQuote2(remoteDir)}`,
|
|
15813
16422
|
`cat > ${shellQuote2(remotePath)}`
|
|
@@ -15816,8 +16425,8 @@ var GitpodProvider = class {
|
|
|
15816
16425
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote2(remotePath)}`);
|
|
15817
16426
|
}
|
|
15818
16427
|
const cmd = parts.join(" && ");
|
|
15819
|
-
await new Promise((
|
|
15820
|
-
const proc = (0,
|
|
16428
|
+
await new Promise((resolve5, reject) => {
|
|
16429
|
+
const proc = (0, import_child_process15.spawn)(
|
|
15821
16430
|
"gitpod",
|
|
15822
16431
|
["workspace", "ssh", workspaceId, "--", cmd],
|
|
15823
16432
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -15828,7 +16437,7 @@ var GitpodProvider = class {
|
|
|
15828
16437
|
});
|
|
15829
16438
|
proc.on("error", reject);
|
|
15830
16439
|
proc.on("exit", (code) => {
|
|
15831
|
-
if (code === 0)
|
|
16440
|
+
if (code === 0) resolve5();
|
|
15832
16441
|
else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
|
|
15833
16442
|
});
|
|
15834
16443
|
proc.stdin?.write(contents);
|
|
@@ -15841,10 +16450,10 @@ function shellQuote2(s) {
|
|
|
15841
16450
|
}
|
|
15842
16451
|
|
|
15843
16452
|
// src/services/providers/gitlab-workspaces.ts
|
|
15844
|
-
var
|
|
16453
|
+
var import_child_process16 = require("child_process");
|
|
15845
16454
|
var import_util5 = require("util");
|
|
15846
|
-
var
|
|
15847
|
-
var execFileP6 = (0, import_util5.promisify)(
|
|
16455
|
+
var path35 = __toESM(require("path"));
|
|
16456
|
+
var execFileP6 = (0, import_util5.promisify)(import_child_process16.execFile);
|
|
15848
16457
|
var MAX_BUFFER3 = 8 * 1024 * 1024;
|
|
15849
16458
|
var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
|
|
15850
16459
|
function resetStdinForChild3() {
|
|
@@ -15885,14 +16494,14 @@ var GitLabWorkspacesProvider = class {
|
|
|
15885
16494
|
"Authenticating GitLab"
|
|
15886
16495
|
);
|
|
15887
16496
|
resetStdinForChild3();
|
|
15888
|
-
await new Promise((
|
|
15889
|
-
const proc = (0,
|
|
16497
|
+
await new Promise((resolve5, reject) => {
|
|
16498
|
+
const proc = (0, import_child_process16.spawn)(
|
|
15890
16499
|
"glab",
|
|
15891
16500
|
["auth", "login", "--scopes", "api,read_user,read_repository"],
|
|
15892
16501
|
{ stdio: "inherit" }
|
|
15893
16502
|
);
|
|
15894
16503
|
proc.on("exit", (code) => {
|
|
15895
|
-
if (code === 0)
|
|
16504
|
+
if (code === 0) resolve5();
|
|
15896
16505
|
else reject(new Error("glab auth login failed."));
|
|
15897
16506
|
});
|
|
15898
16507
|
proc.on("error", reject);
|
|
@@ -16057,13 +16666,13 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
16057
16666
|
async streamCommand(workspaceId, command2) {
|
|
16058
16667
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
16059
16668
|
resetStdinForChild3();
|
|
16060
|
-
return new Promise((
|
|
16061
|
-
const proc = (0,
|
|
16669
|
+
return new Promise((resolve5, reject) => {
|
|
16670
|
+
const proc = (0, import_child_process16.spawn)(
|
|
16062
16671
|
"ssh",
|
|
16063
16672
|
["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
|
|
16064
16673
|
{ stdio: "inherit" }
|
|
16065
16674
|
);
|
|
16066
|
-
proc.on("exit", (code) =>
|
|
16675
|
+
proc.on("exit", (code) => resolve5({ code: code ?? 0 }));
|
|
16067
16676
|
proc.on("error", reject);
|
|
16068
16677
|
});
|
|
16069
16678
|
}
|
|
@@ -16078,9 +16687,9 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
16078
16687
|
tarArgs.push(".");
|
|
16079
16688
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
16080
16689
|
const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
|
|
16081
|
-
await new Promise((
|
|
16082
|
-
const tar = (0,
|
|
16083
|
-
const ssh = (0,
|
|
16690
|
+
await new Promise((resolve5, reject) => {
|
|
16691
|
+
const tar = (0, import_child_process16.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
16692
|
+
const ssh = (0, import_child_process16.spawn)(
|
|
16084
16693
|
"ssh",
|
|
16085
16694
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, remoteCmd],
|
|
16086
16695
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -16096,21 +16705,21 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
16096
16705
|
tar.on("error", reject);
|
|
16097
16706
|
ssh.on("error", reject);
|
|
16098
16707
|
ssh.on("exit", (code) => {
|
|
16099
|
-
if (code === 0)
|
|
16708
|
+
if (code === 0) resolve5();
|
|
16100
16709
|
else reject(new Error(`Remote tar failed: ${(sshErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
|
|
16101
16710
|
});
|
|
16102
16711
|
});
|
|
16103
16712
|
}
|
|
16104
16713
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
16105
16714
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
16106
|
-
const remoteDir =
|
|
16715
|
+
const remoteDir = path35.posix.dirname(remotePath);
|
|
16107
16716
|
const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
|
|
16108
16717
|
if (options.mode != null) {
|
|
16109
16718
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
|
|
16110
16719
|
}
|
|
16111
16720
|
const cmd = parts.join(" && ");
|
|
16112
|
-
await new Promise((
|
|
16113
|
-
const proc = (0,
|
|
16721
|
+
await new Promise((resolve5, reject) => {
|
|
16722
|
+
const proc = (0, import_child_process16.spawn)(
|
|
16114
16723
|
"ssh",
|
|
16115
16724
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
|
|
16116
16725
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -16121,7 +16730,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
16121
16730
|
});
|
|
16122
16731
|
proc.on("error", reject);
|
|
16123
16732
|
proc.on("exit", (code) => {
|
|
16124
|
-
if (code === 0)
|
|
16733
|
+
if (code === 0) resolve5();
|
|
16125
16734
|
else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
|
|
16126
16735
|
});
|
|
16127
16736
|
proc.stdin?.write(contents);
|
|
@@ -16169,10 +16778,10 @@ function shellQuote3(s) {
|
|
|
16169
16778
|
}
|
|
16170
16779
|
|
|
16171
16780
|
// src/services/providers/railway.ts
|
|
16172
|
-
var
|
|
16781
|
+
var import_child_process17 = require("child_process");
|
|
16173
16782
|
var import_util6 = require("util");
|
|
16174
|
-
var
|
|
16175
|
-
var execFileP7 = (0, import_util6.promisify)(
|
|
16783
|
+
var path36 = __toESM(require("path"));
|
|
16784
|
+
var execFileP7 = (0, import_util6.promisify)(import_child_process17.execFile);
|
|
16176
16785
|
var MAX_BUFFER4 = 8 * 1024 * 1024;
|
|
16177
16786
|
function resetStdinForChild4() {
|
|
16178
16787
|
if (process.stdin.isTTY) {
|
|
@@ -16212,10 +16821,10 @@ var RailwayProvider = class {
|
|
|
16212
16821
|
"Authenticating Railway"
|
|
16213
16822
|
);
|
|
16214
16823
|
resetStdinForChild4();
|
|
16215
|
-
await new Promise((
|
|
16216
|
-
const proc = (0,
|
|
16824
|
+
await new Promise((resolve5, reject) => {
|
|
16825
|
+
const proc = (0, import_child_process17.spawn)("railway", ["login"], { stdio: "inherit" });
|
|
16217
16826
|
proc.on("exit", (code) => {
|
|
16218
|
-
if (code === 0)
|
|
16827
|
+
if (code === 0) resolve5();
|
|
16219
16828
|
else reject(new Error("railway login failed."));
|
|
16220
16829
|
});
|
|
16221
16830
|
proc.on("error", reject);
|
|
@@ -16355,13 +16964,13 @@ var RailwayProvider = class {
|
|
|
16355
16964
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
16356
16965
|
}
|
|
16357
16966
|
resetStdinForChild4();
|
|
16358
|
-
return new Promise((
|
|
16359
|
-
const proc = (0,
|
|
16967
|
+
return new Promise((resolve5, reject) => {
|
|
16968
|
+
const proc = (0, import_child_process17.spawn)(
|
|
16360
16969
|
"railway",
|
|
16361
16970
|
["shell", "--project", projectId, "--service", serviceId, "--command", command2],
|
|
16362
16971
|
{ stdio: "inherit" }
|
|
16363
16972
|
);
|
|
16364
|
-
proc.on("exit", (code) =>
|
|
16973
|
+
proc.on("exit", (code) => resolve5({ code: code ?? 0 }));
|
|
16365
16974
|
proc.on("error", reject);
|
|
16366
16975
|
});
|
|
16367
16976
|
}
|
|
@@ -16379,9 +16988,9 @@ var RailwayProvider = class {
|
|
|
16379
16988
|
tarArgs.push(".");
|
|
16380
16989
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
16381
16990
|
const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
|
|
16382
|
-
await new Promise((
|
|
16383
|
-
const tar = (0,
|
|
16384
|
-
const sh = (0,
|
|
16991
|
+
await new Promise((resolve5, reject) => {
|
|
16992
|
+
const tar = (0, import_child_process17.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
16993
|
+
const sh = (0, import_child_process17.spawn)(
|
|
16385
16994
|
"railway",
|
|
16386
16995
|
["shell", "--project", projectId, "--service", serviceId, "--command", remoteCmd],
|
|
16387
16996
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -16397,7 +17006,7 @@ var RailwayProvider = class {
|
|
|
16397
17006
|
tar.on("error", reject);
|
|
16398
17007
|
sh.on("error", reject);
|
|
16399
17008
|
sh.on("exit", (code) => {
|
|
16400
|
-
if (code === 0)
|
|
17009
|
+
if (code === 0) resolve5();
|
|
16401
17010
|
else reject(new Error(`Remote tar failed: ${(shErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
|
|
16402
17011
|
});
|
|
16403
17012
|
});
|
|
@@ -16407,14 +17016,14 @@ var RailwayProvider = class {
|
|
|
16407
17016
|
if (!projectId || !serviceId) {
|
|
16408
17017
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
16409
17018
|
}
|
|
16410
|
-
const remoteDir =
|
|
17019
|
+
const remoteDir = path36.posix.dirname(remotePath);
|
|
16411
17020
|
const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
|
|
16412
17021
|
if (options.mode != null) {
|
|
16413
17022
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
|
|
16414
17023
|
}
|
|
16415
17024
|
const cmd = parts.join(" && ");
|
|
16416
|
-
await new Promise((
|
|
16417
|
-
const proc = (0,
|
|
17025
|
+
await new Promise((resolve5, reject) => {
|
|
17026
|
+
const proc = (0, import_child_process17.spawn)(
|
|
16418
17027
|
"railway",
|
|
16419
17028
|
["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
|
|
16420
17029
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -16425,7 +17034,7 @@ var RailwayProvider = class {
|
|
|
16425
17034
|
});
|
|
16426
17035
|
proc.on("error", reject);
|
|
16427
17036
|
proc.on("exit", (code) => {
|
|
16428
|
-
if (code === 0)
|
|
17037
|
+
if (code === 0) resolve5();
|
|
16429
17038
|
else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
|
|
16430
17039
|
});
|
|
16431
17040
|
proc.stdin?.write(contents);
|
|
@@ -16948,8 +17557,8 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
16948
17557
|
|
|
16949
17558
|
// src/commands/link.ts
|
|
16950
17559
|
var import_node_crypto4 = require("crypto");
|
|
16951
|
-
var
|
|
16952
|
-
var
|
|
17560
|
+
var fs28 = __toESM(require("fs"));
|
|
17561
|
+
var path37 = __toESM(require("path"));
|
|
16953
17562
|
var import_chokidar = __toESM(require("chokidar"));
|
|
16954
17563
|
var import_picocolors11 = __toESM(require("picocolors"));
|
|
16955
17564
|
function buildLinkContext(agentId) {
|
|
@@ -16987,7 +17596,7 @@ function parseLinkArgs(args2) {
|
|
|
16987
17596
|
if (apiKeyFileArg) {
|
|
16988
17597
|
const filePath = apiKeyFileArg.slice("--api-key-file=".length);
|
|
16989
17598
|
try {
|
|
16990
|
-
apiKey =
|
|
17599
|
+
apiKey = fs28.readFileSync(path37.resolve(filePath), "utf8").trim();
|
|
16991
17600
|
} catch (err) {
|
|
16992
17601
|
throw new Error(`Could not read --api-key-file ${filePath}: ${err.message}`);
|
|
16993
17602
|
}
|
|
@@ -17032,7 +17641,7 @@ async function link(args2 = []) {
|
|
|
17032
17641
|
waitSpin.start(waitMsg());
|
|
17033
17642
|
const countdown = setInterval(() => waitSpin.message(waitMsg()), 1e3);
|
|
17034
17643
|
countdown.unref?.();
|
|
17035
|
-
const paired = await new Promise((
|
|
17644
|
+
const paired = await new Promise((resolve5, reject) => {
|
|
17036
17645
|
let stopPoll = null;
|
|
17037
17646
|
const sigint = () => {
|
|
17038
17647
|
clearInterval(countdown);
|
|
@@ -17045,7 +17654,7 @@ async function link(args2 = []) {
|
|
|
17045
17654
|
process.removeListener("SIGINT", sigint);
|
|
17046
17655
|
clearInterval(countdown);
|
|
17047
17656
|
waitSpin.stop("Paired");
|
|
17048
|
-
|
|
17657
|
+
resolve5(info);
|
|
17049
17658
|
},
|
|
17050
17659
|
() => {
|
|
17051
17660
|
clearInterval(countdown);
|
|
@@ -17081,7 +17690,7 @@ async function link(args2 = []) {
|
|
|
17081
17690
|
return;
|
|
17082
17691
|
}
|
|
17083
17692
|
if (parsed.tokenFile) {
|
|
17084
|
-
const credential =
|
|
17693
|
+
const credential = fs28.readFileSync(path37.resolve(parsed.tokenFile), "utf8").trim();
|
|
17085
17694
|
if (!credential) {
|
|
17086
17695
|
showError(`--token-file ${parsed.tokenFile} is empty.`);
|
|
17087
17696
|
process.exit(1);
|
|
@@ -17151,14 +17760,14 @@ async function captureFreshCredentials(ctx) {
|
|
|
17151
17760
|
}
|
|
17152
17761
|
};
|
|
17153
17762
|
try {
|
|
17154
|
-
const token = await new Promise((
|
|
17763
|
+
const token = await new Promise((resolve5, reject) => {
|
|
17155
17764
|
let settled = false;
|
|
17156
17765
|
const tryExtract = async () => {
|
|
17157
17766
|
if (settled) return;
|
|
17158
17767
|
const t2 = await ctx.locator.extract();
|
|
17159
17768
|
if (t2 && !settled) {
|
|
17160
17769
|
settled = true;
|
|
17161
|
-
|
|
17770
|
+
resolve5(t2);
|
|
17162
17771
|
}
|
|
17163
17772
|
};
|
|
17164
17773
|
watcher.on("add", () => void tryExtract());
|
|
@@ -17270,8 +17879,8 @@ async function linkDryRunPreflight(ctx) {
|
|
|
17270
17879
|
var import_node_dns = require("dns");
|
|
17271
17880
|
var import_node_util4 = require("util");
|
|
17272
17881
|
var import_node_crypto5 = require("crypto");
|
|
17273
|
-
var
|
|
17274
|
-
var
|
|
17882
|
+
var fs29 = __toESM(require("fs"));
|
|
17883
|
+
var path38 = __toESM(require("path"));
|
|
17275
17884
|
var import_picocolors12 = __toESM(require("picocolors"));
|
|
17276
17885
|
var dnsResolveP = (0, import_node_util4.promisify)(import_node_dns.resolve);
|
|
17277
17886
|
async function checkDns(apiBase) {
|
|
@@ -17327,13 +17936,13 @@ async function checkHealth(apiBase) {
|
|
|
17327
17936
|
}
|
|
17328
17937
|
}
|
|
17329
17938
|
function checkConfigDir() {
|
|
17330
|
-
const dir =
|
|
17939
|
+
const dir = path38.join(require("os").homedir(), ".codeam");
|
|
17331
17940
|
try {
|
|
17332
|
-
|
|
17333
|
-
const probe =
|
|
17334
|
-
|
|
17335
|
-
const read =
|
|
17336
|
-
|
|
17941
|
+
fs29.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
17942
|
+
const probe = path38.join(dir, ".doctor-probe");
|
|
17943
|
+
fs29.writeFileSync(probe, "ok", { mode: 384 });
|
|
17944
|
+
const read = fs29.readFileSync(probe, "utf8");
|
|
17945
|
+
fs29.unlinkSync(probe);
|
|
17337
17946
|
if (read !== "ok") throw new Error("write/read round-trip mismatch");
|
|
17338
17947
|
return {
|
|
17339
17948
|
id: "config-dir",
|
|
@@ -17397,7 +18006,7 @@ function checkNodePty() {
|
|
|
17397
18006
|
detail: "not required on this platform"
|
|
17398
18007
|
};
|
|
17399
18008
|
}
|
|
17400
|
-
const vendoredPath =
|
|
18009
|
+
const vendoredPath = path38.join(__dirname, "vendor", "node-pty");
|
|
17401
18010
|
for (const target of [vendoredPath, "node-pty"]) {
|
|
17402
18011
|
try {
|
|
17403
18012
|
require(target);
|
|
@@ -17439,7 +18048,7 @@ function checkChokidar() {
|
|
|
17439
18048
|
}
|
|
17440
18049
|
async function doctor(args2 = []) {
|
|
17441
18050
|
const json = args2.includes("--json");
|
|
17442
|
-
const cliVersion = true ? "2.
|
|
18051
|
+
const cliVersion = true ? "2.21.1" : "0.0.0-dev";
|
|
17443
18052
|
const apiBase = resolveApiBaseUrl();
|
|
17444
18053
|
const diagnosticId = (0, import_node_crypto5.randomUUID)();
|
|
17445
18054
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -17638,7 +18247,7 @@ async function completion(args2) {
|
|
|
17638
18247
|
// src/commands/version.ts
|
|
17639
18248
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
17640
18249
|
function version2() {
|
|
17641
|
-
const v = true ? "2.
|
|
18250
|
+
const v = true ? "2.21.1" : "unknown";
|
|
17642
18251
|
console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
|
|
17643
18252
|
}
|
|
17644
18253
|
|
|
@@ -17766,9 +18375,9 @@ function tryShowSubcommandHelp(cmd, args2) {
|
|
|
17766
18375
|
var _subcommandHelpKeys = Object.keys(HELPS);
|
|
17767
18376
|
|
|
17768
18377
|
// src/lib/updateNotifier.ts
|
|
17769
|
-
var
|
|
18378
|
+
var fs30 = __toESM(require("fs"));
|
|
17770
18379
|
var os25 = __toESM(require("os"));
|
|
17771
|
-
var
|
|
18380
|
+
var path39 = __toESM(require("path"));
|
|
17772
18381
|
var https7 = __toESM(require("https"));
|
|
17773
18382
|
var import_picocolors16 = __toESM(require("picocolors"));
|
|
17774
18383
|
var PKG_NAME = "codeam-cli";
|
|
@@ -17776,12 +18385,12 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
|
17776
18385
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
17777
18386
|
var REQUEST_TIMEOUT_MS = 1500;
|
|
17778
18387
|
function cachePath() {
|
|
17779
|
-
const dir =
|
|
17780
|
-
return
|
|
18388
|
+
const dir = path39.join(os25.homedir(), ".codeam");
|
|
18389
|
+
return path39.join(dir, "update-check.json");
|
|
17781
18390
|
}
|
|
17782
18391
|
function readCache() {
|
|
17783
18392
|
try {
|
|
17784
|
-
const raw =
|
|
18393
|
+
const raw = fs30.readFileSync(cachePath(), "utf8");
|
|
17785
18394
|
const parsed = JSON.parse(raw);
|
|
17786
18395
|
if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
|
|
17787
18396
|
return parsed;
|
|
@@ -17792,10 +18401,10 @@ function readCache() {
|
|
|
17792
18401
|
function writeCache(cache) {
|
|
17793
18402
|
try {
|
|
17794
18403
|
const file = cachePath();
|
|
17795
|
-
|
|
18404
|
+
fs30.mkdirSync(path39.dirname(file), { recursive: true });
|
|
17796
18405
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
17797
|
-
|
|
17798
|
-
|
|
18406
|
+
fs30.writeFileSync(tmp, JSON.stringify(cache));
|
|
18407
|
+
fs30.renameSync(tmp, file);
|
|
17799
18408
|
} catch {
|
|
17800
18409
|
}
|
|
17801
18410
|
}
|
|
@@ -17813,14 +18422,14 @@ function compareSemver(a, b) {
|
|
|
17813
18422
|
return 0;
|
|
17814
18423
|
}
|
|
17815
18424
|
function fetchLatest() {
|
|
17816
|
-
return new Promise((
|
|
18425
|
+
return new Promise((resolve5) => {
|
|
17817
18426
|
const req = https7.get(
|
|
17818
18427
|
REGISTRY_URL,
|
|
17819
18428
|
{ headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
|
|
17820
18429
|
(res) => {
|
|
17821
18430
|
if (res.statusCode !== 200) {
|
|
17822
18431
|
res.resume();
|
|
17823
|
-
|
|
18432
|
+
resolve5(null);
|
|
17824
18433
|
return;
|
|
17825
18434
|
}
|
|
17826
18435
|
let buf = "";
|
|
@@ -17832,21 +18441,21 @@ function fetchLatest() {
|
|
|
17832
18441
|
try {
|
|
17833
18442
|
const json = JSON.parse(buf);
|
|
17834
18443
|
if (typeof json.version === "string") {
|
|
17835
|
-
|
|
18444
|
+
resolve5(json.version);
|
|
17836
18445
|
} else {
|
|
17837
|
-
|
|
18446
|
+
resolve5(null);
|
|
17838
18447
|
}
|
|
17839
18448
|
} catch {
|
|
17840
|
-
|
|
18449
|
+
resolve5(null);
|
|
17841
18450
|
}
|
|
17842
18451
|
});
|
|
17843
18452
|
}
|
|
17844
18453
|
);
|
|
17845
18454
|
req.on("timeout", () => {
|
|
17846
18455
|
req.destroy();
|
|
17847
|
-
|
|
18456
|
+
resolve5(null);
|
|
17848
18457
|
});
|
|
17849
|
-
req.on("error", () =>
|
|
18458
|
+
req.on("error", () => resolve5(null));
|
|
17850
18459
|
});
|
|
17851
18460
|
}
|
|
17852
18461
|
function notifyIfStale(currentVersion, latest) {
|
|
@@ -17866,7 +18475,7 @@ function checkForUpdates() {
|
|
|
17866
18475
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
17867
18476
|
if (process.env.CI) return;
|
|
17868
18477
|
if (!process.stdout.isTTY) return;
|
|
17869
|
-
const current = true ? "2.
|
|
18478
|
+
const current = true ? "2.21.1" : null;
|
|
17870
18479
|
if (!current) return;
|
|
17871
18480
|
const cache = readCache();
|
|
17872
18481
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|