@sideboard-ai/core 0.1.238 → 0.1.240
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/dist/{agents-6NAPLJU2.js → agents-CDQCQVWU.js} +1 -1
- package/dist/{agents-HJU23LIF.js → agents-PBRIHUYM.js} +1 -1
- package/dist/{chunk-WRUBICEF.js → chunk-3Q4PVZ3U.js} +14 -9
- package/dist/{chunk-E6UJGLPM.js → chunk-JJSHAVJB.js} +337 -144
- package/dist/{chunk-K6UTJOE6.js → chunk-QXYWLO6W.js} +16 -10
- package/dist/{chunk-32DG3DA2.js → chunk-WPNPIDJS.js} +361 -146
- package/dist/index.cjs +539 -300
- package/dist/index.d.cts +40 -5
- package/dist/index.d.ts +40 -5
- package/dist/index.js +25 -3
- package/dist/mcp/run-stdio.cjs +506 -301
- package/dist/mcp/run-stdio.js +3 -3
- package/dist/{orchestrator-HPMDB2SG.js → orchestrator-A2Q5O4W2.js} +2 -2
- package/dist/{orchestrator-AAYKR4AI.js → orchestrator-MIIKXPHH.js} +2 -2
- package/package.json +1 -1
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
resolveQuotaFallbackAgent,
|
|
25
25
|
stripBrightsyNdjsonNoise,
|
|
26
26
|
toolDescription
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-QXYWLO6W.js";
|
|
28
28
|
import {
|
|
29
29
|
SLACK_REPLY_FORMATTING,
|
|
30
30
|
addWorkspace,
|
|
@@ -745,13 +745,14 @@ function shouldAutoArchiveOnPrMerge(opts) {
|
|
|
745
745
|
import {
|
|
746
746
|
copyFileSync,
|
|
747
747
|
existsSync as existsSync3,
|
|
748
|
-
mkdirSync as
|
|
748
|
+
mkdirSync as mkdirSync4,
|
|
749
749
|
readdirSync,
|
|
750
|
-
readFileSync as
|
|
750
|
+
readFileSync as readFileSync3,
|
|
751
|
+
realpathSync
|
|
751
752
|
} from "fs";
|
|
752
753
|
import { execFileSync } from "child_process";
|
|
753
754
|
import { createServer } from "net";
|
|
754
|
-
import { basename, dirname as
|
|
755
|
+
import { basename, dirname as dirname3, join as join5, resolve, sep } from "path";
|
|
755
756
|
import { execa as execa2 } from "execa";
|
|
756
757
|
import { createInterface as createInterface2 } from "readline";
|
|
757
758
|
|
|
@@ -1664,16 +1665,73 @@ async function requestReview(threadRef, send) {
|
|
|
1664
1665
|
return { tab: started, from };
|
|
1665
1666
|
}
|
|
1666
1667
|
|
|
1668
|
+
// src/hook/worktree-run-ports.ts
|
|
1669
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1670
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
1671
|
+
var RUN_PORTS_REL = ".context/.sideboard/run-ports.json";
|
|
1672
|
+
function worktreeRunPortsPath(worktreePath) {
|
|
1673
|
+
return join4(worktreePath, RUN_PORTS_REL);
|
|
1674
|
+
}
|
|
1675
|
+
function isPort(value) {
|
|
1676
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 && value <= 65535;
|
|
1677
|
+
}
|
|
1678
|
+
function parseFile(raw) {
|
|
1679
|
+
try {
|
|
1680
|
+
const parsed = JSON.parse(raw);
|
|
1681
|
+
if (!parsed || typeof parsed !== "object") return {};
|
|
1682
|
+
const scripts = parsed.scripts;
|
|
1683
|
+
if (!scripts || typeof scripts !== "object") return {};
|
|
1684
|
+
const next = {};
|
|
1685
|
+
for (const [name, ports] of Object.entries(scripts)) {
|
|
1686
|
+
if (!name || !Array.isArray(ports)) continue;
|
|
1687
|
+
const cleaned = ports.filter(isPort);
|
|
1688
|
+
if (cleaned.length) next[name] = cleaned;
|
|
1689
|
+
}
|
|
1690
|
+
return { scripts: next };
|
|
1691
|
+
} catch {
|
|
1692
|
+
return {};
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
function loadWorktreeRunPorts(worktreePath, scriptName) {
|
|
1696
|
+
if (!worktreePath.trim() || !scriptName.trim()) return null;
|
|
1697
|
+
try {
|
|
1698
|
+
const raw = readFileSync2(worktreeRunPortsPath(worktreePath), "utf8");
|
|
1699
|
+
const ports = parseFile(raw).scripts?.[scriptName];
|
|
1700
|
+
return ports?.length ? ports : null;
|
|
1701
|
+
} catch {
|
|
1702
|
+
return null;
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
function saveWorktreeRunPorts(worktreePath, scriptName, ports) {
|
|
1706
|
+
if (!worktreePath.trim() || !scriptName.trim()) return;
|
|
1707
|
+
const cleaned = ports.filter(isPort);
|
|
1708
|
+
if (!cleaned.length) return;
|
|
1709
|
+
const path = worktreeRunPortsPath(worktreePath);
|
|
1710
|
+
let current = {};
|
|
1711
|
+
try {
|
|
1712
|
+
current = parseFile(readFileSync2(path, "utf8"));
|
|
1713
|
+
} catch {
|
|
1714
|
+
current = {};
|
|
1715
|
+
}
|
|
1716
|
+
const scripts = { ...current.scripts ?? {} };
|
|
1717
|
+
scripts[scriptName] = cleaned;
|
|
1718
|
+
mkdirSync3(dirname2(path), { recursive: true });
|
|
1719
|
+
writeFileSync2(path, `${JSON.stringify({ scripts }, null, 2)}
|
|
1720
|
+
`, "utf8");
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1667
1723
|
// src/hook/conductor.ts
|
|
1668
1724
|
var PORT_RANGE_SIZE = 10;
|
|
1725
|
+
var RECLAIM_RETRY_DELAY_MS = 40;
|
|
1726
|
+
var RECLAIM_TRIES = 8;
|
|
1669
1727
|
function matchSimpleGlob(pattern, name) {
|
|
1670
1728
|
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
1671
1729
|
return new RegExp(`^${escaped}$`).test(name);
|
|
1672
1730
|
}
|
|
1673
1731
|
function readWorktreeInclude(repoPath) {
|
|
1674
|
-
const path =
|
|
1732
|
+
const path = join5(repoPath, ".worktreeinclude");
|
|
1675
1733
|
if (!existsSync3(path)) return [];
|
|
1676
|
-
return
|
|
1734
|
+
return readFileSync3(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
1677
1735
|
}
|
|
1678
1736
|
function resolveFilesToCopy(repoPath) {
|
|
1679
1737
|
const fromInclude = readWorktreeInclude(repoPath);
|
|
@@ -1712,10 +1770,10 @@ function copyConfiguredFiles(repoPath, worktreePath) {
|
|
|
1712
1770
|
const patterns = resolveFilesToCopy(repoPath);
|
|
1713
1771
|
const copied = [];
|
|
1714
1772
|
for (const rel of patterns) {
|
|
1715
|
-
const src =
|
|
1773
|
+
const src = join5(repoPath, rel);
|
|
1716
1774
|
if (!existsSync3(src)) continue;
|
|
1717
|
-
const dest =
|
|
1718
|
-
|
|
1775
|
+
const dest = join5(worktreePath, rel);
|
|
1776
|
+
mkdirSync4(dirname3(dest), { recursive: true });
|
|
1719
1777
|
copyFileSync(src, dest);
|
|
1720
1778
|
copied.push(rel);
|
|
1721
1779
|
}
|
|
@@ -1792,6 +1850,57 @@ function killableListenerPids(raw, selfPid = process.pid) {
|
|
|
1792
1850
|
}
|
|
1793
1851
|
return pids;
|
|
1794
1852
|
}
|
|
1853
|
+
function listUnixListenerPids(port, selfPid) {
|
|
1854
|
+
try {
|
|
1855
|
+
const raw = execFileSync("lsof", [`-tiTCP:${port}`, "-sTCP:LISTEN"], {
|
|
1856
|
+
encoding: "utf8",
|
|
1857
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1858
|
+
timeout: 2e3
|
|
1859
|
+
});
|
|
1860
|
+
return killableListenerPids(raw, selfPid);
|
|
1861
|
+
} catch {
|
|
1862
|
+
return [];
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
function listenerCwd(pid) {
|
|
1866
|
+
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
1867
|
+
try {
|
|
1868
|
+
const raw = execFileSync("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], {
|
|
1869
|
+
encoding: "utf8",
|
|
1870
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1871
|
+
timeout: 2e3
|
|
1872
|
+
});
|
|
1873
|
+
for (const line of raw.split("\n")) {
|
|
1874
|
+
if (line.startsWith("n") && line.length > 1) return line.slice(1);
|
|
1875
|
+
}
|
|
1876
|
+
} catch {
|
|
1877
|
+
}
|
|
1878
|
+
return null;
|
|
1879
|
+
}
|
|
1880
|
+
function realpathOrResolve(path) {
|
|
1881
|
+
try {
|
|
1882
|
+
return realpathSync(path);
|
|
1883
|
+
} catch {
|
|
1884
|
+
return resolve(path);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
function isOtherWorktreeListenerCwd(cwd, worktreePath) {
|
|
1888
|
+
const wt = realpathOrResolve(worktreePath);
|
|
1889
|
+
const c = realpathOrResolve(cwd);
|
|
1890
|
+
if (c === wt || c.startsWith(wt + sep)) return false;
|
|
1891
|
+
const parent = dirname3(wt);
|
|
1892
|
+
const thisName = basename(wt);
|
|
1893
|
+
if (!thisName || c === parent) return false;
|
|
1894
|
+
if (!c.startsWith(parent + sep)) return false;
|
|
1895
|
+
const first = c.slice(parent.length + 1).split(sep)[0];
|
|
1896
|
+
return Boolean(first) && first !== thisName;
|
|
1897
|
+
}
|
|
1898
|
+
function signalPid(pid, signal) {
|
|
1899
|
+
try {
|
|
1900
|
+
process.kill(pid, signal);
|
|
1901
|
+
} catch {
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1795
1904
|
function killListenersOnPorts(ports) {
|
|
1796
1905
|
const selfPid = process.pid;
|
|
1797
1906
|
for (const port of ports) {
|
|
@@ -1809,26 +1918,32 @@ function killListenersOnPorts(ports) {
|
|
|
1809
1918
|
);
|
|
1810
1919
|
continue;
|
|
1811
1920
|
}
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
raw = execFileSync("lsof", [`-tiTCP:${port}`, "-sTCP:LISTEN"], {
|
|
1815
|
-
encoding: "utf8",
|
|
1816
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
1817
|
-
timeout: 2e3
|
|
1818
|
-
});
|
|
1819
|
-
} catch {
|
|
1820
|
-
continue;
|
|
1821
|
-
}
|
|
1822
|
-
for (const pid of killableListenerPids(raw, selfPid)) {
|
|
1823
|
-
try {
|
|
1824
|
-
process.kill(pid, "SIGTERM");
|
|
1825
|
-
} catch {
|
|
1826
|
-
}
|
|
1921
|
+
for (const pid of listUnixListenerPids(port, selfPid)) {
|
|
1922
|
+
signalPid(pid, "SIGTERM");
|
|
1827
1923
|
}
|
|
1828
1924
|
} catch {
|
|
1829
1925
|
}
|
|
1830
1926
|
}
|
|
1831
1927
|
}
|
|
1928
|
+
function killStaleWorktreeListeners(ports, worktreePath, signal = "SIGTERM") {
|
|
1929
|
+
if (!worktreePath.trim()) {
|
|
1930
|
+
killListenersOnPorts(ports);
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
if (process.platform === "win32") {
|
|
1934
|
+
killListenersOnPorts(ports);
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
const selfPid = process.pid;
|
|
1938
|
+
for (const port of ports) {
|
|
1939
|
+
if (!Number.isFinite(port) || port <= 0) continue;
|
|
1940
|
+
for (const pid of listUnixListenerPids(port, selfPid)) {
|
|
1941
|
+
const cwd = listenerCwd(pid);
|
|
1942
|
+
if (cwd && isOtherWorktreeListenerCwd(cwd, worktreePath)) continue;
|
|
1943
|
+
signalPid(pid, signal);
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1832
1947
|
function killScriptTree(child, ports = []) {
|
|
1833
1948
|
const pid = child.pid;
|
|
1834
1949
|
if (pid) {
|
|
@@ -1990,7 +2105,7 @@ function getRunMode(worktreePath, repoPath) {
|
|
|
1990
2105
|
return loadWorkspaceSettings(worktreePath, repoPath)?.runMode ?? "concurrent";
|
|
1991
2106
|
}
|
|
1992
2107
|
async function reservePort() {
|
|
1993
|
-
return new Promise((
|
|
2108
|
+
return new Promise((resolve2, reject) => {
|
|
1994
2109
|
const server = createServer();
|
|
1995
2110
|
let released = false;
|
|
1996
2111
|
const release = () => new Promise((res) => {
|
|
@@ -2007,7 +2122,7 @@ async function reservePort() {
|
|
|
2007
2122
|
void release().then(() => reject(new Error("Failed to allocate port")));
|
|
2008
2123
|
return;
|
|
2009
2124
|
}
|
|
2010
|
-
|
|
2125
|
+
resolve2({ port: addr.port, release });
|
|
2011
2126
|
});
|
|
2012
2127
|
server.on("error", reject);
|
|
2013
2128
|
});
|
|
@@ -2018,7 +2133,8 @@ async function allocatePort() {
|
|
|
2018
2133
|
return held.port;
|
|
2019
2134
|
}
|
|
2020
2135
|
async function tryReservePort(port) {
|
|
2021
|
-
|
|
2136
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) return null;
|
|
2137
|
+
return new Promise((resolve2) => {
|
|
2022
2138
|
const server = createServer();
|
|
2023
2139
|
let released = false;
|
|
2024
2140
|
const release = () => new Promise((res) => {
|
|
@@ -2029,23 +2145,63 @@ async function tryReservePort(port) {
|
|
|
2029
2145
|
released = true;
|
|
2030
2146
|
server.close(() => res());
|
|
2031
2147
|
});
|
|
2032
|
-
server.once("error", () =>
|
|
2148
|
+
server.once("error", () => {
|
|
2149
|
+
try {
|
|
2150
|
+
server.close();
|
|
2151
|
+
} catch {
|
|
2152
|
+
}
|
|
2153
|
+
resolve2(null);
|
|
2154
|
+
});
|
|
2033
2155
|
server.listen(port, "127.0.0.1", () => {
|
|
2034
|
-
|
|
2156
|
+
resolve2({ port, release });
|
|
2035
2157
|
});
|
|
2036
2158
|
});
|
|
2037
2159
|
}
|
|
2038
|
-
async function
|
|
2039
|
-
const held = [
|
|
2040
|
-
const
|
|
2160
|
+
async function tryReservePorts(ports) {
|
|
2161
|
+
const held = [];
|
|
2162
|
+
for (const port of ports) {
|
|
2163
|
+
const next = await tryReservePort(port);
|
|
2164
|
+
if (!next) {
|
|
2165
|
+
await Promise.all(held.map((h) => h.release()));
|
|
2166
|
+
return null;
|
|
2167
|
+
}
|
|
2168
|
+
held.push(next);
|
|
2169
|
+
}
|
|
2170
|
+
return held;
|
|
2171
|
+
}
|
|
2172
|
+
function normalizePreferredPorts(ports, size) {
|
|
2173
|
+
if (!ports?.length) return [];
|
|
2174
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2175
|
+
const out = [];
|
|
2176
|
+
for (const p of ports) {
|
|
2177
|
+
if (!Number.isInteger(p) || p <= 0 || p > 65535 || seen.has(p)) continue;
|
|
2178
|
+
seen.add(p);
|
|
2179
|
+
out.push(p);
|
|
2180
|
+
if (out.length >= size) break;
|
|
2181
|
+
}
|
|
2182
|
+
return out;
|
|
2183
|
+
}
|
|
2184
|
+
function delay(ms) {
|
|
2185
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2186
|
+
}
|
|
2187
|
+
async function fillPortRange(held, size) {
|
|
2188
|
+
if (held.length >= size) return held;
|
|
2189
|
+
const base = held[0]?.port;
|
|
2190
|
+
const taken = new Set(held.map((h) => h.port));
|
|
2041
2191
|
try {
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
const
|
|
2192
|
+
let offset = 0;
|
|
2193
|
+
while (held.length < size) {
|
|
2194
|
+
const candidate = base != null ? base + offset : null;
|
|
2195
|
+
offset += 1;
|
|
2196
|
+
if (candidate != null && taken.has(candidate)) continue;
|
|
2197
|
+
const next = candidate != null ? await tryReservePort(candidate) : null;
|
|
2045
2198
|
if (next) {
|
|
2046
2199
|
held.push(next);
|
|
2200
|
+
taken.add(next.port);
|
|
2047
2201
|
} else {
|
|
2048
|
-
|
|
2202
|
+
const ephemeral = await reservePort();
|
|
2203
|
+
held.push(ephemeral);
|
|
2204
|
+
taken.add(ephemeral.port);
|
|
2049
2205
|
}
|
|
2050
2206
|
}
|
|
2051
2207
|
return held;
|
|
@@ -2054,6 +2210,49 @@ async function reservePortRange(size = PORT_RANGE_SIZE) {
|
|
|
2054
2210
|
throw err;
|
|
2055
2211
|
}
|
|
2056
2212
|
}
|
|
2213
|
+
async function tryReserveAvailablePorts(ports) {
|
|
2214
|
+
const held = [];
|
|
2215
|
+
for (const port of ports) {
|
|
2216
|
+
const next = await tryReservePort(port);
|
|
2217
|
+
if (next) held.push(next);
|
|
2218
|
+
}
|
|
2219
|
+
return held;
|
|
2220
|
+
}
|
|
2221
|
+
async function reclaimPreferredPorts(ports, worktreePath) {
|
|
2222
|
+
const primary = ports[0];
|
|
2223
|
+
if (primary == null) return null;
|
|
2224
|
+
const takePrimary = async () => {
|
|
2225
|
+
const held2 = await tryReserveAvailablePorts(ports);
|
|
2226
|
+
if (held2.some((h) => h.port === primary)) return held2;
|
|
2227
|
+
await Promise.all(held2.map((h) => h.release()));
|
|
2228
|
+
return null;
|
|
2229
|
+
};
|
|
2230
|
+
let held = await takePrimary();
|
|
2231
|
+
if (held) return held;
|
|
2232
|
+
if (worktreePath) {
|
|
2233
|
+
killStaleWorktreeListeners(ports, worktreePath, "SIGTERM");
|
|
2234
|
+
} else {
|
|
2235
|
+
killListenersOnPorts(ports);
|
|
2236
|
+
}
|
|
2237
|
+
for (let i = 0; i < RECLAIM_TRIES; i++) {
|
|
2238
|
+
await delay(RECLAIM_RETRY_DELAY_MS);
|
|
2239
|
+
held = await takePrimary();
|
|
2240
|
+
if (held) return held;
|
|
2241
|
+
if (i === 2 && worktreePath) {
|
|
2242
|
+
killStaleWorktreeListeners(ports, worktreePath, "SIGKILL");
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
return null;
|
|
2246
|
+
}
|
|
2247
|
+
async function reservePortRange(size = PORT_RANGE_SIZE, opts) {
|
|
2248
|
+
const preferred = normalizePreferredPorts(opts?.preferred, size);
|
|
2249
|
+
if (preferred.length) {
|
|
2250
|
+
const reused = await reclaimPreferredPorts(preferred, opts?.worktreePath);
|
|
2251
|
+
if (reused) return fillPortRange(reused, size);
|
|
2252
|
+
}
|
|
2253
|
+
const held = [await reservePort()];
|
|
2254
|
+
return fillPortRange(held, size);
|
|
2255
|
+
}
|
|
2057
2256
|
async function allocatePortRange(size = PORT_RANGE_SIZE) {
|
|
2058
2257
|
const held = await reservePortRange(size);
|
|
2059
2258
|
const ports = held.map((h) => h.port);
|
|
@@ -2063,8 +2262,13 @@ async function allocatePortRange(size = PORT_RANGE_SIZE) {
|
|
|
2063
2262
|
async function startDevServer(repoPath, worktreePath, onLine, opts) {
|
|
2064
2263
|
const script = getRunScript(worktreePath, repoPath, opts?.scriptName);
|
|
2065
2264
|
if (!script) return null;
|
|
2066
|
-
const
|
|
2265
|
+
const preferred = loadWorktreeRunPorts(worktreePath, script.name) ?? opts?.preferredPorts;
|
|
2266
|
+
const held = await reservePortRange(PORT_RANGE_SIZE, {
|
|
2267
|
+
preferred: preferred ?? void 0,
|
|
2268
|
+
worktreePath
|
|
2269
|
+
});
|
|
2067
2270
|
const ports = held.map((h) => h.port);
|
|
2271
|
+
saveWorktreeRunPorts(worktreePath, script.name, ports);
|
|
2068
2272
|
try {
|
|
2069
2273
|
const handle = await spawnWorkspaceScript(script.command, {
|
|
2070
2274
|
worktreePath,
|
|
@@ -2090,8 +2294,8 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
|
|
|
2090
2294
|
|
|
2091
2295
|
// src/store/setup-log.ts
|
|
2092
2296
|
import { createHash } from "crypto";
|
|
2093
|
-
import { existsSync as existsSync4, readFileSync as
|
|
2094
|
-
import { join as
|
|
2297
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
2298
|
+
import { join as join6 } from "path";
|
|
2095
2299
|
var EMPTY = {
|
|
2096
2300
|
output: "",
|
|
2097
2301
|
running: false,
|
|
@@ -2133,7 +2337,7 @@ function materialize(mem) {
|
|
|
2133
2337
|
};
|
|
2134
2338
|
}
|
|
2135
2339
|
function setupLogPath(threadId) {
|
|
2136
|
-
return
|
|
2340
|
+
return join6(threadsDir(), `${threadId}.setup.log.json`);
|
|
2137
2341
|
}
|
|
2138
2342
|
function setupLogKeyForWorktree(worktreePath) {
|
|
2139
2343
|
const norm = normalizeWorktreePath(worktreePath);
|
|
@@ -2160,7 +2364,7 @@ function persistNow(threadId) {
|
|
|
2160
2364
|
const mem = memory.get(threadId);
|
|
2161
2365
|
if (!mem) return;
|
|
2162
2366
|
try {
|
|
2163
|
-
|
|
2367
|
+
writeFileSync3(
|
|
2164
2368
|
setupLogPath(threadId),
|
|
2165
2369
|
`${JSON.stringify(materialize(mem))}
|
|
2166
2370
|
`,
|
|
@@ -2183,7 +2387,7 @@ function loadFromDisk(threadId) {
|
|
|
2183
2387
|
const path = setupLogPath(threadId);
|
|
2184
2388
|
if (!existsSync4(path)) return null;
|
|
2185
2389
|
try {
|
|
2186
|
-
const raw = JSON.parse(
|
|
2390
|
+
const raw = JSON.parse(readFileSync4(path, "utf8"));
|
|
2187
2391
|
return {
|
|
2188
2392
|
output: typeof raw.output === "string" ? raw.output : "",
|
|
2189
2393
|
running: Boolean(raw.running),
|
|
@@ -2242,8 +2446,8 @@ function finishSetupLog(threadId, exitCode, source) {
|
|
|
2242
2446
|
|
|
2243
2447
|
// src/store/run-log.ts
|
|
2244
2448
|
import { createHash as createHash2 } from "crypto";
|
|
2245
|
-
import { existsSync as existsSync5, readFileSync as
|
|
2246
|
-
import { join as
|
|
2449
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
2450
|
+
import { join as join7 } from "path";
|
|
2247
2451
|
var EMPTY2 = {
|
|
2248
2452
|
output: "",
|
|
2249
2453
|
running: false,
|
|
@@ -2282,7 +2486,7 @@ function safeScriptToken(scriptName) {
|
|
|
2282
2486
|
return token || "dev";
|
|
2283
2487
|
}
|
|
2284
2488
|
function runLogPath(key) {
|
|
2285
|
-
return
|
|
2489
|
+
return join7(threadsDir(), `${key}.run.log.json`);
|
|
2286
2490
|
}
|
|
2287
2491
|
function runLogKeyForWorktree(worktreePath, scriptName) {
|
|
2288
2492
|
const norm = normalizeWorktreePath(worktreePath);
|
|
@@ -2311,7 +2515,7 @@ function persistNow2(key) {
|
|
|
2311
2515
|
const mem = memory2.get(key);
|
|
2312
2516
|
if (!mem) return;
|
|
2313
2517
|
try {
|
|
2314
|
-
|
|
2518
|
+
writeFileSync4(runLogPath(key), `${JSON.stringify(materialize2(mem))}
|
|
2315
2519
|
`, "utf8");
|
|
2316
2520
|
} catch {
|
|
2317
2521
|
}
|
|
@@ -2330,7 +2534,7 @@ function loadFromDisk2(key) {
|
|
|
2330
2534
|
const path = runLogPath(key);
|
|
2331
2535
|
if (!existsSync5(path)) return null;
|
|
2332
2536
|
try {
|
|
2333
|
-
const raw = JSON.parse(
|
|
2537
|
+
const raw = JSON.parse(readFileSync5(path, "utf8"));
|
|
2334
2538
|
return {
|
|
2335
2539
|
output: typeof raw.output === "string" ? raw.output : "",
|
|
2336
2540
|
running: Boolean(raw.running),
|
|
@@ -2391,7 +2595,7 @@ function flushRunLog(key) {
|
|
|
2391
2595
|
|
|
2392
2596
|
// src/git/orphan-cleanup.ts
|
|
2393
2597
|
import { existsSync as existsSync6, readdirSync as readdirSync2, statSync } from "fs";
|
|
2394
|
-
import { join as
|
|
2598
|
+
import { join as join8 } from "path";
|
|
2395
2599
|
function isSideboardWorktreePath(path) {
|
|
2396
2600
|
return path.includes("/.sideboard/worktrees/") || path.includes("/sideboard/workspaces/");
|
|
2397
2601
|
}
|
|
@@ -2438,9 +2642,9 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
2438
2642
|
if (existsSync6(root)) {
|
|
2439
2643
|
for (const entry of readdirSync2(root, { withFileTypes: true })) {
|
|
2440
2644
|
if (!entry.isDirectory()) continue;
|
|
2441
|
-
const path =
|
|
2645
|
+
const path = join8(root, entry.name).replace(/\/$/, "");
|
|
2442
2646
|
if (known.has(path) || seen.has(path)) continue;
|
|
2443
|
-
if (!existsSync6(
|
|
2647
|
+
if (!existsSync6(join8(path, ".git"))) continue;
|
|
2444
2648
|
seen.add(path);
|
|
2445
2649
|
let mtimeMs = 0;
|
|
2446
2650
|
try {
|
|
@@ -2654,7 +2858,7 @@ async function applyThreadIntoMain(thread, opts) {
|
|
|
2654
2858
|
|
|
2655
2859
|
// src/git/clone-repo.ts
|
|
2656
2860
|
import { existsSync as existsSync7 } from "fs";
|
|
2657
|
-
import { basename as basename2, join as
|
|
2861
|
+
import { basename as basename2, join as join9 } from "path";
|
|
2658
2862
|
import { execa as execa4 } from "execa";
|
|
2659
2863
|
async function cloneRepoIntoSideboard(opts) {
|
|
2660
2864
|
const url = opts.url.trim();
|
|
@@ -2665,7 +2869,7 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
2665
2869
|
name = leaf || "repo";
|
|
2666
2870
|
}
|
|
2667
2871
|
name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
|
|
2668
|
-
const dest =
|
|
2872
|
+
const dest = join9(sideboardReposDir(), name);
|
|
2669
2873
|
if (existsSync7(dest)) {
|
|
2670
2874
|
const repoPath2 = await resolveRepoRoot(dest);
|
|
2671
2875
|
const workspace2 = await ensureWorkspace(repoPath2);
|
|
@@ -2691,13 +2895,13 @@ function isAdoptableRunScriptRequest(req) {
|
|
|
2691
2895
|
}
|
|
2692
2896
|
|
|
2693
2897
|
// src/store/desktop-host.ts
|
|
2694
|
-
import { readFileSync as
|
|
2695
|
-
import { join as
|
|
2898
|
+
import { readFileSync as readFileSync6, unlinkSync, writeFileSync as writeFileSync5 } from "fs";
|
|
2899
|
+
import { join as join10 } from "path";
|
|
2696
2900
|
function desktopHostPidPath() {
|
|
2697
|
-
return
|
|
2901
|
+
return join10(appDataDir(), "desktop-host.pid");
|
|
2698
2902
|
}
|
|
2699
2903
|
function claimDesktopHost(pid = process.pid) {
|
|
2700
|
-
|
|
2904
|
+
writeFileSync5(desktopHostPidPath(), `${pid}
|
|
2701
2905
|
`, "utf8");
|
|
2702
2906
|
}
|
|
2703
2907
|
function releaseDesktopHost(pid = process.pid) {
|
|
@@ -2709,7 +2913,7 @@ function releaseDesktopHost(pid = process.pid) {
|
|
|
2709
2913
|
}
|
|
2710
2914
|
function readDesktopHostPid() {
|
|
2711
2915
|
try {
|
|
2712
|
-
const pid = Number.parseInt(
|
|
2916
|
+
const pid = Number.parseInt(readFileSync6(desktopHostPidPath(), "utf8").trim(), 10);
|
|
2713
2917
|
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
2714
2918
|
return pid;
|
|
2715
2919
|
} catch {
|
|
@@ -2782,12 +2986,12 @@ function notifyParentOfChildHalt(child, status, send) {
|
|
|
2782
2986
|
import {
|
|
2783
2987
|
appendFileSync as appendFileSync2,
|
|
2784
2988
|
existsSync as existsSync8,
|
|
2785
|
-
mkdirSync as
|
|
2989
|
+
mkdirSync as mkdirSync5,
|
|
2786
2990
|
readdirSync as readdirSync3,
|
|
2787
|
-
readFileSync as
|
|
2788
|
-
writeFileSync as
|
|
2991
|
+
readFileSync as readFileSync7,
|
|
2992
|
+
writeFileSync as writeFileSync6
|
|
2789
2993
|
} from "fs";
|
|
2790
|
-
import { dirname as
|
|
2994
|
+
import { dirname as dirname4, join as join11 } from "path";
|
|
2791
2995
|
|
|
2792
2996
|
// src/mcp/wait-for-turn.ts
|
|
2793
2997
|
var MCP_WAIT_FOR_TURN_MAX_MS = 45e3;
|
|
@@ -2863,11 +3067,11 @@ function jobTreeAlive(pid) {
|
|
|
2863
3067
|
}
|
|
2864
3068
|
function readIntFile(file) {
|
|
2865
3069
|
if (!existsSync8(file)) return null;
|
|
2866
|
-
const n = Number.parseInt(
|
|
3070
|
+
const n = Number.parseInt(readFileSync7(file, "utf8").trim(), 10);
|
|
2867
3071
|
return Number.isInteger(n) ? n : null;
|
|
2868
3072
|
}
|
|
2869
3073
|
function jobDir(root, id, legacy = false) {
|
|
2870
|
-
return
|
|
3074
|
+
return join11(root, legacy ? LEGACY_DETACHED_JOBS_DIR : DETACHED_JOBS_DIR, id);
|
|
2871
3075
|
}
|
|
2872
3076
|
function resolveJobDir(root, id) {
|
|
2873
3077
|
const modern = jobDir(root, id, false);
|
|
@@ -2877,7 +3081,7 @@ function resolveJobDir(root, id) {
|
|
|
2877
3081
|
return modern;
|
|
2878
3082
|
}
|
|
2879
3083
|
function listJobIdsIn(root, rel) {
|
|
2880
|
-
const dir =
|
|
3084
|
+
const dir = join11(root, rel);
|
|
2881
3085
|
if (!existsSync8(dir)) return [];
|
|
2882
3086
|
return readdirSync3(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && JOB_ID_RE.test(e.name)).map((e) => e.name);
|
|
2883
3087
|
}
|
|
@@ -2891,7 +3095,7 @@ function listRunningDetachedJobs(worktreePath) {
|
|
|
2891
3095
|
const running = [];
|
|
2892
3096
|
for (const id of ids) {
|
|
2893
3097
|
const dir = resolveJobDir(root, id);
|
|
2894
|
-
const pid = readIntFile(
|
|
3098
|
+
const pid = readIntFile(join11(dir, "pid"));
|
|
2895
3099
|
if (jobTreeAlive(pid)) running.push(id);
|
|
2896
3100
|
}
|
|
2897
3101
|
return running.sort();
|
|
@@ -2975,7 +3179,7 @@ function planJobContinue(opts) {
|
|
|
2975
3179
|
}
|
|
2976
3180
|
function tailProgress(logFile, maxLines = 12) {
|
|
2977
3181
|
if (!existsSync8(logFile)) return "(no log yet)";
|
|
2978
|
-
const lines =
|
|
3182
|
+
const lines = readFileSync7(logFile, "utf8").split("\n");
|
|
2979
3183
|
return lines.slice(-maxLines).join("\n");
|
|
2980
3184
|
}
|
|
2981
3185
|
function lastProgressLine(progress) {
|
|
@@ -2984,7 +3188,7 @@ function lastProgressLine(progress) {
|
|
|
2984
3188
|
}
|
|
2985
3189
|
function readLogLines(file) {
|
|
2986
3190
|
if (!existsSync8(file)) return [];
|
|
2987
|
-
const lines =
|
|
3191
|
+
const lines = readFileSync7(file, "utf8").split("\n");
|
|
2988
3192
|
if (lines.length && lines[lines.length - 1] === "") lines.pop();
|
|
2989
3193
|
return lines;
|
|
2990
3194
|
}
|
|
@@ -2995,15 +3199,15 @@ function takeDelta(logFile, cursorFile) {
|
|
|
2995
3199
|
return { delta: capJobLogDelta(lines.slice(start).join("\n")), nextCursor: lines.length };
|
|
2996
3200
|
}
|
|
2997
3201
|
function snapshotJob(dir) {
|
|
2998
|
-
const pid = readIntFile(
|
|
3202
|
+
const pid = readIntFile(join11(dir, "pid"));
|
|
2999
3203
|
const running = jobTreeAlive(pid);
|
|
3000
3204
|
return {
|
|
3001
3205
|
pid,
|
|
3002
3206
|
running,
|
|
3003
|
-
exitCode: readIntFile(
|
|
3004
|
-
log:
|
|
3005
|
-
cursor:
|
|
3006
|
-
progress: tailProgress(
|
|
3207
|
+
exitCode: readIntFile(join11(dir, "exit")),
|
|
3208
|
+
log: join11(dir, "log"),
|
|
3209
|
+
cursor: join11(dir, "present.cursor"),
|
|
3210
|
+
progress: tailProgress(join11(dir, "log"))
|
|
3007
3211
|
};
|
|
3008
3212
|
}
|
|
3009
3213
|
function toResult(id, snap, extra) {
|
|
@@ -3012,8 +3216,8 @@ function toResult(id, snap, extra) {
|
|
|
3012
3216
|
const stillRunning = snap.running && !ok;
|
|
3013
3217
|
const { delta, nextCursor } = takeDelta(snap.log, snap.cursor);
|
|
3014
3218
|
try {
|
|
3015
|
-
|
|
3016
|
-
|
|
3219
|
+
mkdirSync5(dirname4(snap.cursor), { recursive: true });
|
|
3220
|
+
writeFileSync6(snap.cursor, `${nextCursor}
|
|
3017
3221
|
`);
|
|
3018
3222
|
} catch {
|
|
3019
3223
|
}
|
|
@@ -3034,7 +3238,7 @@ function toResult(id, snap, extra) {
|
|
|
3034
3238
|
};
|
|
3035
3239
|
}
|
|
3036
3240
|
async function sleepMs(ms) {
|
|
3037
|
-
await new Promise((
|
|
3241
|
+
await new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
3038
3242
|
}
|
|
3039
3243
|
async function waitForDetachedJob(cwd, id, opts) {
|
|
3040
3244
|
const jobId = sanitizeDetachedJobId(id);
|
|
@@ -3088,7 +3292,7 @@ function killProcessTree(pid, signal) {
|
|
|
3088
3292
|
}
|
|
3089
3293
|
function appendJobLog(logFile, line) {
|
|
3090
3294
|
try {
|
|
3091
|
-
|
|
3295
|
+
mkdirSync5(dirname4(logFile), { recursive: true });
|
|
3092
3296
|
const body = line.endsWith("\n") ? line : `${line}
|
|
3093
3297
|
`;
|
|
3094
3298
|
appendFileSync2(logFile, body);
|
|
@@ -3137,10 +3341,10 @@ async function stopDetachedJob(cwd, id, opts) {
|
|
|
3137
3341
|
killProcessTree(targetPid, "SIGKILL");
|
|
3138
3342
|
await sleep(100);
|
|
3139
3343
|
}
|
|
3140
|
-
const exitFile =
|
|
3344
|
+
const exitFile = join11(dir, "exit");
|
|
3141
3345
|
if (!existsSync8(exitFile)) {
|
|
3142
3346
|
try {
|
|
3143
|
-
|
|
3347
|
+
writeFileSync6(exitFile, "1\n");
|
|
3144
3348
|
} catch {
|
|
3145
3349
|
}
|
|
3146
3350
|
}
|
|
@@ -3963,7 +4167,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
3963
4167
|
return readThread(thread.id) ?? thread;
|
|
3964
4168
|
}
|
|
3965
4169
|
async function listLinearIssues(agent, repoPath) {
|
|
3966
|
-
const { getAdapter: getAdapter2 } = await import("./agents-
|
|
4170
|
+
const { getAdapter: getAdapter2 } = await import("./agents-CDQCQVWU.js");
|
|
3967
4171
|
await requireAgent(agent, { requireLinear: true });
|
|
3968
4172
|
const adapter = getAdapter2(agent);
|
|
3969
4173
|
if (!adapter.listLinearIssues) {
|
|
@@ -4027,8 +4231,8 @@ import {
|
|
|
4027
4231
|
existsSync as existsSync10,
|
|
4028
4232
|
renameSync,
|
|
4029
4233
|
unlinkSync as unlinkSync2,
|
|
4030
|
-
writeFileSync as
|
|
4031
|
-
readFileSync as
|
|
4234
|
+
writeFileSync as writeFileSync7,
|
|
4235
|
+
readFileSync as readFileSync8
|
|
4032
4236
|
} from "fs";
|
|
4033
4237
|
var buffers = /* @__PURE__ */ new Map();
|
|
4034
4238
|
var FLUSH_MS = 800;
|
|
@@ -4105,7 +4309,7 @@ function writeTurnLive(threadId, progress) {
|
|
|
4105
4309
|
const path = threadLivePath(threadId);
|
|
4106
4310
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
4107
4311
|
try {
|
|
4108
|
-
|
|
4312
|
+
writeFileSync7(tmp, JSON.stringify(progress), "utf8");
|
|
4109
4313
|
renameSync(tmp, path);
|
|
4110
4314
|
} catch {
|
|
4111
4315
|
try {
|
|
@@ -4118,7 +4322,7 @@ function readTurnLive(threadId) {
|
|
|
4118
4322
|
const path = threadLivePath(threadId);
|
|
4119
4323
|
if (!existsSync10(path)) return null;
|
|
4120
4324
|
try {
|
|
4121
|
-
const raw = JSON.parse(
|
|
4325
|
+
const raw = JSON.parse(readFileSync8(path, "utf8"));
|
|
4122
4326
|
if (!raw || typeof raw.summary !== "string") return null;
|
|
4123
4327
|
return raw;
|
|
4124
4328
|
} catch {
|
|
@@ -4345,23 +4549,23 @@ import {
|
|
|
4345
4549
|
existsSync as existsSync11,
|
|
4346
4550
|
mkdtempSync,
|
|
4347
4551
|
readdirSync as readdirSync4,
|
|
4348
|
-
readFileSync as
|
|
4552
|
+
readFileSync as readFileSync9,
|
|
4349
4553
|
rmSync
|
|
4350
4554
|
} from "fs";
|
|
4351
4555
|
import { tmpdir } from "os";
|
|
4352
|
-
import { join as
|
|
4556
|
+
import { join as join12 } from "path";
|
|
4353
4557
|
import { createRequire } from "module";
|
|
4354
|
-
var CONDUCTOR_APP_SUPPORT =
|
|
4558
|
+
var CONDUCTOR_APP_SUPPORT = join12(
|
|
4355
4559
|
process.env.HOME ?? "",
|
|
4356
4560
|
"Library",
|
|
4357
4561
|
"Application Support",
|
|
4358
4562
|
"com.conductor.app"
|
|
4359
4563
|
);
|
|
4360
|
-
var CONDUCTOR_DB =
|
|
4361
|
-
var CURSOR_SDK_STORE =
|
|
4564
|
+
var CONDUCTOR_DB = join12(CONDUCTOR_APP_SUPPORT, "conductor.db");
|
|
4565
|
+
var CURSOR_SDK_STORE = join12(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
|
|
4362
4566
|
function thisModuleFile() {
|
|
4363
4567
|
const cjsFile = typeof __filename !== "undefined" ? __filename : "";
|
|
4364
|
-
return cjsFile || process.argv[1] ||
|
|
4568
|
+
return cjsFile || process.argv[1] || join12(process.cwd(), "package.json");
|
|
4365
4569
|
}
|
|
4366
4570
|
function openReadonlySqlite(file) {
|
|
4367
4571
|
const req = createRequire(thisModuleFile());
|
|
@@ -4389,11 +4593,11 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
4389
4593
|
return null;
|
|
4390
4594
|
}
|
|
4391
4595
|
for (const hash of hashes) {
|
|
4392
|
-
const agentsFile =
|
|
4596
|
+
const agentsFile = join12(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
4393
4597
|
if (!existsSync11(agentsFile)) continue;
|
|
4394
4598
|
let text;
|
|
4395
4599
|
try {
|
|
4396
|
-
text =
|
|
4600
|
+
text = readFileSync9(agentsFile, "utf8");
|
|
4397
4601
|
} catch {
|
|
4398
4602
|
continue;
|
|
4399
4603
|
}
|
|
@@ -4449,8 +4653,8 @@ function listConductorWorkspaces() {
|
|
|
4449
4653
|
if (!existsSync11(CONDUCTOR_DB)) {
|
|
4450
4654
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
4451
4655
|
}
|
|
4452
|
-
const tmp = mkdtempSync(
|
|
4453
|
-
const snapshot =
|
|
4656
|
+
const tmp = mkdtempSync(join12(tmpdir(), "sideboard-conductor-"));
|
|
4657
|
+
const snapshot = join12(tmp, "conductor.db");
|
|
4454
4658
|
try {
|
|
4455
4659
|
copyFileSync2(CONDUCTOR_DB, snapshot);
|
|
4456
4660
|
for (const suffix of ["-wal", "-shm"]) {
|
|
@@ -4540,8 +4744,8 @@ function importConductorWorkspace(workspaceId) {
|
|
|
4540
4744
|
if (!existsSync11(CONDUCTOR_DB)) {
|
|
4541
4745
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
4542
4746
|
}
|
|
4543
|
-
const tmp = mkdtempSync(
|
|
4544
|
-
const snapshot =
|
|
4747
|
+
const tmp = mkdtempSync(join12(tmpdir(), "sideboard-conductor-"));
|
|
4748
|
+
const snapshot = join12(tmp, "conductor.db");
|
|
4545
4749
|
try {
|
|
4546
4750
|
copyFileSync2(CONDUCTOR_DB, snapshot);
|
|
4547
4751
|
for (const suffix of ["-wal", "-shm"]) {
|
|
@@ -4931,8 +5135,8 @@ function stackAgentDefaultsFrom(input) {
|
|
|
4931
5135
|
}
|
|
4932
5136
|
|
|
4933
5137
|
// src/diff/diff.ts
|
|
4934
|
-
import { existsSync as existsSync13, mkdirSync as
|
|
4935
|
-
import { dirname as
|
|
5138
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync10, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
|
|
5139
|
+
import { dirname as dirname5, join as join13 } from "path";
|
|
4936
5140
|
async function inspectGitWorktree(worktreePath) {
|
|
4937
5141
|
if (!worktreePath || !existsSync13(worktreePath)) return "missing_worktree";
|
|
4938
5142
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
@@ -5078,11 +5282,11 @@ new file mode 100644
|
|
|
5078
5282
|
};
|
|
5079
5283
|
}
|
|
5080
5284
|
async function untrackedPatch(worktreePath, path, maxHunk) {
|
|
5081
|
-
const abs =
|
|
5285
|
+
const abs = join13(worktreePath, path);
|
|
5082
5286
|
try {
|
|
5083
5287
|
const st = statSync2(abs);
|
|
5084
5288
|
if (st.isFile() && st.size > maxHunk) {
|
|
5085
|
-
const buf =
|
|
5289
|
+
const buf = readFileSync10(abs).subarray(0, maxHunk);
|
|
5086
5290
|
return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
|
|
5087
5291
|
}
|
|
5088
5292
|
} catch {
|
|
@@ -5550,7 +5754,7 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
5550
5754
|
}
|
|
5551
5755
|
function statWorktreePath(worktreePath, relativePath) {
|
|
5552
5756
|
assertSafeRelativePath(relativePath);
|
|
5553
|
-
const abs =
|
|
5757
|
+
const abs = join13(worktreePath, relativePath);
|
|
5554
5758
|
if (!existsSync13(abs)) return "missing";
|
|
5555
5759
|
const st = statSync2(abs);
|
|
5556
5760
|
if (st.isDirectory()) return "dir";
|
|
@@ -5592,7 +5796,7 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
|
|
|
5592
5796
|
function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
5593
5797
|
assertSafeRelativePath(relativePath);
|
|
5594
5798
|
const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
|
|
5595
|
-
const abs =
|
|
5799
|
+
const abs = join13(worktreePath, relativePath);
|
|
5596
5800
|
const st = statSync2(abs);
|
|
5597
5801
|
if (!st.isFile()) {
|
|
5598
5802
|
throw new Error(`Not a file: ${relativePath}`);
|
|
@@ -5602,7 +5806,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
5602
5806
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
5603
5807
|
);
|
|
5604
5808
|
}
|
|
5605
|
-
const buf =
|
|
5809
|
+
const buf = readFileSync10(abs);
|
|
5606
5810
|
return {
|
|
5607
5811
|
path: relativePath,
|
|
5608
5812
|
contentBase64: buf.toString("base64"),
|
|
@@ -5612,12 +5816,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
5612
5816
|
function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
5613
5817
|
assertSafeRelativePath(relativePath);
|
|
5614
5818
|
const maxBytes = opts?.maxBytes ?? 2e5;
|
|
5615
|
-
const abs =
|
|
5819
|
+
const abs = join13(worktreePath, relativePath);
|
|
5616
5820
|
const st = statSync2(abs);
|
|
5617
5821
|
if (!st.isFile()) {
|
|
5618
5822
|
throw new Error(`Not a file: ${relativePath}`);
|
|
5619
5823
|
}
|
|
5620
|
-
const buf =
|
|
5824
|
+
const buf = readFileSync10(abs);
|
|
5621
5825
|
if (isImageRelativePath(relativePath)) {
|
|
5622
5826
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
5623
5827
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -5660,9 +5864,9 @@ function assertSafeRelativePath(relativePath) {
|
|
|
5660
5864
|
}
|
|
5661
5865
|
function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
5662
5866
|
assertSafeRelativePath(relativePath);
|
|
5663
|
-
const abs =
|
|
5664
|
-
|
|
5665
|
-
|
|
5867
|
+
const abs = join13(worktreePath, relativePath);
|
|
5868
|
+
mkdirSync6(dirname5(abs), { recursive: true });
|
|
5869
|
+
writeFileSync8(abs, content, "utf8");
|
|
5666
5870
|
return { path: relativePath };
|
|
5667
5871
|
}
|
|
5668
5872
|
async function getWorktreeDirtyStat(worktreePath) {
|
|
@@ -5884,9 +6088,9 @@ async function confirmLand(thread, opts) {
|
|
|
5884
6088
|
}
|
|
5885
6089
|
|
|
5886
6090
|
// src/skills/discover.ts
|
|
5887
|
-
import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as
|
|
6091
|
+
import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync3 } from "fs";
|
|
5888
6092
|
import { homedir } from "os";
|
|
5889
|
-
import { join as
|
|
6093
|
+
import { join as join14 } from "path";
|
|
5890
6094
|
|
|
5891
6095
|
// src/skills/bundled/long-running.ts
|
|
5892
6096
|
var LONG_RUNNING_SKILL_COMMAND = "long-running";
|
|
@@ -6023,7 +6227,7 @@ function parseFrontmatter(content) {
|
|
|
6023
6227
|
}
|
|
6024
6228
|
function readSkill(skillMd, source) {
|
|
6025
6229
|
try {
|
|
6026
|
-
const content =
|
|
6230
|
+
const content = readFileSync11(skillMd, "utf8");
|
|
6027
6231
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
6028
6232
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
6029
6233
|
const name = fmName || dirName;
|
|
@@ -6051,7 +6255,7 @@ function scanSkillsDir(dir, source, out) {
|
|
|
6051
6255
|
}
|
|
6052
6256
|
for (const entry of entries) {
|
|
6053
6257
|
if (entry.startsWith(".")) continue;
|
|
6054
|
-
const skillMd =
|
|
6258
|
+
const skillMd = join14(dir, entry, "SKILL.md");
|
|
6055
6259
|
if (!existsSync14(skillMd)) continue;
|
|
6056
6260
|
try {
|
|
6057
6261
|
if (!statSync3(skillMd).isFile()) continue;
|
|
@@ -6073,12 +6277,12 @@ function scanClaudePluginSkills(pluginsRoot, out) {
|
|
|
6073
6277
|
return;
|
|
6074
6278
|
}
|
|
6075
6279
|
if (lookingForSkillsDir && entries.includes("SKILL.md")) {
|
|
6076
|
-
const skill = readSkill(
|
|
6280
|
+
const skill = readSkill(join14(dir, "SKILL.md"), "cli");
|
|
6077
6281
|
if (skill) out.push(skill);
|
|
6078
6282
|
}
|
|
6079
6283
|
for (const entry of entries) {
|
|
6080
6284
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
6081
|
-
const full =
|
|
6285
|
+
const full = join14(dir, entry);
|
|
6082
6286
|
try {
|
|
6083
6287
|
if (!statSync3(full).isDirectory()) continue;
|
|
6084
6288
|
} catch {
|
|
@@ -6098,17 +6302,17 @@ function discoverSkills(worktreePath) {
|
|
|
6098
6302
|
const home = homedir();
|
|
6099
6303
|
const collected = [];
|
|
6100
6304
|
for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
|
|
6101
|
-
scanSkillsDir(
|
|
6305
|
+
scanSkillsDir(join14(worktreePath, rel), "workspace", collected);
|
|
6102
6306
|
}
|
|
6103
6307
|
for (const abs of [
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6308
|
+
join14(home, ".claude/skills"),
|
|
6309
|
+
join14(home, ".cursor/skills"),
|
|
6310
|
+
join14(home, ".sideboard/skills"),
|
|
6311
|
+
join14(home, ".brightsy/skills")
|
|
6108
6312
|
]) {
|
|
6109
6313
|
scanSkillsDir(abs, "user", collected);
|
|
6110
6314
|
}
|
|
6111
|
-
scanClaudePluginSkills(
|
|
6315
|
+
scanClaudePluginSkills(join14(home, ".claude/plugins"), collected);
|
|
6112
6316
|
collected.push(...bundledSkills());
|
|
6113
6317
|
const rank = {
|
|
6114
6318
|
workspace: 0,
|
|
@@ -6134,7 +6338,7 @@ function readSkillBody(skillPath, maxChars = 12e3) {
|
|
|
6134
6338
|
\u2026(truncated)` : body;
|
|
6135
6339
|
}
|
|
6136
6340
|
}
|
|
6137
|
-
const raw =
|
|
6341
|
+
const raw = readFileSync11(skillPath, "utf8");
|
|
6138
6342
|
if (raw.startsWith("---")) {
|
|
6139
6343
|
const end = raw.indexOf("\n---", 3);
|
|
6140
6344
|
if (end >= 0) {
|
|
@@ -6280,11 +6484,11 @@ function shiftQueuedItem(queue, queueAttachments) {
|
|
|
6280
6484
|
|
|
6281
6485
|
// src/agents/instructions.ts
|
|
6282
6486
|
import { existsSync as existsSync16 } from "fs";
|
|
6283
|
-
import { join as
|
|
6487
|
+
import { join as join16 } from "path";
|
|
6284
6488
|
|
|
6285
6489
|
// src/skills/detached-job-path.ts
|
|
6286
6490
|
import { existsSync as existsSync15 } from "fs";
|
|
6287
|
-
import { dirname as
|
|
6491
|
+
import { dirname as dirname6, join as join15 } from "path";
|
|
6288
6492
|
import { fileURLToPath } from "url";
|
|
6289
6493
|
var DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
|
|
6290
6494
|
function compiledModuleDir() {
|
|
@@ -6293,7 +6497,7 @@ function compiledModuleDir() {
|
|
|
6293
6497
|
try {
|
|
6294
6498
|
const url = import.meta.url;
|
|
6295
6499
|
if (typeof url === "string" && url.length > 0) {
|
|
6296
|
-
return
|
|
6500
|
+
return dirname6(fileURLToPath(url));
|
|
6297
6501
|
}
|
|
6298
6502
|
} catch {
|
|
6299
6503
|
}
|
|
@@ -6301,7 +6505,7 @@ function compiledModuleDir() {
|
|
|
6301
6505
|
}
|
|
6302
6506
|
function detachedJobScriptIn(dir) {
|
|
6303
6507
|
for (const name of DETACHED_JOB_FILENAMES) {
|
|
6304
|
-
const script =
|
|
6508
|
+
const script = join15(dir, "scripts", name);
|
|
6305
6509
|
if (existsSync15(script)) return script;
|
|
6306
6510
|
}
|
|
6307
6511
|
return null;
|
|
@@ -6319,7 +6523,7 @@ function resolveDetachedJobScript() {
|
|
|
6319
6523
|
for (let i = 0; i < 8; i++) {
|
|
6320
6524
|
const candidate = detachedJobScriptIn(dir);
|
|
6321
6525
|
if (candidate) return candidate;
|
|
6322
|
-
const parent =
|
|
6526
|
+
const parent = dirname6(dir);
|
|
6323
6527
|
if (parent === dir) break;
|
|
6324
6528
|
dir = parent;
|
|
6325
6529
|
}
|
|
@@ -6424,9 +6628,9 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
6424
6628
|
);
|
|
6425
6629
|
} else {
|
|
6426
6630
|
const slug = opts?.githubSlug ?? "<origin-owner/name>";
|
|
6427
|
-
const
|
|
6631
|
+
const resolve2 = opts?.githubSlug ? "" : " Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree.";
|
|
6428
6632
|
lines.push(
|
|
6429
|
-
`- Prefer a draft PR first: \`gh pr create --draft -R ${slug}\` (update via \`gh pr edit -R ${slug}\`). Always pass \`-R\` \u2014 bare \`gh pr create\` may target upstream on dual-remote checkouts.${
|
|
6633
|
+
`- Prefer a draft PR first: \`gh pr create --draft -R ${slug}\` (update via \`gh pr edit -R ${slug}\`). Always pass \`-R\` \u2014 bare \`gh pr create\` may target upstream on dual-remote checkouts.${resolve2} Mark ready for review only when asked. If GitHub rejects the body as too long, retry with a short \`--body-file\` \u2014 do not paste a changelog or diff into \`--body\`.`
|
|
6430
6634
|
);
|
|
6431
6635
|
}
|
|
6432
6636
|
lines.push(
|
|
@@ -6538,9 +6742,9 @@ function formatPrGateDirective() {
|
|
|
6538
6742
|
}
|
|
6539
6743
|
function formatProcessGuideDirective(opts) {
|
|
6540
6744
|
const worktree = opts?.worktreePath?.trim();
|
|
6541
|
-
const hasGraphSkill = !!worktree && existsSync16(
|
|
6542
|
-
const hasReviewSkill = !!worktree && existsSync16(
|
|
6543
|
-
const hasInstallSkill = !!worktree && existsSync16(
|
|
6745
|
+
const hasGraphSkill = !!worktree && existsSync16(join16(worktree, ".claude/skills/graph-engineering/SKILL.md"));
|
|
6746
|
+
const hasReviewSkill = !!worktree && existsSync16(join16(worktree, ".claude/skills/review/SKILL.md"));
|
|
6747
|
+
const hasInstallSkill = !!worktree && existsSync16(join16(worktree, ".claude/skills/worktree-install/SKILL.md"));
|
|
6544
6748
|
return [
|
|
6545
6749
|
"Process guides (recurring work only):",
|
|
6546
6750
|
hasGraphSkill ? "- Migrations, ports, batch fixes, and other fan-out: follow `.claude/skills/graph-engineering/SKILL.md` (`/graph-engineering`). Judge first; state on disk; grow the rulebook; do not patch around it." : null,
|
|
@@ -6903,8 +7107,8 @@ async function syncThreadBranchFromGit(threadId) {
|
|
|
6903
7107
|
|
|
6904
7108
|
// src/store/schedules.ts
|
|
6905
7109
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
6906
|
-
import { existsSync as existsSync17, readFileSync as
|
|
6907
|
-
import { join as
|
|
7110
|
+
import { existsSync as existsSync17, readFileSync as readFileSync12 } from "fs";
|
|
7111
|
+
import { join as join17 } from "path";
|
|
6908
7112
|
import { Cron } from "croner";
|
|
6909
7113
|
var DURATION_RE = /^(\d+)(s|m|h|d)$/i;
|
|
6910
7114
|
var UNIT_MS = {
|
|
@@ -6914,7 +7118,7 @@ var UNIT_MS = {
|
|
|
6914
7118
|
d: 864e5
|
|
6915
7119
|
};
|
|
6916
7120
|
function schedulesPath() {
|
|
6917
|
-
return
|
|
7121
|
+
return join17(appDataDir(), "schedules.json");
|
|
6918
7122
|
}
|
|
6919
7123
|
function parseDurationMs(every) {
|
|
6920
7124
|
const m = every.trim().match(DURATION_RE);
|
|
@@ -7022,7 +7226,7 @@ function readAll() {
|
|
|
7022
7226
|
const path = schedulesPath();
|
|
7023
7227
|
if (!existsSync17(path)) return [];
|
|
7024
7228
|
try {
|
|
7025
|
-
const parsed = JSON.parse(
|
|
7229
|
+
const parsed = JSON.parse(readFileSync12(path, "utf8"));
|
|
7026
7230
|
if (!Array.isArray(parsed)) return [];
|
|
7027
7231
|
return parsed.map((row) => {
|
|
7028
7232
|
try {
|
|
@@ -7148,7 +7352,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
7148
7352
|
${prompt}`;
|
|
7149
7353
|
}
|
|
7150
7354
|
async function defaultDeps() {
|
|
7151
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
7355
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-A2Q5O4W2.js");
|
|
7152
7356
|
const orch = getOrchestrator2();
|
|
7153
7357
|
return {
|
|
7154
7358
|
findThread: (id) => findThreadByRef(id),
|
|
@@ -7213,14 +7417,14 @@ function armOne(schedule) {
|
|
|
7213
7417
|
if (!schedule.enabled) return;
|
|
7214
7418
|
const at = new Date(schedule.nextRunAt);
|
|
7215
7419
|
if (Number.isNaN(at.getTime())) return;
|
|
7216
|
-
const
|
|
7217
|
-
if (
|
|
7420
|
+
const delay2 = Math.max(0, at.getTime() - Date.now());
|
|
7421
|
+
if (delay2 <= 250) {
|
|
7218
7422
|
void fireSchedule(schedule.id).then(() => {
|
|
7219
7423
|
if (isThisProcessDesktopHost()) armSchedules();
|
|
7220
7424
|
});
|
|
7221
7425
|
return;
|
|
7222
7426
|
}
|
|
7223
|
-
const capped = Math.min(
|
|
7427
|
+
const capped = Math.min(delay2, SET_TIMEOUT_MAX);
|
|
7224
7428
|
const timer = setTimeout(() => {
|
|
7225
7429
|
timers.delete(schedule.id);
|
|
7226
7430
|
const live = getSchedule(schedule.id);
|
|
@@ -7604,8 +7808,8 @@ var Orchestrator = class {
|
|
|
7604
7808
|
scheduleQuotaResume(threadId, resumeAt) {
|
|
7605
7809
|
this.clearQuotaResumeTimer(threadId);
|
|
7606
7810
|
updateThread(threadId, { quotaResumeAt: resumeAt.toISOString() });
|
|
7607
|
-
const
|
|
7608
|
-
const capped = Math.min(
|
|
7811
|
+
const delay2 = Math.max(5e3, resumeAt.getTime() - Date.now());
|
|
7812
|
+
const capped = Math.min(delay2, 2147483647);
|
|
7609
7813
|
const timer = setTimeout(() => {
|
|
7610
7814
|
this.quotaResumeTimers.delete(threadId);
|
|
7611
7815
|
void this.resumeAfterQuotaWait(threadId);
|
|
@@ -8660,6 +8864,7 @@ var Orchestrator = class {
|
|
|
8660
8864
|
return { port: shared.devPort, scriptName: resolvedName, ports: [shared.devPort] };
|
|
8661
8865
|
}
|
|
8662
8866
|
}
|
|
8867
|
+
const preferredPorts = active?.ports?.length ? active.ports : active?.port != null ? [active.port] : void 0;
|
|
8663
8868
|
if (active && !existing) {
|
|
8664
8869
|
killListenersOnPorts(collectActiveRunPorts([active]));
|
|
8665
8870
|
this.syncWorktreeRuns(
|
|
@@ -8698,7 +8903,7 @@ var Orchestrator = class {
|
|
|
8698
8903
|
thread.repoPath,
|
|
8699
8904
|
thread.worktreePath,
|
|
8700
8905
|
runOutput.push,
|
|
8701
|
-
{ scriptName: resolvedName }
|
|
8906
|
+
{ scriptName: resolvedName, preferredPorts }
|
|
8702
8907
|
);
|
|
8703
8908
|
} catch (err) {
|
|
8704
8909
|
runOutput.flush();
|
|
@@ -9147,7 +9352,7 @@ var Orchestrator = class {
|
|
|
9147
9352
|
return thread;
|
|
9148
9353
|
}
|
|
9149
9354
|
const start = Date.now();
|
|
9150
|
-
return new Promise((
|
|
9355
|
+
return new Promise((resolve2, reject) => {
|
|
9151
9356
|
let settled = false;
|
|
9152
9357
|
let timer;
|
|
9153
9358
|
const finish = (fn) => {
|
|
@@ -9165,7 +9370,7 @@ var Orchestrator = class {
|
|
|
9165
9370
|
finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
|
|
9166
9371
|
return;
|
|
9167
9372
|
}
|
|
9168
|
-
finish(() =>
|
|
9373
|
+
finish(() => resolve2(latest));
|
|
9169
9374
|
return;
|
|
9170
9375
|
}
|
|
9171
9376
|
if (event.type === "status_changed") {
|
|
@@ -9175,7 +9380,7 @@ var Orchestrator = class {
|
|
|
9175
9380
|
return;
|
|
9176
9381
|
}
|
|
9177
9382
|
if (!["running", "queued"].includes(latest.status)) {
|
|
9178
|
-
finish(() =>
|
|
9383
|
+
finish(() => resolve2(latest));
|
|
9179
9384
|
}
|
|
9180
9385
|
}
|
|
9181
9386
|
});
|
|
@@ -9187,12 +9392,12 @@ var Orchestrator = class {
|
|
|
9187
9392
|
}
|
|
9188
9393
|
const current = this.healStaleReportedActivity(raw);
|
|
9189
9394
|
if (!this.threadLooksLive(current)) {
|
|
9190
|
-
finish(() =>
|
|
9395
|
+
finish(() => resolve2(current));
|
|
9191
9396
|
return;
|
|
9192
9397
|
}
|
|
9193
9398
|
if (Date.now() - start > timeoutMs) {
|
|
9194
9399
|
if (opts?.resolveIfStillRunning) {
|
|
9195
|
-
finish(() =>
|
|
9400
|
+
finish(() => resolve2(current));
|
|
9196
9401
|
} else {
|
|
9197
9402
|
finish(() => reject(new Error("wait_for_turn timed out")));
|
|
9198
9403
|
}
|
|
@@ -10045,13 +10250,21 @@ export {
|
|
|
10045
10250
|
buildReviewRequestAttachment,
|
|
10046
10251
|
readExistingReviewRequestFile,
|
|
10047
10252
|
requestReview,
|
|
10253
|
+
RUN_PORTS_REL,
|
|
10254
|
+
worktreeRunPortsPath,
|
|
10255
|
+
loadWorktreeRunPorts,
|
|
10256
|
+
saveWorktreeRunPorts,
|
|
10257
|
+
PORT_RANGE_SIZE,
|
|
10048
10258
|
readWorktreeInclude,
|
|
10049
10259
|
resolveFilesToCopy,
|
|
10050
10260
|
copyConfiguredFiles,
|
|
10051
10261
|
captureLoginEnv,
|
|
10052
10262
|
buildWorkspaceScriptEnv,
|
|
10053
10263
|
killableListenerPids,
|
|
10264
|
+
listenerCwd,
|
|
10265
|
+
isOtherWorktreeListenerCwd,
|
|
10054
10266
|
killListenersOnPorts,
|
|
10267
|
+
killStaleWorktreeListeners,
|
|
10055
10268
|
runSetupScript,
|
|
10056
10269
|
runConventionSetup,
|
|
10057
10270
|
runWorkspaceSetup,
|
|
@@ -10062,6 +10275,8 @@ export {
|
|
|
10062
10275
|
getRunMode,
|
|
10063
10276
|
reservePort,
|
|
10064
10277
|
allocatePort,
|
|
10278
|
+
tryReservePort,
|
|
10279
|
+
tryReservePorts,
|
|
10065
10280
|
reservePortRange,
|
|
10066
10281
|
allocatePortRange,
|
|
10067
10282
|
startDevServer,
|