codeam-cli 2.20.2 → 2.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.js +641 -144
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -441,7 +441,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
441
441
|
// package.json
|
|
442
442
|
var package_default = {
|
|
443
443
|
name: "codeam-cli",
|
|
444
|
-
version: "2.
|
|
444
|
+
version: "2.21.0",
|
|
445
445
|
description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
|
|
446
446
|
type: "commonjs",
|
|
447
447
|
main: "dist/index.js",
|
|
@@ -1026,8 +1026,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
|
|
|
1026
1026
|
return decodedFile;
|
|
1027
1027
|
};
|
|
1028
1028
|
}
|
|
1029
|
-
function normalizeWindowsPath(
|
|
1030
|
-
return
|
|
1029
|
+
function normalizeWindowsPath(path39) {
|
|
1030
|
+
return path39.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
|
|
1031
1031
|
}
|
|
1032
1032
|
|
|
1033
1033
|
// ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
|
|
@@ -3507,9 +3507,9 @@ async function addSourceContext(frames) {
|
|
|
3507
3507
|
LRU_FILE_CONTENTS_CACHE.reduce();
|
|
3508
3508
|
return frames;
|
|
3509
3509
|
}
|
|
3510
|
-
function getContextLinesFromFile(
|
|
3510
|
+
function getContextLinesFromFile(path39, ranges, output) {
|
|
3511
3511
|
return new Promise((resolve4) => {
|
|
3512
|
-
const stream = (0, import_node_fs.createReadStream)(
|
|
3512
|
+
const stream = (0, import_node_fs.createReadStream)(path39);
|
|
3513
3513
|
const lineReaded = (0, import_node_readline.createInterface)({
|
|
3514
3514
|
input: stream
|
|
3515
3515
|
});
|
|
@@ -3524,7 +3524,7 @@ function getContextLinesFromFile(path37, ranges, output) {
|
|
|
3524
3524
|
let rangeStart = range[0];
|
|
3525
3525
|
let rangeEnd = range[1];
|
|
3526
3526
|
function onStreamError() {
|
|
3527
|
-
LRU_FILE_CONTENTS_FS_READ_FAILED.set(
|
|
3527
|
+
LRU_FILE_CONTENTS_FS_READ_FAILED.set(path39, 1);
|
|
3528
3528
|
lineReaded.close();
|
|
3529
3529
|
lineReaded.removeAllListeners();
|
|
3530
3530
|
destroyStreamAndResolve();
|
|
@@ -3585,8 +3585,8 @@ function clearLineContext(frame) {
|
|
|
3585
3585
|
delete frame.context_line;
|
|
3586
3586
|
delete frame.post_context;
|
|
3587
3587
|
}
|
|
3588
|
-
function shouldSkipContextLinesForFile(
|
|
3589
|
-
return
|
|
3588
|
+
function shouldSkipContextLinesForFile(path39) {
|
|
3589
|
+
return path39.startsWith("node:") || path39.endsWith(".min.js") || path39.endsWith(".min.cjs") || path39.endsWith(".min.mjs") || path39.startsWith("data:");
|
|
3590
3590
|
}
|
|
3591
3591
|
function shouldSkipContextLinesForFrame(frame) {
|
|
3592
3592
|
if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
|
|
@@ -5740,7 +5740,7 @@ function readAnonId() {
|
|
|
5740
5740
|
}
|
|
5741
5741
|
function superProperties() {
|
|
5742
5742
|
return {
|
|
5743
|
-
cliVersion: true ? "2.
|
|
5743
|
+
cliVersion: true ? "2.21.0" : "0.0.0-dev",
|
|
5744
5744
|
nodeVersion: process.version,
|
|
5745
5745
|
platform: process.platform,
|
|
5746
5746
|
arch: process.arch,
|
|
@@ -10610,11 +10610,11 @@ function parseReview(stdout) {
|
|
|
10610
10610
|
for (const line of lines) {
|
|
10611
10611
|
const m = line.match(HUNK_LINE_RE);
|
|
10612
10612
|
if (!m) continue;
|
|
10613
|
-
const [,
|
|
10614
|
-
if (!
|
|
10613
|
+
const [, path39, lineNo, sevToken, message] = m;
|
|
10614
|
+
if (!path39 || !lineNo || !message) continue;
|
|
10615
10615
|
const cleanedMessage = message.trim().replace(/^[*-]\s+/, "");
|
|
10616
10616
|
hunks.push({
|
|
10617
|
-
path:
|
|
10617
|
+
path: path39.trim(),
|
|
10618
10618
|
line: Number(lineNo),
|
|
10619
10619
|
severity: sevToken ? SEVERITY_MAP[sevToken.toLowerCase()] : void 0,
|
|
10620
10620
|
message: cleanedMessage
|
|
@@ -12576,6 +12576,7 @@ var FileWatcherService = class {
|
|
|
12576
12576
|
);
|
|
12577
12577
|
return;
|
|
12578
12578
|
}
|
|
12579
|
+
this.opts.onRepoDirty?.(gitRoot);
|
|
12579
12580
|
const relPathInRepo = path25.relative(gitRoot, absPath);
|
|
12580
12581
|
if (!relPathInRepo || relPathInRepo.startsWith("..")) return;
|
|
12581
12582
|
const repoPath = path25.relative(this.opts.workingDir, gitRoot);
|
|
@@ -12769,9 +12770,493 @@ function _runGit(cwd, args2, opts = {}) {
|
|
|
12769
12770
|
return _gitSeam.run(cwd, args2, opts);
|
|
12770
12771
|
}
|
|
12771
12772
|
|
|
12772
|
-
// src/services/
|
|
12773
|
+
// src/services/turn-files/turn-file-aggregator.ts
|
|
12773
12774
|
var import_crypto2 = require("crypto");
|
|
12774
12775
|
|
|
12776
|
+
// src/services/turn-files/git-changeset.ts
|
|
12777
|
+
var import_child_process8 = require("child_process");
|
|
12778
|
+
var path26 = __toESM(require("path"));
|
|
12779
|
+
async function collectRepoChangeset(opts) {
|
|
12780
|
+
const status2 = await runGit2(opts.repoRoot, ["status", "--porcelain=v1", "-z"]);
|
|
12781
|
+
if (status2 === null) return null;
|
|
12782
|
+
const numstatRaw = await runGit2(opts.repoRoot, [
|
|
12783
|
+
"diff",
|
|
12784
|
+
"--numstat",
|
|
12785
|
+
"-z",
|
|
12786
|
+
"HEAD"
|
|
12787
|
+
]).catch(() => null);
|
|
12788
|
+
const numstat = parseNumstat(numstatRaw ?? "");
|
|
12789
|
+
const entries = [];
|
|
12790
|
+
for (const row of parseStatus(status2)) {
|
|
12791
|
+
const stats = numstat.get(row.filePath) ?? { added: 0, removed: 0 };
|
|
12792
|
+
entries.push({
|
|
12793
|
+
filePath: row.filePath,
|
|
12794
|
+
fileStatus: row.fileStatus,
|
|
12795
|
+
linesAdded: stats.added,
|
|
12796
|
+
linesRemoved: stats.removed,
|
|
12797
|
+
// hunkCount isn't surfaced by --numstat. For the rail / drawer
|
|
12798
|
+
// it's only a count badge; defaulting to 1 when the file has
|
|
12799
|
+
// any non-zero stat is good enough until we wire a follow-up
|
|
12800
|
+
// per-file `git diff --shortstat` if we ever want exact hunks.
|
|
12801
|
+
hunkCount: stats.added + stats.removed > 0 ? 1 : 0,
|
|
12802
|
+
repoPath: opts.repoPath,
|
|
12803
|
+
repoName: opts.repoName
|
|
12804
|
+
});
|
|
12805
|
+
}
|
|
12806
|
+
return entries;
|
|
12807
|
+
}
|
|
12808
|
+
function parseStatus(raw) {
|
|
12809
|
+
const tokens = raw.split("\0");
|
|
12810
|
+
const rows = [];
|
|
12811
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
12812
|
+
const token = tokens[i];
|
|
12813
|
+
if (!token || token.length < 3) continue;
|
|
12814
|
+
const code = token.slice(0, 2);
|
|
12815
|
+
const filePath = token.slice(3);
|
|
12816
|
+
if (!filePath) continue;
|
|
12817
|
+
const indexCode = code[0];
|
|
12818
|
+
const worktreeCode = code[1];
|
|
12819
|
+
if (indexCode === "R" || worktreeCode === "R") {
|
|
12820
|
+
rows.push({ filePath, fileStatus: "renamed" });
|
|
12821
|
+
i += 1;
|
|
12822
|
+
continue;
|
|
12823
|
+
}
|
|
12824
|
+
if (code === "??" || indexCode === "A" || worktreeCode === "A") {
|
|
12825
|
+
rows.push({ filePath, fileStatus: "added" });
|
|
12826
|
+
continue;
|
|
12827
|
+
}
|
|
12828
|
+
if (indexCode === "D" || worktreeCode === "D") {
|
|
12829
|
+
rows.push({ filePath, fileStatus: "deleted" });
|
|
12830
|
+
continue;
|
|
12831
|
+
}
|
|
12832
|
+
rows.push({ filePath, fileStatus: "modified" });
|
|
12833
|
+
}
|
|
12834
|
+
return rows;
|
|
12835
|
+
}
|
|
12836
|
+
function parseNumstat(raw) {
|
|
12837
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
12838
|
+
for (const record of raw.split("\0")) {
|
|
12839
|
+
if (!record) continue;
|
|
12840
|
+
const parts = record.split(" ");
|
|
12841
|
+
if (parts.length < 3) continue;
|
|
12842
|
+
const added = parts[0] === "-" ? 0 : parseInt(parts[0], 10) || 0;
|
|
12843
|
+
const removed = parts[1] === "-" ? 0 : parseInt(parts[1], 10) || 0;
|
|
12844
|
+
const filePath = parts.slice(2).join(" ");
|
|
12845
|
+
if (!filePath) continue;
|
|
12846
|
+
out2.set(filePath, { added, removed });
|
|
12847
|
+
}
|
|
12848
|
+
return out2;
|
|
12849
|
+
}
|
|
12850
|
+
var _runGitImpl2 = {
|
|
12851
|
+
run: defaultRunGit
|
|
12852
|
+
};
|
|
12853
|
+
function runGit2(cwd, args2) {
|
|
12854
|
+
return _runGitImpl2.run(cwd, args2);
|
|
12855
|
+
}
|
|
12856
|
+
function defaultRunGit(cwd, args2) {
|
|
12857
|
+
return new Promise((resolve4) => {
|
|
12858
|
+
let proc;
|
|
12859
|
+
try {
|
|
12860
|
+
proc = (0, import_child_process8.spawn)("git", args2, { cwd, env: process.env });
|
|
12861
|
+
} catch {
|
|
12862
|
+
resolve4(null);
|
|
12863
|
+
return;
|
|
12864
|
+
}
|
|
12865
|
+
let stdout = "";
|
|
12866
|
+
let stderr = "";
|
|
12867
|
+
proc.stdout?.on("data", (c2) => {
|
|
12868
|
+
stdout += c2.toString();
|
|
12869
|
+
});
|
|
12870
|
+
proc.stderr?.on("data", (c2) => {
|
|
12871
|
+
stderr += c2.toString();
|
|
12872
|
+
});
|
|
12873
|
+
proc.on("error", () => resolve4(null));
|
|
12874
|
+
proc.on("close", (code) => {
|
|
12875
|
+
if (code === 0) {
|
|
12876
|
+
resolve4(stdout);
|
|
12877
|
+
} else {
|
|
12878
|
+
log.trace(
|
|
12879
|
+
"turnFiles",
|
|
12880
|
+
`git ${args2.join(" ")} exited ${code} stderr=${stderr.slice(0, 200)}`
|
|
12881
|
+
);
|
|
12882
|
+
resolve4(null);
|
|
12883
|
+
}
|
|
12884
|
+
});
|
|
12885
|
+
});
|
|
12886
|
+
}
|
|
12887
|
+
async function discoverRepos(workingDir, maxDepth = 4) {
|
|
12888
|
+
const fs30 = await import("fs/promises");
|
|
12889
|
+
const out2 = [];
|
|
12890
|
+
await walk(workingDir, 0);
|
|
12891
|
+
return out2;
|
|
12892
|
+
async function walk(dir, depth) {
|
|
12893
|
+
if (depth > maxDepth) return;
|
|
12894
|
+
let entries = [];
|
|
12895
|
+
try {
|
|
12896
|
+
const dirents = await fs30.readdir(dir, { withFileTypes: true });
|
|
12897
|
+
entries = dirents.filter((d3) => !d3.name.startsWith(".") || d3.name === ".git").map((d3) => ({ name: d3.name, isDirectory: d3.isDirectory() }));
|
|
12898
|
+
} catch {
|
|
12899
|
+
return;
|
|
12900
|
+
}
|
|
12901
|
+
const hasGit = entries.some(
|
|
12902
|
+
(e) => e.name === ".git" && (e.isDirectory || true)
|
|
12903
|
+
);
|
|
12904
|
+
if (hasGit) {
|
|
12905
|
+
out2.push({
|
|
12906
|
+
repoRoot: dir,
|
|
12907
|
+
repoPath: path26.relative(workingDir, dir),
|
|
12908
|
+
repoName: path26.basename(dir)
|
|
12909
|
+
});
|
|
12910
|
+
return;
|
|
12911
|
+
}
|
|
12912
|
+
for (const entry of entries) {
|
|
12913
|
+
if (!entry.isDirectory) continue;
|
|
12914
|
+
if (entry.name === "node_modules") continue;
|
|
12915
|
+
if (entry.name === "dist" || entry.name === "build") continue;
|
|
12916
|
+
await walk(path26.join(dir, entry.name), depth + 1);
|
|
12917
|
+
}
|
|
12918
|
+
}
|
|
12919
|
+
}
|
|
12920
|
+
|
|
12921
|
+
// src/services/turn-files/files-outbox.ts
|
|
12922
|
+
var fs22 = __toESM(require("fs/promises"));
|
|
12923
|
+
var path27 = __toESM(require("path"));
|
|
12924
|
+
var import_os6 = require("os");
|
|
12925
|
+
var HOME_OUTBOX_DIR = ".codeam/outbox";
|
|
12926
|
+
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
12927
|
+
var BACKOFF_STEPS_MS = [
|
|
12928
|
+
1e3,
|
|
12929
|
+
// 1 s
|
|
12930
|
+
2e3,
|
|
12931
|
+
// 2 s
|
|
12932
|
+
4e3,
|
|
12933
|
+
// 4 s
|
|
12934
|
+
8e3,
|
|
12935
|
+
// 8 s
|
|
12936
|
+
16e3,
|
|
12937
|
+
// 16 s
|
|
12938
|
+
32e3,
|
|
12939
|
+
// 32 s
|
|
12940
|
+
6e4,
|
|
12941
|
+
// 1 min
|
|
12942
|
+
12e4,
|
|
12943
|
+
// 2 min
|
|
12944
|
+
3e5
|
|
12945
|
+
// 5 min — cap
|
|
12946
|
+
];
|
|
12947
|
+
var FilesOutbox = class {
|
|
12948
|
+
filePath;
|
|
12949
|
+
post;
|
|
12950
|
+
autoSchedule;
|
|
12951
|
+
flushTimer = null;
|
|
12952
|
+
flushing = false;
|
|
12953
|
+
backoffIndex = 0;
|
|
12954
|
+
stopped = false;
|
|
12955
|
+
constructor(opts) {
|
|
12956
|
+
const base = opts.baseDir ?? path27.join(homeDir(), HOME_OUTBOX_DIR);
|
|
12957
|
+
this.filePath = path27.join(base, `${opts.sessionId}.jsonl`);
|
|
12958
|
+
this.post = opts.post;
|
|
12959
|
+
this.autoSchedule = opts.autoSchedule !== false;
|
|
12960
|
+
}
|
|
12961
|
+
/** Persist the entry to disk and trigger a flush. Returns once the
|
|
12962
|
+
* line is durable on disk (not once the POST succeeds). */
|
|
12963
|
+
async enqueue(entry) {
|
|
12964
|
+
await fs22.mkdir(path27.dirname(this.filePath), { recursive: true });
|
|
12965
|
+
await fs22.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
|
|
12966
|
+
this.backoffIndex = 0;
|
|
12967
|
+
if (this.autoSchedule) this.scheduleFlush(0);
|
|
12968
|
+
}
|
|
12969
|
+
/** Stop the scheduler. Idempotent. The on-disk file is left alone
|
|
12970
|
+
* so the next process pickup can flush whatever's pending. */
|
|
12971
|
+
stop() {
|
|
12972
|
+
this.stopped = true;
|
|
12973
|
+
if (this.flushTimer) {
|
|
12974
|
+
clearTimeout(this.flushTimer);
|
|
12975
|
+
this.flushTimer = null;
|
|
12976
|
+
}
|
|
12977
|
+
}
|
|
12978
|
+
/** Visible for tests. Forces a flush attempt right now. */
|
|
12979
|
+
async _flushNow() {
|
|
12980
|
+
return this.flush();
|
|
12981
|
+
}
|
|
12982
|
+
scheduleFlush(delayMs) {
|
|
12983
|
+
if (this.stopped) return;
|
|
12984
|
+
if (this.flushTimer) clearTimeout(this.flushTimer);
|
|
12985
|
+
const jittered = delayMs === 0 ? 0 : applyJitter(delayMs);
|
|
12986
|
+
this.flushTimer = setTimeout(() => {
|
|
12987
|
+
this.flushTimer = null;
|
|
12988
|
+
void this.flush();
|
|
12989
|
+
}, jittered);
|
|
12990
|
+
}
|
|
12991
|
+
async flush() {
|
|
12992
|
+
if (this.stopped) return;
|
|
12993
|
+
if (this.flushing) {
|
|
12994
|
+
return;
|
|
12995
|
+
}
|
|
12996
|
+
this.flushing = true;
|
|
12997
|
+
try {
|
|
12998
|
+
const entries = await this.readAll();
|
|
12999
|
+
if (entries.length === 0) return;
|
|
13000
|
+
const now = Date.now();
|
|
13001
|
+
const fresh = entries.filter((e) => now - e.enqueuedAt <= MAX_AGE_MS);
|
|
13002
|
+
if (fresh.length < entries.length) {
|
|
13003
|
+
log.warn(
|
|
13004
|
+
"turnFiles",
|
|
13005
|
+
`dropping ${entries.length - fresh.length} outbox entries older than ${MAX_AGE_MS / 1e3}s`
|
|
13006
|
+
);
|
|
13007
|
+
}
|
|
13008
|
+
const stillPending = [];
|
|
13009
|
+
let anyFailed = false;
|
|
13010
|
+
for (const entry of fresh) {
|
|
13011
|
+
if (this.stopped) {
|
|
13012
|
+
stillPending.push(entry);
|
|
13013
|
+
continue;
|
|
13014
|
+
}
|
|
13015
|
+
try {
|
|
13016
|
+
const res = await this.post(entry);
|
|
13017
|
+
if (res.ok) continue;
|
|
13018
|
+
if (res.statusCode === 404 || res.statusCode === 410) {
|
|
13019
|
+
log.warn(
|
|
13020
|
+
"turnFiles",
|
|
13021
|
+
`session dead (status=${res.statusCode}); dropping turnId=${entry.turnId.slice(0, 8)}`
|
|
13022
|
+
);
|
|
13023
|
+
continue;
|
|
13024
|
+
}
|
|
13025
|
+
anyFailed = true;
|
|
13026
|
+
stillPending.push(entry);
|
|
13027
|
+
} catch (err) {
|
|
13028
|
+
anyFailed = true;
|
|
13029
|
+
stillPending.push(entry);
|
|
13030
|
+
log.trace(
|
|
13031
|
+
"turnFiles",
|
|
13032
|
+
`outbox post threw for turnId=${entry.turnId.slice(0, 8)}: ${err.message}`
|
|
13033
|
+
);
|
|
13034
|
+
}
|
|
13035
|
+
}
|
|
13036
|
+
await this.rewrite(stillPending);
|
|
13037
|
+
if (anyFailed) {
|
|
13038
|
+
const delay = BACKOFF_STEPS_MS[Math.min(this.backoffIndex, BACKOFF_STEPS_MS.length - 1)];
|
|
13039
|
+
this.backoffIndex = Math.min(
|
|
13040
|
+
this.backoffIndex + 1,
|
|
13041
|
+
BACKOFF_STEPS_MS.length - 1
|
|
13042
|
+
);
|
|
13043
|
+
this.scheduleFlush(delay);
|
|
13044
|
+
} else {
|
|
13045
|
+
this.backoffIndex = 0;
|
|
13046
|
+
}
|
|
13047
|
+
} finally {
|
|
13048
|
+
this.flushing = false;
|
|
13049
|
+
}
|
|
13050
|
+
}
|
|
13051
|
+
async readAll() {
|
|
13052
|
+
let raw = "";
|
|
13053
|
+
try {
|
|
13054
|
+
raw = await fs22.readFile(this.filePath, "utf8");
|
|
13055
|
+
} catch {
|
|
13056
|
+
return [];
|
|
13057
|
+
}
|
|
13058
|
+
const out2 = [];
|
|
13059
|
+
for (const line of raw.split("\n")) {
|
|
13060
|
+
const trimmed = line.trim();
|
|
13061
|
+
if (!trimmed) continue;
|
|
13062
|
+
try {
|
|
13063
|
+
out2.push(JSON.parse(trimmed));
|
|
13064
|
+
} catch {
|
|
13065
|
+
}
|
|
13066
|
+
}
|
|
13067
|
+
return out2;
|
|
13068
|
+
}
|
|
13069
|
+
/**
|
|
13070
|
+
* Atomic compaction: write to `<file>.tmp`, fsync, rename over the
|
|
13071
|
+
* original. A crash between any of these steps leaves either the
|
|
13072
|
+
* original (no progress) or the new file (clean compaction) — never
|
|
13073
|
+
* a torn write.
|
|
13074
|
+
*/
|
|
13075
|
+
async rewrite(entries) {
|
|
13076
|
+
const tmpPath = `${this.filePath}.${process.pid}.tmp`;
|
|
13077
|
+
if (entries.length === 0) {
|
|
13078
|
+
await fs22.unlink(this.filePath).catch(() => void 0);
|
|
13079
|
+
return;
|
|
13080
|
+
}
|
|
13081
|
+
const payload = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
13082
|
+
await fs22.writeFile(tmpPath, payload, "utf8");
|
|
13083
|
+
await fs22.rename(tmpPath, this.filePath);
|
|
13084
|
+
}
|
|
13085
|
+
};
|
|
13086
|
+
function applyJitter(ms) {
|
|
13087
|
+
const factor = 0.8 + Math.random() * 0.4;
|
|
13088
|
+
return Math.round(ms * factor);
|
|
13089
|
+
}
|
|
13090
|
+
function homeDir() {
|
|
13091
|
+
return process.env.HOME ?? process.env.USERPROFILE ?? (0, import_os6.tmpdir)();
|
|
13092
|
+
}
|
|
13093
|
+
|
|
13094
|
+
// src/services/turn-files/turn-file-aggregator.ts
|
|
13095
|
+
var API_BASE6 = resolveApiBaseUrl();
|
|
13096
|
+
var ENDPOINT = "/api/files/batch";
|
|
13097
|
+
var MAX_BATCH_SIZE = 1e3;
|
|
13098
|
+
var TurnFileAggregator = class {
|
|
13099
|
+
constructor(opts) {
|
|
13100
|
+
this.opts = opts;
|
|
13101
|
+
this.apiBase = opts.apiBaseUrl ?? API_BASE6;
|
|
13102
|
+
this.outbox = new FilesOutbox({
|
|
13103
|
+
sessionId: opts.sessionId,
|
|
13104
|
+
baseDir: opts.outboxDir,
|
|
13105
|
+
post: (entry) => this.postEntry(entry),
|
|
13106
|
+
autoSchedule: opts.outboxAutoSchedule
|
|
13107
|
+
});
|
|
13108
|
+
this.discovering = discoverRepos(opts.workingDir).then((repos) => {
|
|
13109
|
+
this.repos = repos;
|
|
13110
|
+
opts.dirtyTracker?.markAllDirty(repos);
|
|
13111
|
+
log.info(
|
|
13112
|
+
"turnFiles",
|
|
13113
|
+
`discovered ${repos.length} repo(s) under ${opts.workingDir}: ${repos.map((r) => r.repoName || "<root>").join(", ")}`
|
|
13114
|
+
);
|
|
13115
|
+
});
|
|
13116
|
+
}
|
|
13117
|
+
opts;
|
|
13118
|
+
apiBase;
|
|
13119
|
+
outbox;
|
|
13120
|
+
repos = [];
|
|
13121
|
+
discovering = null;
|
|
13122
|
+
stopped = false;
|
|
13123
|
+
/** Stop the outbox scheduler. Idempotent. */
|
|
13124
|
+
stop() {
|
|
13125
|
+
this.stopped = true;
|
|
13126
|
+
this.outbox.stop();
|
|
13127
|
+
}
|
|
13128
|
+
/**
|
|
13129
|
+
* Run the discovery + git collection + POST pipeline for one
|
|
13130
|
+
* turn. Errors are swallowed (logged) so an agent never blocks on a
|
|
13131
|
+
* file-changeset failure; the outbox covers the network half.
|
|
13132
|
+
*/
|
|
13133
|
+
async flushTurn() {
|
|
13134
|
+
if (this.stopped) return;
|
|
13135
|
+
try {
|
|
13136
|
+
if (this.discovering) {
|
|
13137
|
+
await this.discovering;
|
|
13138
|
+
this.discovering = null;
|
|
13139
|
+
}
|
|
13140
|
+
if (this.repos.length === 0) {
|
|
13141
|
+
log.trace("turnFiles", "no repos discovered \u2014 skipping flush");
|
|
13142
|
+
return;
|
|
13143
|
+
}
|
|
13144
|
+
const reposToScan = this.opts.dirtyTracker ? this.filterByDirty(this.opts.dirtyTracker) : this.repos;
|
|
13145
|
+
if (reposToScan.length === 0) {
|
|
13146
|
+
log.trace("turnFiles", "dirty set empty \u2014 skipping flush");
|
|
13147
|
+
return;
|
|
13148
|
+
}
|
|
13149
|
+
const files = [];
|
|
13150
|
+
for (const repo of reposToScan) {
|
|
13151
|
+
const entries = await collectRepoChangeset(repo);
|
|
13152
|
+
if (entries) files.push(...entries);
|
|
13153
|
+
}
|
|
13154
|
+
if (files.length === 0) {
|
|
13155
|
+
log.trace("turnFiles", "no changes detected this turn \u2014 skipping POST");
|
|
13156
|
+
return;
|
|
13157
|
+
}
|
|
13158
|
+
const chunks = chunkArray(files, MAX_BATCH_SIZE);
|
|
13159
|
+
for (const chunk of chunks) {
|
|
13160
|
+
const entry = {
|
|
13161
|
+
turnId: (0, import_crypto2.randomUUID)(),
|
|
13162
|
+
sessionId: this.opts.sessionId,
|
|
13163
|
+
pluginId: this.opts.pluginId,
|
|
13164
|
+
enqueuedAt: Date.now(),
|
|
13165
|
+
files: chunk
|
|
13166
|
+
};
|
|
13167
|
+
await this.outbox.enqueue(entry);
|
|
13168
|
+
}
|
|
13169
|
+
} catch (err) {
|
|
13170
|
+
log.warn(
|
|
13171
|
+
"turnFiles",
|
|
13172
|
+
`flushTurn failed: ${err.message ?? String(err)}`
|
|
13173
|
+
);
|
|
13174
|
+
}
|
|
13175
|
+
}
|
|
13176
|
+
/**
|
|
13177
|
+
* Consume the tracker's dirty set and intersect with the
|
|
13178
|
+
* discovered repos so we never spawn git for a path the watcher
|
|
13179
|
+
* marked outside our discovered set (e.g. a sibling repo that
|
|
13180
|
+
* appeared after construction). Unknown roots get dropped on the
|
|
13181
|
+
* floor — they'll re-mark themselves on the next event if they're
|
|
13182
|
+
* inside `workingDir`.
|
|
13183
|
+
*/
|
|
13184
|
+
filterByDirty(tracker) {
|
|
13185
|
+
const dirty = tracker.consume();
|
|
13186
|
+
if (dirty.size === 0) return [];
|
|
13187
|
+
return this.repos.filter((repo) => dirty.has(repo.repoRoot));
|
|
13188
|
+
}
|
|
13189
|
+
async postEntry(entry) {
|
|
13190
|
+
const url = `${this.apiBase}${ENDPOINT}`;
|
|
13191
|
+
const headers = {
|
|
13192
|
+
"Content-Type": "application/json",
|
|
13193
|
+
"X-Codeam-Protocol-Version": PROTOCOL_VERSION,
|
|
13194
|
+
"X-Plugin-Auth-Token": this.opts.pluginAuthToken
|
|
13195
|
+
};
|
|
13196
|
+
const body = JSON.stringify({
|
|
13197
|
+
sessionId: entry.sessionId,
|
|
13198
|
+
pluginId: entry.pluginId,
|
|
13199
|
+
turnId: entry.turnId,
|
|
13200
|
+
files: entry.files
|
|
13201
|
+
});
|
|
13202
|
+
try {
|
|
13203
|
+
const res = await _transport3.post(url, headers, body);
|
|
13204
|
+
return { ok: res.statusCode >= 200 && res.statusCode < 300, statusCode: res.statusCode };
|
|
13205
|
+
} catch (err) {
|
|
13206
|
+
log.trace(
|
|
13207
|
+
"turnFiles",
|
|
13208
|
+
`batch POST threw turnId=${entry.turnId.slice(0, 8)}: ${err.message}`
|
|
13209
|
+
);
|
|
13210
|
+
return { ok: false, statusCode: 0 };
|
|
13211
|
+
}
|
|
13212
|
+
}
|
|
13213
|
+
};
|
|
13214
|
+
function chunkArray(arr, size) {
|
|
13215
|
+
if (arr.length <= size) return [arr];
|
|
13216
|
+
const out2 = [];
|
|
13217
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
13218
|
+
out2.push(arr.slice(i, i + size));
|
|
13219
|
+
}
|
|
13220
|
+
return out2;
|
|
13221
|
+
}
|
|
13222
|
+
|
|
13223
|
+
// src/services/turn-files/repo-dirty-tracker.ts
|
|
13224
|
+
var RepoDirtyTracker = class {
|
|
13225
|
+
dirty = /* @__PURE__ */ new Set();
|
|
13226
|
+
/** Add a repo root to the dirty set. Idempotent. */
|
|
13227
|
+
markDirty(repoRoot) {
|
|
13228
|
+
this.dirty.add(repoRoot);
|
|
13229
|
+
}
|
|
13230
|
+
/** Seed every known repo as dirty — used right after `discoverRepos`
|
|
13231
|
+
* returns so the first end-of-turn flush captures worktree state
|
|
13232
|
+
* that predates the pairing. */
|
|
13233
|
+
markAllDirty(repoRoots) {
|
|
13234
|
+
for (const r of repoRoots) this.dirty.add(r.repoRoot);
|
|
13235
|
+
}
|
|
13236
|
+
/** Snapshot the current dirty set without clearing — useful for
|
|
13237
|
+
* diagnostic logs / tests. */
|
|
13238
|
+
peek() {
|
|
13239
|
+
return new Set(this.dirty);
|
|
13240
|
+
}
|
|
13241
|
+
/** Atomically read AND clear the dirty set. The aggregator calls
|
|
13242
|
+
* this on each `done:true`; subsequent filesystem events
|
|
13243
|
+
* re-populate it for the next turn. */
|
|
13244
|
+
consume() {
|
|
13245
|
+
const snapshot = new Set(this.dirty);
|
|
13246
|
+
this.dirty.clear();
|
|
13247
|
+
return snapshot;
|
|
13248
|
+
}
|
|
13249
|
+
/** True when the set is non-empty. Cheap pre-flight gate so the
|
|
13250
|
+
* aggregator can early-return on chat-only turns without
|
|
13251
|
+
* touching the dirty set. */
|
|
13252
|
+
hasDirty() {
|
|
13253
|
+
return this.dirty.size > 0;
|
|
13254
|
+
}
|
|
13255
|
+
};
|
|
13256
|
+
|
|
13257
|
+
// src/services/streaming-emitter.service.ts
|
|
13258
|
+
var import_crypto3 = require("crypto");
|
|
13259
|
+
|
|
12775
13260
|
// src/services/streaming/transport.ts
|
|
12776
13261
|
var http6 = __toESM(require("http"));
|
|
12777
13262
|
var https6 = __toESM(require("https"));
|
|
@@ -12863,7 +13348,7 @@ function _get(url, headers) {
|
|
|
12863
13348
|
}
|
|
12864
13349
|
|
|
12865
13350
|
// src/services/streaming-emitter.service.ts
|
|
12866
|
-
var
|
|
13351
|
+
var API_BASE7 = resolveApiBaseUrl();
|
|
12867
13352
|
var TICK_MS = 50;
|
|
12868
13353
|
var ANSWER_POLL_MS = 1500;
|
|
12869
13354
|
var SELECTOR_STABLE_MS = 800;
|
|
@@ -12874,7 +13359,7 @@ var TAIL_KEEP_BYTES2 = 1.5 * 1024 * 1024;
|
|
|
12874
13359
|
var StreamingEmitterService = class {
|
|
12875
13360
|
constructor(opts) {
|
|
12876
13361
|
this.opts = opts;
|
|
12877
|
-
this.apiBase = opts.apiBaseUrl ??
|
|
13362
|
+
this.apiBase = opts.apiBaseUrl ?? API_BASE7;
|
|
12878
13363
|
this.headers = {
|
|
12879
13364
|
"Content-Type": "application/json",
|
|
12880
13365
|
"X-Codeam-Protocol-Version": "2.0.0",
|
|
@@ -12993,7 +13478,7 @@ var StreamingEmitterService = class {
|
|
|
12993
13478
|
});
|
|
12994
13479
|
}
|
|
12995
13480
|
this.activeChunk = {
|
|
12996
|
-
chunkId: (0,
|
|
13481
|
+
chunkId: (0, import_crypto3.randomUUID)(),
|
|
12997
13482
|
kind: last.kind,
|
|
12998
13483
|
emittedContent: "",
|
|
12999
13484
|
currentContent: last.content,
|
|
@@ -13061,7 +13546,7 @@ var StreamingEmitterService = class {
|
|
|
13061
13546
|
}
|
|
13062
13547
|
if (this.pendingAnswer) return;
|
|
13063
13548
|
if (now - this.selectorFirstSeenAt < SELECTOR_STABLE_MS) return;
|
|
13064
|
-
const questionId = (0,
|
|
13549
|
+
const questionId = (0, import_crypto3.randomUUID)();
|
|
13065
13550
|
this.pendingAnswer = {
|
|
13066
13551
|
questionId,
|
|
13067
13552
|
options: selector.options.length > 0 ? selector.options : void 0,
|
|
@@ -13224,13 +13709,13 @@ function fetchQuotaUsage(runtime, historySvc) {
|
|
|
13224
13709
|
}
|
|
13225
13710
|
|
|
13226
13711
|
// src/commands/start/keep-alive.ts
|
|
13227
|
-
var
|
|
13712
|
+
var import_child_process9 = require("child_process");
|
|
13228
13713
|
function buildKeepAlive(ctx) {
|
|
13229
13714
|
let timer = null;
|
|
13230
13715
|
async function setIdleTimeout(minutes) {
|
|
13231
13716
|
if (!ctx.inCodespace || !ctx.codespaceName) return;
|
|
13232
13717
|
await new Promise((resolve4) => {
|
|
13233
|
-
const proc = (0,
|
|
13718
|
+
const proc = (0, import_child_process9.spawn)(
|
|
13234
13719
|
"gh",
|
|
13235
13720
|
[
|
|
13236
13721
|
"api",
|
|
@@ -13267,11 +13752,11 @@ function buildKeepAlive(ctx) {
|
|
|
13267
13752
|
}
|
|
13268
13753
|
|
|
13269
13754
|
// src/commands/start/handlers.ts
|
|
13270
|
-
var
|
|
13755
|
+
var fs25 = __toESM(require("fs"));
|
|
13271
13756
|
var os23 = __toESM(require("os"));
|
|
13272
|
-
var
|
|
13273
|
-
var
|
|
13274
|
-
var
|
|
13757
|
+
var path31 = __toESM(require("path"));
|
|
13758
|
+
var import_crypto5 = require("crypto");
|
|
13759
|
+
var import_child_process12 = require("child_process");
|
|
13275
13760
|
|
|
13276
13761
|
// src/lib/payload.ts
|
|
13277
13762
|
var import_zod2 = require("zod");
|
|
@@ -13327,8 +13812,8 @@ function parsePayload2(schema, raw) {
|
|
|
13327
13812
|
}
|
|
13328
13813
|
|
|
13329
13814
|
// src/services/file-ops.service.ts
|
|
13330
|
-
var
|
|
13331
|
-
var
|
|
13815
|
+
var fs23 = __toESM(require("fs/promises"));
|
|
13816
|
+
var path28 = __toESM(require("path"));
|
|
13332
13817
|
var MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
13333
13818
|
var MAX_WALK_DEPTH = 6;
|
|
13334
13819
|
var MAX_VISITED_DIRS = 5e3;
|
|
@@ -13363,12 +13848,12 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
|
|
|
13363
13848
|
"__pycache__"
|
|
13364
13849
|
]);
|
|
13365
13850
|
function isUnder(parent, candidate) {
|
|
13366
|
-
const rel =
|
|
13367
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
13851
|
+
const rel = path28.relative(parent, candidate);
|
|
13852
|
+
return rel === "" || !rel.startsWith("..") && !path28.isAbsolute(rel);
|
|
13368
13853
|
}
|
|
13369
13854
|
async function isExistingFile(absPath) {
|
|
13370
13855
|
try {
|
|
13371
|
-
const stat3 = await
|
|
13856
|
+
const stat3 = await fs23.stat(absPath);
|
|
13372
13857
|
return stat3.isFile();
|
|
13373
13858
|
} catch {
|
|
13374
13859
|
return false;
|
|
@@ -13381,13 +13866,13 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
13381
13866
|
ctx.visited++;
|
|
13382
13867
|
let entries = [];
|
|
13383
13868
|
try {
|
|
13384
|
-
entries = await
|
|
13869
|
+
entries = await fs23.readdir(dir, { withFileTypes: true });
|
|
13385
13870
|
} catch {
|
|
13386
13871
|
return;
|
|
13387
13872
|
}
|
|
13388
13873
|
for (const e of entries) {
|
|
13389
13874
|
if (!e.isFile()) continue;
|
|
13390
|
-
const full =
|
|
13875
|
+
const full = path28.join(dir, e.name);
|
|
13391
13876
|
if (needleVariants.some((needle) => full.endsWith(needle))) {
|
|
13392
13877
|
ctx.matches.push(full);
|
|
13393
13878
|
if (ctx.matches.length >= ctx.cap) return;
|
|
@@ -13397,21 +13882,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
|
13397
13882
|
if (!e.isDirectory()) continue;
|
|
13398
13883
|
if (SUBDIR_IGNORE.has(e.name)) continue;
|
|
13399
13884
|
if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
|
|
13400
|
-
await walkForSuffix(
|
|
13885
|
+
await walkForSuffix(path28.join(dir, e.name), needleVariants, depth + 1, ctx);
|
|
13401
13886
|
if (ctx.matches.length >= ctx.cap) return;
|
|
13402
13887
|
}
|
|
13403
13888
|
}
|
|
13404
13889
|
async function findFile(rawPath) {
|
|
13405
13890
|
const cwd = process.cwd();
|
|
13406
|
-
if (
|
|
13407
|
-
const abs =
|
|
13891
|
+
if (path28.isAbsolute(rawPath)) {
|
|
13892
|
+
const abs = path28.normalize(rawPath);
|
|
13408
13893
|
if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
|
|
13409
13894
|
}
|
|
13410
|
-
const direct =
|
|
13895
|
+
const direct = path28.resolve(cwd, rawPath);
|
|
13411
13896
|
if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
|
|
13412
|
-
const normalized =
|
|
13897
|
+
const normalized = path28.normalize(rawPath).replace(/^[./\\]+/, "");
|
|
13413
13898
|
const needles = [
|
|
13414
|
-
`${
|
|
13899
|
+
`${path28.sep}${normalized}`,
|
|
13415
13900
|
`/${normalized}`
|
|
13416
13901
|
].filter((v, i, a) => a.indexOf(v) === i);
|
|
13417
13902
|
const ctx = { visited: 0, matches: [], cap: 16 };
|
|
@@ -13425,7 +13910,7 @@ async function findWriteTarget(rawPath) {
|
|
|
13425
13910
|
const found = await findFile(rawPath);
|
|
13426
13911
|
if (found) return found;
|
|
13427
13912
|
const cwd = process.cwd();
|
|
13428
|
-
const fallback =
|
|
13913
|
+
const fallback = path28.isAbsolute(rawPath) ? path28.normalize(rawPath) : path28.resolve(cwd, rawPath);
|
|
13429
13914
|
if (!isUnder(cwd, fallback)) return null;
|
|
13430
13915
|
return fallback;
|
|
13431
13916
|
}
|
|
@@ -13442,11 +13927,11 @@ async function readProjectFile(rawPath) {
|
|
|
13442
13927
|
if (!abs) {
|
|
13443
13928
|
return { error: `File not found in the project tree: ${rawPath}` };
|
|
13444
13929
|
}
|
|
13445
|
-
const stat3 = await
|
|
13930
|
+
const stat3 = await fs23.stat(abs);
|
|
13446
13931
|
if (stat3.size > MAX_FILE_BYTES) {
|
|
13447
13932
|
return { error: `File too large (${(stat3.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
|
|
13448
13933
|
}
|
|
13449
|
-
const buf = await
|
|
13934
|
+
const buf = await fs23.readFile(abs);
|
|
13450
13935
|
if (looksBinary(buf)) {
|
|
13451
13936
|
return { error: "Binary file \u2014 refusing to open in a code editor." };
|
|
13452
13937
|
}
|
|
@@ -13465,8 +13950,8 @@ async function writeProjectFile(rawPath, content) {
|
|
|
13465
13950
|
if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
|
|
13466
13951
|
return { error: "Content too large." };
|
|
13467
13952
|
}
|
|
13468
|
-
await
|
|
13469
|
-
await
|
|
13953
|
+
await fs23.mkdir(path28.dirname(abs), { recursive: true });
|
|
13954
|
+
await fs23.writeFile(abs, content, "utf-8");
|
|
13470
13955
|
return { ok: true };
|
|
13471
13956
|
} catch (e) {
|
|
13472
13957
|
const msg = e instanceof Error ? e.message : "Write failed";
|
|
@@ -13475,11 +13960,11 @@ async function writeProjectFile(rawPath, content) {
|
|
|
13475
13960
|
}
|
|
13476
13961
|
|
|
13477
13962
|
// src/services/project-ops.service.ts
|
|
13478
|
-
var
|
|
13963
|
+
var import_child_process10 = require("child_process");
|
|
13479
13964
|
var import_util2 = require("util");
|
|
13480
|
-
var
|
|
13481
|
-
var
|
|
13482
|
-
var execFileP3 = (0, import_util2.promisify)(
|
|
13965
|
+
var fs24 = __toESM(require("fs/promises"));
|
|
13966
|
+
var path29 = __toESM(require("path"));
|
|
13967
|
+
var execFileP3 = (0, import_util2.promisify)(import_child_process10.execFile);
|
|
13483
13968
|
var PROJECT_IGNORE = /* @__PURE__ */ new Set([
|
|
13484
13969
|
"node_modules",
|
|
13485
13970
|
".git",
|
|
@@ -13526,7 +14011,7 @@ async function listProjectFiles(opts = {}) {
|
|
|
13526
14011
|
}
|
|
13527
14012
|
let entries = [];
|
|
13528
14013
|
try {
|
|
13529
|
-
entries = await
|
|
14014
|
+
entries = await fs24.readdir(dir, { withFileTypes: true });
|
|
13530
14015
|
} catch {
|
|
13531
14016
|
return;
|
|
13532
14017
|
}
|
|
@@ -13536,18 +14021,18 @@ async function listProjectFiles(opts = {}) {
|
|
|
13536
14021
|
return;
|
|
13537
14022
|
}
|
|
13538
14023
|
if (PROJECT_IGNORE.has(e.name)) continue;
|
|
13539
|
-
const full =
|
|
14024
|
+
const full = path29.join(dir, e.name);
|
|
13540
14025
|
if (e.isDirectory()) {
|
|
13541
14026
|
if (depth >= 12) continue;
|
|
13542
14027
|
await walk(full, depth + 1);
|
|
13543
14028
|
} else if (e.isFile()) {
|
|
13544
|
-
const rel =
|
|
14029
|
+
const rel = path29.relative(root, full);
|
|
13545
14030
|
if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
|
|
13546
14031
|
continue;
|
|
13547
14032
|
}
|
|
13548
14033
|
let size = 0;
|
|
13549
14034
|
try {
|
|
13550
|
-
const st3 = await
|
|
14035
|
+
const st3 = await fs24.stat(full);
|
|
13551
14036
|
size = st3.size;
|
|
13552
14037
|
} catch {
|
|
13553
14038
|
}
|
|
@@ -13649,8 +14134,8 @@ async function gitStatus(cwd) {
|
|
|
13649
14134
|
let hasMergeInProgress = false;
|
|
13650
14135
|
try {
|
|
13651
14136
|
const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
|
|
13652
|
-
const mergeHead =
|
|
13653
|
-
await
|
|
14137
|
+
const mergeHead = path29.isAbsolute(gitDir) ? path29.join(gitDir, "MERGE_HEAD") : path29.join(root, gitDir, "MERGE_HEAD");
|
|
14138
|
+
await fs24.access(mergeHead);
|
|
13654
14139
|
hasMergeInProgress = true;
|
|
13655
14140
|
} catch {
|
|
13656
14141
|
}
|
|
@@ -13796,7 +14281,7 @@ async function jsSearchFiles(opts, cwd, cap) {
|
|
|
13796
14281
|
}
|
|
13797
14282
|
let content = "";
|
|
13798
14283
|
try {
|
|
13799
|
-
content = await
|
|
14284
|
+
content = await fs24.readFile(path29.join(cwd, f.path), "utf8");
|
|
13800
14285
|
} catch {
|
|
13801
14286
|
continue;
|
|
13802
14287
|
}
|
|
@@ -13819,8 +14304,8 @@ async function jsSearchFiles(opts, cwd, cap) {
|
|
|
13819
14304
|
}
|
|
13820
14305
|
|
|
13821
14306
|
// src/services/terminal-ops.service.ts
|
|
13822
|
-
var
|
|
13823
|
-
var
|
|
14307
|
+
var import_child_process11 = require("child_process");
|
|
14308
|
+
var import_crypto4 = require("crypto");
|
|
13824
14309
|
var import_path3 = __toESM(require("path"));
|
|
13825
14310
|
var MAX_CONCURRENT_SESSIONS = 4;
|
|
13826
14311
|
var nodePtyModule;
|
|
@@ -13941,7 +14426,7 @@ function createPythonSession(id, shell, cwd, env, cols, rows) {
|
|
|
13941
14426
|
}
|
|
13942
14427
|
let child;
|
|
13943
14428
|
try {
|
|
13944
|
-
child = (0,
|
|
14429
|
+
child = (0, import_child_process11.spawn)(python, ["-c", PYTHON_TERMINAL_HELPER, shell], {
|
|
13945
14430
|
cwd,
|
|
13946
14431
|
env: { ...env, COLUMNS: String(cols), LINES: String(rows) },
|
|
13947
14432
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -13996,7 +14481,7 @@ function openTerminal(opts) {
|
|
|
13996
14481
|
};
|
|
13997
14482
|
const cols = Math.max(1, Math.min(opts.cols ?? 80, 500));
|
|
13998
14483
|
const rows = Math.max(1, Math.min(opts.rows ?? 24, 200));
|
|
13999
|
-
const id = (0,
|
|
14484
|
+
const id = (0, import_crypto4.randomUUID)();
|
|
14000
14485
|
const ptyMod = loadNodePty2();
|
|
14001
14486
|
if (ptyMod) {
|
|
14002
14487
|
try {
|
|
@@ -14080,7 +14565,7 @@ var pendingAttachmentFiles = /* @__PURE__ */ new Set();
|
|
|
14080
14565
|
function cleanupAttachmentTempFiles() {
|
|
14081
14566
|
for (const p2 of pendingAttachmentFiles) {
|
|
14082
14567
|
try {
|
|
14083
|
-
|
|
14568
|
+
fs25.unlinkSync(p2);
|
|
14084
14569
|
} catch {
|
|
14085
14570
|
}
|
|
14086
14571
|
}
|
|
@@ -14089,8 +14574,8 @@ function cleanupAttachmentTempFiles() {
|
|
|
14089
14574
|
function saveFilesTemp(files) {
|
|
14090
14575
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
14091
14576
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
14092
|
-
const tmpPath =
|
|
14093
|
-
|
|
14577
|
+
const tmpPath = path31.join(os23.tmpdir(), `codeam-${(0, import_crypto5.randomUUID)()}-${safeName}`);
|
|
14578
|
+
fs25.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
14094
14579
|
pendingAttachmentFiles.add(tmpPath);
|
|
14095
14580
|
return tmpPath;
|
|
14096
14581
|
});
|
|
@@ -14110,7 +14595,7 @@ var startTask = (ctx, _cmd, parsed) => {
|
|
|
14110
14595
|
setTimeout(() => {
|
|
14111
14596
|
for (const p2 of paths) {
|
|
14112
14597
|
try {
|
|
14113
|
-
|
|
14598
|
+
fs25.unlinkSync(p2);
|
|
14114
14599
|
} catch {
|
|
14115
14600
|
}
|
|
14116
14601
|
pendingAttachmentFiles.delete(p2);
|
|
@@ -14223,7 +14708,7 @@ var sessionTerminated = (ctx) => {
|
|
|
14223
14708
|
} catch {
|
|
14224
14709
|
}
|
|
14225
14710
|
try {
|
|
14226
|
-
const proc = (0,
|
|
14711
|
+
const proc = (0, import_child_process12.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
14227
14712
|
detached: true,
|
|
14228
14713
|
stdio: "ignore"
|
|
14229
14714
|
});
|
|
@@ -14245,7 +14730,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
14245
14730
|
}
|
|
14246
14731
|
if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
|
|
14247
14732
|
try {
|
|
14248
|
-
const stopProc = (0,
|
|
14733
|
+
const stopProc = (0, import_child_process12.spawn)(
|
|
14249
14734
|
"bash",
|
|
14250
14735
|
["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
|
|
14251
14736
|
{ detached: true, stdio: "ignore" }
|
|
@@ -14255,7 +14740,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
14255
14740
|
}
|
|
14256
14741
|
}
|
|
14257
14742
|
try {
|
|
14258
|
-
const proc = (0,
|
|
14743
|
+
const proc = (0, import_child_process12.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
|
|
14259
14744
|
detached: true,
|
|
14260
14745
|
stdio: "ignore"
|
|
14261
14746
|
});
|
|
@@ -14266,7 +14751,7 @@ var shutdownSession = async (ctx, cmd) => {
|
|
|
14266
14751
|
ctx.relay.stop();
|
|
14267
14752
|
process.exit(0);
|
|
14268
14753
|
};
|
|
14269
|
-
var
|
|
14754
|
+
var readFile4 = async (ctx, cmd, parsed) => {
|
|
14270
14755
|
if (!parsed.path) {
|
|
14271
14756
|
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path" });
|
|
14272
14757
|
return;
|
|
@@ -14274,7 +14759,7 @@ var readFile3 = async (ctx, cmd, parsed) => {
|
|
|
14274
14759
|
const result = await readProjectFile(parsed.path);
|
|
14275
14760
|
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
14276
14761
|
};
|
|
14277
|
-
var
|
|
14762
|
+
var writeFile3 = async (ctx, cmd, parsed) => {
|
|
14278
14763
|
if (!parsed.path || typeof parsed.content !== "string") {
|
|
14279
14764
|
await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path or content" });
|
|
14280
14765
|
return;
|
|
@@ -14393,8 +14878,8 @@ var handlers = {
|
|
|
14393
14878
|
set_keep_alive: setKeepAlive,
|
|
14394
14879
|
session_terminated: sessionTerminated,
|
|
14395
14880
|
shutdown_session: shutdownSession,
|
|
14396
|
-
read_file:
|
|
14397
|
-
write_file:
|
|
14881
|
+
read_file: readFile4,
|
|
14882
|
+
write_file: writeFile3,
|
|
14398
14883
|
list_files: listFiles,
|
|
14399
14884
|
search_files: searchFilesH,
|
|
14400
14885
|
terminal_open: terminalOpenH,
|
|
@@ -14480,6 +14965,8 @@ async function start(requestedAgent) {
|
|
|
14480
14965
|
historySvc.uploadDelta().catch(() => {
|
|
14481
14966
|
});
|
|
14482
14967
|
}, 400);
|
|
14968
|
+
turnFiles?.flushTurn().catch(() => {
|
|
14969
|
+
});
|
|
14483
14970
|
},
|
|
14484
14971
|
() => {
|
|
14485
14972
|
const prevCount = historySvc.getCurrentMessageCount();
|
|
@@ -14488,11 +14975,20 @@ async function start(requestedAgent) {
|
|
|
14488
14975
|
session.pluginAuthToken,
|
|
14489
14976
|
runtime
|
|
14490
14977
|
);
|
|
14978
|
+
const dirtyTracker = session.pluginAuthToken ? new RepoDirtyTracker() : null;
|
|
14491
14979
|
const fileWatcher = session.pluginAuthToken ? new FileWatcherService({
|
|
14492
14980
|
workingDir: cwd,
|
|
14493
14981
|
sessionId: session.id,
|
|
14494
14982
|
pluginId,
|
|
14495
|
-
pluginAuthToken: session.pluginAuthToken
|
|
14983
|
+
pluginAuthToken: session.pluginAuthToken,
|
|
14984
|
+
onRepoDirty: dirtyTracker ? (repoRoot) => dirtyTracker.markDirty(repoRoot) : void 0
|
|
14985
|
+
}) : null;
|
|
14986
|
+
const turnFiles = session.pluginAuthToken ? new TurnFileAggregator({
|
|
14987
|
+
workingDir: cwd,
|
|
14988
|
+
sessionId: session.id,
|
|
14989
|
+
pluginId,
|
|
14990
|
+
pluginAuthToken: session.pluginAuthToken,
|
|
14991
|
+
dirtyTracker: dirtyTracker ?? void 0
|
|
14496
14992
|
}) : null;
|
|
14497
14993
|
let streamingEmitter = null;
|
|
14498
14994
|
const agent = new AgentService(
|
|
@@ -14510,6 +15006,7 @@ async function start(requestedAgent) {
|
|
|
14510
15006
|
outputSvc.dispose();
|
|
14511
15007
|
relay.stop();
|
|
14512
15008
|
void fileWatcher?.stop();
|
|
15009
|
+
turnFiles?.stop();
|
|
14513
15010
|
void streamingEmitter?.stop();
|
|
14514
15011
|
closeAllTerminals();
|
|
14515
15012
|
cleanupAttachmentTempFiles();
|
|
@@ -14577,7 +15074,7 @@ async function start(requestedAgent) {
|
|
|
14577
15074
|
}
|
|
14578
15075
|
|
|
14579
15076
|
// src/commands/pair.ts
|
|
14580
|
-
var
|
|
15077
|
+
var import_crypto6 = require("crypto");
|
|
14581
15078
|
var import_picocolors3 = __toESM(require("picocolors"));
|
|
14582
15079
|
|
|
14583
15080
|
// src/ui/prompts.ts
|
|
@@ -14641,7 +15138,7 @@ async function pair(args2 = []) {
|
|
|
14641
15138
|
const flagAgent = parseAgentFlag(args2);
|
|
14642
15139
|
const agentId = dryRun ? flagAgent ?? config.preferredAgent ?? "claude" : flagAgent ?? await promptForAgent(config.preferredAgent ?? "claude");
|
|
14643
15140
|
showIntro();
|
|
14644
|
-
const pluginId = (0,
|
|
15141
|
+
const pluginId = (0, import_crypto6.randomUUID)();
|
|
14645
15142
|
capture("pair_started", { agentId, pluginId, dryRun });
|
|
14646
15143
|
const spin = dist_exports.spinner();
|
|
14647
15144
|
spin.start("Requesting pairing code...");
|
|
@@ -14732,10 +15229,10 @@ async function pair(args2 = []) {
|
|
|
14732
15229
|
}
|
|
14733
15230
|
|
|
14734
15231
|
// src/commands/pair-auto.ts
|
|
14735
|
-
var
|
|
15232
|
+
var fs26 = __toESM(require("fs"));
|
|
14736
15233
|
var os24 = __toESM(require("os"));
|
|
14737
|
-
var
|
|
14738
|
-
var
|
|
15234
|
+
var import_crypto7 = require("crypto");
|
|
15235
|
+
var API_BASE8 = resolveApiBaseUrl();
|
|
14739
15236
|
function fail(msg) {
|
|
14740
15237
|
console.error(`
|
|
14741
15238
|
${msg}
|
|
@@ -14750,12 +15247,12 @@ function readTokenFromArgs(args2) {
|
|
|
14750
15247
|
}
|
|
14751
15248
|
const fileFlag = args2.find((a) => a.startsWith("--token-file="));
|
|
14752
15249
|
if (fileFlag) {
|
|
14753
|
-
const
|
|
15250
|
+
const path39 = fileFlag.slice("--token-file=".length);
|
|
14754
15251
|
try {
|
|
14755
|
-
const content =
|
|
14756
|
-
if (content.length === 0) fail(`--token-file ${
|
|
15252
|
+
const content = fs26.readFileSync(path39, "utf8").trim();
|
|
15253
|
+
if (content.length === 0) fail(`--token-file ${path39} is empty`);
|
|
14757
15254
|
try {
|
|
14758
|
-
|
|
15255
|
+
fs26.unlinkSync(path39);
|
|
14759
15256
|
} catch {
|
|
14760
15257
|
}
|
|
14761
15258
|
return content;
|
|
@@ -14775,7 +15272,7 @@ function networkError(msg, cause) {
|
|
|
14775
15272
|
return err;
|
|
14776
15273
|
}
|
|
14777
15274
|
async function claimOnce(token, pluginId) {
|
|
14778
|
-
const url = `${
|
|
15275
|
+
const url = `${API_BASE8}/api/pairing/claim-auto-token`;
|
|
14779
15276
|
const body = {
|
|
14780
15277
|
token,
|
|
14781
15278
|
pluginId,
|
|
@@ -14839,7 +15336,7 @@ async function claim(token, pluginId) {
|
|
|
14839
15336
|
}
|
|
14840
15337
|
async function pairAuto(args2) {
|
|
14841
15338
|
const token = readTokenFromArgs(args2);
|
|
14842
|
-
const pluginId = (0,
|
|
15339
|
+
const pluginId = (0, import_crypto7.randomUUID)();
|
|
14843
15340
|
capture("pair_auto_started", { pluginId });
|
|
14844
15341
|
console.log(" Claiming pairing token\u2026");
|
|
14845
15342
|
const claimed = await claim(token, pluginId);
|
|
@@ -14965,7 +15462,7 @@ function status() {
|
|
|
14965
15462
|
|
|
14966
15463
|
// src/commands/logout.ts
|
|
14967
15464
|
var import_picocolors6 = __toESM(require("picocolors"));
|
|
14968
|
-
var
|
|
15465
|
+
var API_BASE9 = resolveApiBaseUrl();
|
|
14969
15466
|
async function notifyBackendOffline() {
|
|
14970
15467
|
const cfg = loadCliConfig();
|
|
14971
15468
|
const pluginIds = /* @__PURE__ */ new Set([
|
|
@@ -14977,7 +15474,7 @@ async function notifyBackendOffline() {
|
|
|
14977
15474
|
try {
|
|
14978
15475
|
await Promise.all(
|
|
14979
15476
|
Array.from(pluginIds).map(
|
|
14980
|
-
(pluginId) => _postJson(`${
|
|
15477
|
+
(pluginId) => _postJson(`${API_BASE9}/api/plugin/heartbeat`, {
|
|
14981
15478
|
pluginId,
|
|
14982
15479
|
online: false
|
|
14983
15480
|
}).catch((err) => {
|
|
@@ -15005,11 +15502,11 @@ async function logout() {
|
|
|
15005
15502
|
var import_picocolors9 = __toESM(require("picocolors"));
|
|
15006
15503
|
|
|
15007
15504
|
// src/services/providers/github-codespaces.ts
|
|
15008
|
-
var
|
|
15505
|
+
var import_child_process13 = require("child_process");
|
|
15009
15506
|
var import_util3 = require("util");
|
|
15010
15507
|
var import_picocolors7 = __toESM(require("picocolors"));
|
|
15011
|
-
var
|
|
15012
|
-
var execFileP4 = (0, import_util3.promisify)(
|
|
15508
|
+
var path32 = __toESM(require("path"));
|
|
15509
|
+
var execFileP4 = (0, import_util3.promisify)(import_child_process13.execFile);
|
|
15013
15510
|
var MAX_BUFFER = 8 * 1024 * 1024;
|
|
15014
15511
|
function resetStdinForChild() {
|
|
15015
15512
|
if (process.stdin.isTTY) {
|
|
@@ -15053,7 +15550,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15053
15550
|
if (!isAuthed) {
|
|
15054
15551
|
resetStdinForChild();
|
|
15055
15552
|
await new Promise((resolve4, reject) => {
|
|
15056
|
-
const proc = (0,
|
|
15553
|
+
const proc = (0, import_child_process13.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
|
|
15057
15554
|
stdio: "inherit"
|
|
15058
15555
|
});
|
|
15059
15556
|
proc.on("exit", (code) => {
|
|
@@ -15087,7 +15584,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15087
15584
|
wt(noteLines.join("\n"), "One more permission needed");
|
|
15088
15585
|
resetStdinForChild();
|
|
15089
15586
|
const refreshCode = await new Promise((resolve4, reject) => {
|
|
15090
|
-
const proc = (0,
|
|
15587
|
+
const proc = (0, import_child_process13.spawn)(
|
|
15091
15588
|
"gh",
|
|
15092
15589
|
["auth", "refresh", "-h", "github.com", "-s", "codespace"],
|
|
15093
15590
|
{ stdio: "inherit" }
|
|
@@ -15237,7 +15734,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15237
15734
|
O2.step(`Installing gh via ${installCmd.describe}\u2026`);
|
|
15238
15735
|
resetStdinForChild();
|
|
15239
15736
|
const ok = await new Promise((resolve4) => {
|
|
15240
|
-
const proc = (0,
|
|
15737
|
+
const proc = (0, import_child_process13.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
|
|
15241
15738
|
proc.on("exit", (code) => resolve4(code === 0));
|
|
15242
15739
|
proc.on("error", () => resolve4(false));
|
|
15243
15740
|
});
|
|
@@ -15264,7 +15761,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15264
15761
|
);
|
|
15265
15762
|
resetStdinForChild();
|
|
15266
15763
|
await new Promise((resolve4, reject) => {
|
|
15267
|
-
const proc = (0,
|
|
15764
|
+
const proc = (0, import_child_process13.spawn)(
|
|
15268
15765
|
"gh",
|
|
15269
15766
|
["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
|
|
15270
15767
|
{ stdio: "inherit" }
|
|
@@ -15442,7 +15939,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15442
15939
|
async streamCommand(workspaceId, command2) {
|
|
15443
15940
|
resetStdinForChild();
|
|
15444
15941
|
return new Promise((resolve4, reject) => {
|
|
15445
|
-
const proc = (0,
|
|
15942
|
+
const proc = (0, import_child_process13.spawn)(
|
|
15446
15943
|
"gh",
|
|
15447
15944
|
["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
|
|
15448
15945
|
{ stdio: "inherit" }
|
|
@@ -15469,11 +15966,11 @@ var GitHubCodespacesProvider = class {
|
|
|
15469
15966
|
`mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
|
|
15470
15967
|
];
|
|
15471
15968
|
await new Promise((resolve4, reject) => {
|
|
15472
|
-
const tar = (0,
|
|
15969
|
+
const tar = (0, import_child_process13.spawn)("tar", tarArgs, {
|
|
15473
15970
|
stdio: ["ignore", "pipe", "pipe"],
|
|
15474
15971
|
env: tarEnv
|
|
15475
15972
|
});
|
|
15476
|
-
const ssh = (0,
|
|
15973
|
+
const ssh = (0, import_child_process13.spawn)("gh", sshArgs, {
|
|
15477
15974
|
stdio: [tar.stdout, "pipe", "pipe"]
|
|
15478
15975
|
});
|
|
15479
15976
|
let tarErr = "";
|
|
@@ -15497,7 +15994,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15497
15994
|
});
|
|
15498
15995
|
}
|
|
15499
15996
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
15500
|
-
const remoteDir =
|
|
15997
|
+
const remoteDir = path32.posix.dirname(remotePath);
|
|
15501
15998
|
const parts = [
|
|
15502
15999
|
`mkdir -p ${shellQuote(remoteDir)}`,
|
|
15503
16000
|
`cat > ${shellQuote(remotePath)}`
|
|
@@ -15507,7 +16004,7 @@ var GitHubCodespacesProvider = class {
|
|
|
15507
16004
|
}
|
|
15508
16005
|
const cmd = parts.join(" && ");
|
|
15509
16006
|
await new Promise((resolve4, reject) => {
|
|
15510
|
-
const proc = (0,
|
|
16007
|
+
const proc = (0, import_child_process13.spawn)(
|
|
15511
16008
|
"gh",
|
|
15512
16009
|
["codespace", "ssh", "-c", workspaceId, "--", cmd],
|
|
15513
16010
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -15565,11 +16062,11 @@ function shellQuote(s) {
|
|
|
15565
16062
|
}
|
|
15566
16063
|
|
|
15567
16064
|
// src/services/providers/gitpod.ts
|
|
15568
|
-
var
|
|
16065
|
+
var import_child_process14 = require("child_process");
|
|
15569
16066
|
var import_util4 = require("util");
|
|
15570
|
-
var
|
|
16067
|
+
var path33 = __toESM(require("path"));
|
|
15571
16068
|
var import_picocolors8 = __toESM(require("picocolors"));
|
|
15572
|
-
var execFileP5 = (0, import_util4.promisify)(
|
|
16069
|
+
var execFileP5 = (0, import_util4.promisify)(import_child_process14.execFile);
|
|
15573
16070
|
var MAX_BUFFER2 = 8 * 1024 * 1024;
|
|
15574
16071
|
function resetStdinForChild2() {
|
|
15575
16072
|
if (process.stdin.isTTY) {
|
|
@@ -15609,7 +16106,7 @@ var GitpodProvider = class {
|
|
|
15609
16106
|
);
|
|
15610
16107
|
resetStdinForChild2();
|
|
15611
16108
|
await new Promise((resolve4, reject) => {
|
|
15612
|
-
const proc = (0,
|
|
16109
|
+
const proc = (0, import_child_process14.spawn)("gitpod", ["login"], { stdio: "inherit" });
|
|
15613
16110
|
proc.on("exit", (code) => {
|
|
15614
16111
|
if (code === 0) resolve4();
|
|
15615
16112
|
else reject(new Error("gitpod login failed."));
|
|
@@ -15761,7 +16258,7 @@ var GitpodProvider = class {
|
|
|
15761
16258
|
async streamCommand(workspaceId, command2) {
|
|
15762
16259
|
resetStdinForChild2();
|
|
15763
16260
|
return new Promise((resolve4, reject) => {
|
|
15764
|
-
const proc = (0,
|
|
16261
|
+
const proc = (0, import_child_process14.spawn)(
|
|
15765
16262
|
"gitpod",
|
|
15766
16263
|
["workspace", "ssh", workspaceId, "--", "-tt", command2],
|
|
15767
16264
|
{ stdio: "inherit" }
|
|
@@ -15781,11 +16278,11 @@ var GitpodProvider = class {
|
|
|
15781
16278
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
15782
16279
|
const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
|
|
15783
16280
|
await new Promise((resolve4, reject) => {
|
|
15784
|
-
const tar = (0,
|
|
16281
|
+
const tar = (0, import_child_process14.spawn)("tar", tarArgs, {
|
|
15785
16282
|
stdio: ["ignore", "pipe", "pipe"],
|
|
15786
16283
|
env: tarEnv
|
|
15787
16284
|
});
|
|
15788
|
-
const ssh = (0,
|
|
16285
|
+
const ssh = (0, import_child_process14.spawn)(
|
|
15789
16286
|
"gitpod",
|
|
15790
16287
|
["workspace", "ssh", workspaceId, "--", remoteCmd],
|
|
15791
16288
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -15807,7 +16304,7 @@ var GitpodProvider = class {
|
|
|
15807
16304
|
});
|
|
15808
16305
|
}
|
|
15809
16306
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
15810
|
-
const remoteDir =
|
|
16307
|
+
const remoteDir = path33.posix.dirname(remotePath);
|
|
15811
16308
|
const parts = [
|
|
15812
16309
|
`mkdir -p ${shellQuote2(remoteDir)}`,
|
|
15813
16310
|
`cat > ${shellQuote2(remotePath)}`
|
|
@@ -15817,7 +16314,7 @@ var GitpodProvider = class {
|
|
|
15817
16314
|
}
|
|
15818
16315
|
const cmd = parts.join(" && ");
|
|
15819
16316
|
await new Promise((resolve4, reject) => {
|
|
15820
|
-
const proc = (0,
|
|
16317
|
+
const proc = (0, import_child_process14.spawn)(
|
|
15821
16318
|
"gitpod",
|
|
15822
16319
|
["workspace", "ssh", workspaceId, "--", cmd],
|
|
15823
16320
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -15841,10 +16338,10 @@ function shellQuote2(s) {
|
|
|
15841
16338
|
}
|
|
15842
16339
|
|
|
15843
16340
|
// src/services/providers/gitlab-workspaces.ts
|
|
15844
|
-
var
|
|
16341
|
+
var import_child_process15 = require("child_process");
|
|
15845
16342
|
var import_util5 = require("util");
|
|
15846
|
-
var
|
|
15847
|
-
var execFileP6 = (0, import_util5.promisify)(
|
|
16343
|
+
var path34 = __toESM(require("path"));
|
|
16344
|
+
var execFileP6 = (0, import_util5.promisify)(import_child_process15.execFile);
|
|
15848
16345
|
var MAX_BUFFER3 = 8 * 1024 * 1024;
|
|
15849
16346
|
var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
|
|
15850
16347
|
function resetStdinForChild3() {
|
|
@@ -15886,7 +16383,7 @@ var GitLabWorkspacesProvider = class {
|
|
|
15886
16383
|
);
|
|
15887
16384
|
resetStdinForChild3();
|
|
15888
16385
|
await new Promise((resolve4, reject) => {
|
|
15889
|
-
const proc = (0,
|
|
16386
|
+
const proc = (0, import_child_process15.spawn)(
|
|
15890
16387
|
"glab",
|
|
15891
16388
|
["auth", "login", "--scopes", "api,read_user,read_repository"],
|
|
15892
16389
|
{ stdio: "inherit" }
|
|
@@ -16058,7 +16555,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
16058
16555
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
16059
16556
|
resetStdinForChild3();
|
|
16060
16557
|
return new Promise((resolve4, reject) => {
|
|
16061
|
-
const proc = (0,
|
|
16558
|
+
const proc = (0, import_child_process15.spawn)(
|
|
16062
16559
|
"ssh",
|
|
16063
16560
|
["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
|
|
16064
16561
|
{ stdio: "inherit" }
|
|
@@ -16079,8 +16576,8 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
16079
16576
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
16080
16577
|
const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
|
|
16081
16578
|
await new Promise((resolve4, reject) => {
|
|
16082
|
-
const tar = (0,
|
|
16083
|
-
const ssh = (0,
|
|
16579
|
+
const tar = (0, import_child_process15.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
16580
|
+
const ssh = (0, import_child_process15.spawn)(
|
|
16084
16581
|
"ssh",
|
|
16085
16582
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, remoteCmd],
|
|
16086
16583
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -16103,14 +16600,14 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
16103
16600
|
}
|
|
16104
16601
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
16105
16602
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
16106
|
-
const remoteDir =
|
|
16603
|
+
const remoteDir = path34.posix.dirname(remotePath);
|
|
16107
16604
|
const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
|
|
16108
16605
|
if (options.mode != null) {
|
|
16109
16606
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
|
|
16110
16607
|
}
|
|
16111
16608
|
const cmd = parts.join(" && ");
|
|
16112
16609
|
await new Promise((resolve4, reject) => {
|
|
16113
|
-
const proc = (0,
|
|
16610
|
+
const proc = (0, import_child_process15.spawn)(
|
|
16114
16611
|
"ssh",
|
|
16115
16612
|
["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
|
|
16116
16613
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -16169,10 +16666,10 @@ function shellQuote3(s) {
|
|
|
16169
16666
|
}
|
|
16170
16667
|
|
|
16171
16668
|
// src/services/providers/railway.ts
|
|
16172
|
-
var
|
|
16669
|
+
var import_child_process16 = require("child_process");
|
|
16173
16670
|
var import_util6 = require("util");
|
|
16174
|
-
var
|
|
16175
|
-
var execFileP7 = (0, import_util6.promisify)(
|
|
16671
|
+
var path35 = __toESM(require("path"));
|
|
16672
|
+
var execFileP7 = (0, import_util6.promisify)(import_child_process16.execFile);
|
|
16176
16673
|
var MAX_BUFFER4 = 8 * 1024 * 1024;
|
|
16177
16674
|
function resetStdinForChild4() {
|
|
16178
16675
|
if (process.stdin.isTTY) {
|
|
@@ -16213,7 +16710,7 @@ var RailwayProvider = class {
|
|
|
16213
16710
|
);
|
|
16214
16711
|
resetStdinForChild4();
|
|
16215
16712
|
await new Promise((resolve4, reject) => {
|
|
16216
|
-
const proc = (0,
|
|
16713
|
+
const proc = (0, import_child_process16.spawn)("railway", ["login"], { stdio: "inherit" });
|
|
16217
16714
|
proc.on("exit", (code) => {
|
|
16218
16715
|
if (code === 0) resolve4();
|
|
16219
16716
|
else reject(new Error("railway login failed."));
|
|
@@ -16356,7 +16853,7 @@ var RailwayProvider = class {
|
|
|
16356
16853
|
}
|
|
16357
16854
|
resetStdinForChild4();
|
|
16358
16855
|
return new Promise((resolve4, reject) => {
|
|
16359
|
-
const proc = (0,
|
|
16856
|
+
const proc = (0, import_child_process16.spawn)(
|
|
16360
16857
|
"railway",
|
|
16361
16858
|
["shell", "--project", projectId, "--service", serviceId, "--command", command2],
|
|
16362
16859
|
{ stdio: "inherit" }
|
|
@@ -16380,8 +16877,8 @@ var RailwayProvider = class {
|
|
|
16380
16877
|
const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
|
|
16381
16878
|
const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
|
|
16382
16879
|
await new Promise((resolve4, reject) => {
|
|
16383
|
-
const tar = (0,
|
|
16384
|
-
const sh = (0,
|
|
16880
|
+
const tar = (0, import_child_process16.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
|
|
16881
|
+
const sh = (0, import_child_process16.spawn)(
|
|
16385
16882
|
"railway",
|
|
16386
16883
|
["shell", "--project", projectId, "--service", serviceId, "--command", remoteCmd],
|
|
16387
16884
|
{ stdio: [tar.stdout, "pipe", "pipe"] }
|
|
@@ -16407,14 +16904,14 @@ var RailwayProvider = class {
|
|
|
16407
16904
|
if (!projectId || !serviceId) {
|
|
16408
16905
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
16409
16906
|
}
|
|
16410
|
-
const remoteDir =
|
|
16907
|
+
const remoteDir = path35.posix.dirname(remotePath);
|
|
16411
16908
|
const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
|
|
16412
16909
|
if (options.mode != null) {
|
|
16413
16910
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
|
|
16414
16911
|
}
|
|
16415
16912
|
const cmd = parts.join(" && ");
|
|
16416
16913
|
await new Promise((resolve4, reject) => {
|
|
16417
|
-
const proc = (0,
|
|
16914
|
+
const proc = (0, import_child_process16.spawn)(
|
|
16418
16915
|
"railway",
|
|
16419
16916
|
["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
|
|
16420
16917
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -16948,8 +17445,8 @@ async function stopWorkspaceFromLocal(target) {
|
|
|
16948
17445
|
|
|
16949
17446
|
// src/commands/link.ts
|
|
16950
17447
|
var import_node_crypto4 = require("crypto");
|
|
16951
|
-
var
|
|
16952
|
-
var
|
|
17448
|
+
var fs27 = __toESM(require("fs"));
|
|
17449
|
+
var path36 = __toESM(require("path"));
|
|
16953
17450
|
var import_chokidar = __toESM(require("chokidar"));
|
|
16954
17451
|
var import_picocolors11 = __toESM(require("picocolors"));
|
|
16955
17452
|
function buildLinkContext(agentId) {
|
|
@@ -16987,7 +17484,7 @@ function parseLinkArgs(args2) {
|
|
|
16987
17484
|
if (apiKeyFileArg) {
|
|
16988
17485
|
const filePath = apiKeyFileArg.slice("--api-key-file=".length);
|
|
16989
17486
|
try {
|
|
16990
|
-
apiKey =
|
|
17487
|
+
apiKey = fs27.readFileSync(path36.resolve(filePath), "utf8").trim();
|
|
16991
17488
|
} catch (err) {
|
|
16992
17489
|
throw new Error(`Could not read --api-key-file ${filePath}: ${err.message}`);
|
|
16993
17490
|
}
|
|
@@ -17081,7 +17578,7 @@ async function link(args2 = []) {
|
|
|
17081
17578
|
return;
|
|
17082
17579
|
}
|
|
17083
17580
|
if (parsed.tokenFile) {
|
|
17084
|
-
const credential =
|
|
17581
|
+
const credential = fs27.readFileSync(path36.resolve(parsed.tokenFile), "utf8").trim();
|
|
17085
17582
|
if (!credential) {
|
|
17086
17583
|
showError(`--token-file ${parsed.tokenFile} is empty.`);
|
|
17087
17584
|
process.exit(1);
|
|
@@ -17270,8 +17767,8 @@ async function linkDryRunPreflight(ctx) {
|
|
|
17270
17767
|
var import_node_dns = require("dns");
|
|
17271
17768
|
var import_node_util4 = require("util");
|
|
17272
17769
|
var import_node_crypto5 = require("crypto");
|
|
17273
|
-
var
|
|
17274
|
-
var
|
|
17770
|
+
var fs28 = __toESM(require("fs"));
|
|
17771
|
+
var path37 = __toESM(require("path"));
|
|
17275
17772
|
var import_picocolors12 = __toESM(require("picocolors"));
|
|
17276
17773
|
var dnsResolveP = (0, import_node_util4.promisify)(import_node_dns.resolve);
|
|
17277
17774
|
async function checkDns(apiBase) {
|
|
@@ -17327,13 +17824,13 @@ async function checkHealth(apiBase) {
|
|
|
17327
17824
|
}
|
|
17328
17825
|
}
|
|
17329
17826
|
function checkConfigDir() {
|
|
17330
|
-
const dir =
|
|
17827
|
+
const dir = path37.join(require("os").homedir(), ".codeam");
|
|
17331
17828
|
try {
|
|
17332
|
-
|
|
17333
|
-
const probe =
|
|
17334
|
-
|
|
17335
|
-
const read =
|
|
17336
|
-
|
|
17829
|
+
fs28.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
17830
|
+
const probe = path37.join(dir, ".doctor-probe");
|
|
17831
|
+
fs28.writeFileSync(probe, "ok", { mode: 384 });
|
|
17832
|
+
const read = fs28.readFileSync(probe, "utf8");
|
|
17833
|
+
fs28.unlinkSync(probe);
|
|
17337
17834
|
if (read !== "ok") throw new Error("write/read round-trip mismatch");
|
|
17338
17835
|
return {
|
|
17339
17836
|
id: "config-dir",
|
|
@@ -17397,7 +17894,7 @@ function checkNodePty() {
|
|
|
17397
17894
|
detail: "not required on this platform"
|
|
17398
17895
|
};
|
|
17399
17896
|
}
|
|
17400
|
-
const vendoredPath =
|
|
17897
|
+
const vendoredPath = path37.join(__dirname, "vendor", "node-pty");
|
|
17401
17898
|
for (const target of [vendoredPath, "node-pty"]) {
|
|
17402
17899
|
try {
|
|
17403
17900
|
require(target);
|
|
@@ -17439,7 +17936,7 @@ function checkChokidar() {
|
|
|
17439
17936
|
}
|
|
17440
17937
|
async function doctor(args2 = []) {
|
|
17441
17938
|
const json = args2.includes("--json");
|
|
17442
|
-
const cliVersion = true ? "2.
|
|
17939
|
+
const cliVersion = true ? "2.21.0" : "0.0.0-dev";
|
|
17443
17940
|
const apiBase = resolveApiBaseUrl();
|
|
17444
17941
|
const diagnosticId = (0, import_node_crypto5.randomUUID)();
|
|
17445
17942
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -17638,7 +18135,7 @@ async function completion(args2) {
|
|
|
17638
18135
|
// src/commands/version.ts
|
|
17639
18136
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
17640
18137
|
function version2() {
|
|
17641
|
-
const v = true ? "2.
|
|
18138
|
+
const v = true ? "2.21.0" : "unknown";
|
|
17642
18139
|
console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
|
|
17643
18140
|
}
|
|
17644
18141
|
|
|
@@ -17766,9 +18263,9 @@ function tryShowSubcommandHelp(cmd, args2) {
|
|
|
17766
18263
|
var _subcommandHelpKeys = Object.keys(HELPS);
|
|
17767
18264
|
|
|
17768
18265
|
// src/lib/updateNotifier.ts
|
|
17769
|
-
var
|
|
18266
|
+
var fs29 = __toESM(require("fs"));
|
|
17770
18267
|
var os25 = __toESM(require("os"));
|
|
17771
|
-
var
|
|
18268
|
+
var path38 = __toESM(require("path"));
|
|
17772
18269
|
var https7 = __toESM(require("https"));
|
|
17773
18270
|
var import_picocolors16 = __toESM(require("picocolors"));
|
|
17774
18271
|
var PKG_NAME = "codeam-cli";
|
|
@@ -17776,12 +18273,12 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
|
17776
18273
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
17777
18274
|
var REQUEST_TIMEOUT_MS = 1500;
|
|
17778
18275
|
function cachePath() {
|
|
17779
|
-
const dir =
|
|
17780
|
-
return
|
|
18276
|
+
const dir = path38.join(os25.homedir(), ".codeam");
|
|
18277
|
+
return path38.join(dir, "update-check.json");
|
|
17781
18278
|
}
|
|
17782
18279
|
function readCache() {
|
|
17783
18280
|
try {
|
|
17784
|
-
const raw =
|
|
18281
|
+
const raw = fs29.readFileSync(cachePath(), "utf8");
|
|
17785
18282
|
const parsed = JSON.parse(raw);
|
|
17786
18283
|
if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
|
|
17787
18284
|
return parsed;
|
|
@@ -17792,10 +18289,10 @@ function readCache() {
|
|
|
17792
18289
|
function writeCache(cache) {
|
|
17793
18290
|
try {
|
|
17794
18291
|
const file = cachePath();
|
|
17795
|
-
|
|
18292
|
+
fs29.mkdirSync(path38.dirname(file), { recursive: true });
|
|
17796
18293
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
17797
|
-
|
|
17798
|
-
|
|
18294
|
+
fs29.writeFileSync(tmp, JSON.stringify(cache));
|
|
18295
|
+
fs29.renameSync(tmp, file);
|
|
17799
18296
|
} catch {
|
|
17800
18297
|
}
|
|
17801
18298
|
}
|
|
@@ -17866,7 +18363,7 @@ function checkForUpdates() {
|
|
|
17866
18363
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
17867
18364
|
if (process.env.CI) return;
|
|
17868
18365
|
if (!process.stdout.isTTY) return;
|
|
17869
|
-
const current = true ? "2.
|
|
18366
|
+
const current = true ? "2.21.0" : null;
|
|
17870
18367
|
if (!current) return;
|
|
17871
18368
|
const cache = readCache();
|
|
17872
18369
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|