@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
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
summarizeTurnStderr,
|
|
43
43
|
toolDescription,
|
|
44
44
|
turnFailChatText
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-3Q4PVZ3U.js";
|
|
46
46
|
import {
|
|
47
47
|
SLACK_REPLY_FORMATTING,
|
|
48
48
|
addWorkspace,
|
|
@@ -792,13 +792,14 @@ function shouldAutoArchiveOnPrMerge(opts) {
|
|
|
792
792
|
import {
|
|
793
793
|
copyFileSync,
|
|
794
794
|
existsSync as existsSync3,
|
|
795
|
-
mkdirSync as
|
|
795
|
+
mkdirSync as mkdirSync4,
|
|
796
796
|
readdirSync,
|
|
797
|
-
readFileSync as
|
|
797
|
+
readFileSync as readFileSync3,
|
|
798
|
+
realpathSync
|
|
798
799
|
} from "fs";
|
|
799
800
|
import { execFileSync } from "child_process";
|
|
800
801
|
import { createServer } from "net";
|
|
801
|
-
import { basename, dirname as
|
|
802
|
+
import { basename, dirname as dirname3, join as join5, resolve, sep } from "path";
|
|
802
803
|
import { execa as execa2 } from "execa";
|
|
803
804
|
import { createInterface as createInterface2 } from "readline";
|
|
804
805
|
|
|
@@ -1658,16 +1659,73 @@ async function requestReview(threadRef, send) {
|
|
|
1658
1659
|
return { tab: started, from };
|
|
1659
1660
|
}
|
|
1660
1661
|
|
|
1662
|
+
// src/hook/worktree-run-ports.ts
|
|
1663
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1664
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
1665
|
+
var RUN_PORTS_REL = ".context/.sideboard/run-ports.json";
|
|
1666
|
+
function worktreeRunPortsPath(worktreePath) {
|
|
1667
|
+
return join4(worktreePath, RUN_PORTS_REL);
|
|
1668
|
+
}
|
|
1669
|
+
function isPort(value) {
|
|
1670
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 && value <= 65535;
|
|
1671
|
+
}
|
|
1672
|
+
function parseFile(raw) {
|
|
1673
|
+
try {
|
|
1674
|
+
const parsed = JSON.parse(raw);
|
|
1675
|
+
if (!parsed || typeof parsed !== "object") return {};
|
|
1676
|
+
const scripts = parsed.scripts;
|
|
1677
|
+
if (!scripts || typeof scripts !== "object") return {};
|
|
1678
|
+
const next = {};
|
|
1679
|
+
for (const [name, ports] of Object.entries(scripts)) {
|
|
1680
|
+
if (!name || !Array.isArray(ports)) continue;
|
|
1681
|
+
const cleaned = ports.filter(isPort);
|
|
1682
|
+
if (cleaned.length) next[name] = cleaned;
|
|
1683
|
+
}
|
|
1684
|
+
return { scripts: next };
|
|
1685
|
+
} catch {
|
|
1686
|
+
return {};
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
function loadWorktreeRunPorts(worktreePath, scriptName) {
|
|
1690
|
+
if (!worktreePath.trim() || !scriptName.trim()) return null;
|
|
1691
|
+
try {
|
|
1692
|
+
const raw = readFileSync2(worktreeRunPortsPath(worktreePath), "utf8");
|
|
1693
|
+
const ports = parseFile(raw).scripts?.[scriptName];
|
|
1694
|
+
return ports?.length ? ports : null;
|
|
1695
|
+
} catch {
|
|
1696
|
+
return null;
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
function saveWorktreeRunPorts(worktreePath, scriptName, ports) {
|
|
1700
|
+
if (!worktreePath.trim() || !scriptName.trim()) return;
|
|
1701
|
+
const cleaned = ports.filter(isPort);
|
|
1702
|
+
if (!cleaned.length) return;
|
|
1703
|
+
const path = worktreeRunPortsPath(worktreePath);
|
|
1704
|
+
let current = {};
|
|
1705
|
+
try {
|
|
1706
|
+
current = parseFile(readFileSync2(path, "utf8"));
|
|
1707
|
+
} catch {
|
|
1708
|
+
current = {};
|
|
1709
|
+
}
|
|
1710
|
+
const scripts = { ...current.scripts ?? {} };
|
|
1711
|
+
scripts[scriptName] = cleaned;
|
|
1712
|
+
mkdirSync3(dirname2(path), { recursive: true });
|
|
1713
|
+
writeFileSync2(path, `${JSON.stringify({ scripts }, null, 2)}
|
|
1714
|
+
`, "utf8");
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1661
1717
|
// src/hook/conductor.ts
|
|
1662
1718
|
var PORT_RANGE_SIZE = 10;
|
|
1719
|
+
var RECLAIM_RETRY_DELAY_MS = 40;
|
|
1720
|
+
var RECLAIM_TRIES = 8;
|
|
1663
1721
|
function matchSimpleGlob(pattern, name) {
|
|
1664
1722
|
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
1665
1723
|
return new RegExp(`^${escaped}$`).test(name);
|
|
1666
1724
|
}
|
|
1667
1725
|
function readWorktreeInclude(repoPath) {
|
|
1668
|
-
const path =
|
|
1726
|
+
const path = join5(repoPath, ".worktreeinclude");
|
|
1669
1727
|
if (!existsSync3(path)) return [];
|
|
1670
|
-
return
|
|
1728
|
+
return readFileSync3(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
1671
1729
|
}
|
|
1672
1730
|
function resolveFilesToCopy(repoPath) {
|
|
1673
1731
|
const fromInclude = readWorktreeInclude(repoPath);
|
|
@@ -1706,10 +1764,10 @@ function copyConfiguredFiles(repoPath, worktreePath) {
|
|
|
1706
1764
|
const patterns = resolveFilesToCopy(repoPath);
|
|
1707
1765
|
const copied = [];
|
|
1708
1766
|
for (const rel of patterns) {
|
|
1709
|
-
const src =
|
|
1767
|
+
const src = join5(repoPath, rel);
|
|
1710
1768
|
if (!existsSync3(src)) continue;
|
|
1711
|
-
const dest =
|
|
1712
|
-
|
|
1769
|
+
const dest = join5(worktreePath, rel);
|
|
1770
|
+
mkdirSync4(dirname3(dest), { recursive: true });
|
|
1713
1771
|
copyFileSync(src, dest);
|
|
1714
1772
|
copied.push(rel);
|
|
1715
1773
|
}
|
|
@@ -1786,6 +1844,57 @@ function killableListenerPids(raw, selfPid = process.pid) {
|
|
|
1786
1844
|
}
|
|
1787
1845
|
return pids;
|
|
1788
1846
|
}
|
|
1847
|
+
function listUnixListenerPids(port, selfPid) {
|
|
1848
|
+
try {
|
|
1849
|
+
const raw = execFileSync("lsof", [`-tiTCP:${port}`, "-sTCP:LISTEN"], {
|
|
1850
|
+
encoding: "utf8",
|
|
1851
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1852
|
+
timeout: 2e3
|
|
1853
|
+
});
|
|
1854
|
+
return killableListenerPids(raw, selfPid);
|
|
1855
|
+
} catch {
|
|
1856
|
+
return [];
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
function listenerCwd(pid) {
|
|
1860
|
+
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
1861
|
+
try {
|
|
1862
|
+
const raw = execFileSync("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], {
|
|
1863
|
+
encoding: "utf8",
|
|
1864
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1865
|
+
timeout: 2e3
|
|
1866
|
+
});
|
|
1867
|
+
for (const line of raw.split("\n")) {
|
|
1868
|
+
if (line.startsWith("n") && line.length > 1) return line.slice(1);
|
|
1869
|
+
}
|
|
1870
|
+
} catch {
|
|
1871
|
+
}
|
|
1872
|
+
return null;
|
|
1873
|
+
}
|
|
1874
|
+
function realpathOrResolve(path) {
|
|
1875
|
+
try {
|
|
1876
|
+
return realpathSync(path);
|
|
1877
|
+
} catch {
|
|
1878
|
+
return resolve(path);
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
function isOtherWorktreeListenerCwd(cwd, worktreePath) {
|
|
1882
|
+
const wt = realpathOrResolve(worktreePath);
|
|
1883
|
+
const c = realpathOrResolve(cwd);
|
|
1884
|
+
if (c === wt || c.startsWith(wt + sep)) return false;
|
|
1885
|
+
const parent = dirname3(wt);
|
|
1886
|
+
const thisName = basename(wt);
|
|
1887
|
+
if (!thisName || c === parent) return false;
|
|
1888
|
+
if (!c.startsWith(parent + sep)) return false;
|
|
1889
|
+
const first = c.slice(parent.length + 1).split(sep)[0];
|
|
1890
|
+
return Boolean(first) && first !== thisName;
|
|
1891
|
+
}
|
|
1892
|
+
function signalPid(pid, signal) {
|
|
1893
|
+
try {
|
|
1894
|
+
process.kill(pid, signal);
|
|
1895
|
+
} catch {
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1789
1898
|
function killListenersOnPorts(ports) {
|
|
1790
1899
|
const selfPid = process.pid;
|
|
1791
1900
|
for (const port of ports) {
|
|
@@ -1803,26 +1912,32 @@ function killListenersOnPorts(ports) {
|
|
|
1803
1912
|
);
|
|
1804
1913
|
continue;
|
|
1805
1914
|
}
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
raw = execFileSync("lsof", [`-tiTCP:${port}`, "-sTCP:LISTEN"], {
|
|
1809
|
-
encoding: "utf8",
|
|
1810
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
1811
|
-
timeout: 2e3
|
|
1812
|
-
});
|
|
1813
|
-
} catch {
|
|
1814
|
-
continue;
|
|
1815
|
-
}
|
|
1816
|
-
for (const pid of killableListenerPids(raw, selfPid)) {
|
|
1817
|
-
try {
|
|
1818
|
-
process.kill(pid, "SIGTERM");
|
|
1819
|
-
} catch {
|
|
1820
|
-
}
|
|
1915
|
+
for (const pid of listUnixListenerPids(port, selfPid)) {
|
|
1916
|
+
signalPid(pid, "SIGTERM");
|
|
1821
1917
|
}
|
|
1822
1918
|
} catch {
|
|
1823
1919
|
}
|
|
1824
1920
|
}
|
|
1825
1921
|
}
|
|
1922
|
+
function killStaleWorktreeListeners(ports, worktreePath, signal = "SIGTERM") {
|
|
1923
|
+
if (!worktreePath.trim()) {
|
|
1924
|
+
killListenersOnPorts(ports);
|
|
1925
|
+
return;
|
|
1926
|
+
}
|
|
1927
|
+
if (process.platform === "win32") {
|
|
1928
|
+
killListenersOnPorts(ports);
|
|
1929
|
+
return;
|
|
1930
|
+
}
|
|
1931
|
+
const selfPid = process.pid;
|
|
1932
|
+
for (const port of ports) {
|
|
1933
|
+
if (!Number.isFinite(port) || port <= 0) continue;
|
|
1934
|
+
for (const pid of listUnixListenerPids(port, selfPid)) {
|
|
1935
|
+
const cwd = listenerCwd(pid);
|
|
1936
|
+
if (cwd && isOtherWorktreeListenerCwd(cwd, worktreePath)) continue;
|
|
1937
|
+
signalPid(pid, signal);
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1826
1941
|
function killScriptTree(child, ports = []) {
|
|
1827
1942
|
const pid = child.pid;
|
|
1828
1943
|
if (pid) {
|
|
@@ -1984,7 +2099,7 @@ function getRunMode(worktreePath, repoPath) {
|
|
|
1984
2099
|
return loadWorkspaceSettings(worktreePath, repoPath)?.runMode ?? "concurrent";
|
|
1985
2100
|
}
|
|
1986
2101
|
async function reservePort() {
|
|
1987
|
-
return new Promise((
|
|
2102
|
+
return new Promise((resolve2, reject) => {
|
|
1988
2103
|
const server = createServer();
|
|
1989
2104
|
let released = false;
|
|
1990
2105
|
const release = () => new Promise((res) => {
|
|
@@ -2001,13 +2116,14 @@ async function reservePort() {
|
|
|
2001
2116
|
void release().then(() => reject(new Error("Failed to allocate port")));
|
|
2002
2117
|
return;
|
|
2003
2118
|
}
|
|
2004
|
-
|
|
2119
|
+
resolve2({ port: addr.port, release });
|
|
2005
2120
|
});
|
|
2006
2121
|
server.on("error", reject);
|
|
2007
2122
|
});
|
|
2008
2123
|
}
|
|
2009
2124
|
async function tryReservePort(port) {
|
|
2010
|
-
|
|
2125
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) return null;
|
|
2126
|
+
return new Promise((resolve2) => {
|
|
2011
2127
|
const server = createServer();
|
|
2012
2128
|
let released = false;
|
|
2013
2129
|
const release = () => new Promise((res) => {
|
|
@@ -2018,23 +2134,51 @@ async function tryReservePort(port) {
|
|
|
2018
2134
|
released = true;
|
|
2019
2135
|
server.close(() => res());
|
|
2020
2136
|
});
|
|
2021
|
-
server.once("error", () =>
|
|
2137
|
+
server.once("error", () => {
|
|
2138
|
+
try {
|
|
2139
|
+
server.close();
|
|
2140
|
+
} catch {
|
|
2141
|
+
}
|
|
2142
|
+
resolve2(null);
|
|
2143
|
+
});
|
|
2022
2144
|
server.listen(port, "127.0.0.1", () => {
|
|
2023
|
-
|
|
2145
|
+
resolve2({ port, release });
|
|
2024
2146
|
});
|
|
2025
2147
|
});
|
|
2026
2148
|
}
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
const
|
|
2149
|
+
function normalizePreferredPorts(ports, size) {
|
|
2150
|
+
if (!ports?.length) return [];
|
|
2151
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2152
|
+
const out = [];
|
|
2153
|
+
for (const p of ports) {
|
|
2154
|
+
if (!Number.isInteger(p) || p <= 0 || p > 65535 || seen.has(p)) continue;
|
|
2155
|
+
seen.add(p);
|
|
2156
|
+
out.push(p);
|
|
2157
|
+
if (out.length >= size) break;
|
|
2158
|
+
}
|
|
2159
|
+
return out;
|
|
2160
|
+
}
|
|
2161
|
+
function delay(ms) {
|
|
2162
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2163
|
+
}
|
|
2164
|
+
async function fillPortRange(held, size) {
|
|
2165
|
+
if (held.length >= size) return held;
|
|
2166
|
+
const base = held[0]?.port;
|
|
2167
|
+
const taken = new Set(held.map((h) => h.port));
|
|
2030
2168
|
try {
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
const
|
|
2169
|
+
let offset = 0;
|
|
2170
|
+
while (held.length < size) {
|
|
2171
|
+
const candidate = base != null ? base + offset : null;
|
|
2172
|
+
offset += 1;
|
|
2173
|
+
if (candidate != null && taken.has(candidate)) continue;
|
|
2174
|
+
const next = candidate != null ? await tryReservePort(candidate) : null;
|
|
2034
2175
|
if (next) {
|
|
2035
2176
|
held.push(next);
|
|
2177
|
+
taken.add(next.port);
|
|
2036
2178
|
} else {
|
|
2037
|
-
|
|
2179
|
+
const ephemeral = await reservePort();
|
|
2180
|
+
held.push(ephemeral);
|
|
2181
|
+
taken.add(ephemeral.port);
|
|
2038
2182
|
}
|
|
2039
2183
|
}
|
|
2040
2184
|
return held;
|
|
@@ -2043,11 +2187,59 @@ async function reservePortRange(size = PORT_RANGE_SIZE) {
|
|
|
2043
2187
|
throw err;
|
|
2044
2188
|
}
|
|
2045
2189
|
}
|
|
2190
|
+
async function tryReserveAvailablePorts(ports) {
|
|
2191
|
+
const held = [];
|
|
2192
|
+
for (const port of ports) {
|
|
2193
|
+
const next = await tryReservePort(port);
|
|
2194
|
+
if (next) held.push(next);
|
|
2195
|
+
}
|
|
2196
|
+
return held;
|
|
2197
|
+
}
|
|
2198
|
+
async function reclaimPreferredPorts(ports, worktreePath) {
|
|
2199
|
+
const primary = ports[0];
|
|
2200
|
+
if (primary == null) return null;
|
|
2201
|
+
const takePrimary = async () => {
|
|
2202
|
+
const held2 = await tryReserveAvailablePorts(ports);
|
|
2203
|
+
if (held2.some((h) => h.port === primary)) return held2;
|
|
2204
|
+
await Promise.all(held2.map((h) => h.release()));
|
|
2205
|
+
return null;
|
|
2206
|
+
};
|
|
2207
|
+
let held = await takePrimary();
|
|
2208
|
+
if (held) return held;
|
|
2209
|
+
if (worktreePath) {
|
|
2210
|
+
killStaleWorktreeListeners(ports, worktreePath, "SIGTERM");
|
|
2211
|
+
} else {
|
|
2212
|
+
killListenersOnPorts(ports);
|
|
2213
|
+
}
|
|
2214
|
+
for (let i = 0; i < RECLAIM_TRIES; i++) {
|
|
2215
|
+
await delay(RECLAIM_RETRY_DELAY_MS);
|
|
2216
|
+
held = await takePrimary();
|
|
2217
|
+
if (held) return held;
|
|
2218
|
+
if (i === 2 && worktreePath) {
|
|
2219
|
+
killStaleWorktreeListeners(ports, worktreePath, "SIGKILL");
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
return null;
|
|
2223
|
+
}
|
|
2224
|
+
async function reservePortRange(size = PORT_RANGE_SIZE, opts) {
|
|
2225
|
+
const preferred = normalizePreferredPorts(opts?.preferred, size);
|
|
2226
|
+
if (preferred.length) {
|
|
2227
|
+
const reused = await reclaimPreferredPorts(preferred, opts?.worktreePath);
|
|
2228
|
+
if (reused) return fillPortRange(reused, size);
|
|
2229
|
+
}
|
|
2230
|
+
const held = [await reservePort()];
|
|
2231
|
+
return fillPortRange(held, size);
|
|
2232
|
+
}
|
|
2046
2233
|
async function startDevServer(repoPath, worktreePath, onLine, opts) {
|
|
2047
2234
|
const script = getRunScript(worktreePath, repoPath, opts?.scriptName);
|
|
2048
2235
|
if (!script) return null;
|
|
2049
|
-
const
|
|
2236
|
+
const preferred = loadWorktreeRunPorts(worktreePath, script.name) ?? opts?.preferredPorts;
|
|
2237
|
+
const held = await reservePortRange(PORT_RANGE_SIZE, {
|
|
2238
|
+
preferred: preferred ?? void 0,
|
|
2239
|
+
worktreePath
|
|
2240
|
+
});
|
|
2050
2241
|
const ports = held.map((h) => h.port);
|
|
2242
|
+
saveWorktreeRunPorts(worktreePath, script.name, ports);
|
|
2051
2243
|
try {
|
|
2052
2244
|
const handle = await spawnWorkspaceScript(script.command, {
|
|
2053
2245
|
worktreePath,
|
|
@@ -2073,8 +2265,8 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
|
|
|
2073
2265
|
|
|
2074
2266
|
// src/store/setup-log.ts
|
|
2075
2267
|
import { createHash } from "crypto";
|
|
2076
|
-
import { existsSync as existsSync4, readFileSync as
|
|
2077
|
-
import { join as
|
|
2268
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
2269
|
+
import { join as join6 } from "path";
|
|
2078
2270
|
var EMPTY = {
|
|
2079
2271
|
output: "",
|
|
2080
2272
|
running: false,
|
|
@@ -2116,7 +2308,7 @@ function materialize(mem) {
|
|
|
2116
2308
|
};
|
|
2117
2309
|
}
|
|
2118
2310
|
function setupLogPath(threadId) {
|
|
2119
|
-
return
|
|
2311
|
+
return join6(threadsDir(), `${threadId}.setup.log.json`);
|
|
2120
2312
|
}
|
|
2121
2313
|
function setupLogKeyForWorktree(worktreePath) {
|
|
2122
2314
|
const norm = normalizeWorktreePath(worktreePath);
|
|
@@ -2135,7 +2327,7 @@ function persistNow(threadId) {
|
|
|
2135
2327
|
const mem = memory.get(threadId);
|
|
2136
2328
|
if (!mem) return;
|
|
2137
2329
|
try {
|
|
2138
|
-
|
|
2330
|
+
writeFileSync3(
|
|
2139
2331
|
setupLogPath(threadId),
|
|
2140
2332
|
`${JSON.stringify(materialize(mem))}
|
|
2141
2333
|
`,
|
|
@@ -2158,7 +2350,7 @@ function loadFromDisk(threadId) {
|
|
|
2158
2350
|
const path = setupLogPath(threadId);
|
|
2159
2351
|
if (!existsSync4(path)) return null;
|
|
2160
2352
|
try {
|
|
2161
|
-
const raw = JSON.parse(
|
|
2353
|
+
const raw = JSON.parse(readFileSync4(path, "utf8"));
|
|
2162
2354
|
return {
|
|
2163
2355
|
output: typeof raw.output === "string" ? raw.output : "",
|
|
2164
2356
|
running: Boolean(raw.running),
|
|
@@ -2217,8 +2409,8 @@ function finishSetupLog(threadId, exitCode, source) {
|
|
|
2217
2409
|
|
|
2218
2410
|
// src/store/run-log.ts
|
|
2219
2411
|
import { createHash as createHash2 } from "crypto";
|
|
2220
|
-
import { existsSync as existsSync5, readFileSync as
|
|
2221
|
-
import { join as
|
|
2412
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
2413
|
+
import { join as join7 } from "path";
|
|
2222
2414
|
var EMPTY2 = {
|
|
2223
2415
|
output: "",
|
|
2224
2416
|
running: false,
|
|
@@ -2257,7 +2449,7 @@ function safeScriptToken(scriptName) {
|
|
|
2257
2449
|
return token || "dev";
|
|
2258
2450
|
}
|
|
2259
2451
|
function runLogPath(key) {
|
|
2260
|
-
return
|
|
2452
|
+
return join7(threadsDir(), `${key}.run.log.json`);
|
|
2261
2453
|
}
|
|
2262
2454
|
function runLogKeyForWorktree(worktreePath, scriptName) {
|
|
2263
2455
|
const norm = normalizeWorktreePath(worktreePath);
|
|
@@ -2286,7 +2478,7 @@ function persistNow2(key) {
|
|
|
2286
2478
|
const mem = memory2.get(key);
|
|
2287
2479
|
if (!mem) return;
|
|
2288
2480
|
try {
|
|
2289
|
-
|
|
2481
|
+
writeFileSync4(runLogPath(key), `${JSON.stringify(materialize2(mem))}
|
|
2290
2482
|
`, "utf8");
|
|
2291
2483
|
} catch {
|
|
2292
2484
|
}
|
|
@@ -2305,7 +2497,7 @@ function loadFromDisk2(key) {
|
|
|
2305
2497
|
const path = runLogPath(key);
|
|
2306
2498
|
if (!existsSync5(path)) return null;
|
|
2307
2499
|
try {
|
|
2308
|
-
const raw = JSON.parse(
|
|
2500
|
+
const raw = JSON.parse(readFileSync5(path, "utf8"));
|
|
2309
2501
|
return {
|
|
2310
2502
|
output: typeof raw.output === "string" ? raw.output : "",
|
|
2311
2503
|
running: Boolean(raw.running),
|
|
@@ -2366,7 +2558,7 @@ function flushRunLog(key) {
|
|
|
2366
2558
|
|
|
2367
2559
|
// src/git/orphan-cleanup.ts
|
|
2368
2560
|
import { existsSync as existsSync6, readdirSync as readdirSync2, statSync } from "fs";
|
|
2369
|
-
import { join as
|
|
2561
|
+
import { join as join8 } from "path";
|
|
2370
2562
|
function isSideboardWorktreePath(path) {
|
|
2371
2563
|
return path.includes("/.sideboard/worktrees/") || path.includes("/sideboard/workspaces/");
|
|
2372
2564
|
}
|
|
@@ -2413,9 +2605,9 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
2413
2605
|
if (existsSync6(root)) {
|
|
2414
2606
|
for (const entry of readdirSync2(root, { withFileTypes: true })) {
|
|
2415
2607
|
if (!entry.isDirectory()) continue;
|
|
2416
|
-
const path =
|
|
2608
|
+
const path = join8(root, entry.name).replace(/\/$/, "");
|
|
2417
2609
|
if (known.has(path) || seen.has(path)) continue;
|
|
2418
|
-
if (!existsSync6(
|
|
2610
|
+
if (!existsSync6(join8(path, ".git"))) continue;
|
|
2419
2611
|
seen.add(path);
|
|
2420
2612
|
let mtimeMs = 0;
|
|
2421
2613
|
try {
|
|
@@ -2620,7 +2812,7 @@ async function applyThreadIntoMain(thread, opts) {
|
|
|
2620
2812
|
|
|
2621
2813
|
// src/git/clone-repo.ts
|
|
2622
2814
|
import { existsSync as existsSync7 } from "fs";
|
|
2623
|
-
import { basename as basename2, join as
|
|
2815
|
+
import { basename as basename2, join as join9 } from "path";
|
|
2624
2816
|
import { execa as execa4 } from "execa";
|
|
2625
2817
|
async function cloneRepoIntoSideboard(opts) {
|
|
2626
2818
|
const url = opts.url.trim();
|
|
@@ -2631,7 +2823,7 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
2631
2823
|
name = leaf || "repo";
|
|
2632
2824
|
}
|
|
2633
2825
|
name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
|
|
2634
|
-
const dest =
|
|
2826
|
+
const dest = join9(sideboardReposDir(), name);
|
|
2635
2827
|
if (existsSync7(dest)) {
|
|
2636
2828
|
const repoPath2 = await resolveRepoRoot(dest);
|
|
2637
2829
|
const workspace2 = await ensureWorkspace(repoPath2);
|
|
@@ -2654,14 +2846,14 @@ function isAdoptableRunScriptRequest(req) {
|
|
|
2654
2846
|
}
|
|
2655
2847
|
|
|
2656
2848
|
// src/store/desktop-host.ts
|
|
2657
|
-
import { readFileSync as
|
|
2658
|
-
import { join as
|
|
2849
|
+
import { readFileSync as readFileSync6, unlinkSync, writeFileSync as writeFileSync5 } from "fs";
|
|
2850
|
+
import { join as join10 } from "path";
|
|
2659
2851
|
function desktopHostPidPath() {
|
|
2660
|
-
return
|
|
2852
|
+
return join10(appDataDir(), "desktop-host.pid");
|
|
2661
2853
|
}
|
|
2662
2854
|
function readDesktopHostPid() {
|
|
2663
2855
|
try {
|
|
2664
|
-
const pid = Number.parseInt(
|
|
2856
|
+
const pid = Number.parseInt(readFileSync6(desktopHostPidPath(), "utf8").trim(), 10);
|
|
2665
2857
|
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
2666
2858
|
return pid;
|
|
2667
2859
|
} catch {
|
|
@@ -2734,12 +2926,12 @@ function notifyParentOfChildHalt(child, status, send) {
|
|
|
2734
2926
|
import {
|
|
2735
2927
|
appendFileSync as appendFileSync2,
|
|
2736
2928
|
existsSync as existsSync8,
|
|
2737
|
-
mkdirSync as
|
|
2929
|
+
mkdirSync as mkdirSync5,
|
|
2738
2930
|
readdirSync as readdirSync3,
|
|
2739
|
-
readFileSync as
|
|
2740
|
-
writeFileSync as
|
|
2931
|
+
readFileSync as readFileSync7,
|
|
2932
|
+
writeFileSync as writeFileSync6
|
|
2741
2933
|
} from "fs";
|
|
2742
|
-
import { dirname as
|
|
2934
|
+
import { dirname as dirname4, join as join11 } from "path";
|
|
2743
2935
|
|
|
2744
2936
|
// src/mcp/wait-for-turn.ts
|
|
2745
2937
|
var MCP_WAIT_FOR_TURN_MAX_MS = 45e3;
|
|
@@ -2815,11 +3007,11 @@ function jobTreeAlive(pid) {
|
|
|
2815
3007
|
}
|
|
2816
3008
|
function readIntFile(file) {
|
|
2817
3009
|
if (!existsSync8(file)) return null;
|
|
2818
|
-
const n = Number.parseInt(
|
|
3010
|
+
const n = Number.parseInt(readFileSync7(file, "utf8").trim(), 10);
|
|
2819
3011
|
return Number.isInteger(n) ? n : null;
|
|
2820
3012
|
}
|
|
2821
3013
|
function jobDir(root, id, legacy = false) {
|
|
2822
|
-
return
|
|
3014
|
+
return join11(root, legacy ? LEGACY_DETACHED_JOBS_DIR : DETACHED_JOBS_DIR, id);
|
|
2823
3015
|
}
|
|
2824
3016
|
function resolveJobDir(root, id) {
|
|
2825
3017
|
const modern = jobDir(root, id, false);
|
|
@@ -2829,7 +3021,7 @@ function resolveJobDir(root, id) {
|
|
|
2829
3021
|
return modern;
|
|
2830
3022
|
}
|
|
2831
3023
|
function listJobIdsIn(root, rel) {
|
|
2832
|
-
const dir =
|
|
3024
|
+
const dir = join11(root, rel);
|
|
2833
3025
|
if (!existsSync8(dir)) return [];
|
|
2834
3026
|
return readdirSync3(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && JOB_ID_RE.test(e.name)).map((e) => e.name);
|
|
2835
3027
|
}
|
|
@@ -2843,7 +3035,7 @@ function listRunningDetachedJobs(worktreePath) {
|
|
|
2843
3035
|
const running = [];
|
|
2844
3036
|
for (const id of ids) {
|
|
2845
3037
|
const dir = resolveJobDir(root, id);
|
|
2846
|
-
const pid = readIntFile(
|
|
3038
|
+
const pid = readIntFile(join11(dir, "pid"));
|
|
2847
3039
|
if (jobTreeAlive(pid)) running.push(id);
|
|
2848
3040
|
}
|
|
2849
3041
|
return running.sort();
|
|
@@ -2927,7 +3119,7 @@ function planJobContinue(opts) {
|
|
|
2927
3119
|
}
|
|
2928
3120
|
function tailProgress(logFile, maxLines = 12) {
|
|
2929
3121
|
if (!existsSync8(logFile)) return "(no log yet)";
|
|
2930
|
-
const lines =
|
|
3122
|
+
const lines = readFileSync7(logFile, "utf8").split("\n");
|
|
2931
3123
|
return lines.slice(-maxLines).join("\n");
|
|
2932
3124
|
}
|
|
2933
3125
|
function lastProgressLine(progress) {
|
|
@@ -2936,7 +3128,7 @@ function lastProgressLine(progress) {
|
|
|
2936
3128
|
}
|
|
2937
3129
|
function readLogLines(file) {
|
|
2938
3130
|
if (!existsSync8(file)) return [];
|
|
2939
|
-
const lines =
|
|
3131
|
+
const lines = readFileSync7(file, "utf8").split("\n");
|
|
2940
3132
|
if (lines.length && lines[lines.length - 1] === "") lines.pop();
|
|
2941
3133
|
return lines;
|
|
2942
3134
|
}
|
|
@@ -2947,15 +3139,15 @@ function takeDelta(logFile, cursorFile) {
|
|
|
2947
3139
|
return { delta: capJobLogDelta(lines.slice(start).join("\n")), nextCursor: lines.length };
|
|
2948
3140
|
}
|
|
2949
3141
|
function snapshotJob(dir) {
|
|
2950
|
-
const pid = readIntFile(
|
|
3142
|
+
const pid = readIntFile(join11(dir, "pid"));
|
|
2951
3143
|
const running = jobTreeAlive(pid);
|
|
2952
3144
|
return {
|
|
2953
3145
|
pid,
|
|
2954
3146
|
running,
|
|
2955
|
-
exitCode: readIntFile(
|
|
2956
|
-
log:
|
|
2957
|
-
cursor:
|
|
2958
|
-
progress: tailProgress(
|
|
3147
|
+
exitCode: readIntFile(join11(dir, "exit")),
|
|
3148
|
+
log: join11(dir, "log"),
|
|
3149
|
+
cursor: join11(dir, "present.cursor"),
|
|
3150
|
+
progress: tailProgress(join11(dir, "log"))
|
|
2959
3151
|
};
|
|
2960
3152
|
}
|
|
2961
3153
|
function toResult(id, snap, extra) {
|
|
@@ -2964,8 +3156,8 @@ function toResult(id, snap, extra) {
|
|
|
2964
3156
|
const stillRunning = snap.running && !ok;
|
|
2965
3157
|
const { delta, nextCursor } = takeDelta(snap.log, snap.cursor);
|
|
2966
3158
|
try {
|
|
2967
|
-
|
|
2968
|
-
|
|
3159
|
+
mkdirSync5(dirname4(snap.cursor), { recursive: true });
|
|
3160
|
+
writeFileSync6(snap.cursor, `${nextCursor}
|
|
2969
3161
|
`);
|
|
2970
3162
|
} catch {
|
|
2971
3163
|
}
|
|
@@ -2986,7 +3178,7 @@ function toResult(id, snap, extra) {
|
|
|
2986
3178
|
};
|
|
2987
3179
|
}
|
|
2988
3180
|
async function sleepMs(ms) {
|
|
2989
|
-
await new Promise((
|
|
3181
|
+
await new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2990
3182
|
}
|
|
2991
3183
|
async function waitForDetachedJob(cwd, id, opts) {
|
|
2992
3184
|
const jobId = sanitizeDetachedJobId(id);
|
|
@@ -3040,7 +3232,7 @@ function killProcessTree(pid, signal) {
|
|
|
3040
3232
|
}
|
|
3041
3233
|
function appendJobLog(logFile, line) {
|
|
3042
3234
|
try {
|
|
3043
|
-
|
|
3235
|
+
mkdirSync5(dirname4(logFile), { recursive: true });
|
|
3044
3236
|
const body = line.endsWith("\n") ? line : `${line}
|
|
3045
3237
|
`;
|
|
3046
3238
|
appendFileSync2(logFile, body);
|
|
@@ -3089,10 +3281,10 @@ async function stopDetachedJob(cwd, id, opts) {
|
|
|
3089
3281
|
killProcessTree(targetPid, "SIGKILL");
|
|
3090
3282
|
await sleep(100);
|
|
3091
3283
|
}
|
|
3092
|
-
const exitFile =
|
|
3284
|
+
const exitFile = join11(dir, "exit");
|
|
3093
3285
|
if (!existsSync8(exitFile)) {
|
|
3094
3286
|
try {
|
|
3095
|
-
|
|
3287
|
+
writeFileSync6(exitFile, "1\n");
|
|
3096
3288
|
} catch {
|
|
3097
3289
|
}
|
|
3098
3290
|
}
|
|
@@ -3899,8 +4091,8 @@ import {
|
|
|
3899
4091
|
existsSync as existsSync10,
|
|
3900
4092
|
renameSync,
|
|
3901
4093
|
unlinkSync as unlinkSync2,
|
|
3902
|
-
writeFileSync as
|
|
3903
|
-
readFileSync as
|
|
4094
|
+
writeFileSync as writeFileSync7,
|
|
4095
|
+
readFileSync as readFileSync8
|
|
3904
4096
|
} from "fs";
|
|
3905
4097
|
var buffers = /* @__PURE__ */ new Map();
|
|
3906
4098
|
var FLUSH_MS = 800;
|
|
@@ -3977,7 +4169,7 @@ function writeTurnLive(threadId, progress) {
|
|
|
3977
4169
|
const path = threadLivePath(threadId);
|
|
3978
4170
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
3979
4171
|
try {
|
|
3980
|
-
|
|
4172
|
+
writeFileSync7(tmp, JSON.stringify(progress), "utf8");
|
|
3981
4173
|
renameSync(tmp, path);
|
|
3982
4174
|
} catch {
|
|
3983
4175
|
try {
|
|
@@ -3990,7 +4182,7 @@ function readTurnLive(threadId) {
|
|
|
3990
4182
|
const path = threadLivePath(threadId);
|
|
3991
4183
|
if (!existsSync10(path)) return null;
|
|
3992
4184
|
try {
|
|
3993
|
-
const raw = JSON.parse(
|
|
4185
|
+
const raw = JSON.parse(readFileSync8(path, "utf8"));
|
|
3994
4186
|
if (!raw || typeof raw.summary !== "string") return null;
|
|
3995
4187
|
return raw;
|
|
3996
4188
|
} catch {
|
|
@@ -4214,23 +4406,23 @@ import {
|
|
|
4214
4406
|
existsSync as existsSync11,
|
|
4215
4407
|
mkdtempSync,
|
|
4216
4408
|
readdirSync as readdirSync4,
|
|
4217
|
-
readFileSync as
|
|
4409
|
+
readFileSync as readFileSync9,
|
|
4218
4410
|
rmSync
|
|
4219
4411
|
} from "fs";
|
|
4220
4412
|
import { tmpdir } from "os";
|
|
4221
|
-
import { join as
|
|
4413
|
+
import { join as join12 } from "path";
|
|
4222
4414
|
import { createRequire } from "module";
|
|
4223
|
-
var CONDUCTOR_APP_SUPPORT =
|
|
4415
|
+
var CONDUCTOR_APP_SUPPORT = join12(
|
|
4224
4416
|
process.env.HOME ?? "",
|
|
4225
4417
|
"Library",
|
|
4226
4418
|
"Application Support",
|
|
4227
4419
|
"com.conductor.app"
|
|
4228
4420
|
);
|
|
4229
|
-
var CONDUCTOR_DB =
|
|
4230
|
-
var CURSOR_SDK_STORE =
|
|
4421
|
+
var CONDUCTOR_DB = join12(CONDUCTOR_APP_SUPPORT, "conductor.db");
|
|
4422
|
+
var CURSOR_SDK_STORE = join12(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
|
|
4231
4423
|
function thisModuleFile() {
|
|
4232
4424
|
const cjsFile = typeof __filename !== "undefined" ? __filename : "";
|
|
4233
|
-
return cjsFile || process.argv[1] ||
|
|
4425
|
+
return cjsFile || process.argv[1] || join12(process.cwd(), "package.json");
|
|
4234
4426
|
}
|
|
4235
4427
|
function openReadonlySqlite(file) {
|
|
4236
4428
|
const req = createRequire(thisModuleFile());
|
|
@@ -4258,11 +4450,11 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
4258
4450
|
return null;
|
|
4259
4451
|
}
|
|
4260
4452
|
for (const hash of hashes) {
|
|
4261
|
-
const agentsFile =
|
|
4453
|
+
const agentsFile = join12(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
4262
4454
|
if (!existsSync11(agentsFile)) continue;
|
|
4263
4455
|
let text;
|
|
4264
4456
|
try {
|
|
4265
|
-
text =
|
|
4457
|
+
text = readFileSync9(agentsFile, "utf8");
|
|
4266
4458
|
} catch {
|
|
4267
4459
|
continue;
|
|
4268
4460
|
}
|
|
@@ -4315,8 +4507,8 @@ function listConductorWorkspaces() {
|
|
|
4315
4507
|
if (!existsSync11(CONDUCTOR_DB)) {
|
|
4316
4508
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
4317
4509
|
}
|
|
4318
|
-
const tmp = mkdtempSync(
|
|
4319
|
-
const snapshot =
|
|
4510
|
+
const tmp = mkdtempSync(join12(tmpdir(), "sideboard-conductor-"));
|
|
4511
|
+
const snapshot = join12(tmp, "conductor.db");
|
|
4320
4512
|
try {
|
|
4321
4513
|
copyFileSync2(CONDUCTOR_DB, snapshot);
|
|
4322
4514
|
for (const suffix of ["-wal", "-shm"]) {
|
|
@@ -4406,8 +4598,8 @@ function importConductorWorkspace(workspaceId) {
|
|
|
4406
4598
|
if (!existsSync11(CONDUCTOR_DB)) {
|
|
4407
4599
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
4408
4600
|
}
|
|
4409
|
-
const tmp = mkdtempSync(
|
|
4410
|
-
const snapshot =
|
|
4601
|
+
const tmp = mkdtempSync(join12(tmpdir(), "sideboard-conductor-"));
|
|
4602
|
+
const snapshot = join12(tmp, "conductor.db");
|
|
4411
4603
|
try {
|
|
4412
4604
|
copyFileSync2(CONDUCTOR_DB, snapshot);
|
|
4413
4605
|
for (const suffix of ["-wal", "-shm"]) {
|
|
@@ -4787,8 +4979,8 @@ async function createPrStack(input, onSetupLine) {
|
|
|
4787
4979
|
}
|
|
4788
4980
|
|
|
4789
4981
|
// src/diff/diff.ts
|
|
4790
|
-
import { existsSync as existsSync13, mkdirSync as
|
|
4791
|
-
import { dirname as
|
|
4982
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync10, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
|
|
4983
|
+
import { dirname as dirname5, join as join13 } from "path";
|
|
4792
4984
|
async function inspectGitWorktree(worktreePath) {
|
|
4793
4985
|
if (!worktreePath || !existsSync13(worktreePath)) return "missing_worktree";
|
|
4794
4986
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
@@ -4934,11 +5126,11 @@ new file mode 100644
|
|
|
4934
5126
|
};
|
|
4935
5127
|
}
|
|
4936
5128
|
async function untrackedPatch(worktreePath, path, maxHunk) {
|
|
4937
|
-
const abs =
|
|
5129
|
+
const abs = join13(worktreePath, path);
|
|
4938
5130
|
try {
|
|
4939
5131
|
const st = statSync2(abs);
|
|
4940
5132
|
if (st.isFile() && st.size > maxHunk) {
|
|
4941
|
-
const buf =
|
|
5133
|
+
const buf = readFileSync10(abs).subarray(0, maxHunk);
|
|
4942
5134
|
return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
|
|
4943
5135
|
}
|
|
4944
5136
|
} catch {
|
|
@@ -5406,7 +5598,7 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
5406
5598
|
}
|
|
5407
5599
|
function statWorktreePath(worktreePath, relativePath) {
|
|
5408
5600
|
assertSafeRelativePath(relativePath);
|
|
5409
|
-
const abs =
|
|
5601
|
+
const abs = join13(worktreePath, relativePath);
|
|
5410
5602
|
if (!existsSync13(abs)) return "missing";
|
|
5411
5603
|
const st = statSync2(abs);
|
|
5412
5604
|
if (st.isDirectory()) return "dir";
|
|
@@ -5448,7 +5640,7 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
|
|
|
5448
5640
|
function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
5449
5641
|
assertSafeRelativePath(relativePath);
|
|
5450
5642
|
const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
|
|
5451
|
-
const abs =
|
|
5643
|
+
const abs = join13(worktreePath, relativePath);
|
|
5452
5644
|
const st = statSync2(abs);
|
|
5453
5645
|
if (!st.isFile()) {
|
|
5454
5646
|
throw new Error(`Not a file: ${relativePath}`);
|
|
@@ -5458,7 +5650,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
5458
5650
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
5459
5651
|
);
|
|
5460
5652
|
}
|
|
5461
|
-
const buf =
|
|
5653
|
+
const buf = readFileSync10(abs);
|
|
5462
5654
|
return {
|
|
5463
5655
|
path: relativePath,
|
|
5464
5656
|
contentBase64: buf.toString("base64"),
|
|
@@ -5468,12 +5660,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
5468
5660
|
function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
5469
5661
|
assertSafeRelativePath(relativePath);
|
|
5470
5662
|
const maxBytes = opts?.maxBytes ?? 2e5;
|
|
5471
|
-
const abs =
|
|
5663
|
+
const abs = join13(worktreePath, relativePath);
|
|
5472
5664
|
const st = statSync2(abs);
|
|
5473
5665
|
if (!st.isFile()) {
|
|
5474
5666
|
throw new Error(`Not a file: ${relativePath}`);
|
|
5475
5667
|
}
|
|
5476
|
-
const buf =
|
|
5668
|
+
const buf = readFileSync10(abs);
|
|
5477
5669
|
if (isImageRelativePath(relativePath)) {
|
|
5478
5670
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
5479
5671
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -5516,9 +5708,9 @@ function assertSafeRelativePath(relativePath) {
|
|
|
5516
5708
|
}
|
|
5517
5709
|
function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
5518
5710
|
assertSafeRelativePath(relativePath);
|
|
5519
|
-
const abs =
|
|
5520
|
-
|
|
5521
|
-
|
|
5711
|
+
const abs = join13(worktreePath, relativePath);
|
|
5712
|
+
mkdirSync6(dirname5(abs), { recursive: true });
|
|
5713
|
+
writeFileSync8(abs, content, "utf8");
|
|
5522
5714
|
return { path: relativePath };
|
|
5523
5715
|
}
|
|
5524
5716
|
async function getWorktreeDirtyStat(worktreePath) {
|
|
@@ -5740,9 +5932,9 @@ async function confirmLand(thread, opts) {
|
|
|
5740
5932
|
}
|
|
5741
5933
|
|
|
5742
5934
|
// src/skills/discover.ts
|
|
5743
|
-
import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as
|
|
5935
|
+
import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync3 } from "fs";
|
|
5744
5936
|
import { homedir } from "os";
|
|
5745
|
-
import { join as
|
|
5937
|
+
import { join as join14 } from "path";
|
|
5746
5938
|
|
|
5747
5939
|
// src/skills/bundled/long-running.ts
|
|
5748
5940
|
var LONG_RUNNING_SKILL_COMMAND = "long-running";
|
|
@@ -5879,7 +6071,7 @@ function parseFrontmatter(content) {
|
|
|
5879
6071
|
}
|
|
5880
6072
|
function readSkill(skillMd, source) {
|
|
5881
6073
|
try {
|
|
5882
|
-
const content =
|
|
6074
|
+
const content = readFileSync11(skillMd, "utf8");
|
|
5883
6075
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
5884
6076
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
5885
6077
|
const name = fmName || dirName;
|
|
@@ -5907,7 +6099,7 @@ function scanSkillsDir(dir, source, out) {
|
|
|
5907
6099
|
}
|
|
5908
6100
|
for (const entry of entries) {
|
|
5909
6101
|
if (entry.startsWith(".")) continue;
|
|
5910
|
-
const skillMd =
|
|
6102
|
+
const skillMd = join14(dir, entry, "SKILL.md");
|
|
5911
6103
|
if (!existsSync14(skillMd)) continue;
|
|
5912
6104
|
try {
|
|
5913
6105
|
if (!statSync3(skillMd).isFile()) continue;
|
|
@@ -5929,12 +6121,12 @@ function scanClaudePluginSkills(pluginsRoot, out) {
|
|
|
5929
6121
|
return;
|
|
5930
6122
|
}
|
|
5931
6123
|
if (lookingForSkillsDir && entries.includes("SKILL.md")) {
|
|
5932
|
-
const skill = readSkill(
|
|
6124
|
+
const skill = readSkill(join14(dir, "SKILL.md"), "cli");
|
|
5933
6125
|
if (skill) out.push(skill);
|
|
5934
6126
|
}
|
|
5935
6127
|
for (const entry of entries) {
|
|
5936
6128
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
5937
|
-
const full =
|
|
6129
|
+
const full = join14(dir, entry);
|
|
5938
6130
|
try {
|
|
5939
6131
|
if (!statSync3(full).isDirectory()) continue;
|
|
5940
6132
|
} catch {
|
|
@@ -5954,17 +6146,17 @@ function discoverSkills(worktreePath) {
|
|
|
5954
6146
|
const home = homedir();
|
|
5955
6147
|
const collected = [];
|
|
5956
6148
|
for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
|
|
5957
|
-
scanSkillsDir(
|
|
6149
|
+
scanSkillsDir(join14(worktreePath, rel), "workspace", collected);
|
|
5958
6150
|
}
|
|
5959
6151
|
for (const abs of [
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
6152
|
+
join14(home, ".claude/skills"),
|
|
6153
|
+
join14(home, ".cursor/skills"),
|
|
6154
|
+
join14(home, ".sideboard/skills"),
|
|
6155
|
+
join14(home, ".brightsy/skills")
|
|
5964
6156
|
]) {
|
|
5965
6157
|
scanSkillsDir(abs, "user", collected);
|
|
5966
6158
|
}
|
|
5967
|
-
scanClaudePluginSkills(
|
|
6159
|
+
scanClaudePluginSkills(join14(home, ".claude/plugins"), collected);
|
|
5968
6160
|
collected.push(...bundledSkills());
|
|
5969
6161
|
const rank = {
|
|
5970
6162
|
workspace: 0,
|
|
@@ -5990,7 +6182,7 @@ function readSkillBody(skillPath, maxChars = 12e3) {
|
|
|
5990
6182
|
\u2026(truncated)` : body;
|
|
5991
6183
|
}
|
|
5992
6184
|
}
|
|
5993
|
-
const raw =
|
|
6185
|
+
const raw = readFileSync11(skillPath, "utf8");
|
|
5994
6186
|
if (raw.startsWith("---")) {
|
|
5995
6187
|
const end = raw.indexOf("\n---", 3);
|
|
5996
6188
|
if (end >= 0) {
|
|
@@ -6136,11 +6328,11 @@ function shiftQueuedItem(queue, queueAttachments) {
|
|
|
6136
6328
|
|
|
6137
6329
|
// src/agents/instructions.ts
|
|
6138
6330
|
import { existsSync as existsSync16 } from "fs";
|
|
6139
|
-
import { join as
|
|
6331
|
+
import { join as join16 } from "path";
|
|
6140
6332
|
|
|
6141
6333
|
// src/skills/detached-job-path.ts
|
|
6142
6334
|
import { existsSync as existsSync15 } from "fs";
|
|
6143
|
-
import { dirname as
|
|
6335
|
+
import { dirname as dirname6, join as join15 } from "path";
|
|
6144
6336
|
import { fileURLToPath } from "url";
|
|
6145
6337
|
var DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
|
|
6146
6338
|
function compiledModuleDir() {
|
|
@@ -6149,7 +6341,7 @@ function compiledModuleDir() {
|
|
|
6149
6341
|
try {
|
|
6150
6342
|
const url = import.meta.url;
|
|
6151
6343
|
if (typeof url === "string" && url.length > 0) {
|
|
6152
|
-
return
|
|
6344
|
+
return dirname6(fileURLToPath(url));
|
|
6153
6345
|
}
|
|
6154
6346
|
} catch {
|
|
6155
6347
|
}
|
|
@@ -6157,7 +6349,7 @@ function compiledModuleDir() {
|
|
|
6157
6349
|
}
|
|
6158
6350
|
function detachedJobScriptIn(dir) {
|
|
6159
6351
|
for (const name of DETACHED_JOB_FILENAMES) {
|
|
6160
|
-
const script =
|
|
6352
|
+
const script = join15(dir, "scripts", name);
|
|
6161
6353
|
if (existsSync15(script)) return script;
|
|
6162
6354
|
}
|
|
6163
6355
|
return null;
|
|
@@ -6175,7 +6367,7 @@ function resolveDetachedJobScript() {
|
|
|
6175
6367
|
for (let i = 0; i < 8; i++) {
|
|
6176
6368
|
const candidate = detachedJobScriptIn(dir);
|
|
6177
6369
|
if (candidate) return candidate;
|
|
6178
|
-
const parent =
|
|
6370
|
+
const parent = dirname6(dir);
|
|
6179
6371
|
if (parent === dir) break;
|
|
6180
6372
|
dir = parent;
|
|
6181
6373
|
}
|
|
@@ -6280,9 +6472,9 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
6280
6472
|
);
|
|
6281
6473
|
} else {
|
|
6282
6474
|
const slug = opts?.githubSlug ?? "<origin-owner/name>";
|
|
6283
|
-
const
|
|
6475
|
+
const resolve2 = opts?.githubSlug ? "" : " Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree.";
|
|
6284
6476
|
lines.push(
|
|
6285
|
-
`- 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.${
|
|
6477
|
+
`- 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\`.`
|
|
6286
6478
|
);
|
|
6287
6479
|
}
|
|
6288
6480
|
lines.push(
|
|
@@ -6372,9 +6564,9 @@ function formatPrGateDirective() {
|
|
|
6372
6564
|
}
|
|
6373
6565
|
function formatProcessGuideDirective(opts) {
|
|
6374
6566
|
const worktree = opts?.worktreePath?.trim();
|
|
6375
|
-
const hasGraphSkill = !!worktree && existsSync16(
|
|
6376
|
-
const hasReviewSkill = !!worktree && existsSync16(
|
|
6377
|
-
const hasInstallSkill = !!worktree && existsSync16(
|
|
6567
|
+
const hasGraphSkill = !!worktree && existsSync16(join16(worktree, ".claude/skills/graph-engineering/SKILL.md"));
|
|
6568
|
+
const hasReviewSkill = !!worktree && existsSync16(join16(worktree, ".claude/skills/review/SKILL.md"));
|
|
6569
|
+
const hasInstallSkill = !!worktree && existsSync16(join16(worktree, ".claude/skills/worktree-install/SKILL.md"));
|
|
6378
6570
|
return [
|
|
6379
6571
|
"Process guides (recurring work only):",
|
|
6380
6572
|
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,
|
|
@@ -6575,8 +6767,8 @@ async function syncThreadBranchFromGit(threadId) {
|
|
|
6575
6767
|
|
|
6576
6768
|
// src/store/schedules.ts
|
|
6577
6769
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
6578
|
-
import { existsSync as existsSync17, readFileSync as
|
|
6579
|
-
import { join as
|
|
6770
|
+
import { existsSync as existsSync17, readFileSync as readFileSync12 } from "fs";
|
|
6771
|
+
import { join as join17 } from "path";
|
|
6580
6772
|
import { Cron } from "croner";
|
|
6581
6773
|
var DURATION_RE = /^(\d+)(s|m|h|d)$/i;
|
|
6582
6774
|
var UNIT_MS = {
|
|
@@ -6586,7 +6778,7 @@ var UNIT_MS = {
|
|
|
6586
6778
|
d: 864e5
|
|
6587
6779
|
};
|
|
6588
6780
|
function schedulesPath() {
|
|
6589
|
-
return
|
|
6781
|
+
return join17(appDataDir(), "schedules.json");
|
|
6590
6782
|
}
|
|
6591
6783
|
function parseDurationMs(every) {
|
|
6592
6784
|
const m = every.trim().match(DURATION_RE);
|
|
@@ -6688,7 +6880,7 @@ function readAll() {
|
|
|
6688
6880
|
const path = schedulesPath();
|
|
6689
6881
|
if (!existsSync17(path)) return [];
|
|
6690
6882
|
try {
|
|
6691
|
-
const parsed = JSON.parse(
|
|
6883
|
+
const parsed = JSON.parse(readFileSync12(path, "utf8"));
|
|
6692
6884
|
if (!Array.isArray(parsed)) return [];
|
|
6693
6885
|
return parsed.map((row) => {
|
|
6694
6886
|
try {
|
|
@@ -6811,7 +7003,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
6811
7003
|
${prompt}`;
|
|
6812
7004
|
}
|
|
6813
7005
|
async function defaultDeps() {
|
|
6814
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
7006
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-MIIKXPHH.js");
|
|
6815
7007
|
const orch = getOrchestrator2();
|
|
6816
7008
|
return {
|
|
6817
7009
|
findThread: (id) => findThreadByRef(id),
|
|
@@ -6876,14 +7068,14 @@ function armOne(schedule) {
|
|
|
6876
7068
|
if (!schedule.enabled) return;
|
|
6877
7069
|
const at = new Date(schedule.nextRunAt);
|
|
6878
7070
|
if (Number.isNaN(at.getTime())) return;
|
|
6879
|
-
const
|
|
6880
|
-
if (
|
|
7071
|
+
const delay2 = Math.max(0, at.getTime() - Date.now());
|
|
7072
|
+
if (delay2 <= 250) {
|
|
6881
7073
|
void fireSchedule(schedule.id).then(() => {
|
|
6882
7074
|
if (isThisProcessDesktopHost()) armSchedules();
|
|
6883
7075
|
});
|
|
6884
7076
|
return;
|
|
6885
7077
|
}
|
|
6886
|
-
const capped = Math.min(
|
|
7078
|
+
const capped = Math.min(delay2, SET_TIMEOUT_MAX);
|
|
6887
7079
|
const timer = setTimeout(() => {
|
|
6888
7080
|
timers.delete(schedule.id);
|
|
6889
7081
|
const live = getSchedule(schedule.id);
|
|
@@ -7267,8 +7459,8 @@ var Orchestrator = class {
|
|
|
7267
7459
|
scheduleQuotaResume(threadId, resumeAt) {
|
|
7268
7460
|
this.clearQuotaResumeTimer(threadId);
|
|
7269
7461
|
updateThread(threadId, { quotaResumeAt: resumeAt.toISOString() });
|
|
7270
|
-
const
|
|
7271
|
-
const capped = Math.min(
|
|
7462
|
+
const delay2 = Math.max(5e3, resumeAt.getTime() - Date.now());
|
|
7463
|
+
const capped = Math.min(delay2, 2147483647);
|
|
7272
7464
|
const timer = setTimeout(() => {
|
|
7273
7465
|
this.quotaResumeTimers.delete(threadId);
|
|
7274
7466
|
void this.resumeAfterQuotaWait(threadId);
|
|
@@ -8323,6 +8515,7 @@ var Orchestrator = class {
|
|
|
8323
8515
|
return { port: shared.devPort, scriptName: resolvedName, ports: [shared.devPort] };
|
|
8324
8516
|
}
|
|
8325
8517
|
}
|
|
8518
|
+
const preferredPorts = active?.ports?.length ? active.ports : active?.port != null ? [active.port] : void 0;
|
|
8326
8519
|
if (active && !existing) {
|
|
8327
8520
|
killListenersOnPorts(collectActiveRunPorts([active]));
|
|
8328
8521
|
this.syncWorktreeRuns(
|
|
@@ -8361,7 +8554,7 @@ var Orchestrator = class {
|
|
|
8361
8554
|
thread.repoPath,
|
|
8362
8555
|
thread.worktreePath,
|
|
8363
8556
|
runOutput.push,
|
|
8364
|
-
{ scriptName: resolvedName }
|
|
8557
|
+
{ scriptName: resolvedName, preferredPorts }
|
|
8365
8558
|
);
|
|
8366
8559
|
} catch (err) {
|
|
8367
8560
|
runOutput.flush();
|
|
@@ -8810,7 +9003,7 @@ var Orchestrator = class {
|
|
|
8810
9003
|
return thread;
|
|
8811
9004
|
}
|
|
8812
9005
|
const start = Date.now();
|
|
8813
|
-
return new Promise((
|
|
9006
|
+
return new Promise((resolve2, reject) => {
|
|
8814
9007
|
let settled = false;
|
|
8815
9008
|
let timer;
|
|
8816
9009
|
const finish = (fn) => {
|
|
@@ -8828,7 +9021,7 @@ var Orchestrator = class {
|
|
|
8828
9021
|
finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
|
|
8829
9022
|
return;
|
|
8830
9023
|
}
|
|
8831
|
-
finish(() =>
|
|
9024
|
+
finish(() => resolve2(latest));
|
|
8832
9025
|
return;
|
|
8833
9026
|
}
|
|
8834
9027
|
if (event.type === "status_changed") {
|
|
@@ -8838,7 +9031,7 @@ var Orchestrator = class {
|
|
|
8838
9031
|
return;
|
|
8839
9032
|
}
|
|
8840
9033
|
if (!["running", "queued"].includes(latest.status)) {
|
|
8841
|
-
finish(() =>
|
|
9034
|
+
finish(() => resolve2(latest));
|
|
8842
9035
|
}
|
|
8843
9036
|
}
|
|
8844
9037
|
});
|
|
@@ -8850,12 +9043,12 @@ var Orchestrator = class {
|
|
|
8850
9043
|
}
|
|
8851
9044
|
const current = this.healStaleReportedActivity(raw);
|
|
8852
9045
|
if (!this.threadLooksLive(current)) {
|
|
8853
|
-
finish(() =>
|
|
9046
|
+
finish(() => resolve2(current));
|
|
8854
9047
|
return;
|
|
8855
9048
|
}
|
|
8856
9049
|
if (Date.now() - start > timeoutMs) {
|
|
8857
9050
|
if (opts?.resolveIfStillRunning) {
|
|
8858
|
-
finish(() =>
|
|
9051
|
+
finish(() => resolve2(current));
|
|
8859
9052
|
} else {
|
|
8860
9053
|
finish(() => reject(new Error("wait_for_turn timed out")));
|
|
8861
9054
|
}
|