@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 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() {
@@ -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] ? import_node_path.default.resolve(process.argv[1]) : null;
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, { process: subprocess, command: input.message.argv });
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, { process: subprocess, command: input.message.argv });
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.send(JSON.stringify(message));
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
- await withBranchLock(project.projectId, branchName, async () => {
2198
- ensureBranchWorkspace({
2199
- projectId: project.projectId,
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
- projectRoot,
2203
- syncRoot,
2225
+ projectId: project.projectId,
2204
2226
  branchName,
2205
- manifest: project
2227
+ planRoot
2206
2228
  });
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)}
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.join(" ")}` : "Cancellation queued before command start"
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 withBranchLock(
2280
- message.projectId,
2281
- message.branchName,
2282
- () => executeCommand({ message, projectId: message.projectId, baseUrl, token, projectRoot, syncRoot, artifactRoot, planRoot, manifest })
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 withBranchLock(
2299
- message.projectId,
2300
- message.branchName,
2301
- () => executeStreamingCommand({ ws, message, projectId: message.projectId, baseUrl, token, projectRoot, syncRoot, artifactRoot, planRoot, manifest })
2302
- ).catch((error) => {
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 withBranchLock(
2325
- message.projectId,
2326
- message.branchName,
2327
- () => executeOperation({ message, projectId: message.projectId, baseUrl, token, projectRoot, syncRoot, artifactRoot, manifest })
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) => {
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
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() {
@@ -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] ? path.resolve(process.argv[1]) : null;
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, { process: subprocess, command: input.message.argv });
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, { process: subprocess, command: input.message.argv });
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.send(JSON.stringify(message));
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
- await withBranchLock(project.projectId, branchName, async () => {
2158
- ensureBranchWorkspace({
2159
- projectId: project.projectId,
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
- projectRoot,
2163
- syncRoot,
2185
+ projectId: project.projectId,
2164
2186
  branchName,
2165
- manifest: project
2187
+ planRoot
2166
2188
  });
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)}
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.join(" ")}` : "Cancellation queued before command start"
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 withBranchLock(
2240
- message.projectId,
2241
- message.branchName,
2242
- () => executeCommand({ message, projectId: message.projectId, baseUrl, token, projectRoot, syncRoot, artifactRoot, planRoot, manifest })
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 withBranchLock(
2259
- message.projectId,
2260
- message.branchName,
2261
- () => executeStreamingCommand({ ws, message, projectId: message.projectId, baseUrl, token, projectRoot, syncRoot, artifactRoot, planRoot, manifest })
2262
- ).catch((error) => {
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 withBranchLock(
2285
- message.projectId,
2286
- message.branchName,
2287
- () => executeOperation({ message, projectId: message.projectId, baseUrl, token, projectRoot, syncRoot, artifactRoot, manifest })
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) => {
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
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.17",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/main.cjs",
6
6
  "module": "./dist/mjs/main.mjs",