@ricsam/r5d-worker 0.0.15 → 0.0.17
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 +131 -79
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/main.mjs +131 -79
- 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() {
|
|
@@ -114,8 +114,16 @@ function readInstalledPackageVersion(packageJsonPath) {
|
|
|
114
114
|
}
|
|
115
115
|
return null;
|
|
116
116
|
}
|
|
117
|
+
function resolveEntrypointPath(entrypoint) {
|
|
118
|
+
const resolved = import_node_path.default.resolve(entrypoint);
|
|
119
|
+
try {
|
|
120
|
+
return import_node_fs.default.realpathSync.native(resolved);
|
|
121
|
+
} catch {
|
|
122
|
+
return resolved;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
117
125
|
function getWorkerVersion() {
|
|
118
|
-
const entrypoint = process.argv[1] ?
|
|
126
|
+
const entrypoint = process.argv[1] ? resolveEntrypointPath(process.argv[1]) : null;
|
|
119
127
|
let current = entrypoint ? import_node_path.default.dirname(entrypoint) : process.cwd();
|
|
120
128
|
for (let index = 0; index < 12; index += 1) {
|
|
121
129
|
const packageVersion = readInstalledPackageVersion(import_node_path.default.join(current, "package.json"));
|
|
@@ -690,31 +698,6 @@ function resolveProjectFilePath(branchPath, virtualPath) {
|
|
|
690
698
|
assertInsideBranch(branchPath, absolutePath);
|
|
691
699
|
return { absolutePath, virtualPath: repoRelativePath ? `/${repoRelativePath}` : "/", repoRelativePath };
|
|
692
700
|
}
|
|
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
701
|
function resolveRemoteUrl(baseUrl, remoteUrl) {
|
|
719
702
|
if (/^https?:\/\//i.test(remoteUrl)) {
|
|
720
703
|
return remoteUrl;
|
|
@@ -1660,7 +1643,16 @@ async function executeCommand(input) {
|
|
|
1660
1643
|
...planProcessEnv
|
|
1661
1644
|
}
|
|
1662
1645
|
});
|
|
1663
|
-
activeProcesses.set(input.message.runId, {
|
|
1646
|
+
activeProcesses.set(input.message.runId, {
|
|
1647
|
+
process: subprocess,
|
|
1648
|
+
projectId: input.projectId,
|
|
1649
|
+
sessionId: input.message.sessionId ?? "",
|
|
1650
|
+
branchName: input.message.branchName,
|
|
1651
|
+
argv: input.message.argv,
|
|
1652
|
+
command: input.message.argv.join(" "),
|
|
1653
|
+
cwd: input.message.cwd,
|
|
1654
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1655
|
+
});
|
|
1664
1656
|
if (input.message.timeoutMs) {
|
|
1665
1657
|
timeout = setTimeout(() => {
|
|
1666
1658
|
subprocess.kill("SIGTERM");
|
|
@@ -1698,9 +1690,7 @@ async function executeCommand(input) {
|
|
|
1698
1690
|
}
|
|
1699
1691
|
async function executeStreamingCommand(input) {
|
|
1700
1692
|
const startedAt = Date.now();
|
|
1701
|
-
let timeout;
|
|
1702
1693
|
let started = false;
|
|
1703
|
-
let timedOut = false;
|
|
1704
1694
|
try {
|
|
1705
1695
|
if (cancelledProcessRuns.delete(input.message.runId)) {
|
|
1706
1696
|
sendWorkerMessage(input.ws, {
|
|
@@ -1750,19 +1740,22 @@ async function executeStreamingCommand(input) {
|
|
|
1750
1740
|
...planProcessEnv
|
|
1751
1741
|
}
|
|
1752
1742
|
});
|
|
1753
|
-
activeProcesses.set(input.message.runId, {
|
|
1743
|
+
activeProcesses.set(input.message.runId, {
|
|
1744
|
+
process: subprocess,
|
|
1745
|
+
projectId: input.projectId,
|
|
1746
|
+
sessionId: input.message.sessionId,
|
|
1747
|
+
branchName: input.message.branchName,
|
|
1748
|
+
argv: input.message.argv,
|
|
1749
|
+
command: input.message.command,
|
|
1750
|
+
cwd: input.message.cwd,
|
|
1751
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1752
|
+
});
|
|
1754
1753
|
started = true;
|
|
1755
1754
|
sendWorkerMessage(input.ws, {
|
|
1756
1755
|
type: "exec_started",
|
|
1757
1756
|
requestId: input.message.requestId,
|
|
1758
1757
|
runId: input.message.runId
|
|
1759
1758
|
});
|
|
1760
|
-
if (input.message.timeoutMs) {
|
|
1761
|
-
timeout = setTimeout(() => {
|
|
1762
|
-
timedOut = true;
|
|
1763
|
-
subprocess.kill("SIGTERM");
|
|
1764
|
-
}, input.message.timeoutMs);
|
|
1765
|
-
}
|
|
1766
1759
|
const [exitCode] = await Promise.all([
|
|
1767
1760
|
subprocess.exited,
|
|
1768
1761
|
streamCommandOutput(subprocess.stdout, (data) => {
|
|
@@ -1786,8 +1779,7 @@ async function executeStreamingCommand(input) {
|
|
|
1786
1779
|
type: "exec_exit",
|
|
1787
1780
|
runId: input.message.runId,
|
|
1788
1781
|
exitCode,
|
|
1789
|
-
durationMs: Date.now() - startedAt
|
|
1790
|
-
...timedOut ? { timedOut: true } : {}
|
|
1782
|
+
durationMs: Date.now() - startedAt
|
|
1791
1783
|
});
|
|
1792
1784
|
} catch (error) {
|
|
1793
1785
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1807,15 +1799,36 @@ async function executeStreamingCommand(input) {
|
|
|
1807
1799
|
});
|
|
1808
1800
|
}
|
|
1809
1801
|
} finally {
|
|
1810
|
-
if (timeout) {
|
|
1811
|
-
clearTimeout(timeout);
|
|
1812
|
-
}
|
|
1813
1802
|
activeProcesses.delete(input.message.runId);
|
|
1814
1803
|
cancelledProcessRuns.delete(input.message.runId);
|
|
1815
1804
|
}
|
|
1816
1805
|
}
|
|
1817
1806
|
function sendWorkerMessage(ws, message) {
|
|
1818
|
-
ws
|
|
1807
|
+
const target = currentWorkerSocket ?? ws;
|
|
1808
|
+
try {
|
|
1809
|
+
target.send(JSON.stringify(message));
|
|
1810
|
+
} catch (error) {
|
|
1811
|
+
process.stderr.write(`[r5d-worker] failed to send ${message.type}: ${error instanceof Error ? error.message : String(error)}
|
|
1812
|
+
`);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
function buildActiveProcessReports() {
|
|
1816
|
+
return Array.from(activeProcesses.entries()).filter(([, active]) => active.sessionId.length > 0).map(([runId, active]) => ({
|
|
1817
|
+
runId,
|
|
1818
|
+
projectId: active.projectId,
|
|
1819
|
+
sessionId: active.sessionId,
|
|
1820
|
+
branchName: active.branchName,
|
|
1821
|
+
argv: active.argv,
|
|
1822
|
+
command: active.command,
|
|
1823
|
+
...active.cwd ? { cwd: active.cwd } : {},
|
|
1824
|
+
startedAt: active.startedAt
|
|
1825
|
+
}));
|
|
1826
|
+
}
|
|
1827
|
+
function sendActiveProcessReport(ws) {
|
|
1828
|
+
sendWorkerMessage(ws, {
|
|
1829
|
+
type: "active_processes",
|
|
1830
|
+
processes: buildActiveProcessReports()
|
|
1831
|
+
});
|
|
1819
1832
|
}
|
|
1820
1833
|
const PTY_BRIDGE_SCRIPT = String.raw`
|
|
1821
1834
|
const readline = require("node:readline");
|
|
@@ -2158,6 +2171,7 @@ async function startWorker(options) {
|
|
|
2158
2171
|
}
|
|
2159
2172
|
});
|
|
2160
2173
|
ws.addEventListener("open", () => {
|
|
2174
|
+
currentWorkerSocket = ws;
|
|
2161
2175
|
const hello = {
|
|
2162
2176
|
type: "hello",
|
|
2163
2177
|
hostInfo: {
|
|
@@ -2172,6 +2186,7 @@ async function startWorker(options) {
|
|
|
2172
2186
|
}
|
|
2173
2187
|
};
|
|
2174
2188
|
ws.send(JSON.stringify(hello));
|
|
2189
|
+
sendActiveProcessReport(ws);
|
|
2175
2190
|
process.stdout.write("[r5d-worker] connected\n");
|
|
2176
2191
|
});
|
|
2177
2192
|
ws.addEventListener("message", (event) => {
|
|
@@ -2194,31 +2209,29 @@ async function startWorker(options) {
|
|
|
2194
2209
|
const branches = project.branches.length > 0 ? project.branches : [project.defaultBranch || "main"];
|
|
2195
2210
|
for (const branchName of branches) {
|
|
2196
2211
|
try {
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2212
|
+
ensureBranchWorkspace({
|
|
2213
|
+
projectId: project.projectId,
|
|
2214
|
+
baseUrl,
|
|
2215
|
+
token,
|
|
2216
|
+
projectRoot,
|
|
2217
|
+
syncRoot,
|
|
2218
|
+
branchName,
|
|
2219
|
+
manifest: project
|
|
2220
|
+
});
|
|
2221
|
+
try {
|
|
2222
|
+
await syncProjectPlans({
|
|
2200
2223
|
baseUrl,
|
|
2201
2224
|
token,
|
|
2202
|
-
|
|
2203
|
-
syncRoot,
|
|
2225
|
+
projectId: project.projectId,
|
|
2204
2226
|
branchName,
|
|
2205
|
-
|
|
2227
|
+
planRoot
|
|
2206
2228
|
});
|
|
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)}
|
|
2229
|
+
} catch (error) {
|
|
2230
|
+
process.stderr.write(
|
|
2231
|
+
`[r5d-worker] plan sync skipped for ${project.projectId}/${branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
2218
2232
|
`
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
});
|
|
2233
|
+
);
|
|
2234
|
+
}
|
|
2222
2235
|
} catch (error) {
|
|
2223
2236
|
process.stderr.write(
|
|
2224
2237
|
`[r5d-worker] failed to sync ${project.repoSlug}/${branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
@@ -2246,7 +2259,7 @@ async function startWorker(options) {
|
|
|
2246
2259
|
requestId: message.requestId,
|
|
2247
2260
|
runId: message.runId,
|
|
2248
2261
|
cancelled: true,
|
|
2249
|
-
message: active ? `Sent SIGTERM to ${active.command
|
|
2262
|
+
message: active ? `Sent SIGTERM to ${active.command}` : "Cancellation queued before command start"
|
|
2250
2263
|
})
|
|
2251
2264
|
);
|
|
2252
2265
|
return;
|
|
@@ -2276,11 +2289,17 @@ async function startWorker(options) {
|
|
|
2276
2289
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2277
2290
|
process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
|
|
2278
2291
|
`);
|
|
2279
|
-
const result = await
|
|
2280
|
-
message
|
|
2281
|
-
message.
|
|
2282
|
-
|
|
2283
|
-
|
|
2292
|
+
const result = await executeCommand({
|
|
2293
|
+
message,
|
|
2294
|
+
projectId: message.projectId,
|
|
2295
|
+
baseUrl,
|
|
2296
|
+
token,
|
|
2297
|
+
projectRoot,
|
|
2298
|
+
syncRoot,
|
|
2299
|
+
artifactRoot,
|
|
2300
|
+
planRoot,
|
|
2301
|
+
manifest
|
|
2302
|
+
});
|
|
2284
2303
|
ws.send(JSON.stringify(result));
|
|
2285
2304
|
return;
|
|
2286
2305
|
}
|
|
@@ -2295,11 +2314,18 @@ async function startWorker(options) {
|
|
|
2295
2314
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2296
2315
|
process.stdout.write(`[r5d-worker] exec_start ${message.runId}: ${message.argv.join(" ")}
|
|
2297
2316
|
`);
|
|
2298
|
-
void
|
|
2299
|
-
|
|
2300
|
-
message
|
|
2301
|
-
|
|
2302
|
-
|
|
2317
|
+
void executeStreamingCommand({
|
|
2318
|
+
ws,
|
|
2319
|
+
message,
|
|
2320
|
+
projectId: message.projectId,
|
|
2321
|
+
baseUrl,
|
|
2322
|
+
token,
|
|
2323
|
+
projectRoot,
|
|
2324
|
+
syncRoot,
|
|
2325
|
+
artifactRoot,
|
|
2326
|
+
planRoot,
|
|
2327
|
+
manifest
|
|
2328
|
+
}).catch((error) => {
|
|
2303
2329
|
sendWorkerMessage(ws, {
|
|
2304
2330
|
type: "exec_start_error",
|
|
2305
2331
|
requestId: message.requestId,
|
|
@@ -2321,11 +2347,16 @@ async function startWorker(options) {
|
|
|
2321
2347
|
try {
|
|
2322
2348
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2323
2349
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2324
|
-
const result = await
|
|
2325
|
-
message
|
|
2326
|
-
message.
|
|
2327
|
-
|
|
2328
|
-
|
|
2350
|
+
const result = await executeOperation({
|
|
2351
|
+
message,
|
|
2352
|
+
projectId: message.projectId,
|
|
2353
|
+
baseUrl,
|
|
2354
|
+
token,
|
|
2355
|
+
projectRoot,
|
|
2356
|
+
syncRoot,
|
|
2357
|
+
artifactRoot,
|
|
2358
|
+
manifest
|
|
2359
|
+
});
|
|
2329
2360
|
ws.send(
|
|
2330
2361
|
JSON.stringify({
|
|
2331
2362
|
type: "operation_result",
|
|
@@ -2360,10 +2391,31 @@ async function startWorker(options) {
|
|
|
2360
2391
|
});
|
|
2361
2392
|
});
|
|
2362
2393
|
ws.addEventListener("close", (event) => {
|
|
2394
|
+
if (currentWorkerSocket === ws) {
|
|
2395
|
+
currentWorkerSocket = null;
|
|
2396
|
+
}
|
|
2363
2397
|
closeAllPtys();
|
|
2364
2398
|
const reason = event.reason ? `: ${event.reason}` : "";
|
|
2365
2399
|
process.stderr.write(`[r5d-worker] disconnected (${event.code}${reason})
|
|
2366
2400
|
`);
|
|
2401
|
+
if (activeProcesses.size > 0) {
|
|
2402
|
+
const delayMs = 2e3;
|
|
2403
|
+
process.stderr.write(`[r5d-worker] ${activeProcesses.size} process(es) still active; reconnecting in ${delayMs}ms
|
|
2404
|
+
`);
|
|
2405
|
+
const reconnect = () => {
|
|
2406
|
+
void startWorker(options).catch((error) => {
|
|
2407
|
+
process.stderr.write(`[r5d-worker] reconnect failed: ${error instanceof Error ? error.message : String(error)}
|
|
2408
|
+
`);
|
|
2409
|
+
if (activeProcesses.size > 0) {
|
|
2410
|
+
setTimeout(reconnect, delayMs);
|
|
2411
|
+
return;
|
|
2412
|
+
}
|
|
2413
|
+
process.exit(1);
|
|
2414
|
+
});
|
|
2415
|
+
};
|
|
2416
|
+
setTimeout(reconnect, delayMs);
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2367
2419
|
process.exit(event.code === 1e3 ? 0 : 1);
|
|
2368
2420
|
});
|
|
2369
2421
|
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() {
|
|
@@ -74,8 +74,16 @@ function readInstalledPackageVersion(packageJsonPath) {
|
|
|
74
74
|
}
|
|
75
75
|
return null;
|
|
76
76
|
}
|
|
77
|
+
function resolveEntrypointPath(entrypoint) {
|
|
78
|
+
const resolved = path.resolve(entrypoint);
|
|
79
|
+
try {
|
|
80
|
+
return fs.realpathSync.native(resolved);
|
|
81
|
+
} catch {
|
|
82
|
+
return resolved;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
77
85
|
function getWorkerVersion() {
|
|
78
|
-
const entrypoint = process.argv[1] ?
|
|
86
|
+
const entrypoint = process.argv[1] ? resolveEntrypointPath(process.argv[1]) : null;
|
|
79
87
|
let current = entrypoint ? path.dirname(entrypoint) : process.cwd();
|
|
80
88
|
for (let index = 0; index < 12; index += 1) {
|
|
81
89
|
const packageVersion = readInstalledPackageVersion(path.join(current, "package.json"));
|
|
@@ -650,31 +658,6 @@ function resolveProjectFilePath(branchPath, virtualPath) {
|
|
|
650
658
|
assertInsideBranch(branchPath, absolutePath);
|
|
651
659
|
return { absolutePath, virtualPath: repoRelativePath ? `/${repoRelativePath}` : "/", repoRelativePath };
|
|
652
660
|
}
|
|
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
661
|
function resolveRemoteUrl(baseUrl, remoteUrl) {
|
|
679
662
|
if (/^https?:\/\//i.test(remoteUrl)) {
|
|
680
663
|
return remoteUrl;
|
|
@@ -1620,7 +1603,16 @@ async function executeCommand(input) {
|
|
|
1620
1603
|
...planProcessEnv
|
|
1621
1604
|
}
|
|
1622
1605
|
});
|
|
1623
|
-
activeProcesses.set(input.message.runId, {
|
|
1606
|
+
activeProcesses.set(input.message.runId, {
|
|
1607
|
+
process: subprocess,
|
|
1608
|
+
projectId: input.projectId,
|
|
1609
|
+
sessionId: input.message.sessionId ?? "",
|
|
1610
|
+
branchName: input.message.branchName,
|
|
1611
|
+
argv: input.message.argv,
|
|
1612
|
+
command: input.message.argv.join(" "),
|
|
1613
|
+
cwd: input.message.cwd,
|
|
1614
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1615
|
+
});
|
|
1624
1616
|
if (input.message.timeoutMs) {
|
|
1625
1617
|
timeout = setTimeout(() => {
|
|
1626
1618
|
subprocess.kill("SIGTERM");
|
|
@@ -1658,9 +1650,7 @@ async function executeCommand(input) {
|
|
|
1658
1650
|
}
|
|
1659
1651
|
async function executeStreamingCommand(input) {
|
|
1660
1652
|
const startedAt = Date.now();
|
|
1661
|
-
let timeout;
|
|
1662
1653
|
let started = false;
|
|
1663
|
-
let timedOut = false;
|
|
1664
1654
|
try {
|
|
1665
1655
|
if (cancelledProcessRuns.delete(input.message.runId)) {
|
|
1666
1656
|
sendWorkerMessage(input.ws, {
|
|
@@ -1710,19 +1700,22 @@ async function executeStreamingCommand(input) {
|
|
|
1710
1700
|
...planProcessEnv
|
|
1711
1701
|
}
|
|
1712
1702
|
});
|
|
1713
|
-
activeProcesses.set(input.message.runId, {
|
|
1703
|
+
activeProcesses.set(input.message.runId, {
|
|
1704
|
+
process: subprocess,
|
|
1705
|
+
projectId: input.projectId,
|
|
1706
|
+
sessionId: input.message.sessionId,
|
|
1707
|
+
branchName: input.message.branchName,
|
|
1708
|
+
argv: input.message.argv,
|
|
1709
|
+
command: input.message.command,
|
|
1710
|
+
cwd: input.message.cwd,
|
|
1711
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1712
|
+
});
|
|
1714
1713
|
started = true;
|
|
1715
1714
|
sendWorkerMessage(input.ws, {
|
|
1716
1715
|
type: "exec_started",
|
|
1717
1716
|
requestId: input.message.requestId,
|
|
1718
1717
|
runId: input.message.runId
|
|
1719
1718
|
});
|
|
1720
|
-
if (input.message.timeoutMs) {
|
|
1721
|
-
timeout = setTimeout(() => {
|
|
1722
|
-
timedOut = true;
|
|
1723
|
-
subprocess.kill("SIGTERM");
|
|
1724
|
-
}, input.message.timeoutMs);
|
|
1725
|
-
}
|
|
1726
1719
|
const [exitCode] = await Promise.all([
|
|
1727
1720
|
subprocess.exited,
|
|
1728
1721
|
streamCommandOutput(subprocess.stdout, (data) => {
|
|
@@ -1746,8 +1739,7 @@ async function executeStreamingCommand(input) {
|
|
|
1746
1739
|
type: "exec_exit",
|
|
1747
1740
|
runId: input.message.runId,
|
|
1748
1741
|
exitCode,
|
|
1749
|
-
durationMs: Date.now() - startedAt
|
|
1750
|
-
...timedOut ? { timedOut: true } : {}
|
|
1742
|
+
durationMs: Date.now() - startedAt
|
|
1751
1743
|
});
|
|
1752
1744
|
} catch (error) {
|
|
1753
1745
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1767,15 +1759,36 @@ async function executeStreamingCommand(input) {
|
|
|
1767
1759
|
});
|
|
1768
1760
|
}
|
|
1769
1761
|
} finally {
|
|
1770
|
-
if (timeout) {
|
|
1771
|
-
clearTimeout(timeout);
|
|
1772
|
-
}
|
|
1773
1762
|
activeProcesses.delete(input.message.runId);
|
|
1774
1763
|
cancelledProcessRuns.delete(input.message.runId);
|
|
1775
1764
|
}
|
|
1776
1765
|
}
|
|
1777
1766
|
function sendWorkerMessage(ws, message) {
|
|
1778
|
-
ws
|
|
1767
|
+
const target = currentWorkerSocket ?? ws;
|
|
1768
|
+
try {
|
|
1769
|
+
target.send(JSON.stringify(message));
|
|
1770
|
+
} catch (error) {
|
|
1771
|
+
process.stderr.write(`[r5d-worker] failed to send ${message.type}: ${error instanceof Error ? error.message : String(error)}
|
|
1772
|
+
`);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
function buildActiveProcessReports() {
|
|
1776
|
+
return Array.from(activeProcesses.entries()).filter(([, active]) => active.sessionId.length > 0).map(([runId, active]) => ({
|
|
1777
|
+
runId,
|
|
1778
|
+
projectId: active.projectId,
|
|
1779
|
+
sessionId: active.sessionId,
|
|
1780
|
+
branchName: active.branchName,
|
|
1781
|
+
argv: active.argv,
|
|
1782
|
+
command: active.command,
|
|
1783
|
+
...active.cwd ? { cwd: active.cwd } : {},
|
|
1784
|
+
startedAt: active.startedAt
|
|
1785
|
+
}));
|
|
1786
|
+
}
|
|
1787
|
+
function sendActiveProcessReport(ws) {
|
|
1788
|
+
sendWorkerMessage(ws, {
|
|
1789
|
+
type: "active_processes",
|
|
1790
|
+
processes: buildActiveProcessReports()
|
|
1791
|
+
});
|
|
1779
1792
|
}
|
|
1780
1793
|
const PTY_BRIDGE_SCRIPT = String.raw`
|
|
1781
1794
|
const readline = require("node:readline");
|
|
@@ -2118,6 +2131,7 @@ async function startWorker(options) {
|
|
|
2118
2131
|
}
|
|
2119
2132
|
});
|
|
2120
2133
|
ws.addEventListener("open", () => {
|
|
2134
|
+
currentWorkerSocket = ws;
|
|
2121
2135
|
const hello = {
|
|
2122
2136
|
type: "hello",
|
|
2123
2137
|
hostInfo: {
|
|
@@ -2132,6 +2146,7 @@ async function startWorker(options) {
|
|
|
2132
2146
|
}
|
|
2133
2147
|
};
|
|
2134
2148
|
ws.send(JSON.stringify(hello));
|
|
2149
|
+
sendActiveProcessReport(ws);
|
|
2135
2150
|
process.stdout.write("[r5d-worker] connected\n");
|
|
2136
2151
|
});
|
|
2137
2152
|
ws.addEventListener("message", (event) => {
|
|
@@ -2154,31 +2169,29 @@ async function startWorker(options) {
|
|
|
2154
2169
|
const branches = project.branches.length > 0 ? project.branches : [project.defaultBranch || "main"];
|
|
2155
2170
|
for (const branchName of branches) {
|
|
2156
2171
|
try {
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2172
|
+
ensureBranchWorkspace({
|
|
2173
|
+
projectId: project.projectId,
|
|
2174
|
+
baseUrl,
|
|
2175
|
+
token,
|
|
2176
|
+
projectRoot,
|
|
2177
|
+
syncRoot,
|
|
2178
|
+
branchName,
|
|
2179
|
+
manifest: project
|
|
2180
|
+
});
|
|
2181
|
+
try {
|
|
2182
|
+
await syncProjectPlans({
|
|
2160
2183
|
baseUrl,
|
|
2161
2184
|
token,
|
|
2162
|
-
|
|
2163
|
-
syncRoot,
|
|
2185
|
+
projectId: project.projectId,
|
|
2164
2186
|
branchName,
|
|
2165
|
-
|
|
2187
|
+
planRoot
|
|
2166
2188
|
});
|
|
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)}
|
|
2189
|
+
} catch (error) {
|
|
2190
|
+
process.stderr.write(
|
|
2191
|
+
`[r5d-worker] plan sync skipped for ${project.projectId}/${branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
2178
2192
|
`
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
});
|
|
2193
|
+
);
|
|
2194
|
+
}
|
|
2182
2195
|
} catch (error) {
|
|
2183
2196
|
process.stderr.write(
|
|
2184
2197
|
`[r5d-worker] failed to sync ${project.repoSlug}/${branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
@@ -2206,7 +2219,7 @@ async function startWorker(options) {
|
|
|
2206
2219
|
requestId: message.requestId,
|
|
2207
2220
|
runId: message.runId,
|
|
2208
2221
|
cancelled: true,
|
|
2209
|
-
message: active ? `Sent SIGTERM to ${active.command
|
|
2222
|
+
message: active ? `Sent SIGTERM to ${active.command}` : "Cancellation queued before command start"
|
|
2210
2223
|
})
|
|
2211
2224
|
);
|
|
2212
2225
|
return;
|
|
@@ -2236,11 +2249,17 @@ async function startWorker(options) {
|
|
|
2236
2249
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2237
2250
|
process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
|
|
2238
2251
|
`);
|
|
2239
|
-
const result = await
|
|
2240
|
-
message
|
|
2241
|
-
message.
|
|
2242
|
-
|
|
2243
|
-
|
|
2252
|
+
const result = await executeCommand({
|
|
2253
|
+
message,
|
|
2254
|
+
projectId: message.projectId,
|
|
2255
|
+
baseUrl,
|
|
2256
|
+
token,
|
|
2257
|
+
projectRoot,
|
|
2258
|
+
syncRoot,
|
|
2259
|
+
artifactRoot,
|
|
2260
|
+
planRoot,
|
|
2261
|
+
manifest
|
|
2262
|
+
});
|
|
2244
2263
|
ws.send(JSON.stringify(result));
|
|
2245
2264
|
return;
|
|
2246
2265
|
}
|
|
@@ -2255,11 +2274,18 @@ async function startWorker(options) {
|
|
|
2255
2274
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2256
2275
|
process.stdout.write(`[r5d-worker] exec_start ${message.runId}: ${message.argv.join(" ")}
|
|
2257
2276
|
`);
|
|
2258
|
-
void
|
|
2259
|
-
|
|
2260
|
-
message
|
|
2261
|
-
|
|
2262
|
-
|
|
2277
|
+
void executeStreamingCommand({
|
|
2278
|
+
ws,
|
|
2279
|
+
message,
|
|
2280
|
+
projectId: message.projectId,
|
|
2281
|
+
baseUrl,
|
|
2282
|
+
token,
|
|
2283
|
+
projectRoot,
|
|
2284
|
+
syncRoot,
|
|
2285
|
+
artifactRoot,
|
|
2286
|
+
planRoot,
|
|
2287
|
+
manifest
|
|
2288
|
+
}).catch((error) => {
|
|
2263
2289
|
sendWorkerMessage(ws, {
|
|
2264
2290
|
type: "exec_start_error",
|
|
2265
2291
|
requestId: message.requestId,
|
|
@@ -2281,11 +2307,16 @@ async function startWorker(options) {
|
|
|
2281
2307
|
try {
|
|
2282
2308
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2283
2309
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2284
|
-
const result = await
|
|
2285
|
-
message
|
|
2286
|
-
message.
|
|
2287
|
-
|
|
2288
|
-
|
|
2310
|
+
const result = await executeOperation({
|
|
2311
|
+
message,
|
|
2312
|
+
projectId: message.projectId,
|
|
2313
|
+
baseUrl,
|
|
2314
|
+
token,
|
|
2315
|
+
projectRoot,
|
|
2316
|
+
syncRoot,
|
|
2317
|
+
artifactRoot,
|
|
2318
|
+
manifest
|
|
2319
|
+
});
|
|
2289
2320
|
ws.send(
|
|
2290
2321
|
JSON.stringify({
|
|
2291
2322
|
type: "operation_result",
|
|
@@ -2320,10 +2351,31 @@ async function startWorker(options) {
|
|
|
2320
2351
|
});
|
|
2321
2352
|
});
|
|
2322
2353
|
ws.addEventListener("close", (event) => {
|
|
2354
|
+
if (currentWorkerSocket === ws) {
|
|
2355
|
+
currentWorkerSocket = null;
|
|
2356
|
+
}
|
|
2323
2357
|
closeAllPtys();
|
|
2324
2358
|
const reason = event.reason ? `: ${event.reason}` : "";
|
|
2325
2359
|
process.stderr.write(`[r5d-worker] disconnected (${event.code}${reason})
|
|
2326
2360
|
`);
|
|
2361
|
+
if (activeProcesses.size > 0) {
|
|
2362
|
+
const delayMs = 2e3;
|
|
2363
|
+
process.stderr.write(`[r5d-worker] ${activeProcesses.size} process(es) still active; reconnecting in ${delayMs}ms
|
|
2364
|
+
`);
|
|
2365
|
+
const reconnect = () => {
|
|
2366
|
+
void startWorker(options).catch((error) => {
|
|
2367
|
+
process.stderr.write(`[r5d-worker] reconnect failed: ${error instanceof Error ? error.message : String(error)}
|
|
2368
|
+
`);
|
|
2369
|
+
if (activeProcesses.size > 0) {
|
|
2370
|
+
setTimeout(reconnect, delayMs);
|
|
2371
|
+
return;
|
|
2372
|
+
}
|
|
2373
|
+
process.exit(1);
|
|
2374
|
+
});
|
|
2375
|
+
};
|
|
2376
|
+
setTimeout(reconnect, delayMs);
|
|
2377
|
+
return;
|
|
2378
|
+
}
|
|
2327
2379
|
process.exit(event.code === 1e3 ? 0 : 1);
|
|
2328
2380
|
});
|
|
2329
2381
|
ws.addEventListener("error", (event) => {
|
package/dist/mjs/package.json
CHANGED