@ricsam/r5d-worker 0.0.15 → 0.0.16
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/cjs/main.cjs +122 -78
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/main.mjs +122 -78
- package/dist/mjs/package.json +1 -1
- package/package.json +1 -1
package/dist/cjs/main.cjs
CHANGED
|
@@ -58,7 +58,7 @@ const LEGACY_BUILD_IT_NOW_REMOTE_NAME = "build-it-now";
|
|
|
58
58
|
const activeProcesses = /* @__PURE__ */ new Map();
|
|
59
59
|
const cancelledProcessRuns = /* @__PURE__ */ new Set();
|
|
60
60
|
const activePtys = /* @__PURE__ */ new Map();
|
|
61
|
-
|
|
61
|
+
let currentWorkerSocket = null;
|
|
62
62
|
let containerRegistryCredential = null;
|
|
63
63
|
let visibleGitIdentity = null;
|
|
64
64
|
function defaultConfigPath() {
|
|
@@ -690,31 +690,6 @@ function resolveProjectFilePath(branchPath, virtualPath) {
|
|
|
690
690
|
assertInsideBranch(branchPath, absolutePath);
|
|
691
691
|
return { absolutePath, virtualPath: repoRelativePath ? `/${repoRelativePath}` : "/", repoRelativePath };
|
|
692
692
|
}
|
|
693
|
-
function branchLockKey(projectId, branchName) {
|
|
694
|
-
return `${projectId}:${branchName}`;
|
|
695
|
-
}
|
|
696
|
-
async function withBranchLock(projectId, branchName, fn) {
|
|
697
|
-
const key = branchLockKey(projectId, branchName);
|
|
698
|
-
const previous = branchLocks.get(key) ?? Promise.resolve();
|
|
699
|
-
let release = () => {
|
|
700
|
-
};
|
|
701
|
-
const next = new Promise((resolve) => {
|
|
702
|
-
release = resolve;
|
|
703
|
-
});
|
|
704
|
-
const queued = previous.catch(() => {
|
|
705
|
-
}).then(() => next);
|
|
706
|
-
branchLocks.set(key, queued);
|
|
707
|
-
await previous.catch(() => {
|
|
708
|
-
});
|
|
709
|
-
try {
|
|
710
|
-
return await fn();
|
|
711
|
-
} finally {
|
|
712
|
-
release();
|
|
713
|
-
if (branchLocks.get(key) === queued) {
|
|
714
|
-
branchLocks.delete(key);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
693
|
function resolveRemoteUrl(baseUrl, remoteUrl) {
|
|
719
694
|
if (/^https?:\/\//i.test(remoteUrl)) {
|
|
720
695
|
return remoteUrl;
|
|
@@ -1660,7 +1635,16 @@ async function executeCommand(input) {
|
|
|
1660
1635
|
...planProcessEnv
|
|
1661
1636
|
}
|
|
1662
1637
|
});
|
|
1663
|
-
activeProcesses.set(input.message.runId, {
|
|
1638
|
+
activeProcesses.set(input.message.runId, {
|
|
1639
|
+
process: subprocess,
|
|
1640
|
+
projectId: input.projectId,
|
|
1641
|
+
sessionId: input.message.sessionId ?? "",
|
|
1642
|
+
branchName: input.message.branchName,
|
|
1643
|
+
argv: input.message.argv,
|
|
1644
|
+
command: input.message.argv.join(" "),
|
|
1645
|
+
cwd: input.message.cwd,
|
|
1646
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1647
|
+
});
|
|
1664
1648
|
if (input.message.timeoutMs) {
|
|
1665
1649
|
timeout = setTimeout(() => {
|
|
1666
1650
|
subprocess.kill("SIGTERM");
|
|
@@ -1698,9 +1682,7 @@ async function executeCommand(input) {
|
|
|
1698
1682
|
}
|
|
1699
1683
|
async function executeStreamingCommand(input) {
|
|
1700
1684
|
const startedAt = Date.now();
|
|
1701
|
-
let timeout;
|
|
1702
1685
|
let started = false;
|
|
1703
|
-
let timedOut = false;
|
|
1704
1686
|
try {
|
|
1705
1687
|
if (cancelledProcessRuns.delete(input.message.runId)) {
|
|
1706
1688
|
sendWorkerMessage(input.ws, {
|
|
@@ -1750,19 +1732,22 @@ async function executeStreamingCommand(input) {
|
|
|
1750
1732
|
...planProcessEnv
|
|
1751
1733
|
}
|
|
1752
1734
|
});
|
|
1753
|
-
activeProcesses.set(input.message.runId, {
|
|
1735
|
+
activeProcesses.set(input.message.runId, {
|
|
1736
|
+
process: subprocess,
|
|
1737
|
+
projectId: input.projectId,
|
|
1738
|
+
sessionId: input.message.sessionId,
|
|
1739
|
+
branchName: input.message.branchName,
|
|
1740
|
+
argv: input.message.argv,
|
|
1741
|
+
command: input.message.command,
|
|
1742
|
+
cwd: input.message.cwd,
|
|
1743
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1744
|
+
});
|
|
1754
1745
|
started = true;
|
|
1755
1746
|
sendWorkerMessage(input.ws, {
|
|
1756
1747
|
type: "exec_started",
|
|
1757
1748
|
requestId: input.message.requestId,
|
|
1758
1749
|
runId: input.message.runId
|
|
1759
1750
|
});
|
|
1760
|
-
if (input.message.timeoutMs) {
|
|
1761
|
-
timeout = setTimeout(() => {
|
|
1762
|
-
timedOut = true;
|
|
1763
|
-
subprocess.kill("SIGTERM");
|
|
1764
|
-
}, input.message.timeoutMs);
|
|
1765
|
-
}
|
|
1766
1751
|
const [exitCode] = await Promise.all([
|
|
1767
1752
|
subprocess.exited,
|
|
1768
1753
|
streamCommandOutput(subprocess.stdout, (data) => {
|
|
@@ -1786,8 +1771,7 @@ async function executeStreamingCommand(input) {
|
|
|
1786
1771
|
type: "exec_exit",
|
|
1787
1772
|
runId: input.message.runId,
|
|
1788
1773
|
exitCode,
|
|
1789
|
-
durationMs: Date.now() - startedAt
|
|
1790
|
-
...timedOut ? { timedOut: true } : {}
|
|
1774
|
+
durationMs: Date.now() - startedAt
|
|
1791
1775
|
});
|
|
1792
1776
|
} catch (error) {
|
|
1793
1777
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1807,15 +1791,36 @@ async function executeStreamingCommand(input) {
|
|
|
1807
1791
|
});
|
|
1808
1792
|
}
|
|
1809
1793
|
} finally {
|
|
1810
|
-
if (timeout) {
|
|
1811
|
-
clearTimeout(timeout);
|
|
1812
|
-
}
|
|
1813
1794
|
activeProcesses.delete(input.message.runId);
|
|
1814
1795
|
cancelledProcessRuns.delete(input.message.runId);
|
|
1815
1796
|
}
|
|
1816
1797
|
}
|
|
1817
1798
|
function sendWorkerMessage(ws, message) {
|
|
1818
|
-
ws
|
|
1799
|
+
const target = currentWorkerSocket ?? ws;
|
|
1800
|
+
try {
|
|
1801
|
+
target.send(JSON.stringify(message));
|
|
1802
|
+
} catch (error) {
|
|
1803
|
+
process.stderr.write(`[r5d-worker] failed to send ${message.type}: ${error instanceof Error ? error.message : String(error)}
|
|
1804
|
+
`);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
function buildActiveProcessReports() {
|
|
1808
|
+
return Array.from(activeProcesses.entries()).filter(([, active]) => active.sessionId.length > 0).map(([runId, active]) => ({
|
|
1809
|
+
runId,
|
|
1810
|
+
projectId: active.projectId,
|
|
1811
|
+
sessionId: active.sessionId,
|
|
1812
|
+
branchName: active.branchName,
|
|
1813
|
+
argv: active.argv,
|
|
1814
|
+
command: active.command,
|
|
1815
|
+
...active.cwd ? { cwd: active.cwd } : {},
|
|
1816
|
+
startedAt: active.startedAt
|
|
1817
|
+
}));
|
|
1818
|
+
}
|
|
1819
|
+
function sendActiveProcessReport(ws) {
|
|
1820
|
+
sendWorkerMessage(ws, {
|
|
1821
|
+
type: "active_processes",
|
|
1822
|
+
processes: buildActiveProcessReports()
|
|
1823
|
+
});
|
|
1819
1824
|
}
|
|
1820
1825
|
const PTY_BRIDGE_SCRIPT = String.raw`
|
|
1821
1826
|
const readline = require("node:readline");
|
|
@@ -2158,6 +2163,7 @@ async function startWorker(options) {
|
|
|
2158
2163
|
}
|
|
2159
2164
|
});
|
|
2160
2165
|
ws.addEventListener("open", () => {
|
|
2166
|
+
currentWorkerSocket = ws;
|
|
2161
2167
|
const hello = {
|
|
2162
2168
|
type: "hello",
|
|
2163
2169
|
hostInfo: {
|
|
@@ -2172,6 +2178,7 @@ async function startWorker(options) {
|
|
|
2172
2178
|
}
|
|
2173
2179
|
};
|
|
2174
2180
|
ws.send(JSON.stringify(hello));
|
|
2181
|
+
sendActiveProcessReport(ws);
|
|
2175
2182
|
process.stdout.write("[r5d-worker] connected\n");
|
|
2176
2183
|
});
|
|
2177
2184
|
ws.addEventListener("message", (event) => {
|
|
@@ -2194,31 +2201,29 @@ async function startWorker(options) {
|
|
|
2194
2201
|
const branches = project.branches.length > 0 ? project.branches : [project.defaultBranch || "main"];
|
|
2195
2202
|
for (const branchName of branches) {
|
|
2196
2203
|
try {
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2204
|
+
ensureBranchWorkspace({
|
|
2205
|
+
projectId: project.projectId,
|
|
2206
|
+
baseUrl,
|
|
2207
|
+
token,
|
|
2208
|
+
projectRoot,
|
|
2209
|
+
syncRoot,
|
|
2210
|
+
branchName,
|
|
2211
|
+
manifest: project
|
|
2212
|
+
});
|
|
2213
|
+
try {
|
|
2214
|
+
await syncProjectPlans({
|
|
2200
2215
|
baseUrl,
|
|
2201
2216
|
token,
|
|
2202
|
-
|
|
2203
|
-
syncRoot,
|
|
2217
|
+
projectId: project.projectId,
|
|
2204
2218
|
branchName,
|
|
2205
|
-
|
|
2219
|
+
planRoot
|
|
2206
2220
|
});
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
token,
|
|
2211
|
-
projectId: project.projectId,
|
|
2212
|
-
branchName,
|
|
2213
|
-
planRoot
|
|
2214
|
-
});
|
|
2215
|
-
} catch (error) {
|
|
2216
|
-
process.stderr.write(
|
|
2217
|
-
`[r5d-worker] plan sync skipped for ${project.projectId}/${branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
2221
|
+
} catch (error) {
|
|
2222
|
+
process.stderr.write(
|
|
2223
|
+
`[r5d-worker] plan sync skipped for ${project.projectId}/${branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
2218
2224
|
`
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
});
|
|
2225
|
+
);
|
|
2226
|
+
}
|
|
2222
2227
|
} catch (error) {
|
|
2223
2228
|
process.stderr.write(
|
|
2224
2229
|
`[r5d-worker] failed to sync ${project.repoSlug}/${branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
@@ -2246,7 +2251,7 @@ async function startWorker(options) {
|
|
|
2246
2251
|
requestId: message.requestId,
|
|
2247
2252
|
runId: message.runId,
|
|
2248
2253
|
cancelled: true,
|
|
2249
|
-
message: active ? `Sent SIGTERM to ${active.command
|
|
2254
|
+
message: active ? `Sent SIGTERM to ${active.command}` : "Cancellation queued before command start"
|
|
2250
2255
|
})
|
|
2251
2256
|
);
|
|
2252
2257
|
return;
|
|
@@ -2276,11 +2281,17 @@ async function startWorker(options) {
|
|
|
2276
2281
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2277
2282
|
process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
|
|
2278
2283
|
`);
|
|
2279
|
-
const result = await
|
|
2280
|
-
message
|
|
2281
|
-
message.
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
+
const result = await executeCommand({
|
|
2285
|
+
message,
|
|
2286
|
+
projectId: message.projectId,
|
|
2287
|
+
baseUrl,
|
|
2288
|
+
token,
|
|
2289
|
+
projectRoot,
|
|
2290
|
+
syncRoot,
|
|
2291
|
+
artifactRoot,
|
|
2292
|
+
planRoot,
|
|
2293
|
+
manifest
|
|
2294
|
+
});
|
|
2284
2295
|
ws.send(JSON.stringify(result));
|
|
2285
2296
|
return;
|
|
2286
2297
|
}
|
|
@@ -2295,11 +2306,18 @@ async function startWorker(options) {
|
|
|
2295
2306
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2296
2307
|
process.stdout.write(`[r5d-worker] exec_start ${message.runId}: ${message.argv.join(" ")}
|
|
2297
2308
|
`);
|
|
2298
|
-
void
|
|
2299
|
-
|
|
2300
|
-
message
|
|
2301
|
-
|
|
2302
|
-
|
|
2309
|
+
void executeStreamingCommand({
|
|
2310
|
+
ws,
|
|
2311
|
+
message,
|
|
2312
|
+
projectId: message.projectId,
|
|
2313
|
+
baseUrl,
|
|
2314
|
+
token,
|
|
2315
|
+
projectRoot,
|
|
2316
|
+
syncRoot,
|
|
2317
|
+
artifactRoot,
|
|
2318
|
+
planRoot,
|
|
2319
|
+
manifest
|
|
2320
|
+
}).catch((error) => {
|
|
2303
2321
|
sendWorkerMessage(ws, {
|
|
2304
2322
|
type: "exec_start_error",
|
|
2305
2323
|
requestId: message.requestId,
|
|
@@ -2321,11 +2339,16 @@ async function startWorker(options) {
|
|
|
2321
2339
|
try {
|
|
2322
2340
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2323
2341
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2324
|
-
const result = await
|
|
2325
|
-
message
|
|
2326
|
-
message.
|
|
2327
|
-
|
|
2328
|
-
|
|
2342
|
+
const result = await executeOperation({
|
|
2343
|
+
message,
|
|
2344
|
+
projectId: message.projectId,
|
|
2345
|
+
baseUrl,
|
|
2346
|
+
token,
|
|
2347
|
+
projectRoot,
|
|
2348
|
+
syncRoot,
|
|
2349
|
+
artifactRoot,
|
|
2350
|
+
manifest
|
|
2351
|
+
});
|
|
2329
2352
|
ws.send(
|
|
2330
2353
|
JSON.stringify({
|
|
2331
2354
|
type: "operation_result",
|
|
@@ -2360,10 +2383,31 @@ async function startWorker(options) {
|
|
|
2360
2383
|
});
|
|
2361
2384
|
});
|
|
2362
2385
|
ws.addEventListener("close", (event) => {
|
|
2386
|
+
if (currentWorkerSocket === ws) {
|
|
2387
|
+
currentWorkerSocket = null;
|
|
2388
|
+
}
|
|
2363
2389
|
closeAllPtys();
|
|
2364
2390
|
const reason = event.reason ? `: ${event.reason}` : "";
|
|
2365
2391
|
process.stderr.write(`[r5d-worker] disconnected (${event.code}${reason})
|
|
2366
2392
|
`);
|
|
2393
|
+
if (activeProcesses.size > 0) {
|
|
2394
|
+
const delayMs = 2e3;
|
|
2395
|
+
process.stderr.write(`[r5d-worker] ${activeProcesses.size} process(es) still active; reconnecting in ${delayMs}ms
|
|
2396
|
+
`);
|
|
2397
|
+
const reconnect = () => {
|
|
2398
|
+
void startWorker(options).catch((error) => {
|
|
2399
|
+
process.stderr.write(`[r5d-worker] reconnect failed: ${error instanceof Error ? error.message : String(error)}
|
|
2400
|
+
`);
|
|
2401
|
+
if (activeProcesses.size > 0) {
|
|
2402
|
+
setTimeout(reconnect, delayMs);
|
|
2403
|
+
return;
|
|
2404
|
+
}
|
|
2405
|
+
process.exit(1);
|
|
2406
|
+
});
|
|
2407
|
+
};
|
|
2408
|
+
setTimeout(reconnect, delayMs);
|
|
2409
|
+
return;
|
|
2410
|
+
}
|
|
2367
2411
|
process.exit(event.code === 1e3 ? 0 : 1);
|
|
2368
2412
|
});
|
|
2369
2413
|
ws.addEventListener("error", (event) => {
|
package/dist/cjs/package.json
CHANGED
package/dist/mjs/main.mjs
CHANGED
|
@@ -18,7 +18,7 @@ const LEGACY_BUILD_IT_NOW_REMOTE_NAME = "build-it-now";
|
|
|
18
18
|
const activeProcesses = /* @__PURE__ */ new Map();
|
|
19
19
|
const cancelledProcessRuns = /* @__PURE__ */ new Set();
|
|
20
20
|
const activePtys = /* @__PURE__ */ new Map();
|
|
21
|
-
|
|
21
|
+
let currentWorkerSocket = null;
|
|
22
22
|
let containerRegistryCredential = null;
|
|
23
23
|
let visibleGitIdentity = null;
|
|
24
24
|
function defaultConfigPath() {
|
|
@@ -650,31 +650,6 @@ function resolveProjectFilePath(branchPath, virtualPath) {
|
|
|
650
650
|
assertInsideBranch(branchPath, absolutePath);
|
|
651
651
|
return { absolutePath, virtualPath: repoRelativePath ? `/${repoRelativePath}` : "/", repoRelativePath };
|
|
652
652
|
}
|
|
653
|
-
function branchLockKey(projectId, branchName) {
|
|
654
|
-
return `${projectId}:${branchName}`;
|
|
655
|
-
}
|
|
656
|
-
async function withBranchLock(projectId, branchName, fn) {
|
|
657
|
-
const key = branchLockKey(projectId, branchName);
|
|
658
|
-
const previous = branchLocks.get(key) ?? Promise.resolve();
|
|
659
|
-
let release = () => {
|
|
660
|
-
};
|
|
661
|
-
const next = new Promise((resolve) => {
|
|
662
|
-
release = resolve;
|
|
663
|
-
});
|
|
664
|
-
const queued = previous.catch(() => {
|
|
665
|
-
}).then(() => next);
|
|
666
|
-
branchLocks.set(key, queued);
|
|
667
|
-
await previous.catch(() => {
|
|
668
|
-
});
|
|
669
|
-
try {
|
|
670
|
-
return await fn();
|
|
671
|
-
} finally {
|
|
672
|
-
release();
|
|
673
|
-
if (branchLocks.get(key) === queued) {
|
|
674
|
-
branchLocks.delete(key);
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
653
|
function resolveRemoteUrl(baseUrl, remoteUrl) {
|
|
679
654
|
if (/^https?:\/\//i.test(remoteUrl)) {
|
|
680
655
|
return remoteUrl;
|
|
@@ -1620,7 +1595,16 @@ async function executeCommand(input) {
|
|
|
1620
1595
|
...planProcessEnv
|
|
1621
1596
|
}
|
|
1622
1597
|
});
|
|
1623
|
-
activeProcesses.set(input.message.runId, {
|
|
1598
|
+
activeProcesses.set(input.message.runId, {
|
|
1599
|
+
process: subprocess,
|
|
1600
|
+
projectId: input.projectId,
|
|
1601
|
+
sessionId: input.message.sessionId ?? "",
|
|
1602
|
+
branchName: input.message.branchName,
|
|
1603
|
+
argv: input.message.argv,
|
|
1604
|
+
command: input.message.argv.join(" "),
|
|
1605
|
+
cwd: input.message.cwd,
|
|
1606
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1607
|
+
});
|
|
1624
1608
|
if (input.message.timeoutMs) {
|
|
1625
1609
|
timeout = setTimeout(() => {
|
|
1626
1610
|
subprocess.kill("SIGTERM");
|
|
@@ -1658,9 +1642,7 @@ async function executeCommand(input) {
|
|
|
1658
1642
|
}
|
|
1659
1643
|
async function executeStreamingCommand(input) {
|
|
1660
1644
|
const startedAt = Date.now();
|
|
1661
|
-
let timeout;
|
|
1662
1645
|
let started = false;
|
|
1663
|
-
let timedOut = false;
|
|
1664
1646
|
try {
|
|
1665
1647
|
if (cancelledProcessRuns.delete(input.message.runId)) {
|
|
1666
1648
|
sendWorkerMessage(input.ws, {
|
|
@@ -1710,19 +1692,22 @@ async function executeStreamingCommand(input) {
|
|
|
1710
1692
|
...planProcessEnv
|
|
1711
1693
|
}
|
|
1712
1694
|
});
|
|
1713
|
-
activeProcesses.set(input.message.runId, {
|
|
1695
|
+
activeProcesses.set(input.message.runId, {
|
|
1696
|
+
process: subprocess,
|
|
1697
|
+
projectId: input.projectId,
|
|
1698
|
+
sessionId: input.message.sessionId,
|
|
1699
|
+
branchName: input.message.branchName,
|
|
1700
|
+
argv: input.message.argv,
|
|
1701
|
+
command: input.message.command,
|
|
1702
|
+
cwd: input.message.cwd,
|
|
1703
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1704
|
+
});
|
|
1714
1705
|
started = true;
|
|
1715
1706
|
sendWorkerMessage(input.ws, {
|
|
1716
1707
|
type: "exec_started",
|
|
1717
1708
|
requestId: input.message.requestId,
|
|
1718
1709
|
runId: input.message.runId
|
|
1719
1710
|
});
|
|
1720
|
-
if (input.message.timeoutMs) {
|
|
1721
|
-
timeout = setTimeout(() => {
|
|
1722
|
-
timedOut = true;
|
|
1723
|
-
subprocess.kill("SIGTERM");
|
|
1724
|
-
}, input.message.timeoutMs);
|
|
1725
|
-
}
|
|
1726
1711
|
const [exitCode] = await Promise.all([
|
|
1727
1712
|
subprocess.exited,
|
|
1728
1713
|
streamCommandOutput(subprocess.stdout, (data) => {
|
|
@@ -1746,8 +1731,7 @@ async function executeStreamingCommand(input) {
|
|
|
1746
1731
|
type: "exec_exit",
|
|
1747
1732
|
runId: input.message.runId,
|
|
1748
1733
|
exitCode,
|
|
1749
|
-
durationMs: Date.now() - startedAt
|
|
1750
|
-
...timedOut ? { timedOut: true } : {}
|
|
1734
|
+
durationMs: Date.now() - startedAt
|
|
1751
1735
|
});
|
|
1752
1736
|
} catch (error) {
|
|
1753
1737
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1767,15 +1751,36 @@ async function executeStreamingCommand(input) {
|
|
|
1767
1751
|
});
|
|
1768
1752
|
}
|
|
1769
1753
|
} finally {
|
|
1770
|
-
if (timeout) {
|
|
1771
|
-
clearTimeout(timeout);
|
|
1772
|
-
}
|
|
1773
1754
|
activeProcesses.delete(input.message.runId);
|
|
1774
1755
|
cancelledProcessRuns.delete(input.message.runId);
|
|
1775
1756
|
}
|
|
1776
1757
|
}
|
|
1777
1758
|
function sendWorkerMessage(ws, message) {
|
|
1778
|
-
ws
|
|
1759
|
+
const target = currentWorkerSocket ?? ws;
|
|
1760
|
+
try {
|
|
1761
|
+
target.send(JSON.stringify(message));
|
|
1762
|
+
} catch (error) {
|
|
1763
|
+
process.stderr.write(`[r5d-worker] failed to send ${message.type}: ${error instanceof Error ? error.message : String(error)}
|
|
1764
|
+
`);
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
function buildActiveProcessReports() {
|
|
1768
|
+
return Array.from(activeProcesses.entries()).filter(([, active]) => active.sessionId.length > 0).map(([runId, active]) => ({
|
|
1769
|
+
runId,
|
|
1770
|
+
projectId: active.projectId,
|
|
1771
|
+
sessionId: active.sessionId,
|
|
1772
|
+
branchName: active.branchName,
|
|
1773
|
+
argv: active.argv,
|
|
1774
|
+
command: active.command,
|
|
1775
|
+
...active.cwd ? { cwd: active.cwd } : {},
|
|
1776
|
+
startedAt: active.startedAt
|
|
1777
|
+
}));
|
|
1778
|
+
}
|
|
1779
|
+
function sendActiveProcessReport(ws) {
|
|
1780
|
+
sendWorkerMessage(ws, {
|
|
1781
|
+
type: "active_processes",
|
|
1782
|
+
processes: buildActiveProcessReports()
|
|
1783
|
+
});
|
|
1779
1784
|
}
|
|
1780
1785
|
const PTY_BRIDGE_SCRIPT = String.raw`
|
|
1781
1786
|
const readline = require("node:readline");
|
|
@@ -2118,6 +2123,7 @@ async function startWorker(options) {
|
|
|
2118
2123
|
}
|
|
2119
2124
|
});
|
|
2120
2125
|
ws.addEventListener("open", () => {
|
|
2126
|
+
currentWorkerSocket = ws;
|
|
2121
2127
|
const hello = {
|
|
2122
2128
|
type: "hello",
|
|
2123
2129
|
hostInfo: {
|
|
@@ -2132,6 +2138,7 @@ async function startWorker(options) {
|
|
|
2132
2138
|
}
|
|
2133
2139
|
};
|
|
2134
2140
|
ws.send(JSON.stringify(hello));
|
|
2141
|
+
sendActiveProcessReport(ws);
|
|
2135
2142
|
process.stdout.write("[r5d-worker] connected\n");
|
|
2136
2143
|
});
|
|
2137
2144
|
ws.addEventListener("message", (event) => {
|
|
@@ -2154,31 +2161,29 @@ async function startWorker(options) {
|
|
|
2154
2161
|
const branches = project.branches.length > 0 ? project.branches : [project.defaultBranch || "main"];
|
|
2155
2162
|
for (const branchName of branches) {
|
|
2156
2163
|
try {
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2164
|
+
ensureBranchWorkspace({
|
|
2165
|
+
projectId: project.projectId,
|
|
2166
|
+
baseUrl,
|
|
2167
|
+
token,
|
|
2168
|
+
projectRoot,
|
|
2169
|
+
syncRoot,
|
|
2170
|
+
branchName,
|
|
2171
|
+
manifest: project
|
|
2172
|
+
});
|
|
2173
|
+
try {
|
|
2174
|
+
await syncProjectPlans({
|
|
2160
2175
|
baseUrl,
|
|
2161
2176
|
token,
|
|
2162
|
-
|
|
2163
|
-
syncRoot,
|
|
2177
|
+
projectId: project.projectId,
|
|
2164
2178
|
branchName,
|
|
2165
|
-
|
|
2179
|
+
planRoot
|
|
2166
2180
|
});
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
token,
|
|
2171
|
-
projectId: project.projectId,
|
|
2172
|
-
branchName,
|
|
2173
|
-
planRoot
|
|
2174
|
-
});
|
|
2175
|
-
} catch (error) {
|
|
2176
|
-
process.stderr.write(
|
|
2177
|
-
`[r5d-worker] plan sync skipped for ${project.projectId}/${branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
2181
|
+
} catch (error) {
|
|
2182
|
+
process.stderr.write(
|
|
2183
|
+
`[r5d-worker] plan sync skipped for ${project.projectId}/${branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
2178
2184
|
`
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
});
|
|
2185
|
+
);
|
|
2186
|
+
}
|
|
2182
2187
|
} catch (error) {
|
|
2183
2188
|
process.stderr.write(
|
|
2184
2189
|
`[r5d-worker] failed to sync ${project.repoSlug}/${branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
@@ -2206,7 +2211,7 @@ async function startWorker(options) {
|
|
|
2206
2211
|
requestId: message.requestId,
|
|
2207
2212
|
runId: message.runId,
|
|
2208
2213
|
cancelled: true,
|
|
2209
|
-
message: active ? `Sent SIGTERM to ${active.command
|
|
2214
|
+
message: active ? `Sent SIGTERM to ${active.command}` : "Cancellation queued before command start"
|
|
2210
2215
|
})
|
|
2211
2216
|
);
|
|
2212
2217
|
return;
|
|
@@ -2236,11 +2241,17 @@ async function startWorker(options) {
|
|
|
2236
2241
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2237
2242
|
process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
|
|
2238
2243
|
`);
|
|
2239
|
-
const result = await
|
|
2240
|
-
message
|
|
2241
|
-
message.
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
+
const result = await executeCommand({
|
|
2245
|
+
message,
|
|
2246
|
+
projectId: message.projectId,
|
|
2247
|
+
baseUrl,
|
|
2248
|
+
token,
|
|
2249
|
+
projectRoot,
|
|
2250
|
+
syncRoot,
|
|
2251
|
+
artifactRoot,
|
|
2252
|
+
planRoot,
|
|
2253
|
+
manifest
|
|
2254
|
+
});
|
|
2244
2255
|
ws.send(JSON.stringify(result));
|
|
2245
2256
|
return;
|
|
2246
2257
|
}
|
|
@@ -2255,11 +2266,18 @@ async function startWorker(options) {
|
|
|
2255
2266
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2256
2267
|
process.stdout.write(`[r5d-worker] exec_start ${message.runId}: ${message.argv.join(" ")}
|
|
2257
2268
|
`);
|
|
2258
|
-
void
|
|
2259
|
-
|
|
2260
|
-
message
|
|
2261
|
-
|
|
2262
|
-
|
|
2269
|
+
void executeStreamingCommand({
|
|
2270
|
+
ws,
|
|
2271
|
+
message,
|
|
2272
|
+
projectId: message.projectId,
|
|
2273
|
+
baseUrl,
|
|
2274
|
+
token,
|
|
2275
|
+
projectRoot,
|
|
2276
|
+
syncRoot,
|
|
2277
|
+
artifactRoot,
|
|
2278
|
+
planRoot,
|
|
2279
|
+
manifest
|
|
2280
|
+
}).catch((error) => {
|
|
2263
2281
|
sendWorkerMessage(ws, {
|
|
2264
2282
|
type: "exec_start_error",
|
|
2265
2283
|
requestId: message.requestId,
|
|
@@ -2281,11 +2299,16 @@ async function startWorker(options) {
|
|
|
2281
2299
|
try {
|
|
2282
2300
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2283
2301
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2284
|
-
const result = await
|
|
2285
|
-
message
|
|
2286
|
-
message.
|
|
2287
|
-
|
|
2288
|
-
|
|
2302
|
+
const result = await executeOperation({
|
|
2303
|
+
message,
|
|
2304
|
+
projectId: message.projectId,
|
|
2305
|
+
baseUrl,
|
|
2306
|
+
token,
|
|
2307
|
+
projectRoot,
|
|
2308
|
+
syncRoot,
|
|
2309
|
+
artifactRoot,
|
|
2310
|
+
manifest
|
|
2311
|
+
});
|
|
2289
2312
|
ws.send(
|
|
2290
2313
|
JSON.stringify({
|
|
2291
2314
|
type: "operation_result",
|
|
@@ -2320,10 +2343,31 @@ async function startWorker(options) {
|
|
|
2320
2343
|
});
|
|
2321
2344
|
});
|
|
2322
2345
|
ws.addEventListener("close", (event) => {
|
|
2346
|
+
if (currentWorkerSocket === ws) {
|
|
2347
|
+
currentWorkerSocket = null;
|
|
2348
|
+
}
|
|
2323
2349
|
closeAllPtys();
|
|
2324
2350
|
const reason = event.reason ? `: ${event.reason}` : "";
|
|
2325
2351
|
process.stderr.write(`[r5d-worker] disconnected (${event.code}${reason})
|
|
2326
2352
|
`);
|
|
2353
|
+
if (activeProcesses.size > 0) {
|
|
2354
|
+
const delayMs = 2e3;
|
|
2355
|
+
process.stderr.write(`[r5d-worker] ${activeProcesses.size} process(es) still active; reconnecting in ${delayMs}ms
|
|
2356
|
+
`);
|
|
2357
|
+
const reconnect = () => {
|
|
2358
|
+
void startWorker(options).catch((error) => {
|
|
2359
|
+
process.stderr.write(`[r5d-worker] reconnect failed: ${error instanceof Error ? error.message : String(error)}
|
|
2360
|
+
`);
|
|
2361
|
+
if (activeProcesses.size > 0) {
|
|
2362
|
+
setTimeout(reconnect, delayMs);
|
|
2363
|
+
return;
|
|
2364
|
+
}
|
|
2365
|
+
process.exit(1);
|
|
2366
|
+
});
|
|
2367
|
+
};
|
|
2368
|
+
setTimeout(reconnect, delayMs);
|
|
2369
|
+
return;
|
|
2370
|
+
}
|
|
2327
2371
|
process.exit(event.code === 1e3 ? 0 : 1);
|
|
2328
2372
|
});
|
|
2329
2373
|
ws.addEventListener("error", (event) => {
|
package/dist/mjs/package.json
CHANGED