@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 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
- const branchLocks = /* @__PURE__ */ new Map();
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, { process: subprocess, command: input.message.argv });
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, { process: subprocess, command: input.message.argv });
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.send(JSON.stringify(message));
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
- await withBranchLock(project.projectId, branchName, async () => {
2198
- ensureBranchWorkspace({
2199
- projectId: project.projectId,
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
- projectRoot,
2203
- syncRoot,
2217
+ projectId: project.projectId,
2204
2218
  branchName,
2205
- manifest: project
2219
+ planRoot
2206
2220
  });
2207
- try {
2208
- await syncProjectPlans({
2209
- baseUrl,
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.join(" ")}` : "Cancellation queued before command start"
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 withBranchLock(
2280
- message.projectId,
2281
- message.branchName,
2282
- () => executeCommand({ message, projectId: message.projectId, baseUrl, token, projectRoot, syncRoot, artifactRoot, planRoot, manifest })
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 withBranchLock(
2299
- message.projectId,
2300
- message.branchName,
2301
- () => executeStreamingCommand({ ws, message, projectId: message.projectId, baseUrl, token, projectRoot, syncRoot, artifactRoot, planRoot, manifest })
2302
- ).catch((error) => {
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 withBranchLock(
2325
- message.projectId,
2326
- message.branchName,
2327
- () => executeOperation({ message, projectId: message.projectId, baseUrl, token, projectRoot, syncRoot, artifactRoot, manifest })
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) => {
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "type": "commonjs"
5
5
  }
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
- const branchLocks = /* @__PURE__ */ new Map();
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, { process: subprocess, command: input.message.argv });
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, { process: subprocess, command: input.message.argv });
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.send(JSON.stringify(message));
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
- await withBranchLock(project.projectId, branchName, async () => {
2158
- ensureBranchWorkspace({
2159
- projectId: project.projectId,
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
- projectRoot,
2163
- syncRoot,
2177
+ projectId: project.projectId,
2164
2178
  branchName,
2165
- manifest: project
2179
+ planRoot
2166
2180
  });
2167
- try {
2168
- await syncProjectPlans({
2169
- baseUrl,
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.join(" ")}` : "Cancellation queued before command start"
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 withBranchLock(
2240
- message.projectId,
2241
- message.branchName,
2242
- () => executeCommand({ message, projectId: message.projectId, baseUrl, token, projectRoot, syncRoot, artifactRoot, planRoot, manifest })
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 withBranchLock(
2259
- message.projectId,
2260
- message.branchName,
2261
- () => executeStreamingCommand({ ws, message, projectId: message.projectId, baseUrl, token, projectRoot, syncRoot, artifactRoot, planRoot, manifest })
2262
- ).catch((error) => {
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 withBranchLock(
2285
- message.projectId,
2286
- message.branchName,
2287
- () => executeOperation({ message, projectId: message.projectId, baseUrl, token, projectRoot, syncRoot, artifactRoot, manifest })
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) => {
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "type": "module"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/main.cjs",
6
6
  "module": "./dist/mjs/main.mjs",