@ricsam/r5d-worker 0.0.39 → 0.0.40
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/README.md +2 -0
- package/dist/cjs/main.cjs +22 -8
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/supervisor.cjs +21 -1
- package/dist/mjs/main.mjs +30 -9
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/supervisor.mjs +18 -1
- package/dist/types/supervisor.d.ts +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,6 +27,8 @@ Worker labels are mandatory and unique per user. Choose labels that describe hos
|
|
|
27
27
|
|
|
28
28
|
`r5d-worker start` keeps a lightweight supervisor process attached to the launching terminal or service. When both r5d CLIs are updated from User Settings, the connected runtime verifies the installed versions, exits with a reload signal, and the supervisor reconnects using the new worker package. Updates only run while the worker has no active commands or shells.
|
|
29
29
|
|
|
30
|
+
The supervisor also reconnects automatically after server rollouts and transient network failures. A brief bounded delay prevents a tight retry loop while the server is unavailable. Pressing Ctrl+C still stops the worker, and agent-managed processes retain their existing in-process recovery path while disconnected.
|
|
31
|
+
|
|
30
32
|
One-click updates use the current user's global npm prefix and never invoke `sudo`. If that prefix is not writable, User Settings provides the exact command to run manually.
|
|
31
33
|
|
|
32
34
|
The agent runs commands through the virtual shell command:
|
package/dist/cjs/main.cjs
CHANGED
|
@@ -59,6 +59,9 @@ var import_supervisor = require("./supervisor.cjs");
|
|
|
59
59
|
var import_managed_paths = require("./managed-paths.cjs");
|
|
60
60
|
var import_workspace_sync = require("./workspace-sync.cjs");
|
|
61
61
|
const import_meta = {};
|
|
62
|
+
class WorkerServerUnavailableError extends Error {
|
|
63
|
+
name = "WorkerServerUnavailableError";
|
|
64
|
+
}
|
|
62
65
|
const DEFAULT_BASE_URL = "https://r5d.dev";
|
|
63
66
|
const WORKER_PACKAGE_NAME = "@ricsam/r5d-worker";
|
|
64
67
|
const LABEL_RE = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,62}$/;
|
|
@@ -559,7 +562,7 @@ async function verifyR5dctlAuth(baseUrl, token) {
|
|
|
559
562
|
}
|
|
560
563
|
});
|
|
561
564
|
} catch (error) {
|
|
562
|
-
throw new
|
|
565
|
+
throw new WorkerServerUnavailableError(`Could not reach ${baseUrl}: ${error instanceof Error ? error.message : String(error)}`);
|
|
563
566
|
}
|
|
564
567
|
if (response.ok) {
|
|
565
568
|
return;
|
|
@@ -574,6 +577,9 @@ async function verifyR5dctlAuth(baseUrl, token) {
|
|
|
574
577
|
} catch {
|
|
575
578
|
}
|
|
576
579
|
const detail = serverMessage ? `: ${serverMessage}` : "";
|
|
580
|
+
if ((0, import_supervisor.isRetryableWorkerServerStatus)(response.status)) {
|
|
581
|
+
throw new WorkerServerUnavailableError(`Server unavailable at ${baseUrl} (${response.status})${detail}`);
|
|
582
|
+
}
|
|
577
583
|
throw new Error(
|
|
578
584
|
`Authentication failed against ${baseUrl} (${response.status})${detail}. Run \`r5dctl auth login --base-url ${baseUrl}\` or pass --token/--api-key for this server.`
|
|
579
585
|
);
|
|
@@ -2595,9 +2601,8 @@ async function startWorker(options) {
|
|
|
2595
2601
|
process.exit(import_supervisor.WORKER_RELOAD_EXIT_CODE);
|
|
2596
2602
|
}
|
|
2597
2603
|
if (activeProcesses.size > 0 || pendingProcessTerminals.size > 0) {
|
|
2598
|
-
const delayMs = 2e3;
|
|
2599
2604
|
process.stderr.write(
|
|
2600
|
-
`[r5d-worker] ${activeProcesses.size} process(es) active and ${pendingProcessTerminals.size} terminal report(s) pending; reconnecting in ${
|
|
2605
|
+
`[r5d-worker] ${activeProcesses.size} process(es) active and ${pendingProcessTerminals.size} terminal report(s) pending; reconnecting in ${import_supervisor.WORKER_RECONNECT_DELAY_MS}ms
|
|
2601
2606
|
`
|
|
2602
2607
|
);
|
|
2603
2608
|
const reconnect = () => {
|
|
@@ -2605,16 +2610,16 @@ async function startWorker(options) {
|
|
|
2605
2610
|
process.stderr.write(`[r5d-worker] reconnect failed: ${error instanceof Error ? error.message : String(error)}
|
|
2606
2611
|
`);
|
|
2607
2612
|
if (activeProcesses.size > 0 || pendingProcessTerminals.size > 0) {
|
|
2608
|
-
setTimeout(reconnect,
|
|
2613
|
+
setTimeout(reconnect, import_supervisor.WORKER_RECONNECT_DELAY_MS);
|
|
2609
2614
|
return;
|
|
2610
2615
|
}
|
|
2611
|
-
process.exit(1);
|
|
2616
|
+
process.exit(error instanceof WorkerServerUnavailableError ? import_supervisor.WORKER_RECONNECT_EXIT_CODE : 1);
|
|
2612
2617
|
});
|
|
2613
2618
|
};
|
|
2614
|
-
setTimeout(reconnect,
|
|
2619
|
+
setTimeout(reconnect, import_supervisor.WORKER_RECONNECT_DELAY_MS);
|
|
2615
2620
|
return;
|
|
2616
2621
|
}
|
|
2617
|
-
process.exit(
|
|
2622
|
+
process.exit(import_supervisor.WORKER_RECONNECT_EXIT_CODE);
|
|
2618
2623
|
});
|
|
2619
2624
|
ws.addEventListener("error", (event) => {
|
|
2620
2625
|
const message = "message" in event && typeof event.message === "string" && event.message.trim() ? `: ${event.message.trim()}` : "";
|
|
@@ -2636,7 +2641,16 @@ async function main() {
|
|
|
2636
2641
|
}
|
|
2637
2642
|
if (process.env[import_supervisor.WORKER_RUNTIME_ENV] === "1") {
|
|
2638
2643
|
delete process.env[import_supervisor.WORKER_RUNTIME_ENV];
|
|
2639
|
-
|
|
2644
|
+
try {
|
|
2645
|
+
await startWorker(parsed.options);
|
|
2646
|
+
} catch (error) {
|
|
2647
|
+
if (error instanceof WorkerServerUnavailableError) {
|
|
2648
|
+
process.stderr.write(`[r5d-worker] ${error.message}
|
|
2649
|
+
`);
|
|
2650
|
+
process.exit(import_supervisor.WORKER_RECONNECT_EXIT_CODE);
|
|
2651
|
+
}
|
|
2652
|
+
throw error;
|
|
2653
|
+
}
|
|
2640
2654
|
return;
|
|
2641
2655
|
}
|
|
2642
2656
|
const exitCode = await (0, import_supervisor.superviseWorkerRuntime)(process.argv.slice(2));
|
package/dist/cjs/package.json
CHANGED
package/dist/cjs/supervisor.cjs
CHANGED
|
@@ -18,17 +18,28 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var supervisor_exports = {};
|
|
20
20
|
__export(supervisor_exports, {
|
|
21
|
+
WORKER_RECONNECT_DELAY_MS: () => WORKER_RECONNECT_DELAY_MS,
|
|
22
|
+
WORKER_RECONNECT_EXIT_CODE: () => WORKER_RECONNECT_EXIT_CODE,
|
|
21
23
|
WORKER_RELOAD_EXIT_CODE: () => WORKER_RELOAD_EXIT_CODE,
|
|
22
24
|
WORKER_RUNTIME_ENV: () => WORKER_RUNTIME_ENV,
|
|
25
|
+
isRetryableWorkerServerStatus: () => isRetryableWorkerServerStatus,
|
|
23
26
|
superviseWorkerRuntime: () => superviseWorkerRuntime
|
|
24
27
|
});
|
|
25
28
|
module.exports = __toCommonJS(supervisor_exports);
|
|
26
29
|
const WORKER_RUNTIME_ENV = "R5D_WORKER_INTERNAL_RUNTIME";
|
|
27
30
|
const WORKER_RELOAD_EXIT_CODE = 75;
|
|
31
|
+
const WORKER_RECONNECT_EXIT_CODE = 76;
|
|
32
|
+
const WORKER_RECONNECT_DELAY_MS = 2e3;
|
|
28
33
|
function defaultRuntimeSpawner(argv, options) {
|
|
29
34
|
return Bun.spawn(argv, options);
|
|
30
35
|
}
|
|
31
|
-
|
|
36
|
+
function defaultRuntimeDelay(delayMs) {
|
|
37
|
+
return new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
38
|
+
}
|
|
39
|
+
function isRetryableWorkerServerStatus(status) {
|
|
40
|
+
return status === 408 || status === 429 || status >= 500;
|
|
41
|
+
}
|
|
42
|
+
async function superviseWorkerRuntime(argv, env = process.env, spawnRuntime = defaultRuntimeSpawner, waitBeforeReconnect = defaultRuntimeDelay) {
|
|
32
43
|
if (!process.argv[1]) {
|
|
33
44
|
throw new Error("Cannot locate the r5d-worker entrypoint");
|
|
34
45
|
}
|
|
@@ -47,12 +58,21 @@ async function superviseWorkerRuntime(argv, env = process.env, spawnRuntime = de
|
|
|
47
58
|
process.stdout.write("[r5d-worker] activating updated worker runtime\n");
|
|
48
59
|
continue;
|
|
49
60
|
}
|
|
61
|
+
if (exitCode === WORKER_RECONNECT_EXIT_CODE) {
|
|
62
|
+
process.stderr.write(`[r5d-worker] reconnecting in ${WORKER_RECONNECT_DELAY_MS}ms
|
|
63
|
+
`);
|
|
64
|
+
await waitBeforeReconnect(WORKER_RECONNECT_DELAY_MS);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
50
67
|
return exitCode;
|
|
51
68
|
}
|
|
52
69
|
}
|
|
53
70
|
// Annotate the CommonJS export names for ESM import in node:
|
|
54
71
|
0 && (module.exports = {
|
|
72
|
+
WORKER_RECONNECT_DELAY_MS,
|
|
73
|
+
WORKER_RECONNECT_EXIT_CODE,
|
|
55
74
|
WORKER_RELOAD_EXIT_CODE,
|
|
56
75
|
WORKER_RUNTIME_ENV,
|
|
76
|
+
isRetryableWorkerServerStatus,
|
|
57
77
|
superviseWorkerRuntime
|
|
58
78
|
});
|
package/dist/mjs/main.mjs
CHANGED
|
@@ -8,7 +8,14 @@ import { installCliUpdate, readInstalledCliVersion } from "./cli-update.mjs";
|
|
|
8
8
|
import { configureVisibleGitIdentity } from "./git-identity.mjs";
|
|
9
9
|
import { hasWorkerHeartbeatTimedOut, WORKER_HEARTBEAT_INTERVAL_MS } from "./heartbeat.mjs";
|
|
10
10
|
import { terminateProcessTree } from "./process-tree.mjs";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
isRetryableWorkerServerStatus,
|
|
13
|
+
superviseWorkerRuntime,
|
|
14
|
+
WORKER_RECONNECT_DELAY_MS,
|
|
15
|
+
WORKER_RECONNECT_EXIT_CODE,
|
|
16
|
+
WORKER_RELOAD_EXIT_CODE,
|
|
17
|
+
WORKER_RUNTIME_ENV
|
|
18
|
+
} from "./supervisor.mjs";
|
|
12
19
|
import { managedProjectRoot, migrateLegacyProjectRoots, validateManagedBranchName } from "./managed-paths.mjs";
|
|
13
20
|
import {
|
|
14
21
|
WORKSPACE_PERIODIC_SCAN_INTERVAL_MS,
|
|
@@ -16,6 +23,9 @@ import {
|
|
|
16
23
|
calculateWorkspaceDiffFingerprint,
|
|
17
24
|
synchronizeWorkspace
|
|
18
25
|
} from "./workspace-sync.mjs";
|
|
26
|
+
class WorkerServerUnavailableError extends Error {
|
|
27
|
+
name = "WorkerServerUnavailableError";
|
|
28
|
+
}
|
|
19
29
|
const DEFAULT_BASE_URL = "https://r5d.dev";
|
|
20
30
|
const WORKER_PACKAGE_NAME = "@ricsam/r5d-worker";
|
|
21
31
|
const LABEL_RE = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,62}$/;
|
|
@@ -516,7 +526,7 @@ async function verifyR5dctlAuth(baseUrl, token) {
|
|
|
516
526
|
}
|
|
517
527
|
});
|
|
518
528
|
} catch (error) {
|
|
519
|
-
throw new
|
|
529
|
+
throw new WorkerServerUnavailableError(`Could not reach ${baseUrl}: ${error instanceof Error ? error.message : String(error)}`);
|
|
520
530
|
}
|
|
521
531
|
if (response.ok) {
|
|
522
532
|
return;
|
|
@@ -531,6 +541,9 @@ async function verifyR5dctlAuth(baseUrl, token) {
|
|
|
531
541
|
} catch {
|
|
532
542
|
}
|
|
533
543
|
const detail = serverMessage ? `: ${serverMessage}` : "";
|
|
544
|
+
if (isRetryableWorkerServerStatus(response.status)) {
|
|
545
|
+
throw new WorkerServerUnavailableError(`Server unavailable at ${baseUrl} (${response.status})${detail}`);
|
|
546
|
+
}
|
|
534
547
|
throw new Error(
|
|
535
548
|
`Authentication failed against ${baseUrl} (${response.status})${detail}. Run \`r5dctl auth login --base-url ${baseUrl}\` or pass --token/--api-key for this server.`
|
|
536
549
|
);
|
|
@@ -2552,9 +2565,8 @@ async function startWorker(options) {
|
|
|
2552
2565
|
process.exit(WORKER_RELOAD_EXIT_CODE);
|
|
2553
2566
|
}
|
|
2554
2567
|
if (activeProcesses.size > 0 || pendingProcessTerminals.size > 0) {
|
|
2555
|
-
const delayMs = 2e3;
|
|
2556
2568
|
process.stderr.write(
|
|
2557
|
-
`[r5d-worker] ${activeProcesses.size} process(es) active and ${pendingProcessTerminals.size} terminal report(s) pending; reconnecting in ${
|
|
2569
|
+
`[r5d-worker] ${activeProcesses.size} process(es) active and ${pendingProcessTerminals.size} terminal report(s) pending; reconnecting in ${WORKER_RECONNECT_DELAY_MS}ms
|
|
2558
2570
|
`
|
|
2559
2571
|
);
|
|
2560
2572
|
const reconnect = () => {
|
|
@@ -2562,16 +2574,16 @@ async function startWorker(options) {
|
|
|
2562
2574
|
process.stderr.write(`[r5d-worker] reconnect failed: ${error instanceof Error ? error.message : String(error)}
|
|
2563
2575
|
`);
|
|
2564
2576
|
if (activeProcesses.size > 0 || pendingProcessTerminals.size > 0) {
|
|
2565
|
-
setTimeout(reconnect,
|
|
2577
|
+
setTimeout(reconnect, WORKER_RECONNECT_DELAY_MS);
|
|
2566
2578
|
return;
|
|
2567
2579
|
}
|
|
2568
|
-
process.exit(1);
|
|
2580
|
+
process.exit(error instanceof WorkerServerUnavailableError ? WORKER_RECONNECT_EXIT_CODE : 1);
|
|
2569
2581
|
});
|
|
2570
2582
|
};
|
|
2571
|
-
setTimeout(reconnect,
|
|
2583
|
+
setTimeout(reconnect, WORKER_RECONNECT_DELAY_MS);
|
|
2572
2584
|
return;
|
|
2573
2585
|
}
|
|
2574
|
-
process.exit(
|
|
2586
|
+
process.exit(WORKER_RECONNECT_EXIT_CODE);
|
|
2575
2587
|
});
|
|
2576
2588
|
ws.addEventListener("error", (event) => {
|
|
2577
2589
|
const message = "message" in event && typeof event.message === "string" && event.message.trim() ? `: ${event.message.trim()}` : "";
|
|
@@ -2593,7 +2605,16 @@ async function main() {
|
|
|
2593
2605
|
}
|
|
2594
2606
|
if (process.env[WORKER_RUNTIME_ENV] === "1") {
|
|
2595
2607
|
delete process.env[WORKER_RUNTIME_ENV];
|
|
2596
|
-
|
|
2608
|
+
try {
|
|
2609
|
+
await startWorker(parsed.options);
|
|
2610
|
+
} catch (error) {
|
|
2611
|
+
if (error instanceof WorkerServerUnavailableError) {
|
|
2612
|
+
process.stderr.write(`[r5d-worker] ${error.message}
|
|
2613
|
+
`);
|
|
2614
|
+
process.exit(WORKER_RECONNECT_EXIT_CODE);
|
|
2615
|
+
}
|
|
2616
|
+
throw error;
|
|
2617
|
+
}
|
|
2597
2618
|
return;
|
|
2598
2619
|
}
|
|
2599
2620
|
const exitCode = await superviseWorkerRuntime(process.argv.slice(2));
|
package/dist/mjs/package.json
CHANGED
package/dist/mjs/supervisor.mjs
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
const WORKER_RUNTIME_ENV = "R5D_WORKER_INTERNAL_RUNTIME";
|
|
2
2
|
const WORKER_RELOAD_EXIT_CODE = 75;
|
|
3
|
+
const WORKER_RECONNECT_EXIT_CODE = 76;
|
|
4
|
+
const WORKER_RECONNECT_DELAY_MS = 2e3;
|
|
3
5
|
function defaultRuntimeSpawner(argv, options) {
|
|
4
6
|
return Bun.spawn(argv, options);
|
|
5
7
|
}
|
|
6
|
-
|
|
8
|
+
function defaultRuntimeDelay(delayMs) {
|
|
9
|
+
return new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
10
|
+
}
|
|
11
|
+
function isRetryableWorkerServerStatus(status) {
|
|
12
|
+
return status === 408 || status === 429 || status >= 500;
|
|
13
|
+
}
|
|
14
|
+
async function superviseWorkerRuntime(argv, env = process.env, spawnRuntime = defaultRuntimeSpawner, waitBeforeReconnect = defaultRuntimeDelay) {
|
|
7
15
|
if (!process.argv[1]) {
|
|
8
16
|
throw new Error("Cannot locate the r5d-worker entrypoint");
|
|
9
17
|
}
|
|
@@ -22,11 +30,20 @@ async function superviseWorkerRuntime(argv, env = process.env, spawnRuntime = de
|
|
|
22
30
|
process.stdout.write("[r5d-worker] activating updated worker runtime\n");
|
|
23
31
|
continue;
|
|
24
32
|
}
|
|
33
|
+
if (exitCode === WORKER_RECONNECT_EXIT_CODE) {
|
|
34
|
+
process.stderr.write(`[r5d-worker] reconnecting in ${WORKER_RECONNECT_DELAY_MS}ms
|
|
35
|
+
`);
|
|
36
|
+
await waitBeforeReconnect(WORKER_RECONNECT_DELAY_MS);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
25
39
|
return exitCode;
|
|
26
40
|
}
|
|
27
41
|
}
|
|
28
42
|
export {
|
|
43
|
+
WORKER_RECONNECT_DELAY_MS,
|
|
44
|
+
WORKER_RECONNECT_EXIT_CODE,
|
|
29
45
|
WORKER_RELOAD_EXIT_CODE,
|
|
30
46
|
WORKER_RUNTIME_ENV,
|
|
47
|
+
isRetryableWorkerServerStatus,
|
|
31
48
|
superviseWorkerRuntime
|
|
32
49
|
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare const WORKER_RUNTIME_ENV = "R5D_WORKER_INTERNAL_RUNTIME";
|
|
2
2
|
export declare const WORKER_RELOAD_EXIT_CODE = 75;
|
|
3
|
+
export declare const WORKER_RECONNECT_EXIT_CODE = 76;
|
|
4
|
+
export declare const WORKER_RECONNECT_DELAY_MS = 2000;
|
|
3
5
|
type RuntimeProcess = {
|
|
4
6
|
exited: Promise<number>;
|
|
5
7
|
};
|
|
@@ -9,5 +11,7 @@ export type RuntimeSpawner = (argv: string[], options: {
|
|
|
9
11
|
stderr: "inherit";
|
|
10
12
|
env: Record<string, string | undefined>;
|
|
11
13
|
}) => RuntimeProcess;
|
|
12
|
-
export
|
|
14
|
+
export type RuntimeDelay = (delayMs: number) => Promise<void>;
|
|
15
|
+
export declare function isRetryableWorkerServerStatus(status: number): boolean;
|
|
16
|
+
export declare function superviseWorkerRuntime(argv: string[], env?: Record<string, string | undefined>, spawnRuntime?: RuntimeSpawner, waitBeforeReconnect?: RuntimeDelay): Promise<number>;
|
|
13
17
|
export {};
|