codeam-cli 2.27.1 → 2.27.3
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 +1341 -560
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -300,6 +300,18 @@ var AGENT_REGISTRY = {
|
|
|
300
300
|
// this via the existing --api-key escape hatch in commands/link.ts.
|
|
301
301
|
supportedAuthKinds: ["api_key"],
|
|
302
302
|
preferredAuthKind: "api_key"
|
|
303
|
+
},
|
|
304
|
+
gemini: {
|
|
305
|
+
id: "gemini",
|
|
306
|
+
displayName: "Gemini CLI",
|
|
307
|
+
binaryName: "gemini",
|
|
308
|
+
enabled: true,
|
|
309
|
+
// OAuth via `gemini auth login` (captured by `codeam link gemini`
|
|
310
|
+
// from ~/.gemini/oauth_creds.json) AND GEMINI_API_KEY are both
|
|
311
|
+
// accepted by the backend's GeminiProvisioningStrategy and propagated
|
|
312
|
+
// into codespace deploys.
|
|
313
|
+
supportedAuthKinds: ["oauth_token", "api_key"],
|
|
314
|
+
preferredAuthKind: "oauth_token"
|
|
303
315
|
}
|
|
304
316
|
};
|
|
305
317
|
function getEnabledAgents() {
|
|
@@ -311,7 +323,7 @@ function getAgent(id) {
|
|
|
311
323
|
return meta;
|
|
312
324
|
}
|
|
313
325
|
function isKnownAgentId(id) {
|
|
314
|
-
return id === "claude" || id === "codex" || id === "copilot" || id === "coderabbit" || id === "cursor" || id === "aider";
|
|
326
|
+
return id === "claude" || id === "codex" || id === "copilot" || id === "coderabbit" || id === "cursor" || id === "aider" || id === "gemini";
|
|
315
327
|
}
|
|
316
328
|
|
|
317
329
|
// ../../packages/shared/src/api-url.ts
|
|
@@ -486,7 +498,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
486
498
|
// package.json
|
|
487
499
|
var package_default = {
|
|
488
500
|
name: "codeam-cli",
|
|
489
|
-
version: "2.27.
|
|
501
|
+
version: "2.27.3",
|
|
490
502
|
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.",
|
|
491
503
|
type: "commonjs",
|
|
492
504
|
main: "dist/index.js",
|
|
@@ -556,8 +568,12 @@ var package_default = {
|
|
|
556
568
|
node: ">=18.0.0"
|
|
557
569
|
},
|
|
558
570
|
dependencies: {
|
|
571
|
+
"@agentclientprotocol/claude-agent-acp": "^0.42.0",
|
|
572
|
+
"@agentclientprotocol/codex-acp": "^0.0.45",
|
|
573
|
+
"@agentclientprotocol/sdk": "^0.25.0",
|
|
559
574
|
"@clack/prompts": "^1.2.0",
|
|
560
575
|
chokidar: "^3.6.0",
|
|
576
|
+
"cursor-agent-acp": "^0.1.1",
|
|
561
577
|
picocolors: "^1.1.0",
|
|
562
578
|
"qrcode-terminal": "^0.12.0",
|
|
563
579
|
which: "^5.0.0",
|
|
@@ -853,7 +869,7 @@ async function postPreviewEvent(input) {
|
|
|
853
869
|
}
|
|
854
870
|
}
|
|
855
871
|
async function _postJsonAuthed(url, body, pluginAuthToken) {
|
|
856
|
-
return new Promise((
|
|
872
|
+
return new Promise((resolve6, reject) => {
|
|
857
873
|
const data = JSON.stringify(body);
|
|
858
874
|
const u2 = new URL(url);
|
|
859
875
|
const transport = u2.protocol === "https:" ? https : http;
|
|
@@ -874,8 +890,8 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
|
|
|
874
890
|
(res) => {
|
|
875
891
|
res.on("error", reject);
|
|
876
892
|
let responseBody = "";
|
|
877
|
-
res.on("data", (
|
|
878
|
-
responseBody +=
|
|
893
|
+
res.on("data", (chunk2) => {
|
|
894
|
+
responseBody += chunk2.toString();
|
|
879
895
|
});
|
|
880
896
|
res.on("end", () => {
|
|
881
897
|
if (res.statusCode && res.statusCode >= 400) {
|
|
@@ -885,9 +901,9 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
|
|
|
885
901
|
return;
|
|
886
902
|
}
|
|
887
903
|
try {
|
|
888
|
-
|
|
904
|
+
resolve6(JSON.parse(responseBody));
|
|
889
905
|
} catch {
|
|
890
|
-
|
|
906
|
+
resolve6(null);
|
|
891
907
|
}
|
|
892
908
|
});
|
|
893
909
|
}
|
|
@@ -902,7 +918,7 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
|
|
|
902
918
|
});
|
|
903
919
|
}
|
|
904
920
|
async function _postJson(url, body) {
|
|
905
|
-
return new Promise((
|
|
921
|
+
return new Promise((resolve6, reject) => {
|
|
906
922
|
const data = JSON.stringify(body);
|
|
907
923
|
const u2 = new URL(url);
|
|
908
924
|
const transport = u2.protocol === "https:" ? https : http;
|
|
@@ -922,8 +938,8 @@ async function _postJson(url, body) {
|
|
|
922
938
|
(res) => {
|
|
923
939
|
res.on("error", reject);
|
|
924
940
|
let body2 = "";
|
|
925
|
-
res.on("data", (
|
|
926
|
-
body2 +=
|
|
941
|
+
res.on("data", (chunk2) => {
|
|
942
|
+
body2 += chunk2.toString();
|
|
927
943
|
});
|
|
928
944
|
res.on("end", () => {
|
|
929
945
|
if (res.statusCode && res.statusCode >= 400) {
|
|
@@ -931,9 +947,9 @@ async function _postJson(url, body) {
|
|
|
931
947
|
return;
|
|
932
948
|
}
|
|
933
949
|
try {
|
|
934
|
-
|
|
950
|
+
resolve6(JSON.parse(body2));
|
|
935
951
|
} catch {
|
|
936
|
-
|
|
952
|
+
resolve6(null);
|
|
937
953
|
}
|
|
938
954
|
});
|
|
939
955
|
}
|
|
@@ -948,7 +964,7 @@ async function _postJson(url, body) {
|
|
|
948
964
|
});
|
|
949
965
|
}
|
|
950
966
|
async function _getJson(url) {
|
|
951
|
-
return new Promise((
|
|
967
|
+
return new Promise((resolve6, reject) => {
|
|
952
968
|
const u2 = new URL(url);
|
|
953
969
|
const transport = u2.protocol === "https:" ? https : http;
|
|
954
970
|
const req = transport.request(
|
|
@@ -963,8 +979,8 @@ async function _getJson(url) {
|
|
|
963
979
|
(res) => {
|
|
964
980
|
res.on("error", reject);
|
|
965
981
|
let body = "";
|
|
966
|
-
res.on("data", (
|
|
967
|
-
body +=
|
|
982
|
+
res.on("data", (chunk2) => {
|
|
983
|
+
body += chunk2.toString();
|
|
968
984
|
});
|
|
969
985
|
res.on("end", () => {
|
|
970
986
|
if (res.statusCode && res.statusCode >= 400) {
|
|
@@ -972,9 +988,9 @@ async function _getJson(url) {
|
|
|
972
988
|
return;
|
|
973
989
|
}
|
|
974
990
|
try {
|
|
975
|
-
|
|
991
|
+
resolve6(JSON.parse(body));
|
|
976
992
|
} catch {
|
|
977
|
-
|
|
993
|
+
resolve6(null);
|
|
978
994
|
}
|
|
979
995
|
});
|
|
980
996
|
}
|
|
@@ -1144,8 +1160,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
|
|
|
1144
1160
|
return decodedFile;
|
|
1145
1161
|
};
|
|
1146
1162
|
}
|
|
1147
|
-
function normalizeWindowsPath(
|
|
1148
|
-
return
|
|
1163
|
+
function normalizeWindowsPath(path45) {
|
|
1164
|
+
return path45.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
|
|
1149
1165
|
}
|
|
1150
1166
|
|
|
1151
1167
|
// ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
|
|
@@ -3625,15 +3641,15 @@ async function addSourceContext(frames) {
|
|
|
3625
3641
|
LRU_FILE_CONTENTS_CACHE.reduce();
|
|
3626
3642
|
return frames;
|
|
3627
3643
|
}
|
|
3628
|
-
function getContextLinesFromFile(
|
|
3629
|
-
return new Promise((
|
|
3630
|
-
const stream = (0, import_node_fs.createReadStream)(
|
|
3644
|
+
function getContextLinesFromFile(path45, ranges, output) {
|
|
3645
|
+
return new Promise((resolve6) => {
|
|
3646
|
+
const stream = (0, import_node_fs.createReadStream)(path45);
|
|
3631
3647
|
const lineReaded = (0, import_node_readline.createInterface)({
|
|
3632
3648
|
input: stream
|
|
3633
3649
|
});
|
|
3634
3650
|
function destroyStreamAndResolve() {
|
|
3635
3651
|
stream.destroy();
|
|
3636
|
-
|
|
3652
|
+
resolve6();
|
|
3637
3653
|
}
|
|
3638
3654
|
let lineNumber = 0;
|
|
3639
3655
|
let currentRangeIndex = 0;
|
|
@@ -3642,7 +3658,7 @@ function getContextLinesFromFile(path43, ranges, output) {
|
|
|
3642
3658
|
let rangeStart = range[0];
|
|
3643
3659
|
let rangeEnd = range[1];
|
|
3644
3660
|
function onStreamError() {
|
|
3645
|
-
LRU_FILE_CONTENTS_FS_READ_FAILED.set(
|
|
3661
|
+
LRU_FILE_CONTENTS_FS_READ_FAILED.set(path45, 1);
|
|
3646
3662
|
lineReaded.close();
|
|
3647
3663
|
lineReaded.removeAllListeners();
|
|
3648
3664
|
destroyStreamAndResolve();
|
|
@@ -3703,8 +3719,8 @@ function clearLineContext(frame) {
|
|
|
3703
3719
|
delete frame.context_line;
|
|
3704
3720
|
delete frame.post_context;
|
|
3705
3721
|
}
|
|
3706
|
-
function shouldSkipContextLinesForFile(
|
|
3707
|
-
return
|
|
3722
|
+
function shouldSkipContextLinesForFile(path45) {
|
|
3723
|
+
return path45.startsWith("node:") || path45.endsWith(".min.js") || path45.endsWith(".min.cjs") || path45.endsWith(".min.mjs") || path45.startsWith("data:");
|
|
3708
3724
|
}
|
|
3709
3725
|
function shouldSkipContextLinesForFrame(frame) {
|
|
3710
3726
|
if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
|
|
@@ -4920,9 +4936,9 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
|
|
|
4920
4936
|
if (!waitUntil) return;
|
|
4921
4937
|
if (this.disabled || this.optedOut) return;
|
|
4922
4938
|
if (!this._waitUntilCycle) {
|
|
4923
|
-
let
|
|
4939
|
+
let resolve6;
|
|
4924
4940
|
const promise = new Promise((r) => {
|
|
4925
|
-
|
|
4941
|
+
resolve6 = r;
|
|
4926
4942
|
});
|
|
4927
4943
|
try {
|
|
4928
4944
|
waitUntil(promise);
|
|
@@ -4930,7 +4946,7 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
|
|
|
4930
4946
|
return;
|
|
4931
4947
|
}
|
|
4932
4948
|
this._waitUntilCycle = {
|
|
4933
|
-
resolve:
|
|
4949
|
+
resolve: resolve6,
|
|
4934
4950
|
startedAt: Date.now(),
|
|
4935
4951
|
timer: void 0
|
|
4936
4952
|
};
|
|
@@ -4954,12 +4970,12 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
|
|
|
4954
4970
|
return cycle?.resolve;
|
|
4955
4971
|
}
|
|
4956
4972
|
async resolveWaitUntilFlush() {
|
|
4957
|
-
const
|
|
4973
|
+
const resolve6 = this._consumeWaitUntilCycle();
|
|
4958
4974
|
try {
|
|
4959
4975
|
await super.flush();
|
|
4960
4976
|
} catch {
|
|
4961
4977
|
} finally {
|
|
4962
|
-
|
|
4978
|
+
resolve6?.();
|
|
4963
4979
|
}
|
|
4964
4980
|
}
|
|
4965
4981
|
getPersistedProperty(key) {
|
|
@@ -5051,15 +5067,15 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
|
|
|
5051
5067
|
async waitForLocalEvaluationReady(timeoutMs = THIRTY_SECONDS) {
|
|
5052
5068
|
if (this.isLocalEvaluationReady()) return true;
|
|
5053
5069
|
if (void 0 === this.featureFlagsPoller) return false;
|
|
5054
|
-
return new Promise((
|
|
5070
|
+
return new Promise((resolve6) => {
|
|
5055
5071
|
const timeout = setTimeout(() => {
|
|
5056
5072
|
cleanup();
|
|
5057
|
-
|
|
5073
|
+
resolve6(false);
|
|
5058
5074
|
}, timeoutMs);
|
|
5059
5075
|
const cleanup = this._events.on("localEvaluationFlagsLoaded", (count) => {
|
|
5060
5076
|
clearTimeout(timeout);
|
|
5061
5077
|
cleanup();
|
|
5062
|
-
|
|
5078
|
+
resolve6(count > 0);
|
|
5063
5079
|
});
|
|
5064
5080
|
});
|
|
5065
5081
|
}
|
|
@@ -5496,13 +5512,13 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
|
|
|
5496
5512
|
this.context?.enter(data, options);
|
|
5497
5513
|
}
|
|
5498
5514
|
async _shutdown(shutdownTimeoutMs) {
|
|
5499
|
-
const
|
|
5515
|
+
const resolve6 = this._consumeWaitUntilCycle();
|
|
5500
5516
|
await this.featureFlagsPoller?.stopPoller(shutdownTimeoutMs);
|
|
5501
5517
|
this.errorTracking.shutdown();
|
|
5502
5518
|
try {
|
|
5503
5519
|
return await super._shutdown(shutdownTimeoutMs);
|
|
5504
5520
|
} finally {
|
|
5505
|
-
|
|
5521
|
+
resolve6?.();
|
|
5506
5522
|
}
|
|
5507
5523
|
}
|
|
5508
5524
|
async _requestRemoteConfigPayload(flagKey) {
|
|
@@ -5858,7 +5874,7 @@ function readAnonId() {
|
|
|
5858
5874
|
}
|
|
5859
5875
|
function superProperties() {
|
|
5860
5876
|
return {
|
|
5861
|
-
cliVersion: true ? "2.27.
|
|
5877
|
+
cliVersion: true ? "2.27.3" : "0.0.0-dev",
|
|
5862
5878
|
nodeVersion: process.version,
|
|
5863
5879
|
platform: process.platform,
|
|
5864
5880
|
arch: process.arch,
|
|
@@ -6065,9 +6081,9 @@ var CommandRelayService = class {
|
|
|
6065
6081
|
this.armSseWatchdog();
|
|
6066
6082
|
let buffer = "";
|
|
6067
6083
|
res.setEncoding("utf8");
|
|
6068
|
-
res.on("data", (
|
|
6084
|
+
res.on("data", (chunk2) => {
|
|
6069
6085
|
this.sseLastByteAt = Date.now();
|
|
6070
|
-
buffer +=
|
|
6086
|
+
buffer += chunk2;
|
|
6071
6087
|
let frameEnd;
|
|
6072
6088
|
while ((frameEnd = buffer.indexOf("\n\n")) !== -1) {
|
|
6073
6089
|
const frame = buffer.slice(0, frameEnd);
|
|
@@ -6736,9 +6752,9 @@ var UnixPtyStrategy = class {
|
|
|
6736
6752
|
);
|
|
6737
6753
|
process.exit(1);
|
|
6738
6754
|
});
|
|
6739
|
-
this.proc.stdout?.on("data", (
|
|
6740
|
-
process.stdout.write(
|
|
6741
|
-
this.opts.onData(
|
|
6755
|
+
this.proc.stdout?.on("data", (chunk2) => {
|
|
6756
|
+
process.stdout.write(chunk2);
|
|
6757
|
+
this.opts.onData(chunk2.toString("utf8"));
|
|
6742
6758
|
});
|
|
6743
6759
|
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
6744
6760
|
process.stdin.resume();
|
|
@@ -6830,8 +6846,8 @@ var UnixPtyStrategy = class {
|
|
|
6830
6846
|
}
|
|
6831
6847
|
}
|
|
6832
6848
|
}
|
|
6833
|
-
stdinHandler = (
|
|
6834
|
-
this.proc?.stdin?.write(
|
|
6849
|
+
stdinHandler = (chunk2) => {
|
|
6850
|
+
this.proc?.stdin?.write(chunk2);
|
|
6835
6851
|
};
|
|
6836
6852
|
handleResize = () => {
|
|
6837
6853
|
if (this.proc?.pid) {
|
|
@@ -7017,8 +7033,8 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
|
|
|
7017
7033
|
}
|
|
7018
7034
|
this.pty = null;
|
|
7019
7035
|
}
|
|
7020
|
-
stdinHandler = (
|
|
7021
|
-
this.pty?.write(
|
|
7036
|
+
stdinHandler = (chunk2) => {
|
|
7037
|
+
this.pty?.write(chunk2.toString("utf8"));
|
|
7022
7038
|
};
|
|
7023
7039
|
};
|
|
7024
7040
|
|
|
@@ -7051,9 +7067,9 @@ var WindowsPtyStrategy = class {
|
|
|
7051
7067
|
);
|
|
7052
7068
|
process.exit(1);
|
|
7053
7069
|
});
|
|
7054
|
-
this.proc.stdout?.on("data", (
|
|
7055
|
-
process.stdout.write(
|
|
7056
|
-
this.opts.onData(
|
|
7070
|
+
this.proc.stdout?.on("data", (chunk2) => {
|
|
7071
|
+
process.stdout.write(chunk2);
|
|
7072
|
+
this.opts.onData(chunk2.toString("utf8"));
|
|
7057
7073
|
});
|
|
7058
7074
|
this.proc.stdin?.write("");
|
|
7059
7075
|
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
@@ -7080,8 +7096,8 @@ var WindowsPtyStrategy = class {
|
|
|
7080
7096
|
}
|
|
7081
7097
|
}
|
|
7082
7098
|
}
|
|
7083
|
-
stdinHandler = (
|
|
7084
|
-
this.proc?.stdin?.write(
|
|
7099
|
+
stdinHandler = (chunk2) => {
|
|
7100
|
+
this.proc?.stdin?.write(chunk2);
|
|
7085
7101
|
};
|
|
7086
7102
|
};
|
|
7087
7103
|
|
|
@@ -7200,10 +7216,10 @@ function buildForPlatform(platform2) {
|
|
|
7200
7216
|
var import_node_crypto4 = require("crypto");
|
|
7201
7217
|
|
|
7202
7218
|
// src/agents/claude/resolver.ts
|
|
7203
|
-
function buildClaudeLaunch(extraArgs = [],
|
|
7204
|
-
const found =
|
|
7219
|
+
function buildClaudeLaunch(extraArgs = [], os28 = createOsStrategy()) {
|
|
7220
|
+
const found = os28.findInPath("claude") ?? os28.findInPath("claude-code");
|
|
7205
7221
|
if (!found) return null;
|
|
7206
|
-
return
|
|
7222
|
+
return os28.buildLaunch(found, extraArgs);
|
|
7207
7223
|
}
|
|
7208
7224
|
|
|
7209
7225
|
// src/agents/claude/installer.ts
|
|
@@ -9159,15 +9175,15 @@ function runInstaller() {
|
|
|
9159
9175
|
"-Command",
|
|
9160
9176
|
"irm https://claude.ai/install.ps1 | iex"
|
|
9161
9177
|
] : ["-c", "curl -fsSL https://claude.ai/install.sh | bash"];
|
|
9162
|
-
return new Promise((
|
|
9178
|
+
return new Promise((resolve6) => {
|
|
9163
9179
|
const proc = (0, import_child_process4.spawn)(cmd, args2, { stdio: "inherit" });
|
|
9164
9180
|
proc.on("error", (err) => {
|
|
9165
9181
|
console.error(`
|
|
9166
9182
|
\u2717 Installer failed to launch: ${err.message}`);
|
|
9167
|
-
|
|
9183
|
+
resolve6(false);
|
|
9168
9184
|
});
|
|
9169
9185
|
proc.on("exit", (code) => {
|
|
9170
|
-
|
|
9186
|
+
resolve6(code === 0);
|
|
9171
9187
|
});
|
|
9172
9188
|
});
|
|
9173
9189
|
}
|
|
@@ -9344,17 +9360,17 @@ function parseUsageOutput(raw) {
|
|
|
9344
9360
|
return { percent, resetAt };
|
|
9345
9361
|
}
|
|
9346
9362
|
async function fetchClaudeQuota() {
|
|
9347
|
-
return new Promise((
|
|
9363
|
+
return new Promise((resolve6) => {
|
|
9348
9364
|
const claudeCmd = findInPath("claude") ? "claude" : "claude-code";
|
|
9349
9365
|
if (!claudeCmd) {
|
|
9350
|
-
|
|
9366
|
+
resolve6(null);
|
|
9351
9367
|
return;
|
|
9352
9368
|
}
|
|
9353
9369
|
const helperPath = path11.join(os10.tmpdir(), "codeam-quota-helper.py");
|
|
9354
9370
|
fs8.writeFileSync(helperPath, HELPER_SCRIPT, { mode: 420 });
|
|
9355
9371
|
const python = findInPath("python3") ?? findInPath("python");
|
|
9356
9372
|
if (!python) {
|
|
9357
|
-
|
|
9373
|
+
resolve6(null);
|
|
9358
9374
|
return;
|
|
9359
9375
|
}
|
|
9360
9376
|
const proc = (0, import_child_process5.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
|
|
@@ -9364,8 +9380,8 @@ async function fetchClaudeQuota() {
|
|
|
9364
9380
|
});
|
|
9365
9381
|
let output = "";
|
|
9366
9382
|
let resolved = false;
|
|
9367
|
-
proc.stdout?.on("data", (
|
|
9368
|
-
output +=
|
|
9383
|
+
proc.stdout?.on("data", (chunk2) => {
|
|
9384
|
+
output += chunk2.toString("utf8");
|
|
9369
9385
|
});
|
|
9370
9386
|
setTimeout(() => {
|
|
9371
9387
|
proc.stdin?.write("/usage\r");
|
|
@@ -9381,13 +9397,13 @@ async function fetchClaudeQuota() {
|
|
|
9381
9397
|
fs8.unlinkSync(helperPath);
|
|
9382
9398
|
} catch {
|
|
9383
9399
|
}
|
|
9384
|
-
|
|
9400
|
+
resolve6(result);
|
|
9385
9401
|
}, 5e3);
|
|
9386
9402
|
}, 8e3);
|
|
9387
9403
|
setTimeout(() => {
|
|
9388
9404
|
if (!resolved) {
|
|
9389
9405
|
resolved = true;
|
|
9390
|
-
|
|
9406
|
+
resolve6(null);
|
|
9391
9407
|
}
|
|
9392
9408
|
try {
|
|
9393
9409
|
proc.kill();
|
|
@@ -9418,12 +9434,12 @@ function killActiveSpawnAndCaptureChildren() {
|
|
|
9418
9434
|
}
|
|
9419
9435
|
async function spawnAndCapture(cmd, args2, opts = {}) {
|
|
9420
9436
|
const timeoutMs = opts.timeoutMs ?? 6e4;
|
|
9421
|
-
return new Promise((
|
|
9437
|
+
return new Promise((resolve6) => {
|
|
9422
9438
|
let settled = false;
|
|
9423
9439
|
const settle = (value) => {
|
|
9424
9440
|
if (settled) return;
|
|
9425
9441
|
settled = true;
|
|
9426
|
-
|
|
9442
|
+
resolve6(value);
|
|
9427
9443
|
};
|
|
9428
9444
|
let child;
|
|
9429
9445
|
try {
|
|
@@ -9438,11 +9454,11 @@ async function spawnAndCapture(cmd, args2, opts = {}) {
|
|
|
9438
9454
|
}
|
|
9439
9455
|
activeChildren.add(child);
|
|
9440
9456
|
let stdout = "";
|
|
9441
|
-
child.stdout?.on("data", (
|
|
9442
|
-
stdout +=
|
|
9457
|
+
child.stdout?.on("data", (chunk2) => {
|
|
9458
|
+
stdout += chunk2.toString("utf8");
|
|
9443
9459
|
});
|
|
9444
|
-
child.stderr?.on("data", (
|
|
9445
|
-
opts.onStderr?.(
|
|
9460
|
+
child.stderr?.on("data", (chunk2) => {
|
|
9461
|
+
opts.onStderr?.(chunk2.toString("utf8"));
|
|
9446
9462
|
});
|
|
9447
9463
|
const timer = setTimeout(() => {
|
|
9448
9464
|
try {
|
|
@@ -9930,13 +9946,13 @@ function detectStartupBanner(lines) {
|
|
|
9930
9946
|
while (artStart > 0 && BANNER_ART_RE.test(lines[artStart - 1])) artStart--;
|
|
9931
9947
|
if (metaIdx - artStart < 2) return null;
|
|
9932
9948
|
const pathLine = (lines[metaIdx + 1] ?? "").trim();
|
|
9933
|
-
const
|
|
9949
|
+
const path45 = pathLine && !BANNER_ART_RE.test(pathLine) ? pathLine : "";
|
|
9934
9950
|
return {
|
|
9935
9951
|
title: "",
|
|
9936
9952
|
subtitle: lines[metaIdx].trim(),
|
|
9937
|
-
path:
|
|
9953
|
+
path: path45,
|
|
9938
9954
|
startIdx: artStart,
|
|
9939
|
-
endIdx: metaIdx + (
|
|
9955
|
+
endIdx: metaIdx + (path45 ? 1 : 0)
|
|
9940
9956
|
};
|
|
9941
9957
|
}
|
|
9942
9958
|
|
|
@@ -9946,8 +9962,8 @@ var ClaudeRuntimeStrategy = class {
|
|
|
9946
9962
|
meta = getAgent("claude");
|
|
9947
9963
|
mode = "interactive";
|
|
9948
9964
|
os;
|
|
9949
|
-
constructor(
|
|
9950
|
-
this.os =
|
|
9965
|
+
constructor(os28) {
|
|
9966
|
+
this.os = os28;
|
|
9951
9967
|
}
|
|
9952
9968
|
/**
|
|
9953
9969
|
* Claude Code's react-ink TUI enables bracketed-paste mode at
|
|
@@ -10955,8 +10971,8 @@ function codexCredentialLocator() {
|
|
|
10955
10971
|
function codexLoginLauncher() {
|
|
10956
10972
|
return {
|
|
10957
10973
|
async ensureInstalled() {
|
|
10958
|
-
const
|
|
10959
|
-
return
|
|
10974
|
+
const os28 = createOsStrategy();
|
|
10975
|
+
return os28.findInPath("codex") !== null;
|
|
10960
10976
|
},
|
|
10961
10977
|
launch() {
|
|
10962
10978
|
return (0, import_node_child_process3.spawn)("codex", ["login"], { stdio: "inherit" });
|
|
@@ -10979,8 +10995,8 @@ var CodexRuntimeStrategy = class {
|
|
|
10979
10995
|
meta = getAgent("codex");
|
|
10980
10996
|
mode = "interactive";
|
|
10981
10997
|
os;
|
|
10982
|
-
constructor(
|
|
10983
|
-
this.os =
|
|
10998
|
+
constructor(os28) {
|
|
10999
|
+
this.os = os28;
|
|
10984
11000
|
}
|
|
10985
11001
|
async prepareLaunch() {
|
|
10986
11002
|
let binary = this.os.findInPath("codex");
|
|
@@ -11086,16 +11102,16 @@ var CodexRuntimeStrategy = class {
|
|
|
11086
11102
|
});
|
|
11087
11103
|
}
|
|
11088
11104
|
};
|
|
11089
|
-
function resolveNpm(
|
|
11090
|
-
return
|
|
11105
|
+
function resolveNpm(os28) {
|
|
11106
|
+
return os28.id === "win32" ? "npm.cmd" : "npm";
|
|
11091
11107
|
}
|
|
11092
|
-
async function installCodexViaNpm(
|
|
11093
|
-
return new Promise((
|
|
11094
|
-
const proc = (0, import_node_child_process4.spawn)(resolveNpm(
|
|
11108
|
+
async function installCodexViaNpm(os28) {
|
|
11109
|
+
return new Promise((resolve6, reject) => {
|
|
11110
|
+
const proc = (0, import_node_child_process4.spawn)(resolveNpm(os28), ["install", "-g", "@openai/codex"], {
|
|
11095
11111
|
stdio: "inherit"
|
|
11096
11112
|
});
|
|
11097
11113
|
proc.on("close", (code) => {
|
|
11098
|
-
if (code === 0)
|
|
11114
|
+
if (code === 0) resolve6();
|
|
11099
11115
|
else reject(new Error(`npm install -g @openai/codex exited ${code}`));
|
|
11100
11116
|
});
|
|
11101
11117
|
proc.on("error", (err) => {
|
|
@@ -11108,16 +11124,16 @@ async function installCodexViaNpm(os27) {
|
|
|
11108
11124
|
});
|
|
11109
11125
|
});
|
|
11110
11126
|
}
|
|
11111
|
-
function augmentNpmGlobalBin(
|
|
11127
|
+
function augmentNpmGlobalBin(os28) {
|
|
11112
11128
|
try {
|
|
11113
|
-
const result = (0, import_node_child_process4.spawnSync)(resolveNpm(
|
|
11129
|
+
const result = (0, import_node_child_process4.spawnSync)(resolveNpm(os28), ["prefix", "-g"], {
|
|
11114
11130
|
stdio: ["ignore", "pipe", "ignore"]
|
|
11115
11131
|
});
|
|
11116
11132
|
if (result.status !== 0) return;
|
|
11117
11133
|
const prefix = result.stdout.toString().trim();
|
|
11118
11134
|
if (!prefix) return;
|
|
11119
|
-
const binDir =
|
|
11120
|
-
|
|
11135
|
+
const binDir = os28.id === "win32" ? prefix : path17.join(prefix, "bin");
|
|
11136
|
+
os28.augmentPath([binDir]);
|
|
11121
11137
|
} catch {
|
|
11122
11138
|
}
|
|
11123
11139
|
}
|
|
@@ -11201,25 +11217,25 @@ var import_node_child_process7 = require("child_process");
|
|
|
11201
11217
|
// src/agents/coderabbit/installer.ts
|
|
11202
11218
|
var import_node_child_process5 = require("child_process");
|
|
11203
11219
|
var INSTALL_URL = "https://cli.coderabbit.ai/install.sh";
|
|
11204
|
-
async function ensureCoderabbitInstalled(
|
|
11205
|
-
if (
|
|
11206
|
-
if (
|
|
11220
|
+
async function ensureCoderabbitInstalled(os28) {
|
|
11221
|
+
if (os28.findInPath("coderabbit")) return true;
|
|
11222
|
+
if (os28.id === "win32") {
|
|
11207
11223
|
console.error(
|
|
11208
11224
|
"\n \u2717 CodeRabbit on Windows requires WSL.\n Install the CLI inside your WSL distribution\n (curl -fsSL https://cli.coderabbit.ai/install.sh | sh)\n then re-run `codeam link coderabbit` from WSL.\n"
|
|
11209
11225
|
);
|
|
11210
11226
|
return false;
|
|
11211
11227
|
}
|
|
11212
11228
|
console.log("\n CodeRabbit CLI not found \u2014 installing via the official script\u2026\n");
|
|
11213
|
-
const ok = await new Promise((
|
|
11229
|
+
const ok = await new Promise((resolve6) => {
|
|
11214
11230
|
const proc = (0, import_node_child_process5.spawn)("sh", ["-c", `curl -fsSL ${INSTALL_URL} | sh`], {
|
|
11215
11231
|
stdio: "inherit"
|
|
11216
11232
|
});
|
|
11217
|
-
proc.on("close", (code) =>
|
|
11218
|
-
proc.on("error", () =>
|
|
11233
|
+
proc.on("close", (code) => resolve6(code === 0));
|
|
11234
|
+
proc.on("error", () => resolve6(false));
|
|
11219
11235
|
});
|
|
11220
11236
|
if (!ok) return false;
|
|
11221
|
-
|
|
11222
|
-
return
|
|
11237
|
+
os28.augmentPath([`${os28.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
|
|
11238
|
+
return os28.findInPath("coderabbit") !== null;
|
|
11223
11239
|
}
|
|
11224
11240
|
|
|
11225
11241
|
// src/agents/coderabbit/link.ts
|
|
@@ -11246,10 +11262,10 @@ function coderabbitCredentialLocator() {
|
|
|
11246
11262
|
extract: extractLocalCoderabbitToken
|
|
11247
11263
|
};
|
|
11248
11264
|
}
|
|
11249
|
-
function coderabbitLoginLauncher(
|
|
11265
|
+
function coderabbitLoginLauncher(os28) {
|
|
11250
11266
|
return {
|
|
11251
11267
|
async ensureInstalled() {
|
|
11252
|
-
return ensureCoderabbitInstalled(
|
|
11268
|
+
return ensureCoderabbitInstalled(os28);
|
|
11253
11269
|
},
|
|
11254
11270
|
launch() {
|
|
11255
11271
|
return (0, import_node_child_process6.spawn)("coderabbit", ["login"], { stdio: "inherit" });
|
|
@@ -11272,11 +11288,11 @@ function parseReview(stdout) {
|
|
|
11272
11288
|
for (const line of lines) {
|
|
11273
11289
|
const m = line.match(HUNK_LINE_RE);
|
|
11274
11290
|
if (!m) continue;
|
|
11275
|
-
const [,
|
|
11276
|
-
if (!
|
|
11291
|
+
const [, path45, lineNo, sevToken, message] = m;
|
|
11292
|
+
if (!path45 || !lineNo || !message) continue;
|
|
11277
11293
|
const cleanedMessage = message.trim().replace(/^[*-]\s+/, "");
|
|
11278
11294
|
hunks.push({
|
|
11279
|
-
path:
|
|
11295
|
+
path: path45.trim(),
|
|
11280
11296
|
line: Number(lineNo),
|
|
11281
11297
|
severity: sevToken ? SEVERITY_MAP[sevToken.toLowerCase()] : void 0,
|
|
11282
11298
|
message: cleanedMessage
|
|
@@ -11295,8 +11311,8 @@ var CoderabbitRuntimeStrategy = class {
|
|
|
11295
11311
|
meta = getAgent("coderabbit");
|
|
11296
11312
|
mode = "batch";
|
|
11297
11313
|
os;
|
|
11298
|
-
constructor(
|
|
11299
|
-
this.os =
|
|
11314
|
+
constructor(os28) {
|
|
11315
|
+
this.os = os28;
|
|
11300
11316
|
}
|
|
11301
11317
|
getDefaultArgs() {
|
|
11302
11318
|
return ["review"];
|
|
@@ -11334,7 +11350,7 @@ var CoderabbitRuntimeStrategy = class {
|
|
|
11334
11350
|
}
|
|
11335
11351
|
async runOneShot(input) {
|
|
11336
11352
|
const launch = await this.prepareInvocation(input);
|
|
11337
|
-
return new Promise((
|
|
11353
|
+
return new Promise((resolve6, reject) => {
|
|
11338
11354
|
const stdoutBuf = [];
|
|
11339
11355
|
const stderrBuf = [];
|
|
11340
11356
|
const proc = (0, import_node_child_process7.spawn)(launch.cmd, launch.args, {
|
|
@@ -11345,7 +11361,7 @@ var CoderabbitRuntimeStrategy = class {
|
|
|
11345
11361
|
proc.stderr?.on("data", (b) => stderrBuf.push(b));
|
|
11346
11362
|
proc.on("error", (err) => reject(err));
|
|
11347
11363
|
proc.on("close", (code) => {
|
|
11348
|
-
|
|
11364
|
+
resolve6(
|
|
11349
11365
|
this.parseOutput({
|
|
11350
11366
|
exitCode: code ?? 0,
|
|
11351
11367
|
stdout: Buffer.concat(stdoutBuf).toString("utf8"),
|
|
@@ -11411,10 +11427,10 @@ function cursorCredentialLocator() {
|
|
|
11411
11427
|
extract: extractLocalCursorToken
|
|
11412
11428
|
};
|
|
11413
11429
|
}
|
|
11414
|
-
function cursorLoginLauncher(
|
|
11430
|
+
function cursorLoginLauncher(os28) {
|
|
11415
11431
|
return {
|
|
11416
11432
|
async ensureInstalled() {
|
|
11417
|
-
if (
|
|
11433
|
+
if (os28.findInPath("cursor-agent")) return true;
|
|
11418
11434
|
console.error(
|
|
11419
11435
|
"\n \u2717 cursor-agent binary not on PATH.\n Install Cursor (https://cursor.com/) and ensure the CLI\n plugin is enabled, then re-run `codeam link cursor`.\n"
|
|
11420
11436
|
);
|
|
@@ -11476,8 +11492,8 @@ var CursorRuntimeStrategy = class {
|
|
|
11476
11492
|
meta = getAgent("cursor");
|
|
11477
11493
|
mode = "interactive";
|
|
11478
11494
|
os;
|
|
11479
|
-
constructor(
|
|
11480
|
-
this.os =
|
|
11495
|
+
constructor(os28) {
|
|
11496
|
+
this.os = os28;
|
|
11481
11497
|
}
|
|
11482
11498
|
async prepareLaunch() {
|
|
11483
11499
|
const binary = this.os.findInPath("cursor-agent");
|
|
@@ -11597,10 +11613,10 @@ function aiderCredentialLocator() {
|
|
|
11597
11613
|
extract: extractLocalAiderToken
|
|
11598
11614
|
};
|
|
11599
11615
|
}
|
|
11600
|
-
function aiderLoginLauncher(
|
|
11616
|
+
function aiderLoginLauncher(os28) {
|
|
11601
11617
|
return {
|
|
11602
11618
|
async ensureInstalled() {
|
|
11603
|
-
if (
|
|
11619
|
+
if (os28.findInPath("aider")) return true;
|
|
11604
11620
|
console.error(
|
|
11605
11621
|
"\n \u2717 aider binary not on PATH.\n Install Aider:\n pip install aider-chat\n then re-run `codeam link aider`.\n"
|
|
11606
11622
|
);
|
|
@@ -11610,7 +11626,7 @@ function aiderLoginLauncher(os27) {
|
|
|
11610
11626
|
console.error(
|
|
11611
11627
|
"\n Aider has no interactive login flow.\n Set ANTHROPIC_API_KEY or OPENAI_API_KEY in your shell,\n or re-run `codeam link aider --api-key=<your-key>`.\n"
|
|
11612
11628
|
);
|
|
11613
|
-
return (0, import_node_child_process9.spawn)(
|
|
11629
|
+
return (0, import_node_child_process9.spawn)(os28.id === "win32" ? "cmd.exe" : "sh", os28.id === "win32" ? ["/c", "exit", "0"] : ["-c", "exit 0"], {
|
|
11614
11630
|
stdio: "ignore"
|
|
11615
11631
|
});
|
|
11616
11632
|
}
|
|
@@ -11682,8 +11698,8 @@ var AiderRuntimeStrategy = class {
|
|
|
11682
11698
|
meta = getAgent("aider");
|
|
11683
11699
|
mode = "interactive";
|
|
11684
11700
|
os;
|
|
11685
|
-
constructor(
|
|
11686
|
-
this.os =
|
|
11701
|
+
constructor(os28) {
|
|
11702
|
+
this.os = os28;
|
|
11687
11703
|
}
|
|
11688
11704
|
async prepareLaunch() {
|
|
11689
11705
|
const binary = this.os.findInPath("aider");
|
|
@@ -11750,19 +11766,184 @@ var AiderRuntimeStrategy = class {
|
|
|
11750
11766
|
}
|
|
11751
11767
|
};
|
|
11752
11768
|
|
|
11769
|
+
// src/agents/gemini/link.ts
|
|
11770
|
+
var import_node_child_process10 = require("child_process");
|
|
11771
|
+
|
|
11772
|
+
// src/agents/gemini/local-token.ts
|
|
11773
|
+
var fs20 = __toESM(require("fs"));
|
|
11774
|
+
var os21 = __toESM(require("os"));
|
|
11775
|
+
var path24 = __toESM(require("path"));
|
|
11776
|
+
function geminiCredentialsPath() {
|
|
11777
|
+
return path24.join(os21.homedir(), ".gemini", "oauth_creds.json");
|
|
11778
|
+
}
|
|
11779
|
+
function geminiCredentialsPaths() {
|
|
11780
|
+
return [geminiCredentialsPath()];
|
|
11781
|
+
}
|
|
11782
|
+
async function extractLocalGeminiToken() {
|
|
11783
|
+
const file = geminiCredentialsPath();
|
|
11784
|
+
if (!fs20.existsSync(file)) return null;
|
|
11785
|
+
const credential = fs20.readFileSync(file, "utf8").trim();
|
|
11786
|
+
if (credential.length === 0) return null;
|
|
11787
|
+
return { method: "oauth", credential, source: "flat-file" };
|
|
11788
|
+
}
|
|
11789
|
+
function validateLocalGeminiToken(credential) {
|
|
11790
|
+
let parsed;
|
|
11791
|
+
try {
|
|
11792
|
+
parsed = JSON.parse(credential);
|
|
11793
|
+
} catch {
|
|
11794
|
+
return { status: "unknown" };
|
|
11795
|
+
}
|
|
11796
|
+
if (typeof parsed.expiry_date !== "number") return { status: "unknown" };
|
|
11797
|
+
const expiresAt = parsed.expiry_date;
|
|
11798
|
+
if (Date.now() >= expiresAt) {
|
|
11799
|
+
return {
|
|
11800
|
+
status: "expired",
|
|
11801
|
+
reason: "Gemini OAuth access token expired",
|
|
11802
|
+
expiresAt
|
|
11803
|
+
};
|
|
11804
|
+
}
|
|
11805
|
+
return { status: "valid", expiresAt };
|
|
11806
|
+
}
|
|
11807
|
+
|
|
11808
|
+
// src/agents/gemini/link.ts
|
|
11809
|
+
function geminiCredentialLocator() {
|
|
11810
|
+
return {
|
|
11811
|
+
publicId: "gemini",
|
|
11812
|
+
vendor: "Google",
|
|
11813
|
+
hint: "~/.gemini/oauth_creds.json",
|
|
11814
|
+
watchPaths: geminiCredentialsPaths,
|
|
11815
|
+
extract: extractLocalGeminiToken,
|
|
11816
|
+
validate: (token) => {
|
|
11817
|
+
const result = validateLocalGeminiToken(token.credential);
|
|
11818
|
+
return { status: result.status, reason: result.reason };
|
|
11819
|
+
}
|
|
11820
|
+
};
|
|
11821
|
+
}
|
|
11822
|
+
function geminiLoginLauncher() {
|
|
11823
|
+
return {
|
|
11824
|
+
async ensureInstalled() {
|
|
11825
|
+
const os28 = createOsStrategy();
|
|
11826
|
+
return os28.findInPath("gemini") !== null;
|
|
11827
|
+
},
|
|
11828
|
+
launch() {
|
|
11829
|
+
return (0, import_node_child_process10.spawn)("gemini", ["auth", "login"], { stdio: "inherit" });
|
|
11830
|
+
}
|
|
11831
|
+
};
|
|
11832
|
+
}
|
|
11833
|
+
|
|
11834
|
+
// src/agents/gemini/runtime.ts
|
|
11835
|
+
var GEMINI_PRO_CONTEXT_WINDOW = 1e6;
|
|
11836
|
+
var GEMINI_FLASH_CONTEXT_WINDOW = 1e6;
|
|
11837
|
+
var GEMINI_MODELS = [
|
|
11838
|
+
{
|
|
11839
|
+
id: "gemini-2.5-pro",
|
|
11840
|
+
label: "Gemini 2.5 Pro",
|
|
11841
|
+
contextWindow: GEMINI_PRO_CONTEXT_WINDOW
|
|
11842
|
+
},
|
|
11843
|
+
{
|
|
11844
|
+
id: "gemini-2.5-flash",
|
|
11845
|
+
label: "Gemini 2.5 Flash",
|
|
11846
|
+
contextWindow: GEMINI_FLASH_CONTEXT_WINDOW
|
|
11847
|
+
},
|
|
11848
|
+
{
|
|
11849
|
+
id: "gemini-2.5-flash-lite",
|
|
11850
|
+
label: "Gemini 2.5 Flash-Lite",
|
|
11851
|
+
contextWindow: GEMINI_FLASH_CONTEXT_WINDOW
|
|
11852
|
+
}
|
|
11853
|
+
];
|
|
11854
|
+
var GeminiRuntimeStrategy = class {
|
|
11855
|
+
id = "gemini";
|
|
11856
|
+
meta = getAgent("gemini");
|
|
11857
|
+
mode = "interactive";
|
|
11858
|
+
os;
|
|
11859
|
+
constructor(os28) {
|
|
11860
|
+
this.os = os28;
|
|
11861
|
+
}
|
|
11862
|
+
async prepareLaunch() {
|
|
11863
|
+
const binary = this.os.findInPath("gemini");
|
|
11864
|
+
if (!binary) {
|
|
11865
|
+
throw new Error(
|
|
11866
|
+
"Gemini CLI is not on PATH. Install it with:\n npm install -g @google/gemini-cli\n Then run `codeam pair` again.\n\n Tip: set CODEAM_ACP_ENABLED=1 before pairing to use the\n ACP runtime \u2014 it gives mobile typed messages instead of\n raw PTY output for Gemini."
|
|
11867
|
+
);
|
|
11868
|
+
}
|
|
11869
|
+
return this.os.buildLaunch(binary);
|
|
11870
|
+
}
|
|
11871
|
+
// Gemini's REPL has no documented "resume previous session" flag,
|
|
11872
|
+
// so a relaunch starts fresh. Returning an empty array is the
|
|
11873
|
+
// documented "no-op resume" path (Cursor / Aider do the same).
|
|
11874
|
+
resumeLaunchArgs(_sessionId, _opts) {
|
|
11875
|
+
return [];
|
|
11876
|
+
}
|
|
11877
|
+
resolveHistoryDir(_cwd) {
|
|
11878
|
+
return null;
|
|
11879
|
+
}
|
|
11880
|
+
parseHistoryFile(_filePath) {
|
|
11881
|
+
return [];
|
|
11882
|
+
}
|
|
11883
|
+
getCurrentUsage(_historyDir) {
|
|
11884
|
+
return null;
|
|
11885
|
+
}
|
|
11886
|
+
async fetchWeeklyUsage() {
|
|
11887
|
+
return null;
|
|
11888
|
+
}
|
|
11889
|
+
async listModels() {
|
|
11890
|
+
return GEMINI_MODELS;
|
|
11891
|
+
}
|
|
11892
|
+
/**
|
|
11893
|
+
* Gemini's `/model <id>` swaps the active model inside the REPL —
|
|
11894
|
+
* same shape as Claude / Codex / Cursor.
|
|
11895
|
+
*/
|
|
11896
|
+
changeModelInstruction(modelId) {
|
|
11897
|
+
return { type: "pty", ptyInput: `/model ${modelId}\r` };
|
|
11898
|
+
}
|
|
11899
|
+
/**
|
|
11900
|
+
* `/compress` is Gemini's context-compression slash command (the
|
|
11901
|
+
* closest analogue to Claude's `/compact`). No auto-mode equivalent.
|
|
11902
|
+
*/
|
|
11903
|
+
summarizeInstruction(_mode) {
|
|
11904
|
+
return { ptyInput: "/compress\r" };
|
|
11905
|
+
}
|
|
11906
|
+
/**
|
|
11907
|
+
* Pass-through filter. Gemini's TUI chrome isn't worth
|
|
11908
|
+
* hand-detecting — users who want clean output should use ACP
|
|
11909
|
+
* (`CODEAM_ACP_ENABLED=1`) instead. Returning the input verbatim
|
|
11910
|
+
* satisfies the contract's idempotency assertion and keeps mobile
|
|
11911
|
+
* showing whatever the REPL prints.
|
|
11912
|
+
*/
|
|
11913
|
+
filterTuiOutput(lines) {
|
|
11914
|
+
return lines;
|
|
11915
|
+
}
|
|
11916
|
+
/**
|
|
11917
|
+
* No interactive-selector detection — Gemini's REPL uses input
|
|
11918
|
+
* lines, not arrow-key menus we'd need to detect. ACP surfaces
|
|
11919
|
+
* permission requests as typed messages, which is the path we want
|
|
11920
|
+
* users on anyway.
|
|
11921
|
+
*/
|
|
11922
|
+
detectInteractivePrompt(_lines) {
|
|
11923
|
+
return null;
|
|
11924
|
+
}
|
|
11925
|
+
credentialLocator() {
|
|
11926
|
+
return geminiCredentialLocator();
|
|
11927
|
+
}
|
|
11928
|
+
loginLauncher() {
|
|
11929
|
+
return geminiLoginLauncher();
|
|
11930
|
+
}
|
|
11931
|
+
};
|
|
11932
|
+
|
|
11753
11933
|
// src/agents/registry.ts
|
|
11754
11934
|
var runtimeBuilders = {
|
|
11755
|
-
claude: (
|
|
11756
|
-
codex: (
|
|
11757
|
-
coderabbit: (
|
|
11758
|
-
cursor: (
|
|
11759
|
-
aider: (
|
|
11935
|
+
claude: (os28) => new ClaudeRuntimeStrategy(os28),
|
|
11936
|
+
codex: (os28) => new CodexRuntimeStrategy(os28),
|
|
11937
|
+
coderabbit: (os28) => new CoderabbitRuntimeStrategy(os28),
|
|
11938
|
+
cursor: (os28) => new CursorRuntimeStrategy(os28),
|
|
11939
|
+
aider: (os28) => new AiderRuntimeStrategy(os28),
|
|
11940
|
+
gemini: (os28) => new GeminiRuntimeStrategy(os28)
|
|
11760
11941
|
};
|
|
11761
11942
|
var deployBuilders = {
|
|
11762
11943
|
claude: () => new ClaudeDeployStrategy(),
|
|
11763
11944
|
codex: () => new CodexDeployStrategy()
|
|
11764
11945
|
};
|
|
11765
|
-
function createAgentStrategy(agent,
|
|
11946
|
+
function createAgentStrategy(agent, os28 = createOsStrategy()) {
|
|
11766
11947
|
if (!AGENT_REGISTRY[agent]?.enabled) {
|
|
11767
11948
|
throw new Error(
|
|
11768
11949
|
`Agent "${agent}" is not supported in this codeam-cli version. Upgrade with 'npm i -g codeam-cli@latest'.`
|
|
@@ -11772,10 +11953,10 @@ function createAgentStrategy(agent, os27 = createOsStrategy()) {
|
|
|
11772
11953
|
if (!build) {
|
|
11773
11954
|
throw new Error(`No runtime strategy registered for agent "${agent}"`);
|
|
11774
11955
|
}
|
|
11775
|
-
return build(
|
|
11956
|
+
return build(os28);
|
|
11776
11957
|
}
|
|
11777
|
-
function createInteractiveAgentStrategy(agent,
|
|
11778
|
-
const s = createAgentStrategy(agent,
|
|
11958
|
+
function createInteractiveAgentStrategy(agent, os28 = createOsStrategy()) {
|
|
11959
|
+
const s = createAgentStrategy(agent, os28);
|
|
11779
11960
|
if (s.mode !== "interactive") {
|
|
11780
11961
|
throw new Error(
|
|
11781
11962
|
`Agent "${agent}" is a batch agent; use createAgentStrategy + .runOneShot for one-shot reviews.`
|
|
@@ -11795,87 +11976,762 @@ function createDeployStrategy(agent) {
|
|
|
11795
11976
|
return build();
|
|
11796
11977
|
}
|
|
11797
11978
|
|
|
11798
|
-
// src/
|
|
11799
|
-
var
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
11803
|
-
|
|
11804
|
-
|
|
11979
|
+
// src/agents/acp/adapters.ts
|
|
11980
|
+
var path25 = __toESM(require("path"));
|
|
11981
|
+
var require_ = require;
|
|
11982
|
+
function resolveBin(pkgName, binName) {
|
|
11983
|
+
try {
|
|
11984
|
+
const manifestPath = require_.resolve(`${pkgName}/package.json`);
|
|
11985
|
+
const manifest = require_(`${pkgName}/package.json`);
|
|
11986
|
+
const pkgDir = path25.dirname(manifestPath);
|
|
11987
|
+
const bin = manifest.bin;
|
|
11988
|
+
if (!bin) return null;
|
|
11989
|
+
if (typeof bin === "string") return path25.resolve(pkgDir, bin);
|
|
11990
|
+
const target = binName ?? Object.keys(bin)[0];
|
|
11991
|
+
if (!target || !bin[target]) return null;
|
|
11992
|
+
return path25.resolve(pkgDir, bin[target]);
|
|
11993
|
+
} catch {
|
|
11994
|
+
return null;
|
|
11995
|
+
}
|
|
11996
|
+
}
|
|
11997
|
+
var REGISTRY = {
|
|
11998
|
+
claude: () => {
|
|
11999
|
+
const bin = resolveBin("@agentclientprotocol/claude-agent-acp", "claude-agent-acp");
|
|
12000
|
+
if (!bin) return null;
|
|
12001
|
+
return {
|
|
12002
|
+
command: process.execPath,
|
|
12003
|
+
args: [bin],
|
|
12004
|
+
requiresAgentBinary: "claude"
|
|
12005
|
+
};
|
|
12006
|
+
},
|
|
12007
|
+
codex: () => {
|
|
12008
|
+
const bin = resolveBin("@agentclientprotocol/codex-acp", "codex-acp");
|
|
12009
|
+
if (!bin) return null;
|
|
12010
|
+
return {
|
|
12011
|
+
command: process.execPath,
|
|
12012
|
+
args: [bin],
|
|
12013
|
+
requiresAgentBinary: "codex"
|
|
12014
|
+
};
|
|
12015
|
+
},
|
|
12016
|
+
cursor: () => {
|
|
12017
|
+
const bin = resolveBin("cursor-agent-acp", "cursor-agent-acp");
|
|
12018
|
+
if (!bin) return null;
|
|
12019
|
+
return {
|
|
12020
|
+
command: process.execPath,
|
|
12021
|
+
args: [bin],
|
|
12022
|
+
requiresAgentBinary: "cursor-agent"
|
|
12023
|
+
};
|
|
12024
|
+
},
|
|
12025
|
+
// Gemini speaks ACP natively via `gemini --acp` — no npm adapter
|
|
12026
|
+
// package, just the user-installed `gemini` binary on PATH. Same
|
|
12027
|
+
// {@link AdapterSpec} shape; the only difference is `command` is
|
|
12028
|
+
// resolved from PATH at spawn time instead of being absolute.
|
|
12029
|
+
gemini: () => ({
|
|
12030
|
+
command: "gemini",
|
|
12031
|
+
args: ["--acp"],
|
|
12032
|
+
requiresAgentBinary: "gemini"
|
|
12033
|
+
})
|
|
12034
|
+
};
|
|
12035
|
+
function getAcpAdapter(agent) {
|
|
12036
|
+
const factory = REGISTRY[agent];
|
|
12037
|
+
return factory ? factory() : null;
|
|
12038
|
+
}
|
|
12039
|
+
|
|
12040
|
+
// src/agents/acp/runner.ts
|
|
12041
|
+
var import_node_crypto6 = require("crypto");
|
|
12042
|
+
|
|
12043
|
+
// src/agents/acp/client.ts
|
|
12044
|
+
var import_node_child_process11 = require("child_process");
|
|
12045
|
+
var fs21 = __toESM(require("fs/promises"));
|
|
12046
|
+
var import_node_stream = require("stream");
|
|
12047
|
+
var import_sdk = require("@agentclientprotocol/sdk");
|
|
12048
|
+
var PROTOCOL_VERSION2 = 1;
|
|
12049
|
+
var CLIENT_CAPABILITIES = {
|
|
12050
|
+
fs: { readTextFile: true, writeTextFile: true },
|
|
12051
|
+
terminal: false
|
|
12052
|
+
};
|
|
12053
|
+
var AcpClient = class {
|
|
12054
|
+
constructor(opts) {
|
|
12055
|
+
this.opts = opts;
|
|
12056
|
+
}
|
|
12057
|
+
opts;
|
|
12058
|
+
child = null;
|
|
12059
|
+
connection = null;
|
|
12060
|
+
stopping = false;
|
|
12061
|
+
sessionId = null;
|
|
12062
|
+
/**
|
|
12063
|
+
* Spawn the adapter + perform the initial handshake (initialize
|
|
12064
|
+
* → newSession). Returns the ACP-assigned sessionId so the
|
|
12065
|
+
* caller can route subsequent prompts.
|
|
12066
|
+
*/
|
|
12067
|
+
async start() {
|
|
12068
|
+
if (this.child) throw new Error("AcpClient already started");
|
|
12069
|
+
const { adapter, cwd } = this.opts;
|
|
12070
|
+
const child = (0, import_node_child_process11.spawn)(adapter.command, adapter.args, {
|
|
12071
|
+
cwd,
|
|
12072
|
+
env: process.env,
|
|
12073
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
12074
|
+
});
|
|
12075
|
+
this.child = child;
|
|
12076
|
+
child.stderr?.setEncoding("utf8");
|
|
12077
|
+
child.stderr?.on("data", (chunk2) => {
|
|
12078
|
+
for (const line of chunk2.split(/\r?\n/)) {
|
|
12079
|
+
const trimmed = line.trim();
|
|
12080
|
+
if (trimmed) this.opts.onStderr?.(trimmed);
|
|
12081
|
+
}
|
|
12082
|
+
});
|
|
12083
|
+
child.on("exit", (code, signal) => {
|
|
12084
|
+
if (this.stopping) return;
|
|
12085
|
+
log.warn("acpClient", `adapter exited unexpectedly code=${code} signal=${signal}`);
|
|
12086
|
+
this.opts.onUnexpectedExit?.(code, signal);
|
|
12087
|
+
});
|
|
12088
|
+
if (!child.stdin || !child.stdout) {
|
|
12089
|
+
throw new Error("Spawned ACP adapter is missing stdio handles");
|
|
12090
|
+
}
|
|
12091
|
+
const input = import_node_stream.Readable.toWeb(child.stdout);
|
|
12092
|
+
const output = import_node_stream.Writable.toWeb(child.stdin);
|
|
12093
|
+
const stream = (0, import_sdk.ndJsonStream)(output, input);
|
|
12094
|
+
this.connection = new import_sdk.ClientSideConnection(
|
|
12095
|
+
(_agent) => this.buildClient(),
|
|
12096
|
+
stream
|
|
12097
|
+
);
|
|
12098
|
+
const initialize = await this.connection.initialize({
|
|
12099
|
+
protocolVersion: PROTOCOL_VERSION2,
|
|
12100
|
+
clientCapabilities: CLIENT_CAPABILITIES
|
|
12101
|
+
});
|
|
12102
|
+
const newSession = await this.connection.newSession({
|
|
12103
|
+
cwd,
|
|
12104
|
+
mcpServers: []
|
|
12105
|
+
});
|
|
12106
|
+
this.sessionId = newSession.sessionId;
|
|
12107
|
+
return { sessionId: newSession.sessionId, initialize };
|
|
11805
12108
|
}
|
|
11806
12109
|
/**
|
|
11807
|
-
*
|
|
11808
|
-
*
|
|
11809
|
-
*
|
|
11810
|
-
*
|
|
11811
|
-
* @param parseLine Per-agent chrome parser — `runtime.parseTuiChrome`
|
|
11812
|
-
* bound to the active strategy, or a no-op `() => null`
|
|
11813
|
-
* when the agent doesn't surface tool-call chrome.
|
|
12110
|
+
* Send a user prompt to the active session. Returns the
|
|
12111
|
+
* {@link PromptResponse} which carries the agent's stop reason
|
|
12112
|
+
* once the turn finishes. Session/update notifications keep
|
|
12113
|
+
* arriving on `onSessionUpdate` while the turn streams.
|
|
11814
12114
|
*/
|
|
11815
|
-
|
|
11816
|
-
|
|
11817
|
-
|
|
11818
|
-
for (const step of visible) {
|
|
11819
|
-
const exists = this.history.some(
|
|
11820
|
-
(s) => s.tool === step.tool && s.label === step.label
|
|
11821
|
-
);
|
|
11822
|
-
if (!exists) this.history.push(step);
|
|
12115
|
+
async prompt(text) {
|
|
12116
|
+
if (!this.connection || !this.sessionId) {
|
|
12117
|
+
throw new Error("AcpClient.prompt called before start()");
|
|
11823
12118
|
}
|
|
12119
|
+
return this.connection.prompt({
|
|
12120
|
+
sessionId: this.sessionId,
|
|
12121
|
+
prompt: [{ type: "text", text }]
|
|
12122
|
+
});
|
|
11824
12123
|
}
|
|
11825
12124
|
/**
|
|
11826
|
-
*
|
|
11827
|
-
*
|
|
11828
|
-
* the last call. Caller forwards this as the chunk's
|
|
11829
|
-
* `appendSteps` payload.
|
|
12125
|
+
* Cancel the in-flight prompt turn. Notification — no response.
|
|
12126
|
+
* Safe to call when nothing is in flight (the adapter no-ops).
|
|
11830
12127
|
*/
|
|
11831
|
-
|
|
11832
|
-
if (this.
|
|
11833
|
-
|
|
11834
|
-
this.sentCount = this.history.length;
|
|
11835
|
-
return delta;
|
|
12128
|
+
async cancel() {
|
|
12129
|
+
if (!this.connection || !this.sessionId) return;
|
|
12130
|
+
await this.connection.cancel({ sessionId: this.sessionId });
|
|
11836
12131
|
}
|
|
11837
|
-
/**
|
|
11838
|
-
|
|
11839
|
-
|
|
12132
|
+
/**
|
|
12133
|
+
* Kill the adapter process and tear down the connection. Safe
|
|
12134
|
+
* to call multiple times. Suppresses the unexpected-exit
|
|
12135
|
+
* callback for this teardown.
|
|
12136
|
+
*/
|
|
12137
|
+
async stop() {
|
|
12138
|
+
this.stopping = true;
|
|
12139
|
+
const child = this.child;
|
|
12140
|
+
if (!child) return;
|
|
12141
|
+
this.child = null;
|
|
12142
|
+
this.connection = null;
|
|
12143
|
+
this.sessionId = null;
|
|
12144
|
+
try {
|
|
12145
|
+
child.kill("SIGTERM");
|
|
12146
|
+
const grace = new Promise((resolve6) => {
|
|
12147
|
+
const t2 = setTimeout(() => {
|
|
12148
|
+
try {
|
|
12149
|
+
child.kill("SIGKILL");
|
|
12150
|
+
} catch {
|
|
12151
|
+
}
|
|
12152
|
+
resolve6();
|
|
12153
|
+
}, 2e3);
|
|
12154
|
+
child.once("exit", () => {
|
|
12155
|
+
clearTimeout(t2);
|
|
12156
|
+
resolve6();
|
|
12157
|
+
});
|
|
12158
|
+
});
|
|
12159
|
+
await grace;
|
|
12160
|
+
} catch (err) {
|
|
12161
|
+
log.trace("acpClient", "stop teardown error", err);
|
|
12162
|
+
}
|
|
12163
|
+
}
|
|
12164
|
+
// ─── Client surface (what the agent calls into) ───────────────────
|
|
12165
|
+
buildClient() {
|
|
12166
|
+
return {
|
|
12167
|
+
sessionUpdate: async (params) => {
|
|
12168
|
+
this.opts.onSessionUpdate(params);
|
|
12169
|
+
},
|
|
12170
|
+
requestPermission: (params) => {
|
|
12171
|
+
return this.opts.onRequestPermission(params);
|
|
12172
|
+
},
|
|
12173
|
+
readTextFile: async (params) => {
|
|
12174
|
+
const content = await fs21.readFile(params.path, "utf8");
|
|
12175
|
+
return applyLineRange(content, params.line ?? null, params.limit ?? null);
|
|
12176
|
+
},
|
|
12177
|
+
writeTextFile: async (params) => {
|
|
12178
|
+
await fs21.writeFile(params.path, params.content, "utf8");
|
|
12179
|
+
return {};
|
|
12180
|
+
},
|
|
12181
|
+
// Terminal capability is declared `false` above so adapters
|
|
12182
|
+
// shouldn't call these; provide explicit "not implemented"
|
|
12183
|
+
// stubs so a misbehaving adapter gets a clean error instead
|
|
12184
|
+
// of a hung promise.
|
|
12185
|
+
createTerminal: async () => {
|
|
12186
|
+
throw new Error("terminal capability not implemented in this client (Phase 1)");
|
|
12187
|
+
},
|
|
12188
|
+
terminalOutput: async () => {
|
|
12189
|
+
throw new Error("terminal capability not implemented in this client (Phase 1)");
|
|
12190
|
+
},
|
|
12191
|
+
releaseTerminal: async () => {
|
|
12192
|
+
throw new Error("terminal capability not implemented in this client (Phase 1)");
|
|
12193
|
+
},
|
|
12194
|
+
waitForTerminalExit: async () => {
|
|
12195
|
+
throw new Error("terminal capability not implemented in this client (Phase 1)");
|
|
12196
|
+
},
|
|
12197
|
+
killTerminal: async () => {
|
|
12198
|
+
throw new Error("terminal capability not implemented in this client (Phase 1)");
|
|
12199
|
+
}
|
|
12200
|
+
};
|
|
11840
12201
|
}
|
|
11841
12202
|
};
|
|
12203
|
+
function applyLineRange(content, line, limit) {
|
|
12204
|
+
if (line === null && limit === null) return { content };
|
|
12205
|
+
const lines = content.split("\n");
|
|
12206
|
+
const start2 = Math.max(0, (line ?? 1) - 1);
|
|
12207
|
+
const end = limit !== null ? start2 + limit : lines.length;
|
|
12208
|
+
return { content: lines.slice(start2, end).join("\n") };
|
|
12209
|
+
}
|
|
11842
12210
|
|
|
11843
|
-
// src/services/
|
|
11844
|
-
var https3 = __toESM(require("https"));
|
|
12211
|
+
// src/services/streaming/transport.ts
|
|
11845
12212
|
var http3 = __toESM(require("http"));
|
|
11846
|
-
var
|
|
11847
|
-
|
|
11848
|
-
|
|
11849
|
-
|
|
11850
|
-
|
|
12213
|
+
var https3 = __toESM(require("https"));
|
|
12214
|
+
var _transport2 = {
|
|
12215
|
+
post: _post,
|
|
12216
|
+
get: _get
|
|
12217
|
+
};
|
|
12218
|
+
function _post(url, headers, payload) {
|
|
12219
|
+
return new Promise((resolve6, reject) => {
|
|
12220
|
+
let settled = false;
|
|
12221
|
+
const u2 = new URL(url);
|
|
12222
|
+
const lib = u2.protocol === "https:" ? https3 : http3;
|
|
12223
|
+
const req = lib.request(
|
|
11851
12224
|
{
|
|
11852
|
-
|
|
11853
|
-
"
|
|
11854
|
-
|
|
12225
|
+
hostname: u2.hostname,
|
|
12226
|
+
port: u2.port || (u2.protocol === "https:" ? 443 : 80),
|
|
12227
|
+
path: u2.pathname + u2.search,
|
|
12228
|
+
method: "POST",
|
|
12229
|
+
headers: {
|
|
12230
|
+
...headers,
|
|
12231
|
+
...vercelBypassHeader(),
|
|
12232
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
12233
|
+
},
|
|
12234
|
+
timeout: 8e3
|
|
11855
12235
|
},
|
|
11856
|
-
|
|
12236
|
+
(res) => {
|
|
12237
|
+
let body = "";
|
|
12238
|
+
res.on("data", (c2) => {
|
|
12239
|
+
body += c2.toString();
|
|
12240
|
+
});
|
|
12241
|
+
res.on("end", () => {
|
|
12242
|
+
if (settled) return;
|
|
12243
|
+
settled = true;
|
|
12244
|
+
resolve6({ statusCode: res.statusCode ?? 0, body });
|
|
12245
|
+
});
|
|
12246
|
+
}
|
|
11857
12247
|
);
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
}
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
|
|
11874
|
-
|
|
11875
|
-
|
|
11876
|
-
|
|
11877
|
-
|
|
11878
|
-
|
|
12248
|
+
req.on("error", (err) => {
|
|
12249
|
+
if (settled) return;
|
|
12250
|
+
settled = true;
|
|
12251
|
+
reject(err);
|
|
12252
|
+
});
|
|
12253
|
+
req.on("timeout", () => {
|
|
12254
|
+
req.destroy();
|
|
12255
|
+
});
|
|
12256
|
+
req.write(payload);
|
|
12257
|
+
req.end();
|
|
12258
|
+
});
|
|
12259
|
+
}
|
|
12260
|
+
function _get(url, headers) {
|
|
12261
|
+
return new Promise((resolve6, reject) => {
|
|
12262
|
+
let settled = false;
|
|
12263
|
+
const u2 = new URL(url);
|
|
12264
|
+
const lib = u2.protocol === "https:" ? https3 : http3;
|
|
12265
|
+
const req = lib.request(
|
|
12266
|
+
{
|
|
12267
|
+
hostname: u2.hostname,
|
|
12268
|
+
port: u2.port || (u2.protocol === "https:" ? 443 : 80),
|
|
12269
|
+
path: u2.pathname + u2.search,
|
|
12270
|
+
method: "GET",
|
|
12271
|
+
headers: {
|
|
12272
|
+
...headers,
|
|
12273
|
+
...vercelBypassHeader()
|
|
12274
|
+
},
|
|
12275
|
+
timeout: 8e3
|
|
12276
|
+
},
|
|
12277
|
+
(res) => {
|
|
12278
|
+
let body = "";
|
|
12279
|
+
res.on("data", (c2) => {
|
|
12280
|
+
body += c2.toString();
|
|
12281
|
+
});
|
|
12282
|
+
res.on("end", () => {
|
|
12283
|
+
if (settled) return;
|
|
12284
|
+
settled = true;
|
|
12285
|
+
resolve6({ statusCode: res.statusCode ?? 0, body });
|
|
12286
|
+
});
|
|
12287
|
+
}
|
|
12288
|
+
);
|
|
12289
|
+
req.on("error", (err) => {
|
|
12290
|
+
if (settled) return;
|
|
12291
|
+
settled = true;
|
|
12292
|
+
reject(err);
|
|
12293
|
+
});
|
|
12294
|
+
req.on("timeout", () => {
|
|
12295
|
+
req.destroy();
|
|
12296
|
+
});
|
|
12297
|
+
req.end();
|
|
12298
|
+
});
|
|
12299
|
+
}
|
|
12300
|
+
|
|
12301
|
+
// src/agents/acp/publisher.ts
|
|
12302
|
+
var AcpPublisher = class {
|
|
12303
|
+
constructor(opts) {
|
|
12304
|
+
this.opts = opts;
|
|
12305
|
+
this.apiBase = opts.apiBaseUrl ?? resolveApiBaseUrl();
|
|
12306
|
+
this.headers = {
|
|
12307
|
+
"Content-Type": "application/json",
|
|
12308
|
+
"X-Codeam-Protocol-Version": "2.0.0",
|
|
12309
|
+
"X-Plugin-Auth-Token": opts.pluginAuthToken
|
|
12310
|
+
};
|
|
12311
|
+
}
|
|
12312
|
+
opts;
|
|
12313
|
+
apiBase;
|
|
12314
|
+
headers;
|
|
12315
|
+
/**
|
|
12316
|
+
* Fire-and-forget chunk POST. The backend's per-user SSE bus
|
|
12317
|
+
* forwards each chunk to mobile/landing within ~20 ms (PRO) /
|
|
12318
|
+
* ~80 ms (FREE). Errors are logged but never thrown — a missed
|
|
12319
|
+
* chunk shouldn't bring down the whole session.
|
|
12320
|
+
*/
|
|
12321
|
+
async publishChunk(event) {
|
|
12322
|
+
const url = `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/streaming-chunk`;
|
|
12323
|
+
try {
|
|
12324
|
+
const { statusCode, body } = await _transport2.post(
|
|
12325
|
+
url,
|
|
12326
|
+
this.headers,
|
|
12327
|
+
JSON.stringify(event)
|
|
12328
|
+
);
|
|
12329
|
+
if (statusCode < 200 || statusCode >= 300) {
|
|
12330
|
+
log.warn("acpPublisher", `chunk status=${statusCode} body=${body.slice(0, 200)}`);
|
|
12331
|
+
}
|
|
12332
|
+
} catch (err) {
|
|
12333
|
+
log.trace("acpPublisher", "chunk post failed", err);
|
|
12334
|
+
}
|
|
12335
|
+
}
|
|
12336
|
+
/**
|
|
12337
|
+
* Publish an awaiting-answer event so the mobile renders the
|
|
12338
|
+
* pending-prompt sheet. The CLI follows up with
|
|
12339
|
+
* {@link pollPendingAnswer} until the user replies (or the
|
|
12340
|
+
* 5 min Redis TTL expires upstream).
|
|
12341
|
+
*/
|
|
12342
|
+
async publishAwaitingAnswer(event) {
|
|
12343
|
+
const url = `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/awaiting-answer`;
|
|
12344
|
+
try {
|
|
12345
|
+
const { statusCode, body } = await _transport2.post(
|
|
12346
|
+
url,
|
|
12347
|
+
this.headers,
|
|
12348
|
+
JSON.stringify(event)
|
|
12349
|
+
);
|
|
12350
|
+
if (statusCode < 200 || statusCode >= 300) {
|
|
12351
|
+
log.warn("acpPublisher", `awaiting-answer status=${statusCode} body=${body.slice(0, 200)}`);
|
|
12352
|
+
}
|
|
12353
|
+
} catch (err) {
|
|
12354
|
+
log.trace("acpPublisher", "awaiting-answer post failed", err);
|
|
12355
|
+
}
|
|
12356
|
+
}
|
|
12357
|
+
/**
|
|
12358
|
+
* Drain the pending-answer endpoint. Returns the resolved answer
|
|
12359
|
+
* when the user has replied, `null` otherwise. The caller polls
|
|
12360
|
+
* this on a 1.5 s cadence (same as the legacy emitter) until a
|
|
12361
|
+
* non-null result lands.
|
|
12362
|
+
*/
|
|
12363
|
+
async pollPendingAnswer(questionId) {
|
|
12364
|
+
const url = `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/pending-answer?questionId=${encodeURIComponent(questionId)}&pluginId=${encodeURIComponent(this.opts.pluginId)}`;
|
|
12365
|
+
try {
|
|
12366
|
+
const { statusCode, body } = await _transport2.get(url, this.headers);
|
|
12367
|
+
if (statusCode === 204 || statusCode === 404) return null;
|
|
12368
|
+
if (statusCode < 200 || statusCode >= 300) {
|
|
12369
|
+
log.warn("acpPublisher", `pending-answer status=${statusCode} body=${body.slice(0, 200)}`);
|
|
12370
|
+
return null;
|
|
12371
|
+
}
|
|
12372
|
+
return parsePendingAnswerResponse(body, questionId);
|
|
12373
|
+
} catch (err) {
|
|
12374
|
+
log.trace("acpPublisher", "pending-answer poll failed", err);
|
|
12375
|
+
return null;
|
|
12376
|
+
}
|
|
12377
|
+
}
|
|
12378
|
+
};
|
|
12379
|
+
function parsePendingAnswerResponse(body, questionId) {
|
|
12380
|
+
if (!body) return null;
|
|
12381
|
+
let parsed;
|
|
12382
|
+
try {
|
|
12383
|
+
parsed = JSON.parse(body);
|
|
12384
|
+
} catch {
|
|
12385
|
+
return null;
|
|
12386
|
+
}
|
|
12387
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
12388
|
+
const root = parsed;
|
|
12389
|
+
const candidate = root.data && typeof root.data === "object" ? root.data : root;
|
|
12390
|
+
const qid = candidate.questionId;
|
|
12391
|
+
const answer = candidate.answer;
|
|
12392
|
+
const optionIndex = candidate.optionIndex;
|
|
12393
|
+
if (typeof qid !== "string" || qid !== questionId) return null;
|
|
12394
|
+
if (typeof answer !== "string") return null;
|
|
12395
|
+
const result = { questionId: qid, answer };
|
|
12396
|
+
if (typeof optionIndex === "number" && Number.isInteger(optionIndex)) {
|
|
12397
|
+
result.optionIndex = optionIndex;
|
|
12398
|
+
}
|
|
12399
|
+
return result;
|
|
12400
|
+
}
|
|
12401
|
+
|
|
12402
|
+
// src/agents/acp/mappers.ts
|
|
12403
|
+
var import_node_crypto5 = require("crypto");
|
|
12404
|
+
function mapSessionUpdate(notification) {
|
|
12405
|
+
const update = notification.update;
|
|
12406
|
+
switch (update.sessionUpdate) {
|
|
12407
|
+
case "agent_message_chunk": {
|
|
12408
|
+
const text = extractText2(update.content);
|
|
12409
|
+
if (!text) return [];
|
|
12410
|
+
return [chunk(messageChunkId(update.messageId), "text", text)];
|
|
12411
|
+
}
|
|
12412
|
+
case "agent_thought_chunk": {
|
|
12413
|
+
const text = extractText2(update.content);
|
|
12414
|
+
if (!text) return [];
|
|
12415
|
+
return [chunk(messageChunkId(update.messageId), "thinking", text)];
|
|
12416
|
+
}
|
|
12417
|
+
case "tool_call": {
|
|
12418
|
+
const summary = describeToolCall(update);
|
|
12419
|
+
if (!summary) return [];
|
|
12420
|
+
return [chunk(update.toolCallId, "tool_use", summary)];
|
|
12421
|
+
}
|
|
12422
|
+
case "tool_call_update": {
|
|
12423
|
+
if (update.status !== "completed" && update.status !== "failed") {
|
|
12424
|
+
return [];
|
|
12425
|
+
}
|
|
12426
|
+
const body = describeToolCallUpdate(update);
|
|
12427
|
+
if (!body) return [];
|
|
12428
|
+
const prefix = update.status === "failed" ? "[failed] " : "";
|
|
12429
|
+
return [chunk(update.toolCallId, "tool_result", prefix + body)];
|
|
12430
|
+
}
|
|
12431
|
+
case "user_message_chunk":
|
|
12432
|
+
return [];
|
|
12433
|
+
case "plan":
|
|
12434
|
+
case "plan_update":
|
|
12435
|
+
case "plan_removed":
|
|
12436
|
+
case "available_commands_update":
|
|
12437
|
+
case "current_mode_update":
|
|
12438
|
+
case "config_option_update":
|
|
12439
|
+
case "session_info_update":
|
|
12440
|
+
case "usage_update":
|
|
12441
|
+
return [];
|
|
12442
|
+
default:
|
|
12443
|
+
return [];
|
|
12444
|
+
}
|
|
12445
|
+
}
|
|
12446
|
+
function mapPermissionRequest(request) {
|
|
12447
|
+
const prompt = describeToolCall(request.toolCall) ?? "The agent requested permission to continue.";
|
|
12448
|
+
const optionIdByLabel = {};
|
|
12449
|
+
const kindByLabel = {};
|
|
12450
|
+
const labels = [];
|
|
12451
|
+
for (const opt of request.options) {
|
|
12452
|
+
const label = opt.name?.trim() || humanizeKind(opt.kind);
|
|
12453
|
+
if (label in optionIdByLabel) continue;
|
|
12454
|
+
optionIdByLabel[label] = opt.optionId;
|
|
12455
|
+
kindByLabel[label] = opt.kind;
|
|
12456
|
+
labels.push(label);
|
|
12457
|
+
}
|
|
12458
|
+
return {
|
|
12459
|
+
event: {
|
|
12460
|
+
questionId: (0, import_node_crypto5.randomUUID)(),
|
|
12461
|
+
prompt,
|
|
12462
|
+
options: labels.length > 0 ? labels : void 0
|
|
12463
|
+
},
|
|
12464
|
+
optionIdByLabel,
|
|
12465
|
+
kindByLabel
|
|
12466
|
+
};
|
|
12467
|
+
}
|
|
12468
|
+
function chunk(chunkId, kind, content) {
|
|
12469
|
+
return { chunkId, kind, content, isFinal: true };
|
|
12470
|
+
}
|
|
12471
|
+
function messageChunkId(messageId) {
|
|
12472
|
+
if (typeof messageId === "string" && messageId.length > 0) return messageId;
|
|
12473
|
+
return (0, import_node_crypto5.randomUUID)();
|
|
12474
|
+
}
|
|
12475
|
+
function extractText2(content) {
|
|
12476
|
+
if (!content || typeof content !== "object") return null;
|
|
12477
|
+
if ("type" in content && content.type === "text") {
|
|
12478
|
+
const t2 = content.text;
|
|
12479
|
+
return typeof t2 === "string" && t2.length > 0 ? t2 : null;
|
|
12480
|
+
}
|
|
12481
|
+
return null;
|
|
12482
|
+
}
|
|
12483
|
+
function describeToolCall(call) {
|
|
12484
|
+
const title = call.title?.trim();
|
|
12485
|
+
const kind = call.kind?.trim();
|
|
12486
|
+
if (title && title.length > 0) return title;
|
|
12487
|
+
if (kind && kind.length > 0) return kind;
|
|
12488
|
+
if (call.rawInput && typeof call.rawInput === "object") {
|
|
12489
|
+
try {
|
|
12490
|
+
const summary = JSON.stringify(call.rawInput);
|
|
12491
|
+
if (summary.length > 240) return `${summary.slice(0, 240)}\u2026`;
|
|
12492
|
+
return summary;
|
|
12493
|
+
} catch {
|
|
12494
|
+
return null;
|
|
12495
|
+
}
|
|
12496
|
+
}
|
|
12497
|
+
return null;
|
|
12498
|
+
}
|
|
12499
|
+
function describeToolCallUpdate(update) {
|
|
12500
|
+
const parts = [];
|
|
12501
|
+
if (Array.isArray(update.content)) {
|
|
12502
|
+
for (const item of update.content) {
|
|
12503
|
+
if (!item || typeof item !== "object") continue;
|
|
12504
|
+
if (item.type === "content" && item.content) {
|
|
12505
|
+
const text = extractText2(item.content);
|
|
12506
|
+
if (text) parts.push(text);
|
|
12507
|
+
} else if (item.type === "diff") {
|
|
12508
|
+
const p2 = item.path;
|
|
12509
|
+
parts.push(p2 ? `diff: ${p2}` : "diff");
|
|
12510
|
+
} else if (item.type === "terminal") {
|
|
12511
|
+
const id = item.terminalId;
|
|
12512
|
+
parts.push(id ? `terminal: ${id}` : "terminal");
|
|
12513
|
+
}
|
|
12514
|
+
}
|
|
12515
|
+
}
|
|
12516
|
+
if (parts.length > 0) return parts.join("\n");
|
|
12517
|
+
const title = update.title?.trim();
|
|
12518
|
+
return title && title.length > 0 ? title : null;
|
|
12519
|
+
}
|
|
12520
|
+
function humanizeKind(kind) {
|
|
12521
|
+
switch (kind) {
|
|
12522
|
+
case "allow_once":
|
|
12523
|
+
return "Allow once";
|
|
12524
|
+
case "allow_always":
|
|
12525
|
+
return "Always allow";
|
|
12526
|
+
case "reject_once":
|
|
12527
|
+
return "Reject";
|
|
12528
|
+
case "reject_always":
|
|
12529
|
+
return "Always reject";
|
|
12530
|
+
default:
|
|
12531
|
+
return kind;
|
|
12532
|
+
}
|
|
12533
|
+
}
|
|
12534
|
+
|
|
12535
|
+
// src/agents/acp/runner.ts
|
|
12536
|
+
var ANSWER_POLL_MS = 1500;
|
|
12537
|
+
var PERMISSION_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
12538
|
+
async function runAcpSession(opts) {
|
|
12539
|
+
const publisher = new AcpPublisher({
|
|
12540
|
+
sessionId: opts.sessionId,
|
|
12541
|
+
pluginId: opts.pluginId,
|
|
12542
|
+
pluginAuthToken: opts.pluginAuthToken
|
|
12543
|
+
});
|
|
12544
|
+
const client2 = new AcpClient({
|
|
12545
|
+
adapter: opts.adapter,
|
|
12546
|
+
cwd: opts.cwd,
|
|
12547
|
+
onSessionUpdate: (notification) => {
|
|
12548
|
+
const chunks = mapSessionUpdate(notification);
|
|
12549
|
+
for (const chunk2 of chunks) {
|
|
12550
|
+
void publisher.publishChunk(chunk2);
|
|
12551
|
+
}
|
|
12552
|
+
},
|
|
12553
|
+
onRequestPermission: async (request) => {
|
|
12554
|
+
const { event, optionIdByLabel } = mapPermissionRequest(request);
|
|
12555
|
+
await publisher.publishAwaitingAnswer(event);
|
|
12556
|
+
const answer = await waitForAnswer(publisher, event.questionId);
|
|
12557
|
+
if (!answer) {
|
|
12558
|
+
return { outcome: { outcome: "cancelled" } };
|
|
12559
|
+
}
|
|
12560
|
+
const optionId = optionIdByLabel[answer.answer];
|
|
12561
|
+
if (!optionId) {
|
|
12562
|
+
log.warn(
|
|
12563
|
+
"acpRunner",
|
|
12564
|
+
`pending-answer label not in option map; reply="${answer.answer.slice(0, 80)}"`
|
|
12565
|
+
);
|
|
12566
|
+
return { outcome: { outcome: "cancelled" } };
|
|
12567
|
+
}
|
|
12568
|
+
return { outcome: { outcome: "selected", optionId } };
|
|
12569
|
+
},
|
|
12570
|
+
onStderr: (line) => {
|
|
12571
|
+
log.trace("acpAdapter", line);
|
|
12572
|
+
},
|
|
12573
|
+
onUnexpectedExit: (code, signal) => {
|
|
12574
|
+
log.warn("acpRunner", `adapter died code=${code} signal=${signal}; shutting down session`);
|
|
12575
|
+
void publisher.publishChunk({
|
|
12576
|
+
chunkId: (0, import_node_crypto6.randomUUID)(),
|
|
12577
|
+
kind: "text",
|
|
12578
|
+
content: `Agent adapter exited unexpectedly (code=${code ?? "null"} signal=${signal ?? "null"}).`,
|
|
12579
|
+
isFinal: true
|
|
12580
|
+
});
|
|
12581
|
+
process.exit(1);
|
|
12582
|
+
}
|
|
12583
|
+
});
|
|
12584
|
+
showInfo(`Starting ${opts.agent} via ACP adapter (${opts.adapter.requiresAgentBinary})\u2026`);
|
|
12585
|
+
const { sessionId: acpSessionId, initialize } = await client2.start();
|
|
12586
|
+
log.trace(
|
|
12587
|
+
"acpRunner",
|
|
12588
|
+
`adapter handshake ok protocolVersion=${initialize.protocolVersion} sessionId=${acpSessionId.slice(0, 8)}`
|
|
12589
|
+
);
|
|
12590
|
+
const relay = new CommandRelayService(
|
|
12591
|
+
opts.pluginId,
|
|
12592
|
+
async (cmd) => {
|
|
12593
|
+
await handleCommand(cmd, client2);
|
|
12594
|
+
},
|
|
12595
|
+
{ id: opts.agent, name: opts.agent, displayName: opts.agent }
|
|
12596
|
+
);
|
|
12597
|
+
relay.start();
|
|
12598
|
+
const shutdown = async (signal) => {
|
|
12599
|
+
showInfo(`Shutting down ACP session (${signal})\u2026`);
|
|
12600
|
+
relay.stop();
|
|
12601
|
+
await client2.stop();
|
|
12602
|
+
process.exit(0);
|
|
12603
|
+
};
|
|
12604
|
+
process.once("SIGINT", () => void shutdown("SIGINT"));
|
|
12605
|
+
process.once("SIGTERM", () => void shutdown("SIGTERM"));
|
|
12606
|
+
process.once("SIGHUP", () => void shutdown("SIGHUP"));
|
|
12607
|
+
await new Promise(() => {
|
|
12608
|
+
});
|
|
12609
|
+
}
|
|
12610
|
+
async function handleCommand(cmd, client2) {
|
|
12611
|
+
switch (cmd.type) {
|
|
12612
|
+
case "start_task": {
|
|
12613
|
+
const payload = cmd.payload;
|
|
12614
|
+
const prompt = payload?.prompt?.trim();
|
|
12615
|
+
if (!prompt) {
|
|
12616
|
+
log.warn("acpRunner", "start_task with empty prompt; ignoring");
|
|
12617
|
+
return;
|
|
12618
|
+
}
|
|
12619
|
+
try {
|
|
12620
|
+
await client2.prompt(prompt);
|
|
12621
|
+
} catch (err) {
|
|
12622
|
+
log.warn("acpRunner", `prompt failed: ${describeError(err)}`);
|
|
12623
|
+
}
|
|
12624
|
+
return;
|
|
12625
|
+
}
|
|
12626
|
+
case "stop_task":
|
|
12627
|
+
case "escape_key": {
|
|
12628
|
+
try {
|
|
12629
|
+
await client2.cancel();
|
|
12630
|
+
} catch (err) {
|
|
12631
|
+
log.warn("acpRunner", `cancel failed: ${describeError(err)}`);
|
|
12632
|
+
}
|
|
12633
|
+
return;
|
|
12634
|
+
}
|
|
12635
|
+
default:
|
|
12636
|
+
log.trace("acpRunner", `command type "${cmd.type}" not supported in Phase 1 ACP mode`);
|
|
12637
|
+
return;
|
|
12638
|
+
}
|
|
12639
|
+
}
|
|
12640
|
+
async function waitForAnswer(publisher, questionId) {
|
|
12641
|
+
const deadline = Date.now() + PERMISSION_TIMEOUT_MS;
|
|
12642
|
+
while (Date.now() < deadline) {
|
|
12643
|
+
const reply = await publisher.pollPendingAnswer(questionId);
|
|
12644
|
+
if (reply) return { answer: reply.answer };
|
|
12645
|
+
await new Promise((r) => setTimeout(r, ANSWER_POLL_MS));
|
|
12646
|
+
}
|
|
12647
|
+
return null;
|
|
12648
|
+
}
|
|
12649
|
+
function describeError(err) {
|
|
12650
|
+
if (err instanceof Error) return err.message;
|
|
12651
|
+
return String(err);
|
|
12652
|
+
}
|
|
12653
|
+
|
|
12654
|
+
// src/services/output/chrome-tracker.ts
|
|
12655
|
+
var ChromeStepTracker = class {
|
|
12656
|
+
history = [];
|
|
12657
|
+
sentCount = 0;
|
|
12658
|
+
reset() {
|
|
12659
|
+
this.history = [];
|
|
12660
|
+
this.sentCount = 0;
|
|
12661
|
+
}
|
|
12662
|
+
/**
|
|
12663
|
+
* Parse the rendered lines using the supplied per-agent parser,
|
|
12664
|
+
* appending unseen steps to the cumulative history.
|
|
12665
|
+
*
|
|
12666
|
+
* @param lines Screen lines from `renderToLines`.
|
|
12667
|
+
* @param parseLine Per-agent chrome parser — `runtime.parseTuiChrome`
|
|
12668
|
+
* bound to the active strategy, or a no-op `() => null`
|
|
12669
|
+
* when the agent doesn't surface tool-call chrome.
|
|
12670
|
+
*/
|
|
12671
|
+
ingest(lines, parseLine2) {
|
|
12672
|
+
const visible = lines.map((l) => parseLine2(l)).filter((s) => s !== null);
|
|
12673
|
+
if (visible.length === 0) return;
|
|
12674
|
+
for (const step of visible) {
|
|
12675
|
+
const exists = this.history.some(
|
|
12676
|
+
(s) => s.tool === step.tool && s.label === step.label
|
|
12677
|
+
);
|
|
12678
|
+
if (!exists) this.history.push(step);
|
|
12679
|
+
}
|
|
12680
|
+
}
|
|
12681
|
+
/**
|
|
12682
|
+
* Returns the steps that have NOT yet been shipped on the wire,
|
|
12683
|
+
* marking them as shipped. Empty array means nothing new since
|
|
12684
|
+
* the last call. Caller forwards this as the chunk's
|
|
12685
|
+
* `appendSteps` payload.
|
|
12686
|
+
*/
|
|
12687
|
+
consumeDelta() {
|
|
12688
|
+
if (this.history.length === this.sentCount) return [];
|
|
12689
|
+
const delta = this.history.slice(this.sentCount);
|
|
12690
|
+
this.sentCount = this.history.length;
|
|
12691
|
+
return delta;
|
|
12692
|
+
}
|
|
12693
|
+
/** Snapshot of the cumulative unique-step history (debug + tests). */
|
|
12694
|
+
get cumulativeHistory() {
|
|
12695
|
+
return this.history;
|
|
12696
|
+
}
|
|
12697
|
+
};
|
|
12698
|
+
|
|
12699
|
+
// src/services/output/chunk-emitter.ts
|
|
12700
|
+
var https4 = __toESM(require("https"));
|
|
12701
|
+
var http4 = __toESM(require("http"));
|
|
12702
|
+
var API_BASE3 = resolveApiBaseUrl();
|
|
12703
|
+
async function refreshAuthToken(sessionId, pluginId) {
|
|
12704
|
+
try {
|
|
12705
|
+
const { statusCode, body } = await _transport3.post(
|
|
12706
|
+
`${API_BASE3}/api/pairing/reconnect`,
|
|
12707
|
+
{
|
|
12708
|
+
"Content-Type": "application/json",
|
|
12709
|
+
"X-Codeam-Protocol-Version": PROTOCOL_VERSION,
|
|
12710
|
+
...vercelBypassHeader()
|
|
12711
|
+
},
|
|
12712
|
+
JSON.stringify({ sessionId, pluginId })
|
|
12713
|
+
);
|
|
12714
|
+
if (statusCode === 404) {
|
|
12715
|
+
log.warn("chunkEmitter", "[auth] reconnect 404 \u2014 session gone server-side");
|
|
12716
|
+
return null;
|
|
12717
|
+
}
|
|
12718
|
+
if (statusCode >= 400) {
|
|
12719
|
+
log.warn("chunkEmitter", `[auth] reconnect failed status=${statusCode}`);
|
|
12720
|
+
return null;
|
|
12721
|
+
}
|
|
12722
|
+
const parsed = JSON.parse(body);
|
|
12723
|
+
const fresh = parsed.data?.pluginAuthToken;
|
|
12724
|
+
if (typeof fresh !== "string" || fresh.length === 0) {
|
|
12725
|
+
log.warn("chunkEmitter", "[auth] reconnect response missing pluginAuthToken");
|
|
12726
|
+
return null;
|
|
12727
|
+
}
|
|
12728
|
+
return fresh;
|
|
12729
|
+
} catch (err) {
|
|
12730
|
+
log.warn("chunkEmitter", `[auth] reconnect threw: ${String(err)}`);
|
|
12731
|
+
return null;
|
|
12732
|
+
}
|
|
12733
|
+
}
|
|
12734
|
+
var ChunkEmitter = class {
|
|
11879
12735
|
constructor(opts) {
|
|
11880
12736
|
this.opts = opts;
|
|
11881
12737
|
this.headers = {
|
|
@@ -11909,14 +12765,14 @@ var ChunkEmitter = class {
|
|
|
11909
12765
|
"chunkEmitter",
|
|
11910
12766
|
`send type=${body.type ?? "(clear)"} bytes=${payload.length} done=${body.done === true}`
|
|
11911
12767
|
);
|
|
11912
|
-
return new Promise((
|
|
12768
|
+
return new Promise((resolve6) => {
|
|
11913
12769
|
const attempt = (attemptsLeft) => {
|
|
11914
|
-
|
|
12770
|
+
_transport3.post(this.url, this.headers, payload).then(({ statusCode, body: resBody }) => {
|
|
11915
12771
|
const tookMs = Date.now() - t0;
|
|
11916
12772
|
if (statusCode === 410 || statusCode === 404 && /SESSION_NOT_FOUND|SESSION_GONE/.test(resBody)) {
|
|
11917
12773
|
process.stderr.write("[codeam] session was deleted/disconnected \u2014 stopping output stream.\n");
|
|
11918
12774
|
log.info("chunkEmitter", `dead status=${statusCode} took=${tookMs}ms`);
|
|
11919
|
-
|
|
12775
|
+
resolve6({ dead: true });
|
|
11920
12776
|
return;
|
|
11921
12777
|
}
|
|
11922
12778
|
if (statusCode === 401) {
|
|
@@ -11932,7 +12788,7 @@ var ChunkEmitter = class {
|
|
|
11932
12788
|
return;
|
|
11933
12789
|
}
|
|
11934
12790
|
}
|
|
11935
|
-
|
|
12791
|
+
resolve6({ dead: false });
|
|
11936
12792
|
})();
|
|
11937
12793
|
return;
|
|
11938
12794
|
}
|
|
@@ -11943,7 +12799,7 @@ var ChunkEmitter = class {
|
|
|
11943
12799
|
} else {
|
|
11944
12800
|
log.info("chunkEmitter", `ok status=${statusCode} took=${tookMs}ms`);
|
|
11945
12801
|
}
|
|
11946
|
-
|
|
12802
|
+
resolve6({ dead: false });
|
|
11947
12803
|
}).catch((err) => {
|
|
11948
12804
|
log.warn(
|
|
11949
12805
|
"chunkEmitter",
|
|
@@ -11954,7 +12810,7 @@ var ChunkEmitter = class {
|
|
|
11954
12810
|
const delay = 200 * (maxRetries - attemptsLeft + 1);
|
|
11955
12811
|
setTimeout(() => attempt(attemptsLeft - 1), delay);
|
|
11956
12812
|
} else {
|
|
11957
|
-
|
|
12813
|
+
resolve6({ dead: false });
|
|
11958
12814
|
}
|
|
11959
12815
|
});
|
|
11960
12816
|
};
|
|
@@ -11962,14 +12818,14 @@ var ChunkEmitter = class {
|
|
|
11962
12818
|
});
|
|
11963
12819
|
}
|
|
11964
12820
|
};
|
|
11965
|
-
var
|
|
11966
|
-
post:
|
|
12821
|
+
var _transport3 = {
|
|
12822
|
+
post: _post2
|
|
11967
12823
|
};
|
|
11968
|
-
function
|
|
11969
|
-
return new Promise((
|
|
12824
|
+
function _post2(url, headers, payload) {
|
|
12825
|
+
return new Promise((resolve6, reject) => {
|
|
11970
12826
|
let settled = false;
|
|
11971
12827
|
const u2 = new URL(url);
|
|
11972
|
-
const transport = u2.protocol === "https:" ?
|
|
12828
|
+
const transport = u2.protocol === "https:" ? https4 : http4;
|
|
11973
12829
|
const req = transport.request(
|
|
11974
12830
|
{
|
|
11975
12831
|
hostname: u2.hostname,
|
|
@@ -11990,7 +12846,7 @@ function _post(url, headers, payload) {
|
|
|
11990
12846
|
res.on("end", () => {
|
|
11991
12847
|
if (settled) return;
|
|
11992
12848
|
settled = true;
|
|
11993
|
-
|
|
12849
|
+
resolve6({ statusCode: res.statusCode ?? 0, body: resData });
|
|
11994
12850
|
});
|
|
11995
12851
|
}
|
|
11996
12852
|
);
|
|
@@ -12564,11 +13420,11 @@ var OutputService = class _OutputService {
|
|
|
12564
13420
|
};
|
|
12565
13421
|
|
|
12566
13422
|
// src/services/history.service.ts
|
|
12567
|
-
var
|
|
12568
|
-
var
|
|
12569
|
-
var
|
|
12570
|
-
var
|
|
12571
|
-
var
|
|
13423
|
+
var fs22 = __toESM(require("fs"));
|
|
13424
|
+
var path26 = __toESM(require("path"));
|
|
13425
|
+
var os22 = __toESM(require("os"));
|
|
13426
|
+
var https5 = __toESM(require("https"));
|
|
13427
|
+
var http5 = __toESM(require("http"));
|
|
12572
13428
|
var import_zod = require("zod");
|
|
12573
13429
|
var historyRecordSchema = import_zod.z.object({
|
|
12574
13430
|
type: import_zod.z.string().optional(),
|
|
@@ -12581,7 +13437,7 @@ var historyRecordSchema = import_zod.z.object({
|
|
|
12581
13437
|
}).passthrough().optional()
|
|
12582
13438
|
}).passthrough();
|
|
12583
13439
|
var API_BASE4 = resolveApiBaseUrl();
|
|
12584
|
-
function
|
|
13440
|
+
function extractText3(content) {
|
|
12585
13441
|
if (typeof content === "string") return content;
|
|
12586
13442
|
if (Array.isArray(content)) {
|
|
12587
13443
|
return content.filter((b) => b["type"] === "text").map((b) => b["text"]).join("\n");
|
|
@@ -12593,7 +13449,7 @@ function parseJsonl(filePath) {
|
|
|
12593
13449
|
const messages = [];
|
|
12594
13450
|
let raw;
|
|
12595
13451
|
try {
|
|
12596
|
-
raw =
|
|
13452
|
+
raw = fs22.readFileSync(filePath, "utf8");
|
|
12597
13453
|
} catch (err) {
|
|
12598
13454
|
if (err.code !== "ENOENT") {
|
|
12599
13455
|
log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
|
|
@@ -12620,20 +13476,20 @@ function parseJsonl(filePath) {
|
|
|
12620
13476
|
const uuid = record.uuid ?? `${Date.now()}-${Math.random()}`;
|
|
12621
13477
|
const msg = record.message;
|
|
12622
13478
|
if (record.type === "user" && msg) {
|
|
12623
|
-
const text =
|
|
13479
|
+
const text = extractText3(msg.content).trim();
|
|
12624
13480
|
if (text) messages.push({ id: uuid, role: "user", text, timestamp });
|
|
12625
13481
|
} else if (record.type === "assistant" && msg) {
|
|
12626
|
-
const text =
|
|
13482
|
+
const text = extractText3(msg.content).trim();
|
|
12627
13483
|
if (text) messages.push({ id: uuid, role: "agent", text, timestamp });
|
|
12628
13484
|
}
|
|
12629
13485
|
}
|
|
12630
13486
|
return messages;
|
|
12631
13487
|
}
|
|
12632
13488
|
function post(endpoint, body) {
|
|
12633
|
-
return new Promise((
|
|
13489
|
+
return new Promise((resolve6) => {
|
|
12634
13490
|
const payload = JSON.stringify(body);
|
|
12635
13491
|
const u2 = new URL(`${API_BASE4}${endpoint}`);
|
|
12636
|
-
const transport = u2.protocol === "https:" ?
|
|
13492
|
+
const transport = u2.protocol === "https:" ? https5 : http5;
|
|
12637
13493
|
const req = transport.request(
|
|
12638
13494
|
{
|
|
12639
13495
|
hostname: u2.hostname,
|
|
@@ -12651,17 +13507,17 @@ function post(endpoint, body) {
|
|
|
12651
13507
|
res.resume();
|
|
12652
13508
|
const ok = res.statusCode !== void 0 && res.statusCode >= 200 && res.statusCode < 300;
|
|
12653
13509
|
if (!ok) log.warn("history:post", `${endpoint} \u2192 HTTP ${res.statusCode}`);
|
|
12654
|
-
|
|
13510
|
+
resolve6(ok);
|
|
12655
13511
|
}
|
|
12656
13512
|
);
|
|
12657
13513
|
req.on("error", (err) => {
|
|
12658
13514
|
log.warn("history:post", `${endpoint} network error`, err);
|
|
12659
|
-
|
|
13515
|
+
resolve6(false);
|
|
12660
13516
|
});
|
|
12661
13517
|
req.on("timeout", () => {
|
|
12662
13518
|
log.warn("history:post", `${endpoint} timeout after 15s`);
|
|
12663
13519
|
req.destroy();
|
|
12664
|
-
|
|
13520
|
+
resolve6(false);
|
|
12665
13521
|
});
|
|
12666
13522
|
req.write(payload);
|
|
12667
13523
|
req.end();
|
|
@@ -12728,7 +13584,7 @@ var HistoryService = class _HistoryService {
|
|
|
12728
13584
|
return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
|
|
12729
13585
|
}
|
|
12730
13586
|
get projectDir() {
|
|
12731
|
-
return this.runtime.resolveHistoryDir(this.cwd) ??
|
|
13587
|
+
return this.runtime.resolveHistoryDir(this.cwd) ?? path26.join(os22.homedir(), ".claude", "projects", encodeCwd(this.cwd));
|
|
12732
13588
|
}
|
|
12733
13589
|
/** Set the current Claude conversation ID (extracted from /cost command or session start) */
|
|
12734
13590
|
setCurrentConversationId(id) {
|
|
@@ -12740,7 +13596,7 @@ var HistoryService = class _HistoryService {
|
|
|
12740
13596
|
/** Return the current message count in the active conversation. */
|
|
12741
13597
|
getCurrentMessageCount() {
|
|
12742
13598
|
if (!this.currentConversationId) return 0;
|
|
12743
|
-
const filePath =
|
|
13599
|
+
const filePath = path26.join(this.projectDir, `${this.currentConversationId}.jsonl`);
|
|
12744
13600
|
return parseJsonl(filePath).length;
|
|
12745
13601
|
}
|
|
12746
13602
|
/**
|
|
@@ -12751,7 +13607,7 @@ var HistoryService = class _HistoryService {
|
|
|
12751
13607
|
const deadline = Date.now() + timeoutMs;
|
|
12752
13608
|
while (Date.now() < deadline) {
|
|
12753
13609
|
if (!this.currentConversationId) return null;
|
|
12754
|
-
const filePath =
|
|
13610
|
+
const filePath = path26.join(this.projectDir, `${this.currentConversationId}.jsonl`);
|
|
12755
13611
|
const messages = parseJsonl(filePath);
|
|
12756
13612
|
if (messages.length > previousCount) {
|
|
12757
13613
|
for (let i = messages.length - 1; i >= previousCount; i--) {
|
|
@@ -12777,16 +13633,16 @@ var HistoryService = class _HistoryService {
|
|
|
12777
13633
|
const dir = this.projectDir;
|
|
12778
13634
|
const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
|
|
12779
13635
|
try {
|
|
12780
|
-
const files =
|
|
13636
|
+
const files = fs22.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
12781
13637
|
try {
|
|
12782
|
-
const stat3 =
|
|
13638
|
+
const stat3 = fs22.statSync(path26.join(dir, e.name));
|
|
12783
13639
|
return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
|
|
12784
13640
|
} catch {
|
|
12785
13641
|
return { name: e.name, mtime: 0, birthtime: 0 };
|
|
12786
13642
|
}
|
|
12787
13643
|
}).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
|
|
12788
13644
|
if (files.length > 0) {
|
|
12789
|
-
this.currentConversationId =
|
|
13645
|
+
this.currentConversationId = path26.basename(files[0].name, ".jsonl");
|
|
12790
13646
|
}
|
|
12791
13647
|
} catch {
|
|
12792
13648
|
}
|
|
@@ -12820,13 +13676,13 @@ var HistoryService = class _HistoryService {
|
|
|
12820
13676
|
const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
|
|
12821
13677
|
let entries;
|
|
12822
13678
|
try {
|
|
12823
|
-
entries =
|
|
13679
|
+
entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
12824
13680
|
} catch {
|
|
12825
13681
|
return null;
|
|
12826
13682
|
}
|
|
12827
13683
|
const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
12828
13684
|
try {
|
|
12829
|
-
const stat3 =
|
|
13685
|
+
const stat3 = fs22.statSync(path26.join(dir, e.name));
|
|
12830
13686
|
return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
|
|
12831
13687
|
} catch {
|
|
12832
13688
|
return { name: e.name, mtime: 0, birthtime: 0 };
|
|
@@ -12835,12 +13691,12 @@ var HistoryService = class _HistoryService {
|
|
|
12835
13691
|
if (files.length === 0) return null;
|
|
12836
13692
|
const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
|
|
12837
13693
|
if (!files.some((f) => f.name === targetFile)) return null;
|
|
12838
|
-
return this.extractUsageFromFile(
|
|
13694
|
+
return this.extractUsageFromFile(path26.join(dir, targetFile));
|
|
12839
13695
|
}
|
|
12840
13696
|
extractUsageFromFile(filePath) {
|
|
12841
13697
|
let raw;
|
|
12842
13698
|
try {
|
|
12843
|
-
raw =
|
|
13699
|
+
raw = fs22.readFileSync(filePath, "utf8");
|
|
12844
13700
|
} catch {
|
|
12845
13701
|
return null;
|
|
12846
13702
|
}
|
|
@@ -12885,9 +13741,9 @@ var HistoryService = class _HistoryService {
|
|
|
12885
13741
|
let totalCost = 0;
|
|
12886
13742
|
let files;
|
|
12887
13743
|
try {
|
|
12888
|
-
files =
|
|
13744
|
+
files = fs22.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
|
|
12889
13745
|
try {
|
|
12890
|
-
return
|
|
13746
|
+
return fs22.statSync(path26.join(projectDir, f)).mtimeMs >= monthStartMs;
|
|
12891
13747
|
} catch {
|
|
12892
13748
|
return false;
|
|
12893
13749
|
}
|
|
@@ -12898,7 +13754,7 @@ var HistoryService = class _HistoryService {
|
|
|
12898
13754
|
for (const file of files) {
|
|
12899
13755
|
let raw;
|
|
12900
13756
|
try {
|
|
12901
|
-
raw =
|
|
13757
|
+
raw = fs22.readFileSync(path26.join(projectDir, file), "utf8");
|
|
12902
13758
|
} catch {
|
|
12903
13759
|
continue;
|
|
12904
13760
|
}
|
|
@@ -12962,7 +13818,7 @@ var HistoryService = class _HistoryService {
|
|
|
12962
13818
|
* showing an empty conversation.
|
|
12963
13819
|
*/
|
|
12964
13820
|
async loadConversation(sessionId) {
|
|
12965
|
-
const filePath =
|
|
13821
|
+
const filePath = path26.join(this.projectDir, `${sessionId}.jsonl`);
|
|
12966
13822
|
const messages = parseJsonl(filePath);
|
|
12967
13823
|
if (messages.length === 0) return;
|
|
12968
13824
|
const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
|
|
@@ -13016,7 +13872,7 @@ var HistoryService = class _HistoryService {
|
|
|
13016
13872
|
if (!this.currentConversationId) return 0;
|
|
13017
13873
|
}
|
|
13018
13874
|
const sessionId = this.currentConversationId;
|
|
13019
|
-
const filePath =
|
|
13875
|
+
const filePath = path26.join(this.projectDir, `${sessionId}.jsonl`);
|
|
13020
13876
|
const messages = parseJsonl(filePath);
|
|
13021
13877
|
if (messages.length === 0) return 0;
|
|
13022
13878
|
const marker = this.lastUploadedUuid.get(sessionId);
|
|
@@ -13047,9 +13903,9 @@ var HistoryService = class _HistoryService {
|
|
|
13047
13903
|
|
|
13048
13904
|
// src/services/file-watcher.service.ts
|
|
13049
13905
|
var import_child_process8 = require("child_process");
|
|
13050
|
-
var
|
|
13051
|
-
var
|
|
13052
|
-
var
|
|
13906
|
+
var fs23 = __toESM(require("fs"));
|
|
13907
|
+
var os23 = __toESM(require("os"));
|
|
13908
|
+
var path27 = __toESM(require("path"));
|
|
13053
13909
|
|
|
13054
13910
|
// src/services/file-watcher/diff-parser.ts
|
|
13055
13911
|
var HUNK_HEADER_RE = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
|
|
@@ -13138,16 +13994,16 @@ function isIgnoredFilePath(filePath) {
|
|
|
13138
13994
|
}
|
|
13139
13995
|
|
|
13140
13996
|
// src/services/file-watcher/transport.ts
|
|
13141
|
-
var
|
|
13142
|
-
var
|
|
13143
|
-
var
|
|
13144
|
-
post:
|
|
13997
|
+
var http6 = __toESM(require("http"));
|
|
13998
|
+
var https6 = __toESM(require("https"));
|
|
13999
|
+
var _transport4 = {
|
|
14000
|
+
post: _post3
|
|
13145
14001
|
};
|
|
13146
|
-
function
|
|
13147
|
-
return new Promise((
|
|
14002
|
+
function _post3(url, headers, payload) {
|
|
14003
|
+
return new Promise((resolve6, reject) => {
|
|
13148
14004
|
let settled = false;
|
|
13149
14005
|
const u2 = new URL(url);
|
|
13150
|
-
const lib = u2.protocol === "https:" ?
|
|
14006
|
+
const lib = u2.protocol === "https:" ? https6 : http6;
|
|
13151
14007
|
const req = lib.request(
|
|
13152
14008
|
{
|
|
13153
14009
|
hostname: u2.hostname,
|
|
@@ -13169,7 +14025,7 @@ function _post2(url, headers, payload) {
|
|
|
13169
14025
|
res.on("end", () => {
|
|
13170
14026
|
if (settled) return;
|
|
13171
14027
|
settled = true;
|
|
13172
|
-
|
|
14028
|
+
resolve6({ statusCode: res.statusCode ?? 0, body });
|
|
13173
14029
|
});
|
|
13174
14030
|
}
|
|
13175
14031
|
);
|
|
@@ -13207,10 +14063,10 @@ var WINDOWS_LEGACY_JUNCTIONS = [
|
|
|
13207
14063
|
/[\\/]Start Menu([\\/]|$)/i,
|
|
13208
14064
|
/[\\/]Templates([\\/]|$)/i
|
|
13209
14065
|
];
|
|
13210
|
-
function isUnsafeWindowsWatchRoot(dir,
|
|
14066
|
+
function isUnsafeWindowsWatchRoot(dir, homedir20) {
|
|
13211
14067
|
const norm = (p2) => p2.replace(/\//g, "\\").replace(/\\+$/, "").toLowerCase();
|
|
13212
14068
|
const cwd = norm(dir);
|
|
13213
|
-
const home = norm(
|
|
14069
|
+
const home = norm(homedir20);
|
|
13214
14070
|
if (cwd === home) return true;
|
|
13215
14071
|
if (/^[a-z]:$/.test(cwd)) return true;
|
|
13216
14072
|
const sysRoots = [
|
|
@@ -13240,18 +14096,18 @@ var _findGitRootSeam = {
|
|
|
13240
14096
|
resolve: _defaultFindGitRoot
|
|
13241
14097
|
};
|
|
13242
14098
|
function _defaultFindGitRoot(startDir) {
|
|
13243
|
-
let dir =
|
|
14099
|
+
let dir = path27.resolve(startDir);
|
|
13244
14100
|
const seen = /* @__PURE__ */ new Set();
|
|
13245
14101
|
for (let i = 0; i < 256; i++) {
|
|
13246
14102
|
if (seen.has(dir)) return null;
|
|
13247
14103
|
seen.add(dir);
|
|
13248
14104
|
try {
|
|
13249
|
-
const gitPath =
|
|
13250
|
-
const stat3 =
|
|
14105
|
+
const gitPath = path27.join(dir, ".git");
|
|
14106
|
+
const stat3 = fs23.statSync(gitPath, { throwIfNoEntry: false });
|
|
13251
14107
|
if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
|
|
13252
14108
|
} catch {
|
|
13253
14109
|
}
|
|
13254
|
-
const parent =
|
|
14110
|
+
const parent = path27.dirname(dir);
|
|
13255
14111
|
if (parent === dir) return null;
|
|
13256
14112
|
dir = parent;
|
|
13257
14113
|
}
|
|
@@ -13298,7 +14154,7 @@ var FileWatcherService = class {
|
|
|
13298
14154
|
throw new Error("FileWatcherService has already been stopped \u2014 re-instantiate to restart.");
|
|
13299
14155
|
}
|
|
13300
14156
|
const isWin = process.platform === "win32";
|
|
13301
|
-
if (isWin && isUnsafeWindowsWatchRoot(this.opts.workingDir,
|
|
14157
|
+
if (isWin && isUnsafeWindowsWatchRoot(this.opts.workingDir, os23.homedir())) {
|
|
13302
14158
|
log.warn(
|
|
13303
14159
|
"fileWatcher",
|
|
13304
14160
|
`refusing to watch ${this.opts.workingDir} \u2014 looks like a Windows user-profile or system path. Run codeam from your project folder to enable file change emission.`
|
|
@@ -13485,7 +14341,7 @@ var FileWatcherService = class {
|
|
|
13485
14341
|
}
|
|
13486
14342
|
async emitForFile(absPath, changeType) {
|
|
13487
14343
|
if (this.stopped) return;
|
|
13488
|
-
const fileDir =
|
|
14344
|
+
const fileDir = path27.dirname(absPath);
|
|
13489
14345
|
let gitRoot = this.gitRootByDir.get(fileDir);
|
|
13490
14346
|
if (gitRoot === void 0) {
|
|
13491
14347
|
gitRoot = findGitRoot(fileDir);
|
|
@@ -13499,10 +14355,10 @@ var FileWatcherService = class {
|
|
|
13499
14355
|
return;
|
|
13500
14356
|
}
|
|
13501
14357
|
this.opts.onRepoDirty?.(gitRoot);
|
|
13502
|
-
const relPathInRepo =
|
|
14358
|
+
const relPathInRepo = path27.relative(gitRoot, absPath);
|
|
13503
14359
|
if (!relPathInRepo || relPathInRepo.startsWith("..")) return;
|
|
13504
|
-
const repoPath =
|
|
13505
|
-
const repoName =
|
|
14360
|
+
const repoPath = path27.relative(this.opts.workingDir, gitRoot);
|
|
14361
|
+
const repoName = path27.basename(gitRoot);
|
|
13506
14362
|
let diffText = "";
|
|
13507
14363
|
let fileStatus = "modified";
|
|
13508
14364
|
if (changeType === "unlink") {
|
|
@@ -13646,7 +14502,7 @@ var FileWatcherService = class {
|
|
|
13646
14502
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt += 1) {
|
|
13647
14503
|
if (this.stopped) return;
|
|
13648
14504
|
try {
|
|
13649
|
-
const { statusCode, body: resBody } = await
|
|
14505
|
+
const { statusCode, body: resBody } = await _transport4.post(url, headers, payload);
|
|
13650
14506
|
if (statusCode >= 200 && statusCode < 300) {
|
|
13651
14507
|
log.trace(
|
|
13652
14508
|
"fileWatcher",
|
|
@@ -13765,12 +14621,12 @@ var _gitSeam = {
|
|
|
13765
14621
|
run: _runGitImpl
|
|
13766
14622
|
};
|
|
13767
14623
|
async function _runGitImpl(cwd, args2, opts = {}) {
|
|
13768
|
-
return new Promise((
|
|
14624
|
+
return new Promise((resolve6) => {
|
|
13769
14625
|
let proc;
|
|
13770
14626
|
try {
|
|
13771
14627
|
proc = (0, import_child_process8.spawn)("git", args2, { cwd, env: process.env });
|
|
13772
14628
|
} catch {
|
|
13773
|
-
|
|
14629
|
+
resolve6(null);
|
|
13774
14630
|
return;
|
|
13775
14631
|
}
|
|
13776
14632
|
let stdout = "";
|
|
@@ -13781,13 +14637,13 @@ async function _runGitImpl(cwd, args2, opts = {}) {
|
|
|
13781
14637
|
proc.stderr?.on("data", (c2) => {
|
|
13782
14638
|
stderr += c2.toString();
|
|
13783
14639
|
});
|
|
13784
|
-
proc.on("error", () =>
|
|
14640
|
+
proc.on("error", () => resolve6(null));
|
|
13785
14641
|
proc.on("close", (code) => {
|
|
13786
14642
|
if (code === 0 || opts.allowNonZeroExit) {
|
|
13787
|
-
|
|
14643
|
+
resolve6(stdout);
|
|
13788
14644
|
} else {
|
|
13789
14645
|
log.trace("fileWatcher", `git ${args2.join(" ")} exited ${code} stderr=${stderr.slice(0, 200)}`);
|
|
13790
|
-
|
|
14646
|
+
resolve6(null);
|
|
13791
14647
|
}
|
|
13792
14648
|
});
|
|
13793
14649
|
});
|
|
@@ -13801,7 +14657,7 @@ var import_crypto2 = require("crypto");
|
|
|
13801
14657
|
|
|
13802
14658
|
// src/services/turn-files/git-changeset.ts
|
|
13803
14659
|
var import_child_process9 = require("child_process");
|
|
13804
|
-
var
|
|
14660
|
+
var path28 = __toESM(require("path"));
|
|
13805
14661
|
async function collectRepoChangeset(opts) {
|
|
13806
14662
|
const status2 = await runGit2(opts.repoRoot, ["status", "--porcelain=v1", "-z"]);
|
|
13807
14663
|
if (status2 === null) return null;
|
|
@@ -13881,12 +14737,12 @@ function runGit2(cwd, args2) {
|
|
|
13881
14737
|
return _runGitImpl2.run(cwd, args2);
|
|
13882
14738
|
}
|
|
13883
14739
|
function defaultRunGit(cwd, args2) {
|
|
13884
|
-
return new Promise((
|
|
14740
|
+
return new Promise((resolve6) => {
|
|
13885
14741
|
let proc;
|
|
13886
14742
|
try {
|
|
13887
14743
|
proc = (0, import_child_process9.spawn)("git", args2, { cwd, env: process.env });
|
|
13888
14744
|
} catch {
|
|
13889
|
-
|
|
14745
|
+
resolve6(null);
|
|
13890
14746
|
return;
|
|
13891
14747
|
}
|
|
13892
14748
|
let stdout = "";
|
|
@@ -13897,22 +14753,22 @@ function defaultRunGit(cwd, args2) {
|
|
|
13897
14753
|
proc.stderr?.on("data", (c2) => {
|
|
13898
14754
|
stderr += c2.toString();
|
|
13899
14755
|
});
|
|
13900
|
-
proc.on("error", () =>
|
|
14756
|
+
proc.on("error", () => resolve6(null));
|
|
13901
14757
|
proc.on("close", (code) => {
|
|
13902
14758
|
if (code === 0) {
|
|
13903
|
-
|
|
14759
|
+
resolve6(stdout);
|
|
13904
14760
|
} else {
|
|
13905
14761
|
log.trace(
|
|
13906
14762
|
"turnFiles",
|
|
13907
14763
|
`git ${args2.join(" ")} exited ${code} stderr=${stderr.slice(0, 200)}`
|
|
13908
14764
|
);
|
|
13909
|
-
|
|
14765
|
+
resolve6(null);
|
|
13910
14766
|
}
|
|
13911
14767
|
});
|
|
13912
14768
|
});
|
|
13913
14769
|
}
|
|
13914
14770
|
async function discoverRepos(workingDir, maxDepth = 4) {
|
|
13915
|
-
const
|
|
14771
|
+
const fs36 = await import("fs/promises");
|
|
13916
14772
|
const out2 = [];
|
|
13917
14773
|
await walk(workingDir, 0);
|
|
13918
14774
|
return out2;
|
|
@@ -13920,7 +14776,7 @@ async function discoverRepos(workingDir, maxDepth = 4) {
|
|
|
13920
14776
|
if (depth > maxDepth) return;
|
|
13921
14777
|
let entries = [];
|
|
13922
14778
|
try {
|
|
13923
|
-
const dirents = await
|
|
14779
|
+
const dirents = await fs36.readdir(dir, { withFileTypes: true });
|
|
13924
14780
|
entries = dirents.filter((d3) => !d3.name.startsWith(".") || d3.name === ".git").map((d3) => ({ name: d3.name, isDirectory: d3.isDirectory() }));
|
|
13925
14781
|
} catch {
|
|
13926
14782
|
return;
|
|
@@ -13931,8 +14787,8 @@ async function discoverRepos(workingDir, maxDepth = 4) {
|
|
|
13931
14787
|
if (hasGit) {
|
|
13932
14788
|
out2.push({
|
|
13933
14789
|
repoRoot: dir,
|
|
13934
|
-
repoPath:
|
|
13935
|
-
repoName:
|
|
14790
|
+
repoPath: path28.relative(workingDir, dir),
|
|
14791
|
+
repoName: path28.basename(dir)
|
|
13936
14792
|
});
|
|
13937
14793
|
return;
|
|
13938
14794
|
}
|
|
@@ -13940,15 +14796,15 @@ async function discoverRepos(workingDir, maxDepth = 4) {
|
|
|
13940
14796
|
if (!entry.isDirectory) continue;
|
|
13941
14797
|
if (entry.name === "node_modules") continue;
|
|
13942
14798
|
if (entry.name === "dist" || entry.name === "build") continue;
|
|
13943
|
-
await walk(
|
|
14799
|
+
await walk(path28.join(dir, entry.name), depth + 1);
|
|
13944
14800
|
}
|
|
13945
14801
|
}
|
|
13946
14802
|
}
|
|
13947
14803
|
|
|
13948
14804
|
// src/services/turn-files/files-outbox.ts
|
|
13949
|
-
var
|
|
13950
|
-
var
|
|
13951
|
-
var
|
|
14805
|
+
var fs24 = __toESM(require("fs/promises"));
|
|
14806
|
+
var path29 = __toESM(require("path"));
|
|
14807
|
+
var import_os7 = require("os");
|
|
13952
14808
|
var HOME_OUTBOX_DIR = ".codeam/outbox";
|
|
13953
14809
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
13954
14810
|
var BACKOFF_STEPS_MS = [
|
|
@@ -13980,16 +14836,16 @@ var FilesOutbox = class {
|
|
|
13980
14836
|
backoffIndex = 0;
|
|
13981
14837
|
stopped = false;
|
|
13982
14838
|
constructor(opts) {
|
|
13983
|
-
const base = opts.baseDir ??
|
|
13984
|
-
this.filePath =
|
|
14839
|
+
const base = opts.baseDir ?? path29.join(homeDir(), HOME_OUTBOX_DIR);
|
|
14840
|
+
this.filePath = path29.join(base, `${opts.sessionId}.jsonl`);
|
|
13985
14841
|
this.post = opts.post;
|
|
13986
14842
|
this.autoSchedule = opts.autoSchedule !== false;
|
|
13987
14843
|
}
|
|
13988
14844
|
/** Persist the entry to disk and trigger a flush. Returns once the
|
|
13989
14845
|
* line is durable on disk (not once the POST succeeds). */
|
|
13990
14846
|
async enqueue(entry) {
|
|
13991
|
-
await
|
|
13992
|
-
await
|
|
14847
|
+
await fs24.mkdir(path29.dirname(this.filePath), { recursive: true });
|
|
14848
|
+
await fs24.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
|
|
13993
14849
|
this.backoffIndex = 0;
|
|
13994
14850
|
if (this.autoSchedule) this.scheduleFlush(0);
|
|
13995
14851
|
}
|
|
@@ -14078,7 +14934,7 @@ var FilesOutbox = class {
|
|
|
14078
14934
|
async readAll() {
|
|
14079
14935
|
let raw = "";
|
|
14080
14936
|
try {
|
|
14081
|
-
raw = await
|
|
14937
|
+
raw = await fs24.readFile(this.filePath, "utf8");
|
|
14082
14938
|
} catch {
|
|
14083
14939
|
return [];
|
|
14084
14940
|
}
|
|
@@ -14102,12 +14958,12 @@ var FilesOutbox = class {
|
|
|
14102
14958
|
async rewrite(entries) {
|
|
14103
14959
|
const tmpPath = `${this.filePath}.${process.pid}.tmp`;
|
|
14104
14960
|
if (entries.length === 0) {
|
|
14105
|
-
await
|
|
14961
|
+
await fs24.unlink(this.filePath).catch(() => void 0);
|
|
14106
14962
|
return;
|
|
14107
14963
|
}
|
|
14108
14964
|
const payload = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
14109
|
-
await
|
|
14110
|
-
await
|
|
14965
|
+
await fs24.writeFile(tmpPath, payload, "utf8");
|
|
14966
|
+
await fs24.rename(tmpPath, this.filePath);
|
|
14111
14967
|
}
|
|
14112
14968
|
};
|
|
14113
14969
|
function applyJitter(ms) {
|
|
@@ -14115,7 +14971,7 @@ function applyJitter(ms) {
|
|
|
14115
14971
|
return Math.round(ms * factor);
|
|
14116
14972
|
}
|
|
14117
14973
|
function homeDir() {
|
|
14118
|
-
return process.env.HOME ?? process.env.USERPROFILE ?? (0,
|
|
14974
|
+
return process.env.HOME ?? process.env.USERPROFILE ?? (0, import_os7.tmpdir)();
|
|
14119
14975
|
}
|
|
14120
14976
|
|
|
14121
14977
|
// src/services/turn-files/turn-file-aggregator.ts
|
|
@@ -14222,13 +15078,13 @@ var TurnFileAggregator = class {
|
|
|
14222
15078
|
return;
|
|
14223
15079
|
}
|
|
14224
15080
|
const chunks = chunkArray(novel, MAX_BATCH_SIZE);
|
|
14225
|
-
for (const
|
|
15081
|
+
for (const chunk2 of chunks) {
|
|
14226
15082
|
const entry = {
|
|
14227
15083
|
turnId: (0, import_crypto2.randomUUID)(),
|
|
14228
15084
|
sessionId: this.opts.sessionId,
|
|
14229
15085
|
pluginId: this.opts.pluginId,
|
|
14230
15086
|
enqueuedAt: Date.now(),
|
|
14231
|
-
files:
|
|
15087
|
+
files: chunk2
|
|
14232
15088
|
};
|
|
14233
15089
|
await this.outbox.enqueue(entry);
|
|
14234
15090
|
}
|
|
@@ -14267,7 +15123,7 @@ var TurnFileAggregator = class {
|
|
|
14267
15123
|
files: entry.files
|
|
14268
15124
|
});
|
|
14269
15125
|
try {
|
|
14270
|
-
const res = await
|
|
15126
|
+
const res = await _transport4.post(url, headers, body);
|
|
14271
15127
|
return { ok: res.statusCode >= 200 && res.statusCode < 300, statusCode: res.statusCode };
|
|
14272
15128
|
} catch (err) {
|
|
14273
15129
|
log.trace(
|
|
@@ -14326,101 +15182,9 @@ var RepoDirtyTracker = class {
|
|
|
14326
15182
|
|
|
14327
15183
|
// src/services/streaming-emitter.service.ts
|
|
14328
15184
|
var import_crypto3 = require("crypto");
|
|
14329
|
-
|
|
14330
|
-
// src/services/streaming/transport.ts
|
|
14331
|
-
var http6 = __toESM(require("http"));
|
|
14332
|
-
var https6 = __toESM(require("https"));
|
|
14333
|
-
var _transport4 = {
|
|
14334
|
-
post: _post3,
|
|
14335
|
-
get: _get
|
|
14336
|
-
};
|
|
14337
|
-
function _post3(url, headers, payload) {
|
|
14338
|
-
return new Promise((resolve5, reject) => {
|
|
14339
|
-
let settled = false;
|
|
14340
|
-
const u2 = new URL(url);
|
|
14341
|
-
const lib = u2.protocol === "https:" ? https6 : http6;
|
|
14342
|
-
const req = lib.request(
|
|
14343
|
-
{
|
|
14344
|
-
hostname: u2.hostname,
|
|
14345
|
-
port: u2.port || (u2.protocol === "https:" ? 443 : 80),
|
|
14346
|
-
path: u2.pathname + u2.search,
|
|
14347
|
-
method: "POST",
|
|
14348
|
-
headers: {
|
|
14349
|
-
...headers,
|
|
14350
|
-
...vercelBypassHeader(),
|
|
14351
|
-
"Content-Length": Buffer.byteLength(payload)
|
|
14352
|
-
},
|
|
14353
|
-
timeout: 8e3
|
|
14354
|
-
},
|
|
14355
|
-
(res) => {
|
|
14356
|
-
let body = "";
|
|
14357
|
-
res.on("data", (c2) => {
|
|
14358
|
-
body += c2.toString();
|
|
14359
|
-
});
|
|
14360
|
-
res.on("end", () => {
|
|
14361
|
-
if (settled) return;
|
|
14362
|
-
settled = true;
|
|
14363
|
-
resolve5({ statusCode: res.statusCode ?? 0, body });
|
|
14364
|
-
});
|
|
14365
|
-
}
|
|
14366
|
-
);
|
|
14367
|
-
req.on("error", (err) => {
|
|
14368
|
-
if (settled) return;
|
|
14369
|
-
settled = true;
|
|
14370
|
-
reject(err);
|
|
14371
|
-
});
|
|
14372
|
-
req.on("timeout", () => {
|
|
14373
|
-
req.destroy();
|
|
14374
|
-
});
|
|
14375
|
-
req.write(payload);
|
|
14376
|
-
req.end();
|
|
14377
|
-
});
|
|
14378
|
-
}
|
|
14379
|
-
function _get(url, headers) {
|
|
14380
|
-
return new Promise((resolve5, reject) => {
|
|
14381
|
-
let settled = false;
|
|
14382
|
-
const u2 = new URL(url);
|
|
14383
|
-
const lib = u2.protocol === "https:" ? https6 : http6;
|
|
14384
|
-
const req = lib.request(
|
|
14385
|
-
{
|
|
14386
|
-
hostname: u2.hostname,
|
|
14387
|
-
port: u2.port || (u2.protocol === "https:" ? 443 : 80),
|
|
14388
|
-
path: u2.pathname + u2.search,
|
|
14389
|
-
method: "GET",
|
|
14390
|
-
headers: {
|
|
14391
|
-
...headers,
|
|
14392
|
-
...vercelBypassHeader()
|
|
14393
|
-
},
|
|
14394
|
-
timeout: 8e3
|
|
14395
|
-
},
|
|
14396
|
-
(res) => {
|
|
14397
|
-
let body = "";
|
|
14398
|
-
res.on("data", (c2) => {
|
|
14399
|
-
body += c2.toString();
|
|
14400
|
-
});
|
|
14401
|
-
res.on("end", () => {
|
|
14402
|
-
if (settled) return;
|
|
14403
|
-
settled = true;
|
|
14404
|
-
resolve5({ statusCode: res.statusCode ?? 0, body });
|
|
14405
|
-
});
|
|
14406
|
-
}
|
|
14407
|
-
);
|
|
14408
|
-
req.on("error", (err) => {
|
|
14409
|
-
if (settled) return;
|
|
14410
|
-
settled = true;
|
|
14411
|
-
reject(err);
|
|
14412
|
-
});
|
|
14413
|
-
req.on("timeout", () => {
|
|
14414
|
-
req.destroy();
|
|
14415
|
-
});
|
|
14416
|
-
req.end();
|
|
14417
|
-
});
|
|
14418
|
-
}
|
|
14419
|
-
|
|
14420
|
-
// src/services/streaming-emitter.service.ts
|
|
14421
15185
|
var API_BASE7 = resolveApiBaseUrl();
|
|
14422
15186
|
var TICK_MS = 50;
|
|
14423
|
-
var
|
|
15187
|
+
var ANSWER_POLL_MS2 = 1500;
|
|
14424
15188
|
var SELECTOR_STABLE_MS = 800;
|
|
14425
15189
|
var MAX_RETRIES2 = 1;
|
|
14426
15190
|
var RETRY_BACKOFF_MS2 = 200;
|
|
@@ -14460,7 +15224,7 @@ var StreamingEmitterService = class {
|
|
|
14460
15224
|
this.tickTimer = setInterval(() => this.tick(), TICK_MS);
|
|
14461
15225
|
this.answerPollTimer = setInterval(() => {
|
|
14462
15226
|
void this.pollPendingAnswer();
|
|
14463
|
-
},
|
|
15227
|
+
}, ANSWER_POLL_MS2);
|
|
14464
15228
|
log.trace("streamingEmitter", `started session=${this.opts.sessionId.slice(0, 8)}`);
|
|
14465
15229
|
}
|
|
14466
15230
|
/**
|
|
@@ -14481,11 +15245,11 @@ var StreamingEmitterService = class {
|
|
|
14481
15245
|
}
|
|
14482
15246
|
if (this.activeChunk) {
|
|
14483
15247
|
const finalContent = this.activeChunk.currentContent;
|
|
14484
|
-
const
|
|
15248
|
+
const chunk2 = this.activeChunk;
|
|
14485
15249
|
this.activeChunk = null;
|
|
14486
15250
|
await this.postChunk({
|
|
14487
|
-
chunkId:
|
|
14488
|
-
kind:
|
|
15251
|
+
chunkId: chunk2.chunkId,
|
|
15252
|
+
kind: chunk2.kind,
|
|
14489
15253
|
content: finalContent,
|
|
14490
15254
|
isFinal: true
|
|
14491
15255
|
});
|
|
@@ -14557,17 +15321,17 @@ var StreamingEmitterService = class {
|
|
|
14557
15321
|
this.maybeFlushActive(false);
|
|
14558
15322
|
}
|
|
14559
15323
|
maybeFlushActive(force) {
|
|
14560
|
-
const
|
|
14561
|
-
if (!
|
|
15324
|
+
const chunk2 = this.activeChunk;
|
|
15325
|
+
if (!chunk2) return;
|
|
14562
15326
|
const now = Date.now();
|
|
14563
|
-
if (!force && now -
|
|
14564
|
-
if (
|
|
14565
|
-
|
|
14566
|
-
|
|
15327
|
+
if (!force && now - chunk2.lastEmitAt < TICK_MS) return;
|
|
15328
|
+
if (chunk2.currentContent === chunk2.emittedContent) return;
|
|
15329
|
+
chunk2.emittedContent = chunk2.currentContent;
|
|
15330
|
+
chunk2.lastEmitAt = now;
|
|
14567
15331
|
void this.postChunk({
|
|
14568
|
-
chunkId:
|
|
14569
|
-
kind:
|
|
14570
|
-
content:
|
|
15332
|
+
chunkId: chunk2.chunkId,
|
|
15333
|
+
kind: chunk2.kind,
|
|
15334
|
+
content: chunk2.currentContent,
|
|
14571
15335
|
isFinal: false
|
|
14572
15336
|
});
|
|
14573
15337
|
}
|
|
@@ -14634,7 +15398,7 @@ var StreamingEmitterService = class {
|
|
|
14634
15398
|
if (!this.pendingAnswer) return;
|
|
14635
15399
|
const questionId = this.pendingAnswer.questionId;
|
|
14636
15400
|
try {
|
|
14637
|
-
const { statusCode, body } = await
|
|
15401
|
+
const { statusCode, body } = await _transport2.get(
|
|
14638
15402
|
`${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/pending-answer?questionId=${encodeURIComponent(questionId)}&pluginId=${encodeURIComponent(this.opts.pluginId)}`,
|
|
14639
15403
|
this.headers
|
|
14640
15404
|
);
|
|
@@ -14645,7 +15409,7 @@ var StreamingEmitterService = class {
|
|
|
14645
15409
|
log.warn("streamingEmitter", `pending-answer status=${statusCode} body=${body.slice(0, 200)}`);
|
|
14646
15410
|
return;
|
|
14647
15411
|
}
|
|
14648
|
-
const parsed =
|
|
15412
|
+
const parsed = parsePendingAnswerResponse2(body);
|
|
14649
15413
|
if (!parsed || parsed.questionId !== questionId) return;
|
|
14650
15414
|
const pending = this.pendingAnswer;
|
|
14651
15415
|
this.pendingAnswer = null;
|
|
@@ -14695,7 +15459,7 @@ var StreamingEmitterService = class {
|
|
|
14695
15459
|
});
|
|
14696
15460
|
for (let attempt = 0; attempt <= MAX_RETRIES2; attempt += 1) {
|
|
14697
15461
|
try {
|
|
14698
|
-
const { statusCode, body: resBody } = await
|
|
15462
|
+
const { statusCode, body: resBody } = await _transport2.post(url, this.headers, payload);
|
|
14699
15463
|
if (statusCode >= 200 && statusCode < 300) {
|
|
14700
15464
|
log.trace("streamingEmitter", `post ok url=${url} status=${statusCode}`);
|
|
14701
15465
|
return;
|
|
@@ -14742,7 +15506,7 @@ function classifyLine(line, parseChrome, runtime) {
|
|
|
14742
15506
|
if (filtered.length === 0) return null;
|
|
14743
15507
|
return "text";
|
|
14744
15508
|
}
|
|
14745
|
-
function
|
|
15509
|
+
function parsePendingAnswerResponse2(body) {
|
|
14746
15510
|
if (!body) return null;
|
|
14747
15511
|
let parsed;
|
|
14748
15512
|
try {
|
|
@@ -14784,7 +15548,7 @@ function buildKeepAlive(ctx) {
|
|
|
14784
15548
|
let timer = null;
|
|
14785
15549
|
async function setIdleTimeout(minutes) {
|
|
14786
15550
|
if (!ctx.inCodespace || !ctx.codespaceName) return;
|
|
14787
|
-
await new Promise((
|
|
15551
|
+
await new Promise((resolve6) => {
|
|
14788
15552
|
const proc = (0, import_child_process10.spawn)(
|
|
14789
15553
|
"gh",
|
|
14790
15554
|
[
|
|
@@ -14798,8 +15562,8 @@ function buildKeepAlive(ctx) {
|
|
|
14798
15562
|
{ stdio: "ignore", detached: true }
|
|
14799
15563
|
);
|
|
14800
15564
|
proc.unref();
|
|
14801
|
-
proc.on("exit", () =>
|
|
14802
|
-
proc.on("error", () =>
|
|
15565
|
+
proc.on("exit", () => resolve6());
|
|
15566
|
+
proc.on("error", () => resolve6());
|
|
14803
15567
|
});
|
|
14804
15568
|
}
|
|
14805
15569
|
return {
|
|
@@ -14822,9 +15586,9 @@ function buildKeepAlive(ctx) {
|
|
|
14822
15586
|
}
|
|
14823
15587
|
|
|
14824
15588
|
// src/commands/start/handlers.ts
|
|
14825
|
-
var
|
|
14826
|
-
var
|
|
14827
|
-
var
|
|
15589
|
+
var fs32 = __toESM(require("fs"));
|
|
15590
|
+
var os25 = __toESM(require("os"));
|
|
15591
|
+
var path38 = __toESM(require("path"));
|
|
14828
15592
|
var import_crypto5 = require("crypto");
|
|
14829
15593
|
var import_child_process15 = require("child_process");
|
|
14830
15594
|
|
|
@@ -14948,8 +15712,8 @@ function parsePayload2(schema, raw) {
|
|
|
14948
15712
|
}
|
|
14949
15713
|
|
|
14950
15714
|
// src/services/file-ops.service.ts
|
|
14951
|
-
var
|
|
14952
|
-
var
|
|
15715
|
+
var fs25 = __toESM(require("fs/promises"));
|
|
15716
|
+
var path30 = __toESM(require("path"));
|
|
14953
15717
|
var MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
14954
15718
|
var MAX_WALK_DEPTH = 6;
|
|
14955
15719
|
var MAX_VISITED_DIRS = 5e3;
|
|
@@ -14984,12 +15748,12 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
|
|
|
14984
15748
|
"__pycache__"
|
|
14985
15749
|
]);
|
|
14986
15750
|
function isUnder(parent, candidate) {
|
|
14987
|
-
const rel =
|
|
14988
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
15751
|
+
const rel = path30.relative(parent, candidate);
|
|
15752
|
+
return rel === "" || !rel.startsWith("..") && !path30.isAbsolute(rel);
|
|
14989
15753
|
}
|
|
14990
15754
|
async function isExistingFile(absPath) {
|
|
14991
15755
|
try {
|
|
14992
|
-
const stat3 = await
|
|
15756
|
+
const stat3 = await fs25.stat(absPath);
|
|
14993
15757
|
return stat3.isFile();
|
|
14994
15758
|
} catch {
|
|
14995
15759
|
return false;
|
|
@@ -15002,13 +15766,13 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
15002
15766
|
ctx.visited++;
|
|
15003
15767
|
let entries = [];
|
|
15004
15768
|
try {
|
|
15005
|
-
entries = await
|
|
15769
|
+
entries = await fs25.readdir(dir, { withFileTypes: true });
|
|
15006
15770
|
} catch {
|
|
15007
15771
|
return;
|
|
15008
15772
|
}
|
|
15009
15773
|
for (const e of entries) {
|
|
15010
15774
|
if (!e.isFile()) continue;
|
|
15011
|
-
const full =
|
|
15775
|
+
const full = path30.join(dir, e.name);
|
|
15012
15776
|
if (needleVariants.some((needle) => full.endsWith(needle))) {
|
|
15013
15777
|
ctx.matches.push(full);
|
|
15014
15778
|
if (ctx.matches.length >= ctx.cap) return;
|
|
@@ -15018,21 +15782,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
15018
15782
|
if (!e.isDirectory()) continue;
|
|
15019
15783
|
if (SUBDIR_IGNORE.has(e.name)) continue;
|
|
15020
15784
|
if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
|
|
15021
|
-
await walkForSuffix(
|
|
15785
|
+
await walkForSuffix(path30.join(dir, e.name), needleVariants, depth + 1, ctx);
|
|
15022
15786
|
if (ctx.matches.length >= ctx.cap) return;
|
|
15023
15787
|
}
|
|
15024
15788
|
}
|
|
15025
15789
|
async function findFile(rawPath) {
|
|
15026
15790
|
const cwd = process.cwd();
|
|
15027
|
-
if (
|
|
15028
|
-
const abs =
|
|
15791
|
+
if (path30.isAbsolute(rawPath)) {
|
|
15792
|
+
const abs = path30.normalize(rawPath);
|
|
15029
15793
|
if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
|
|
15030
15794
|
}
|
|
15031
|
-
const direct =
|
|
15795
|
+
const direct = path30.resolve(cwd, rawPath);
|
|
15032
15796
|
if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
|
|
15033
|
-
const normalized =
|
|
15797
|
+
const normalized = path30.normalize(rawPath).replace(/^[./\\]+/, "");
|
|
15034
15798
|
const needles = [
|
|
15035
|
-
`${
|
|
15799
|
+
`${path30.sep}${normalized}`,
|
|
15036
15800
|
`/${normalized}`
|
|
15037
15801
|
].filter((v, i, a) => a.indexOf(v) === i);
|
|
15038
15802
|
const ctx = { visited: 0, matches: [], cap: 16 };
|
|
@@ -15046,7 +15810,7 @@ async function findWriteTarget(rawPath) {
|
|
|
15046
15810
|
const found = await findFile(rawPath);
|
|
15047
15811
|
if (found) return found;
|
|
15048
15812
|
const cwd = process.cwd();
|
|
15049
|
-
const fallback =
|
|
15813
|
+
const fallback = path30.isAbsolute(rawPath) ? path30.normalize(rawPath) : path30.resolve(cwd, rawPath);
|
|
15050
15814
|
if (!isUnder(cwd, fallback)) return null;
|
|
15051
15815
|
return fallback;
|
|
15052
15816
|
}
|
|
@@ -15063,11 +15827,11 @@ async function readProjectFile(rawPath) {
|
|
|
15063
15827
|
if (!abs) {
|
|
15064
15828
|
return { error: `File not found in the project tree: ${rawPath}` };
|
|
15065
15829
|
}
|
|
15066
|
-
const stat3 = await
|
|
15830
|
+
const stat3 = await fs25.stat(abs);
|
|
15067
15831
|
if (stat3.size > MAX_FILE_BYTES) {
|
|
15068
15832
|
return { error: `File too large (${(stat3.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
|
|
15069
15833
|
}
|
|
15070
|
-
const buf = await
|
|
15834
|
+
const buf = await fs25.readFile(abs);
|
|
15071
15835
|
if (looksBinary(buf)) {
|
|
15072
15836
|
return { error: "Binary file \u2014 refusing to open in a code editor." };
|
|
15073
15837
|
}
|
|
@@ -15086,8 +15850,8 @@ async function writeProjectFile(rawPath, content) {
|
|
|
15086
15850
|
if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
|
|
15087
15851
|
return { error: "Content too large." };
|
|
15088
15852
|
}
|
|
15089
|
-
await
|
|
15090
|
-
await
|
|
15853
|
+
await fs25.mkdir(path30.dirname(abs), { recursive: true });
|
|
15854
|
+
await fs25.writeFile(abs, content, "utf-8");
|
|
15091
15855
|
return { ok: true };
|
|
15092
15856
|
} catch (e) {
|
|
15093
15857
|
const msg = e instanceof Error ? e.message : "Write failed";
|
|
@@ -15098,8 +15862,8 @@ async function writeProjectFile(rawPath, content) {
|
|
|
15098
15862
|
// src/services/project-ops.service.ts
|
|
15099
15863
|
var import_child_process11 = require("child_process");
|
|
15100
15864
|
var import_util2 = require("util");
|
|
15101
|
-
var
|
|
15102
|
-
var
|
|
15865
|
+
var fs26 = __toESM(require("fs/promises"));
|
|
15866
|
+
var path31 = __toESM(require("path"));
|
|
15103
15867
|
var execFileP3 = (0, import_util2.promisify)(import_child_process11.execFile);
|
|
15104
15868
|
var PROJECT_IGNORE = /* @__PURE__ */ new Set([
|
|
15105
15869
|
"node_modules",
|
|
@@ -15147,7 +15911,7 @@ async function listProjectFiles(opts = {}) {
|
|
|
15147
15911
|
}
|
|
15148
15912
|
let entries = [];
|
|
15149
15913
|
try {
|
|
15150
|
-
entries = await
|
|
15914
|
+
entries = await fs26.readdir(dir, { withFileTypes: true });
|
|
15151
15915
|
} catch {
|
|
15152
15916
|
return;
|
|
15153
15917
|
}
|
|
@@ -15157,18 +15921,18 @@ async function listProjectFiles(opts = {}) {
|
|
|
15157
15921
|
return;
|
|
15158
15922
|
}
|
|
15159
15923
|
if (PROJECT_IGNORE.has(e.name)) continue;
|
|
15160
|
-
const full =
|
|
15924
|
+
const full = path31.join(dir, e.name);
|
|
15161
15925
|
if (e.isDirectory()) {
|
|
15162
15926
|
if (depth >= 12) continue;
|
|
15163
15927
|
await walk(full, depth + 1);
|
|
15164
15928
|
} else if (e.isFile()) {
|
|
15165
|
-
const rel =
|
|
15929
|
+
const rel = path31.relative(root, full);
|
|
15166
15930
|
if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
|
|
15167
15931
|
continue;
|
|
15168
15932
|
}
|
|
15169
15933
|
let size = 0;
|
|
15170
15934
|
try {
|
|
15171
|
-
const st3 = await
|
|
15935
|
+
const st3 = await fs26.stat(full);
|
|
15172
15936
|
size = st3.size;
|
|
15173
15937
|
} catch {
|
|
15174
15938
|
}
|
|
@@ -15270,8 +16034,8 @@ async function gitStatus(cwd) {
|
|
|
15270
16034
|
let hasMergeInProgress = false;
|
|
15271
16035
|
try {
|
|
15272
16036
|
const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
|
|
15273
|
-
const mergeHead =
|
|
15274
|
-
await
|
|
16037
|
+
const mergeHead = path31.isAbsolute(gitDir) ? path31.join(gitDir, "MERGE_HEAD") : path31.join(root, gitDir, "MERGE_HEAD");
|
|
16038
|
+
await fs26.access(mergeHead);
|
|
15275
16039
|
hasMergeInProgress = true;
|
|
15276
16040
|
} catch {
|
|
15277
16041
|
}
|
|
@@ -15417,7 +16181,7 @@ async function jsSearchFiles(opts, cwd, cap) {
|
|
|
15417
16181
|
}
|
|
15418
16182
|
let content = "";
|
|
15419
16183
|
try {
|
|
15420
|
-
content = await
|
|
16184
|
+
content = await fs26.readFile(path31.join(cwd, f.path), "utf8");
|
|
15421
16185
|
} catch {
|
|
15422
16186
|
continue;
|
|
15423
16187
|
}
|
|
@@ -15698,14 +16462,14 @@ function closeAllTerminals() {
|
|
|
15698
16462
|
|
|
15699
16463
|
// src/services/apply-file-review.service.ts
|
|
15700
16464
|
var import_child_process13 = require("child_process");
|
|
15701
|
-
var
|
|
15702
|
-
var
|
|
16465
|
+
var fs27 = __toESM(require("fs"));
|
|
16466
|
+
var path33 = __toESM(require("path"));
|
|
15703
16467
|
async function applyFileReview(workingDir, filePath, action) {
|
|
15704
|
-
if (filePath.includes("..") ||
|
|
16468
|
+
if (filePath.includes("..") || path33.isAbsolute(filePath)) {
|
|
15705
16469
|
return { ok: false, action, filePath, error: "invalid file path" };
|
|
15706
16470
|
}
|
|
15707
|
-
const absFile =
|
|
15708
|
-
const repoRoot = findGitRoot2(
|
|
16471
|
+
const absFile = path33.resolve(workingDir, filePath);
|
|
16472
|
+
const repoRoot = findGitRoot2(path33.dirname(absFile));
|
|
15709
16473
|
if (!repoRoot) {
|
|
15710
16474
|
return {
|
|
15711
16475
|
ok: false,
|
|
@@ -15714,7 +16478,7 @@ async function applyFileReview(workingDir, filePath, action) {
|
|
|
15714
16478
|
error: `no enclosing git repo for ${filePath}`
|
|
15715
16479
|
};
|
|
15716
16480
|
}
|
|
15717
|
-
const relInRepo =
|
|
16481
|
+
const relInRepo = path33.relative(repoRoot, absFile);
|
|
15718
16482
|
if (!relInRepo || relInRepo.startsWith("..")) {
|
|
15719
16483
|
return { ok: false, action, filePath, error: "path escapes repo root" };
|
|
15720
16484
|
}
|
|
@@ -15736,12 +16500,12 @@ async function applyFileReview(workingDir, filePath, action) {
|
|
|
15736
16500
|
return { ok: true, action, filePath, repoRoot };
|
|
15737
16501
|
}
|
|
15738
16502
|
function runGit3(cwd, args2) {
|
|
15739
|
-
return new Promise((
|
|
16503
|
+
return new Promise((resolve6) => {
|
|
15740
16504
|
let proc;
|
|
15741
16505
|
try {
|
|
15742
16506
|
proc = (0, import_child_process13.spawn)("git", args2, { cwd, env: process.env });
|
|
15743
16507
|
} catch (err) {
|
|
15744
|
-
|
|
16508
|
+
resolve6({ ok: false, code: -1, stdout: "", stderr: err.message });
|
|
15745
16509
|
return;
|
|
15746
16510
|
}
|
|
15747
16511
|
let stdout = "";
|
|
@@ -15754,26 +16518,26 @@ function runGit3(cwd, args2) {
|
|
|
15754
16518
|
});
|
|
15755
16519
|
proc.on(
|
|
15756
16520
|
"error",
|
|
15757
|
-
(err) =>
|
|
16521
|
+
(err) => resolve6({ ok: false, code: -1, stdout, stderr: stderr + err.message })
|
|
15758
16522
|
);
|
|
15759
16523
|
proc.on(
|
|
15760
16524
|
"close",
|
|
15761
|
-
(code) =>
|
|
16525
|
+
(code) => resolve6({ ok: code === 0, code: code ?? -1, stdout, stderr })
|
|
15762
16526
|
);
|
|
15763
16527
|
});
|
|
15764
16528
|
}
|
|
15765
16529
|
function findGitRoot2(startDir) {
|
|
15766
|
-
let dir =
|
|
16530
|
+
let dir = path33.resolve(startDir);
|
|
15767
16531
|
const seen = /* @__PURE__ */ new Set();
|
|
15768
16532
|
for (let i = 0; i < 256; i++) {
|
|
15769
16533
|
if (seen.has(dir)) return null;
|
|
15770
16534
|
seen.add(dir);
|
|
15771
16535
|
try {
|
|
15772
|
-
const stat3 =
|
|
16536
|
+
const stat3 = fs27.statSync(path33.join(dir, ".git"), { throwIfNoEntry: false });
|
|
15773
16537
|
if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
|
|
15774
16538
|
} catch {
|
|
15775
16539
|
}
|
|
15776
|
-
const parent =
|
|
16540
|
+
const parent = path33.dirname(dir);
|
|
15777
16541
|
if (parent === dir) return null;
|
|
15778
16542
|
dir = parent;
|
|
15779
16543
|
}
|
|
@@ -15781,9 +16545,9 @@ function findGitRoot2(startDir) {
|
|
|
15781
16545
|
}
|
|
15782
16546
|
|
|
15783
16547
|
// src/commands/link.ts
|
|
15784
|
-
var
|
|
15785
|
-
var
|
|
15786
|
-
var
|
|
16548
|
+
var import_node_crypto7 = require("crypto");
|
|
16549
|
+
var fs28 = __toESM(require("fs"));
|
|
16550
|
+
var path34 = __toESM(require("path"));
|
|
15787
16551
|
var import_chokidar = __toESM(require("chokidar"));
|
|
15788
16552
|
var import_picocolors2 = __toESM(require("picocolors"));
|
|
15789
16553
|
|
|
@@ -15843,7 +16607,7 @@ function parseLinkArgs(args2) {
|
|
|
15843
16607
|
if (apiKeyFileArg) {
|
|
15844
16608
|
const filePath = apiKeyFileArg.slice("--api-key-file=".length);
|
|
15845
16609
|
try {
|
|
15846
|
-
apiKey =
|
|
16610
|
+
apiKey = fs28.readFileSync(path34.resolve(filePath), "utf8").trim();
|
|
15847
16611
|
} catch (err) {
|
|
15848
16612
|
throw new Error(`Could not read --api-key-file ${filePath}: ${err.message}`);
|
|
15849
16613
|
}
|
|
@@ -15870,7 +16634,7 @@ async function link(args2 = []) {
|
|
|
15870
16634
|
await linkDryRunPreflight(ctx);
|
|
15871
16635
|
return;
|
|
15872
16636
|
}
|
|
15873
|
-
const pluginId = (0,
|
|
16637
|
+
const pluginId = (0, import_node_crypto7.randomUUID)();
|
|
15874
16638
|
const spin = dist_exports.spinner();
|
|
15875
16639
|
spin.start("Requesting pairing code...");
|
|
15876
16640
|
const pairing = await requestCode(pluginId);
|
|
@@ -15888,7 +16652,7 @@ async function link(args2 = []) {
|
|
|
15888
16652
|
waitSpin.start(waitMsg());
|
|
15889
16653
|
const countdown = setInterval(() => waitSpin.message(waitMsg()), 1e3);
|
|
15890
16654
|
countdown.unref?.();
|
|
15891
|
-
const paired = await new Promise((
|
|
16655
|
+
const paired = await new Promise((resolve6, reject) => {
|
|
15892
16656
|
let stopPoll = null;
|
|
15893
16657
|
const sigint = () => {
|
|
15894
16658
|
clearInterval(countdown);
|
|
@@ -15901,7 +16665,7 @@ async function link(args2 = []) {
|
|
|
15901
16665
|
process.removeListener("SIGINT", sigint);
|
|
15902
16666
|
clearInterval(countdown);
|
|
15903
16667
|
waitSpin.stop("Paired");
|
|
15904
|
-
|
|
16668
|
+
resolve6(info);
|
|
15905
16669
|
},
|
|
15906
16670
|
() => {
|
|
15907
16671
|
clearInterval(countdown);
|
|
@@ -15937,7 +16701,7 @@ async function link(args2 = []) {
|
|
|
15937
16701
|
return;
|
|
15938
16702
|
}
|
|
15939
16703
|
if (parsed.tokenFile) {
|
|
15940
|
-
const credential =
|
|
16704
|
+
const credential = fs28.readFileSync(path34.resolve(parsed.tokenFile), "utf8").trim();
|
|
15941
16705
|
if (!credential) {
|
|
15942
16706
|
showError(`--token-file ${parsed.tokenFile} is empty.`);
|
|
15943
16707
|
process.exit(1);
|
|
@@ -16036,14 +16800,14 @@ async function captureFreshCredentials(ctx) {
|
|
|
16036
16800
|
}
|
|
16037
16801
|
};
|
|
16038
16802
|
try {
|
|
16039
|
-
const token = await new Promise((
|
|
16803
|
+
const token = await new Promise((resolve6, reject) => {
|
|
16040
16804
|
let settled = false;
|
|
16041
16805
|
const tryExtract = async () => {
|
|
16042
16806
|
if (settled) return;
|
|
16043
16807
|
const t2 = await ctx.locator.extract();
|
|
16044
16808
|
if (t2 && !settled) {
|
|
16045
16809
|
settled = true;
|
|
16046
|
-
|
|
16810
|
+
resolve6(t2);
|
|
16047
16811
|
}
|
|
16048
16812
|
};
|
|
16049
16813
|
watcher.on("add", () => void tryExtract());
|
|
@@ -16156,11 +16920,11 @@ async function linkDryRunPreflight(ctx) {
|
|
|
16156
16920
|
var import_promises = require("dns/promises");
|
|
16157
16921
|
var import_fs = require("fs");
|
|
16158
16922
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
16159
|
-
var
|
|
16923
|
+
var import_os8 = __toESM(require("os"));
|
|
16160
16924
|
var import_path4 = __toESM(require("path"));
|
|
16161
16925
|
var import_promises3 = require("stream/promises");
|
|
16162
16926
|
var import_which = __toESM(require("which"));
|
|
16163
|
-
var CACHED_BINARY = import_path4.default.join(
|
|
16927
|
+
var CACHED_BINARY = import_path4.default.join(import_os8.default.homedir(), ".codeam", "bin", "cloudflared");
|
|
16164
16928
|
async function waitForCloudflaredReady(url, timeoutMs = 6e4) {
|
|
16165
16929
|
const hostname3 = new URL(url).hostname;
|
|
16166
16930
|
const resolver = new import_promises.Resolver();
|
|
@@ -16447,7 +17211,7 @@ var pendingAttachmentFiles = /* @__PURE__ */ new Set();
|
|
|
16447
17211
|
function cleanupAttachmentTempFiles() {
|
|
16448
17212
|
for (const p2 of pendingAttachmentFiles) {
|
|
16449
17213
|
try {
|
|
16450
|
-
|
|
17214
|
+
fs32.unlinkSync(p2);
|
|
16451
17215
|
} catch {
|
|
16452
17216
|
}
|
|
16453
17217
|
}
|
|
@@ -16456,8 +17220,8 @@ function cleanupAttachmentTempFiles() {
|
|
|
16456
17220
|
function saveFilesTemp(files) {
|
|
16457
17221
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
16458
17222
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
16459
|
-
const tmpPath =
|
|
16460
|
-
|
|
17223
|
+
const tmpPath = path38.join(os25.tmpdir(), `codeam-${(0, import_crypto5.randomUUID)()}-${safeName}`);
|
|
17224
|
+
fs32.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
16461
17225
|
pendingAttachmentFiles.add(tmpPath);
|
|
16462
17226
|
return tmpPath;
|
|
16463
17227
|
});
|
|
@@ -16477,7 +17241,7 @@ var startTask = (ctx, _cmd, parsed) => {
|
|
|
16477
17241
|
setTimeout(() => {
|
|
16478
17242
|
for (const p2 of paths) {
|
|
16479
17243
|
try {
|
|
16480
|
-
|
|
17244
|
+
fs32.unlinkSync(p2);
|
|
16481
17245
|
} catch {
|
|
16482
17246
|
}
|
|
16483
17247
|
pendingAttachmentFiles.delete(p2);
|
|
@@ -16645,7 +17409,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
16645
17409
|
ctx.relay.stop();
|
|
16646
17410
|
process.exit(0);
|
|
16647
17411
|
};
|
|
16648
|
-
var
|
|
17412
|
+
var readFile5 = async (ctx, cmd, parsed) => {
|
|
16649
17413
|
if (!parsed.path) {
|
|
16650
17414
|
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path" });
|
|
16651
17415
|
return;
|
|
@@ -16653,7 +17417,7 @@ var readFile4 = async (ctx, cmd, parsed) => {
|
|
|
16653
17417
|
const result = await readProjectFile(parsed.path);
|
|
16654
17418
|
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
16655
17419
|
};
|
|
16656
|
-
var
|
|
17420
|
+
var writeFile4 = async (ctx, cmd, parsed) => {
|
|
16657
17421
|
if (!parsed.path || typeof parsed.content !== "string") {
|
|
16658
17422
|
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path or content" });
|
|
16659
17423
|
return;
|
|
@@ -17104,8 +17868,8 @@ var previewStartH = (ctx, _cmd, parsed) => {
|
|
|
17104
17868
|
let readyMatched = false;
|
|
17105
17869
|
let expoUrl = null;
|
|
17106
17870
|
const readyRe = new RegExp(detection.ready_pattern);
|
|
17107
|
-
const onChunk = (
|
|
17108
|
-
const s =
|
|
17871
|
+
const onChunk = (chunk2) => {
|
|
17872
|
+
const s = chunk2.toString();
|
|
17109
17873
|
if (!readyMatched && readyRe.test(s)) readyMatched = true;
|
|
17110
17874
|
if (!expoUrl && detection.framework === "Expo") expoUrl = parseExpoUrl(s);
|
|
17111
17875
|
};
|
|
@@ -17228,8 +17992,8 @@ var previewStartH = (ctx, _cmd, parsed) => {
|
|
|
17228
17992
|
stdio: ["ignore", "pipe", "pipe"]
|
|
17229
17993
|
});
|
|
17230
17994
|
let parsedUrl = null;
|
|
17231
|
-
const onTunnelChunk = (
|
|
17232
|
-
const s =
|
|
17995
|
+
const onTunnelChunk = (chunk2) => {
|
|
17996
|
+
const s = chunk2.toString();
|
|
17233
17997
|
if (!parsedUrl) parsedUrl = parseCloudflaredUrl(s);
|
|
17234
17998
|
const trimmed = s.replace(/\n+$/g, "");
|
|
17235
17999
|
if (trimmed.length > 0) log.info("preview", `cloudflared: ${trimmed}`);
|
|
@@ -17321,22 +18085,22 @@ var previewStopH = (ctx) => {
|
|
|
17321
18085
|
})();
|
|
17322
18086
|
};
|
|
17323
18087
|
function runOnce(cmd, args2, cwd, env) {
|
|
17324
|
-
return new Promise((
|
|
18088
|
+
return new Promise((resolve6) => {
|
|
17325
18089
|
const child = (0, import_child_process15.spawn)(cmd, args2, {
|
|
17326
18090
|
cwd,
|
|
17327
18091
|
env: { ...process.env, ...env ?? {} },
|
|
17328
18092
|
stdio: ["ignore", "pipe", "pipe"]
|
|
17329
18093
|
});
|
|
17330
18094
|
const tag = `setup:${cmd}`;
|
|
17331
|
-
const onChunk = (
|
|
17332
|
-
const text =
|
|
18095
|
+
const onChunk = (chunk2) => {
|
|
18096
|
+
const text = chunk2.toString().replace(/\n+$/g, "");
|
|
17333
18097
|
if (text.length === 0) return;
|
|
17334
18098
|
log.info("preview", `${tag}: ${text}`);
|
|
17335
18099
|
};
|
|
17336
18100
|
child.stdout?.on("data", onChunk);
|
|
17337
18101
|
child.stderr?.on("data", onChunk);
|
|
17338
|
-
child.once("exit", (code) =>
|
|
17339
|
-
child.once("error", () =>
|
|
18102
|
+
child.once("exit", (code) => resolve6(code));
|
|
18103
|
+
child.once("error", () => resolve6(-1));
|
|
17340
18104
|
});
|
|
17341
18105
|
}
|
|
17342
18106
|
var savePreviewConfigH = (_ctx, _cmd, parsed) => {
|
|
@@ -17364,8 +18128,8 @@ var handlers = {
|
|
|
17364
18128
|
set_keep_alive: setKeepAlive,
|
|
17365
18129
|
session_terminated: sessionTerminated,
|
|
17366
18130
|
shutdown_session: shutdownSession,
|
|
17367
|
-
read_file:
|
|
17368
|
-
write_file:
|
|
18131
|
+
read_file: readFile5,
|
|
18132
|
+
write_file: writeFile4,
|
|
17369
18133
|
list_files: listFiles,
|
|
17370
18134
|
search_files: searchFilesH,
|
|
17371
18135
|
terminal_open: terminalOpenH,
|
|
@@ -17441,6 +18205,23 @@ async function start(requestedAgent) {
|
|
|
17441
18205
|
requestedAgent: requestedAgent ?? null
|
|
17442
18206
|
});
|
|
17443
18207
|
const cwd = process.cwd();
|
|
18208
|
+
if (process.env.CODEAM_ACP_ENABLED === "1" && session.pluginAuthToken) {
|
|
18209
|
+
const adapter = getAcpAdapter(session.agent);
|
|
18210
|
+
if (adapter) {
|
|
18211
|
+
await runAcpSession({
|
|
18212
|
+
agent: session.agent,
|
|
18213
|
+
sessionId: session.id,
|
|
18214
|
+
pluginId,
|
|
18215
|
+
pluginAuthToken: session.pluginAuthToken,
|
|
18216
|
+
adapter,
|
|
18217
|
+
cwd
|
|
18218
|
+
});
|
|
18219
|
+
return;
|
|
18220
|
+
}
|
|
18221
|
+
showInfo(
|
|
18222
|
+
`CODEAM_ACP_ENABLED is set but no ACP adapter is registered for "${session.agent}" \u2014 falling back to PTY runtime.`
|
|
18223
|
+
);
|
|
18224
|
+
}
|
|
17444
18225
|
const runtime = createRuntimeStrategy(session.agent);
|
|
17445
18226
|
const historySvc = new HistoryService(runtime, pluginId, cwd);
|
|
17446
18227
|
const keepAliveCtx = {
|
|
@@ -17688,7 +18469,7 @@ async function pair(args2 = []) {
|
|
|
17688
18469
|
waitSpin.message(waitMessage());
|
|
17689
18470
|
}, 1e3);
|
|
17690
18471
|
countdownInterval.unref?.();
|
|
17691
|
-
await new Promise((
|
|
18472
|
+
await new Promise((resolve6) => {
|
|
17692
18473
|
let stopPolling = null;
|
|
17693
18474
|
function sigintHandler() {
|
|
17694
18475
|
clearInterval(countdownInterval);
|
|
@@ -17736,7 +18517,7 @@ async function pair(args2 = []) {
|
|
|
17736
18517
|
pluginAuthToken: info.pluginAuthToken
|
|
17737
18518
|
});
|
|
17738
18519
|
}
|
|
17739
|
-
|
|
18520
|
+
resolve6();
|
|
17740
18521
|
},
|
|
17741
18522
|
() => {
|
|
17742
18523
|
clearInterval(countdownInterval);
|
|
@@ -17788,8 +18569,8 @@ async function autoLinkAfterPair(opts) {
|
|
|
17788
18569
|
}
|
|
17789
18570
|
|
|
17790
18571
|
// src/commands/pair-auto.ts
|
|
17791
|
-
var
|
|
17792
|
-
var
|
|
18572
|
+
var fs33 = __toESM(require("fs"));
|
|
18573
|
+
var os26 = __toESM(require("os"));
|
|
17793
18574
|
var import_crypto7 = require("crypto");
|
|
17794
18575
|
|
|
17795
18576
|
// src/commands/start-infra-only.ts
|
|
@@ -17957,12 +18738,12 @@ function readTokenFromArgs(args2) {
|
|
|
17957
18738
|
}
|
|
17958
18739
|
const fileFlag = args2.find((a) => a.startsWith("--token-file="));
|
|
17959
18740
|
if (fileFlag) {
|
|
17960
|
-
const
|
|
18741
|
+
const path45 = fileFlag.slice("--token-file=".length);
|
|
17961
18742
|
try {
|
|
17962
|
-
const content =
|
|
17963
|
-
if (content.length === 0) fail(`--token-file ${
|
|
18743
|
+
const content = fs33.readFileSync(path45, "utf8").trim();
|
|
18744
|
+
if (content.length === 0) fail(`--token-file ${path45} is empty`);
|
|
17964
18745
|
try {
|
|
17965
|
-
|
|
18746
|
+
fs33.unlinkSync(path45);
|
|
17966
18747
|
} catch {
|
|
17967
18748
|
}
|
|
17968
18749
|
return content;
|
|
@@ -17988,7 +18769,7 @@ async function claimOnce(token, pluginId) {
|
|
|
17988
18769
|
pluginId,
|
|
17989
18770
|
ideName: "codeam-cli (codespace)",
|
|
17990
18771
|
ideVersion: process.env.npm_package_version ?? "unknown",
|
|
17991
|
-
hostname:
|
|
18772
|
+
hostname: os26.hostname(),
|
|
17992
18773
|
codespaceName: process.env.CODESPACE_NAME ?? "",
|
|
17993
18774
|
// Current git branch of the codespace's working directory, so the
|
|
17994
18775
|
// backend can populate `PairedSession.branch` for the codespace pair.
|
|
@@ -18228,7 +19009,7 @@ var import_picocolors10 = __toESM(require("picocolors"));
|
|
|
18228
19009
|
var import_child_process16 = require("child_process");
|
|
18229
19010
|
var import_util4 = require("util");
|
|
18230
19011
|
var import_picocolors8 = __toESM(require("picocolors"));
|
|
18231
|
-
var
|
|
19012
|
+
var path39 = __toESM(require("path"));
|
|
18232
19013
|
var execFileP5 = (0, import_util4.promisify)(import_child_process16.execFile);
|
|
18233
19014
|
var MAX_BUFFER = 8 * 1024 * 1024;
|
|
18234
19015
|
function resetStdinForChild() {
|
|
@@ -18272,12 +19053,12 @@ var GitHubCodespacesProvider = class {
|
|
|
18272
19053
|
}
|
|
18273
19054
|
if (!isAuthed) {
|
|
18274
19055
|
resetStdinForChild();
|
|
18275
|
-
await new Promise((
|
|
19056
|
+
await new Promise((resolve6, reject) => {
|
|
18276
19057
|
const proc = (0, import_child_process16.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
|
|
18277
19058
|
stdio: "inherit"
|
|
18278
19059
|
});
|
|
18279
19060
|
proc.on("exit", (code) => {
|
|
18280
|
-
if (code === 0)
|
|
19061
|
+
if (code === 0) resolve6();
|
|
18281
19062
|
else reject(new Error("gh auth login failed."));
|
|
18282
19063
|
});
|
|
18283
19064
|
proc.on("error", reject);
|
|
@@ -18306,13 +19087,13 @@ var GitHubCodespacesProvider = class {
|
|
|
18306
19087
|
}
|
|
18307
19088
|
wt(noteLines.join("\n"), "One more permission needed");
|
|
18308
19089
|
resetStdinForChild();
|
|
18309
|
-
const refreshCode = await new Promise((
|
|
19090
|
+
const refreshCode = await new Promise((resolve6, reject) => {
|
|
18310
19091
|
const proc = (0, import_child_process16.spawn)(
|
|
18311
19092
|
"gh",
|
|
18312
19093
|
["auth", "refresh", "-h", "github.com", "-s", "codespace"],
|
|
18313
19094
|
{ stdio: "inherit" }
|
|
18314
19095
|
);
|
|
18315
|
-
proc.on("exit", (code) =>
|
|
19096
|
+
proc.on("exit", (code) => resolve6(code ?? 1));
|
|
18316
19097
|
proc.on("error", reject);
|
|
18317
19098
|
});
|
|
18318
19099
|
if (refreshCode !== 0) {
|
|
@@ -18456,10 +19237,10 @@ var GitHubCodespacesProvider = class {
|
|
|
18456
19237
|
if (q(proceed) || !proceed) return;
|
|
18457
19238
|
O2.step(`Installing gh via ${installCmd.describe}\u2026`);
|
|
18458
19239
|
resetStdinForChild();
|
|
18459
|
-
const ok = await new Promise((
|
|
19240
|
+
const ok = await new Promise((resolve6) => {
|
|
18460
19241
|
const proc = (0, import_child_process16.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
|
|
18461
|
-
proc.on("exit", (code) =>
|
|
18462
|
-
proc.on("error", () =>
|
|
19242
|
+
proc.on("exit", (code) => resolve6(code === 0));
|
|
19243
|
+
proc.on("error", () => resolve6(false));
|
|
18463
19244
|
});
|
|
18464
19245
|
if (ok) O2.success("gh installed");
|
|
18465
19246
|
else O2.error("gh install failed");
|
|
@@ -18483,14 +19264,14 @@ var GitHubCodespacesProvider = class {
|
|
|
18483
19264
|
"Expanding GitHub scopes"
|
|
18484
19265
|
);
|
|
18485
19266
|
resetStdinForChild();
|
|
18486
|
-
await new Promise((
|
|
19267
|
+
await new Promise((resolve6, reject) => {
|
|
18487
19268
|
const proc = (0, import_child_process16.spawn)(
|
|
18488
19269
|
"gh",
|
|
18489
19270
|
["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
|
|
18490
19271
|
{ stdio: "inherit" }
|
|
18491
19272
|
);
|
|
18492
19273
|
proc.on("exit", (code) => {
|
|
18493
|
-
if (code === 0)
|
|
19274
|
+
if (code === 0) resolve6();
|
|
18494
19275
|
else reject(new Error(
|
|
18495
19276
|
"gh auth refresh failed. Re-run `gh auth refresh -h github.com -s repo,read:org` manually."
|
|
18496
19277
|
));
|
|
@@ -18661,13 +19442,13 @@ var GitHubCodespacesProvider = class {
|
|
|
18661
19442
|
}
|
|
18662
19443
|
async streamCommand(workspaceId, command2) {
|
|
18663
19444
|
resetStdinForChild();
|
|
18664
|
-
return new Promise((
|
|
19445
|
+
return new Promise((resolve6, reject) => {
|
|
18665
19446
|
const proc = (0, import_child_process16.spawn)(
|
|
18666
19447
|
"gh",
|
|
18667
19448
|
["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
|
|
18668
19449
|
{ stdio: "inherit" }
|
|
18669
19450
|
);
|
|
18670
|
-
proc.on("exit", (code) =>
|
|
19451
|
+
proc.on("exit", (code) => resolve6({ code: code ?? 0 }));
|
|
18671
19452
|
proc.on("error", reject);
|
|
18672
19453
|
});
|
|
18673
19454
|
}
|
|
@@ -18688,7 +19469,7 @@ var GitHubCodespacesProvider = class {
|
|
|
18688
19469
|
"--",
|
|
18689
19470
|
`mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
|
|
18690
19471
|
];
|
|
18691
|
-
await new Promise((
|
|
19472
|
+
await new Promise((resolve6, reject) => {
|
|
18692
19473
|
const tar = (0, import_child_process16.spawn)("tar", tarArgs, {
|
|
18693
19474
|
stdio: ["ignore", "pipe", "pipe"],
|
|
18694
19475
|
env: tarEnv
|
|
@@ -18708,7 +19489,7 @@ var GitHubCodespacesProvider = class {
|
|
|
18708
19489
|
ssh.on("error", reject);
|
|
18709
19490
|
ssh.on("exit", (code) => {
|
|
18710
19491
|
if (code === 0) {
|
|
18711
|
-
|
|
19492
|
+
resolve6();
|
|
18712
19493
|
} else {
|
|
18713
19494
|
const reason = (sshErr || tarErr || `exit ${code}`).trim().slice(0, 500);
|
|
18714
19495
|
reject(new Error(`Remote tar failed: ${reason}`));
|
|
@@ -18717,7 +19498,7 @@ var GitHubCodespacesProvider = class {
|
|
|
18717
19498
|
});
|
|
18718
19499
|
}
|
|
18719
19500
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
18720
|
-
const remoteDir =
|
|
19501
|
+
const remoteDir = path39.posix.dirname(remotePath);
|
|
18721
19502
|
const parts = [
|
|
18722
19503
|
`mkdir -p ${shellQuote(remoteDir)}`,
|
|
18723
19504
|
`cat > ${shellQuote(remotePath)}`
|
|
@@ -18726,7 +19507,7 @@ var GitHubCodespacesProvider = class {
|
|
|
18726
19507
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote(remotePath)}`);
|
|
18727
19508
|
}
|
|
18728
19509
|
const cmd = parts.join(" && ");
|
|
18729
|
-
await new Promise((
|
|
19510
|
+
await new Promise((resolve6, reject) => {
|
|
18730
19511
|
const proc = (0, import_child_process16.spawn)(
|
|
18731
19512
|
"gh",
|
|
18732
19513
|
["codespace", "ssh", "-c", workspaceId, "--", cmd],
|
|
@@ -18738,7 +19519,7 @@ var GitHubCodespacesProvider = class {
|
|
|
18738
19519
|
});
|
|
18739
19520
|
proc.on("error", reject);
|
|
18740
19521
|
proc.on("exit", (code) => {
|
|
18741
|
-
if (code === 0)
|
|
19522
|
+
if (code === 0) resolve6();
|
|
18742
19523
|
else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
|
|
18743
19524
|
});
|
|
18744
19525
|
proc.stdin?.write(contents);
|
|
@@ -18787,7 +19568,7 @@ function shellQuote(s) {
|
|
|
18787
19568
|
// src/services/providers/gitpod.ts
|
|
18788
19569
|
var import_child_process17 = require("child_process");
|
|
18789
19570
|
var import_util5 = require("util");
|
|
18790
|
-
var
|
|
19571
|
+
var path40 = __toESM(require("path"));
|
|
18791
19572
|
var import_picocolors9 = __toESM(require("picocolors"));
|
|
18792
19573
|
var execFileP6 = (0, import_util5.promisify)(import_child_process17.execFile);
|
|
18793
19574
|
var MAX_BUFFER2 = 8 * 1024 * 1024;
|
|
@@ -18828,10 +19609,10 @@ var GitpodProvider = class {
|
|
|
18828
19609
|
"Authenticating Gitpod"
|
|
18829
19610
|
);
|
|
18830
19611
|
resetStdinForChild2();
|
|
18831
|
-
await new Promise((
|
|
19612
|
+
await new Promise((resolve6, reject) => {
|
|
18832
19613
|
const proc = (0, import_child_process17.spawn)("gitpod", ["login"], { stdio: "inherit" });
|
|
18833
19614
|
proc.on("exit", (code) => {
|
|
18834
|
-
if (code === 0)
|
|
19615
|
+
if (code === 0) resolve6();
|
|
18835
19616
|
else reject(new Error("gitpod login failed."));
|
|
18836
19617
|
});
|
|
18837
19618
|
proc.on("error", reject);
|
|
@@ -18980,13 +19761,13 @@ var GitpodProvider = class {
|
|
|
18980
19761
|
}
|
|
18981
19762
|
async streamCommand(workspaceId, command2) {
|
|
18982
19763
|
resetStdinForChild2();
|
|
18983
|
-
return new Promise((
|
|
19764
|
+
return new Promise((resolve6, reject) => {
|
|
18984
19765
|
const proc = (0, import_child_process17.spawn)(
|
|
18985
19766
|
"gitpod",
|
|
18986
19767
|
["workspace", "ssh", workspaceId, "--", "-tt", command2],
|
|
18987
19768
|
{ stdio: "inherit" }
|
|
18988
19769
|
);
|
|
18989
|
-
proc.on("exit", (code) =>
|
|
19770
|
+
proc.on("exit", (code) => resolve6({ code: code ?? 0 }));
|
|
18990
19771
|
proc.on("error", reject);
|
|
18991
19772
|
});
|
|
18992
19773
|
}
|
|
@@ -19000,7 +19781,7 @@ var GitpodProvider = class {
|
|
|
19000
19781
|
tarArgs.push(".");
|
|
19001
19782
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
19002
19783
|
const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
|
|
19003
|
-
await new Promise((
|
|
19784
|
+
await new Promise((resolve6, reject) => {
|
|
19004
19785
|
const tar = (0, import_child_process17.spawn)("tar", tarArgs, {
|
|
19005
19786
|
stdio: ["ignore", "pipe", "pipe"],
|
|
19006
19787
|
env: tarEnv
|
|
@@ -19021,13 +19802,13 @@ var GitpodProvider = class {
|
|
|
19021
19802
|
tar.on("error", reject);
|
|
19022
19803
|
ssh.on("error", reject);
|
|
19023
19804
|
ssh.on("exit", (code) => {
|
|
19024
|
-
if (code === 0)
|
|
19805
|
+
if (code === 0) resolve6();
|
|
19025
19806
|
else reject(new Error(`Remote tar failed: ${(sshErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
|
|
19026
19807
|
});
|
|
19027
19808
|
});
|
|
19028
19809
|
}
|
|
19029
19810
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
19030
|
-
const remoteDir =
|
|
19811
|
+
const remoteDir = path40.posix.dirname(remotePath);
|
|
19031
19812
|
const parts = [
|
|
19032
19813
|
`mkdir -p ${shellQuote2(remoteDir)}`,
|
|
19033
19814
|
`cat > ${shellQuote2(remotePath)}`
|
|
@@ -19036,7 +19817,7 @@ var GitpodProvider = class {
|
|
|
19036
19817
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote2(remotePath)}`);
|
|
19037
19818
|
}
|
|
19038
19819
|
const cmd = parts.join(" && ");
|
|
19039
|
-
await new Promise((
|
|
19820
|
+
await new Promise((resolve6, reject) => {
|
|
19040
19821
|
const proc = (0, import_child_process17.spawn)(
|
|
19041
19822
|
"gitpod",
|
|
19042
19823
|
["workspace", "ssh", workspaceId, "--", cmd],
|
|
@@ -19048,7 +19829,7 @@ var GitpodProvider = class {
|
|
|
19048
19829
|
});
|
|
19049
19830
|
proc.on("error", reject);
|
|
19050
19831
|
proc.on("exit", (code) => {
|
|
19051
|
-
if (code === 0)
|
|
19832
|
+
if (code === 0) resolve6();
|
|
19052
19833
|
else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
|
|
19053
19834
|
});
|
|
19054
19835
|
proc.stdin?.write(contents);
|
|
@@ -19063,7 +19844,7 @@ function shellQuote2(s) {
|
|
|
19063
19844
|
// src/services/providers/gitlab-workspaces.ts
|
|
19064
19845
|
var import_child_process18 = require("child_process");
|
|
19065
19846
|
var import_util6 = require("util");
|
|
19066
|
-
var
|
|
19847
|
+
var path41 = __toESM(require("path"));
|
|
19067
19848
|
var execFileP7 = (0, import_util6.promisify)(import_child_process18.execFile);
|
|
19068
19849
|
var MAX_BUFFER3 = 8 * 1024 * 1024;
|
|
19069
19850
|
var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
|
|
@@ -19105,14 +19886,14 @@ var GitLabWorkspacesProvider = class {
|
|
|
19105
19886
|
"Authenticating GitLab"
|
|
19106
19887
|
);
|
|
19107
19888
|
resetStdinForChild3();
|
|
19108
|
-
await new Promise((
|
|
19889
|
+
await new Promise((resolve6, reject) => {
|
|
19109
19890
|
const proc = (0, import_child_process18.spawn)(
|
|
19110
19891
|
"glab",
|
|
19111
19892
|
["auth", "login", "--scopes", "api,read_user,read_repository"],
|
|
19112
19893
|
{ stdio: "inherit" }
|
|
19113
19894
|
);
|
|
19114
19895
|
proc.on("exit", (code) => {
|
|
19115
|
-
if (code === 0)
|
|
19896
|
+
if (code === 0) resolve6();
|
|
19116
19897
|
else reject(new Error("glab auth login failed."));
|
|
19117
19898
|
});
|
|
19118
19899
|
proc.on("error", reject);
|
|
@@ -19277,13 +20058,13 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
19277
20058
|
async streamCommand(workspaceId, command2) {
|
|
19278
20059
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
19279
20060
|
resetStdinForChild3();
|
|
19280
|
-
return new Promise((
|
|
20061
|
+
return new Promise((resolve6, reject) => {
|
|
19281
20062
|
const proc = (0, import_child_process18.spawn)(
|
|
19282
20063
|
"ssh",
|
|
19283
20064
|
["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
|
|
19284
20065
|
{ stdio: "inherit" }
|
|
19285
20066
|
);
|
|
19286
|
-
proc.on("exit", (code) =>
|
|
20067
|
+
proc.on("exit", (code) => resolve6({ code: code ?? 0 }));
|
|
19287
20068
|
proc.on("error", reject);
|
|
19288
20069
|
});
|
|
19289
20070
|
}
|
|
@@ -19298,7 +20079,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
19298
20079
|
tarArgs.push(".");
|
|
19299
20080
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
19300
20081
|
const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
|
|
19301
|
-
await new Promise((
|
|
20082
|
+
await new Promise((resolve6, reject) => {
|
|
19302
20083
|
const tar = (0, import_child_process18.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
19303
20084
|
const ssh = (0, import_child_process18.spawn)(
|
|
19304
20085
|
"ssh",
|
|
@@ -19316,20 +20097,20 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
19316
20097
|
tar.on("error", reject);
|
|
19317
20098
|
ssh.on("error", reject);
|
|
19318
20099
|
ssh.on("exit", (code) => {
|
|
19319
|
-
if (code === 0)
|
|
20100
|
+
if (code === 0) resolve6();
|
|
19320
20101
|
else reject(new Error(`Remote tar failed: ${(sshErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
|
|
19321
20102
|
});
|
|
19322
20103
|
});
|
|
19323
20104
|
}
|
|
19324
20105
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
19325
20106
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
19326
|
-
const remoteDir =
|
|
20107
|
+
const remoteDir = path41.posix.dirname(remotePath);
|
|
19327
20108
|
const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
|
|
19328
20109
|
if (options.mode != null) {
|
|
19329
20110
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
|
|
19330
20111
|
}
|
|
19331
20112
|
const cmd = parts.join(" && ");
|
|
19332
|
-
await new Promise((
|
|
20113
|
+
await new Promise((resolve6, reject) => {
|
|
19333
20114
|
const proc = (0, import_child_process18.spawn)(
|
|
19334
20115
|
"ssh",
|
|
19335
20116
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
|
|
@@ -19341,7 +20122,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
19341
20122
|
});
|
|
19342
20123
|
proc.on("error", reject);
|
|
19343
20124
|
proc.on("exit", (code) => {
|
|
19344
|
-
if (code === 0)
|
|
20125
|
+
if (code === 0) resolve6();
|
|
19345
20126
|
else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
|
|
19346
20127
|
});
|
|
19347
20128
|
proc.stdin?.write(contents);
|
|
@@ -19391,7 +20172,7 @@ function shellQuote3(s) {
|
|
|
19391
20172
|
// src/services/providers/railway.ts
|
|
19392
20173
|
var import_child_process19 = require("child_process");
|
|
19393
20174
|
var import_util7 = require("util");
|
|
19394
|
-
var
|
|
20175
|
+
var path42 = __toESM(require("path"));
|
|
19395
20176
|
var execFileP8 = (0, import_util7.promisify)(import_child_process19.execFile);
|
|
19396
20177
|
var MAX_BUFFER4 = 8 * 1024 * 1024;
|
|
19397
20178
|
function resetStdinForChild4() {
|
|
@@ -19432,10 +20213,10 @@ var RailwayProvider = class {
|
|
|
19432
20213
|
"Authenticating Railway"
|
|
19433
20214
|
);
|
|
19434
20215
|
resetStdinForChild4();
|
|
19435
|
-
await new Promise((
|
|
20216
|
+
await new Promise((resolve6, reject) => {
|
|
19436
20217
|
const proc = (0, import_child_process19.spawn)("railway", ["login"], { stdio: "inherit" });
|
|
19437
20218
|
proc.on("exit", (code) => {
|
|
19438
|
-
if (code === 0)
|
|
20219
|
+
if (code === 0) resolve6();
|
|
19439
20220
|
else reject(new Error("railway login failed."));
|
|
19440
20221
|
});
|
|
19441
20222
|
proc.on("error", reject);
|
|
@@ -19575,13 +20356,13 @@ var RailwayProvider = class {
|
|
|
19575
20356
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
19576
20357
|
}
|
|
19577
20358
|
resetStdinForChild4();
|
|
19578
|
-
return new Promise((
|
|
20359
|
+
return new Promise((resolve6, reject) => {
|
|
19579
20360
|
const proc = (0, import_child_process19.spawn)(
|
|
19580
20361
|
"railway",
|
|
19581
20362
|
["shell", "--project", projectId, "--service", serviceId, "--command", command2],
|
|
19582
20363
|
{ stdio: "inherit" }
|
|
19583
20364
|
);
|
|
19584
|
-
proc.on("exit", (code) =>
|
|
20365
|
+
proc.on("exit", (code) => resolve6({ code: code ?? 0 }));
|
|
19585
20366
|
proc.on("error", reject);
|
|
19586
20367
|
});
|
|
19587
20368
|
}
|
|
@@ -19599,7 +20380,7 @@ var RailwayProvider = class {
|
|
|
19599
20380
|
tarArgs.push(".");
|
|
19600
20381
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
19601
20382
|
const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
|
|
19602
|
-
await new Promise((
|
|
20383
|
+
await new Promise((resolve6, reject) => {
|
|
19603
20384
|
const tar = (0, import_child_process19.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
19604
20385
|
const sh = (0, import_child_process19.spawn)(
|
|
19605
20386
|
"railway",
|
|
@@ -19617,7 +20398,7 @@ var RailwayProvider = class {
|
|
|
19617
20398
|
tar.on("error", reject);
|
|
19618
20399
|
sh.on("error", reject);
|
|
19619
20400
|
sh.on("exit", (code) => {
|
|
19620
|
-
if (code === 0)
|
|
20401
|
+
if (code === 0) resolve6();
|
|
19621
20402
|
else reject(new Error(`Remote tar failed: ${(shErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
|
|
19622
20403
|
});
|
|
19623
20404
|
});
|
|
@@ -19627,13 +20408,13 @@ var RailwayProvider = class {
|
|
|
19627
20408
|
if (!projectId || !serviceId) {
|
|
19628
20409
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
19629
20410
|
}
|
|
19630
|
-
const remoteDir =
|
|
20411
|
+
const remoteDir = path42.posix.dirname(remotePath);
|
|
19631
20412
|
const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
|
|
19632
20413
|
if (options.mode != null) {
|
|
19633
20414
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
|
|
19634
20415
|
}
|
|
19635
20416
|
const cmd = parts.join(" && ");
|
|
19636
|
-
await new Promise((
|
|
20417
|
+
await new Promise((resolve6, reject) => {
|
|
19637
20418
|
const proc = (0, import_child_process19.spawn)(
|
|
19638
20419
|
"railway",
|
|
19639
20420
|
["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
|
|
@@ -19645,7 +20426,7 @@ var RailwayProvider = class {
|
|
|
19645
20426
|
});
|
|
19646
20427
|
proc.on("error", reject);
|
|
19647
20428
|
proc.on("exit", (code) => {
|
|
19648
|
-
if (code === 0)
|
|
20429
|
+
if (code === 0) resolve6();
|
|
19649
20430
|
else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
|
|
19650
20431
|
});
|
|
19651
20432
|
proc.stdin?.write(contents);
|
|
@@ -20169,9 +20950,9 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
20169
20950
|
// src/commands/doctor.ts
|
|
20170
20951
|
var import_node_dns = require("dns");
|
|
20171
20952
|
var import_node_util4 = require("util");
|
|
20172
|
-
var
|
|
20173
|
-
var
|
|
20174
|
-
var
|
|
20953
|
+
var import_node_crypto8 = require("crypto");
|
|
20954
|
+
var fs34 = __toESM(require("fs"));
|
|
20955
|
+
var path43 = __toESM(require("path"));
|
|
20175
20956
|
var import_picocolors12 = __toESM(require("picocolors"));
|
|
20176
20957
|
var dnsResolveP = (0, import_node_util4.promisify)(import_node_dns.resolve);
|
|
20177
20958
|
async function checkDns(apiBase) {
|
|
@@ -20227,13 +21008,13 @@ async function checkHealth(apiBase) {
|
|
|
20227
21008
|
}
|
|
20228
21009
|
}
|
|
20229
21010
|
function checkConfigDir() {
|
|
20230
|
-
const dir =
|
|
21011
|
+
const dir = path43.join(require("os").homedir(), ".codeam");
|
|
20231
21012
|
try {
|
|
20232
|
-
|
|
20233
|
-
const probe =
|
|
20234
|
-
|
|
20235
|
-
const read =
|
|
20236
|
-
|
|
21013
|
+
fs34.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
21014
|
+
const probe = path43.join(dir, ".doctor-probe");
|
|
21015
|
+
fs34.writeFileSync(probe, "ok", { mode: 384 });
|
|
21016
|
+
const read = fs34.readFileSync(probe, "utf8");
|
|
21017
|
+
fs34.unlinkSync(probe);
|
|
20237
21018
|
if (read !== "ok") throw new Error("write/read round-trip mismatch");
|
|
20238
21019
|
return {
|
|
20239
21020
|
id: "config-dir",
|
|
@@ -20273,9 +21054,9 @@ function checkSessions() {
|
|
|
20273
21054
|
}
|
|
20274
21055
|
}
|
|
20275
21056
|
function checkAgentBinaries() {
|
|
20276
|
-
const
|
|
21057
|
+
const os28 = createOsStrategy();
|
|
20277
21058
|
return getEnabledAgents().map((meta) => {
|
|
20278
|
-
const found =
|
|
21059
|
+
const found = os28.findInPath(meta.binaryName);
|
|
20279
21060
|
return {
|
|
20280
21061
|
id: `agent-${meta.id}`,
|
|
20281
21062
|
label: `Agent binary: ${meta.displayName} (${meta.binaryName})`,
|
|
@@ -20297,7 +21078,7 @@ function checkNodePty() {
|
|
|
20297
21078
|
detail: "not required on this platform"
|
|
20298
21079
|
};
|
|
20299
21080
|
}
|
|
20300
|
-
const vendoredPath =
|
|
21081
|
+
const vendoredPath = path43.join(__dirname, "vendor", "node-pty");
|
|
20301
21082
|
for (const target of [vendoredPath, "node-pty"]) {
|
|
20302
21083
|
try {
|
|
20303
21084
|
require(target);
|
|
@@ -20339,9 +21120,9 @@ function checkChokidar() {
|
|
|
20339
21120
|
}
|
|
20340
21121
|
async function doctor(args2 = []) {
|
|
20341
21122
|
const json = args2.includes("--json");
|
|
20342
|
-
const cliVersion = true ? "2.27.
|
|
21123
|
+
const cliVersion = true ? "2.27.3" : "0.0.0-dev";
|
|
20343
21124
|
const apiBase = resolveApiBaseUrl();
|
|
20344
|
-
const diagnosticId = (0,
|
|
21125
|
+
const diagnosticId = (0, import_node_crypto8.randomUUID)();
|
|
20345
21126
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
20346
21127
|
const [dns, health] = await Promise.all([
|
|
20347
21128
|
checkDns(apiBase),
|
|
@@ -20538,7 +21319,7 @@ async function completion(args2) {
|
|
|
20538
21319
|
// src/commands/version.ts
|
|
20539
21320
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
20540
21321
|
function version2() {
|
|
20541
|
-
const v = true ? "2.27.
|
|
21322
|
+
const v = true ? "2.27.3" : "unknown";
|
|
20542
21323
|
console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
|
|
20543
21324
|
}
|
|
20544
21325
|
|
|
@@ -20666,9 +21447,9 @@ function tryShowSubcommandHelp(cmd, args2) {
|
|
|
20666
21447
|
var _subcommandHelpKeys = Object.keys(HELPS);
|
|
20667
21448
|
|
|
20668
21449
|
// src/lib/updateNotifier.ts
|
|
20669
|
-
var
|
|
20670
|
-
var
|
|
20671
|
-
var
|
|
21450
|
+
var fs35 = __toESM(require("fs"));
|
|
21451
|
+
var os27 = __toESM(require("os"));
|
|
21452
|
+
var path44 = __toESM(require("path"));
|
|
20672
21453
|
var https7 = __toESM(require("https"));
|
|
20673
21454
|
var import_picocolors16 = __toESM(require("picocolors"));
|
|
20674
21455
|
var PKG_NAME = "codeam-cli";
|
|
@@ -20676,12 +21457,12 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
|
20676
21457
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
20677
21458
|
var REQUEST_TIMEOUT_MS = 1500;
|
|
20678
21459
|
function cachePath() {
|
|
20679
|
-
const dir =
|
|
20680
|
-
return
|
|
21460
|
+
const dir = path44.join(os27.homedir(), ".codeam");
|
|
21461
|
+
return path44.join(dir, "update-check.json");
|
|
20681
21462
|
}
|
|
20682
21463
|
function readCache() {
|
|
20683
21464
|
try {
|
|
20684
|
-
const raw =
|
|
21465
|
+
const raw = fs35.readFileSync(cachePath(), "utf8");
|
|
20685
21466
|
const parsed = JSON.parse(raw);
|
|
20686
21467
|
if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
|
|
20687
21468
|
return parsed;
|
|
@@ -20692,10 +21473,10 @@ function readCache() {
|
|
|
20692
21473
|
function writeCache(cache) {
|
|
20693
21474
|
try {
|
|
20694
21475
|
const file = cachePath();
|
|
20695
|
-
|
|
21476
|
+
fs35.mkdirSync(path44.dirname(file), { recursive: true });
|
|
20696
21477
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
20697
|
-
|
|
20698
|
-
|
|
21478
|
+
fs35.writeFileSync(tmp, JSON.stringify(cache));
|
|
21479
|
+
fs35.renameSync(tmp, file);
|
|
20699
21480
|
} catch {
|
|
20700
21481
|
}
|
|
20701
21482
|
}
|
|
@@ -20713,40 +21494,40 @@ function compareSemver(a, b) {
|
|
|
20713
21494
|
return 0;
|
|
20714
21495
|
}
|
|
20715
21496
|
function fetchLatest() {
|
|
20716
|
-
return new Promise((
|
|
21497
|
+
return new Promise((resolve6) => {
|
|
20717
21498
|
const req = https7.get(
|
|
20718
21499
|
REGISTRY_URL,
|
|
20719
21500
|
{ headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
|
|
20720
21501
|
(res) => {
|
|
20721
21502
|
if (res.statusCode !== 200) {
|
|
20722
21503
|
res.resume();
|
|
20723
|
-
|
|
21504
|
+
resolve6(null);
|
|
20724
21505
|
return;
|
|
20725
21506
|
}
|
|
20726
21507
|
let buf = "";
|
|
20727
21508
|
res.setEncoding("utf8");
|
|
20728
|
-
res.on("data", (
|
|
20729
|
-
buf +=
|
|
21509
|
+
res.on("data", (chunk2) => {
|
|
21510
|
+
buf += chunk2;
|
|
20730
21511
|
});
|
|
20731
21512
|
res.on("end", () => {
|
|
20732
21513
|
try {
|
|
20733
21514
|
const json = JSON.parse(buf);
|
|
20734
21515
|
if (typeof json.version === "string") {
|
|
20735
|
-
|
|
21516
|
+
resolve6(json.version);
|
|
20736
21517
|
} else {
|
|
20737
|
-
|
|
21518
|
+
resolve6(null);
|
|
20738
21519
|
}
|
|
20739
21520
|
} catch {
|
|
20740
|
-
|
|
21521
|
+
resolve6(null);
|
|
20741
21522
|
}
|
|
20742
21523
|
});
|
|
20743
21524
|
}
|
|
20744
21525
|
);
|
|
20745
21526
|
req.on("timeout", () => {
|
|
20746
21527
|
req.destroy();
|
|
20747
|
-
|
|
21528
|
+
resolve6(null);
|
|
20748
21529
|
});
|
|
20749
|
-
req.on("error", () =>
|
|
21530
|
+
req.on("error", () => resolve6(null));
|
|
20750
21531
|
});
|
|
20751
21532
|
}
|
|
20752
21533
|
function notifyIfStale(currentVersion, latest) {
|
|
@@ -20766,7 +21547,7 @@ function checkForUpdates() {
|
|
|
20766
21547
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
20767
21548
|
if (process.env.CI) return;
|
|
20768
21549
|
if (!process.stdout.isTTY) return;
|
|
20769
|
-
const current = true ? "2.27.
|
|
21550
|
+
const current = true ? "2.27.3" : null;
|
|
20770
21551
|
if (!current) return;
|
|
20771
21552
|
const cache = readCache();
|
|
20772
21553
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|