@ricsam/r5d-worker 0.0.115 → 0.0.117

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 CHANGED
@@ -52,6 +52,8 @@ Worker labels are mandatory and unique per user. Choose labels that describe hos
52
52
 
53
53
  `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.
54
54
 
55
+ Before every new agent generation (including subagents, queued work that starts a new generation, and resumed sessions), the server requires the connected runtime to be at least the worker release offered in User Settings. This is the server's `packages/VERSION.txt` release, or its configured `MANAGED_DEV_WORKER_PACKAGE_*` override; admission does not query the npm registry. Missing or invalid runtime versions also block starts. The error includes the running and required versions plus the exact update command. Update in User Settings, or install the packages on the worker host and restart `r5d-worker`: a newer package on disk does not update an already connected process. From 0.0.116 onward, a runtime keeps its startup version across reconnections even if the package on disk changes. Existing agent generations are allowed to finish, and connected workers remain available for updates and synchronization. Protocol cutover checks still apply separately when connecting. Publish the matching packages before deploying a server that requires their new version.
56
+
55
57
  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. In-process reconnects retain initialized project configurations alongside those processes, so an unchanged checkout can reconnect while a dev server is running. Changed layouts and checkouts that need initialization still wait for active commands to finish.
56
58
 
57
59
  The runtime opens its control connection before recovering interrupted project snapshots. During recovery and initial project configuration it remains connected but non-dispatchable, reports its current phase plus branch/byte progress to the server, and prints the same progress locally. Recovery runs in a credential-sanitized helper process so the control loop can continue answering heartbeats. A durable `restoring` transaction state permits same-filesystem snapshots to be restored with one atomic directory move; cross-filesystem layouts retain the copy-and-fsync fallback.
package/dist/cjs/main.cjs CHANGED
@@ -56,6 +56,7 @@ __export(main_exports, {
56
56
  writeWorkerTextFile: () => writeWorkerTextFile
57
57
  });
58
58
  module.exports = __toCommonJS(main_exports);
59
+ var import_runtime_version = require("./runtime-version.cjs");
59
60
  var import_plan_paths = require("./plan-paths.cjs");
60
61
  var import_plan_parser = require("./plan-parser.cjs");
61
62
  var import_plan_lint = require("./plan-lint.cjs");
@@ -143,7 +144,6 @@ class WorkerServerUnavailableError extends Error {
143
144
  name = "WorkerServerUnavailableError";
144
145
  }
145
146
  const DEFAULT_BASE_URL = "https://r5d.dev";
146
- const WORKER_PACKAGE_NAME = "@ricsam/r5d-worker";
147
147
  const LABEL_RE = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,62}$/;
148
148
  const DEFAULT_READ_LIMIT = 2e3;
149
149
  const DEFAULT_READ_MAX_BYTES = 5e4;
@@ -452,55 +452,8 @@ Options:
452
452
  -h, --help Show this help
453
453
  `);
454
454
  }
455
- function readVersionFile(filePath) {
456
- try {
457
- const version = import_node_fs.default.readFileSync(filePath, "utf8").trim();
458
- return version || null;
459
- } catch {
460
- return null;
461
- }
462
- }
463
- function readInstalledPackageVersion(packageJsonPath) {
464
- try {
465
- const parsed = JSON.parse(import_node_fs.default.readFileSync(packageJsonPath, "utf8"));
466
- if (parsed.name === WORKER_PACKAGE_NAME && typeof parsed.version === "string" && parsed.version.trim()) {
467
- return parsed.version.trim();
468
- }
469
- } catch {
470
- return null;
471
- }
472
- return null;
473
- }
474
- function resolveEntrypointPath(entrypoint) {
475
- const resolved = import_node_path.default.resolve(entrypoint);
476
- try {
477
- return import_node_fs.default.realpathSync.native(resolved);
478
- } catch {
479
- return resolved;
480
- }
481
- }
482
- function getWorkerVersion() {
483
- const entrypoint = process.argv[1] ? resolveEntrypointPath(process.argv[1]) : null;
484
- let current = entrypoint ? import_node_path.default.dirname(entrypoint) : process.cwd();
485
- for (let index = 0; index < 12; index += 1) {
486
- const packageVersion = readInstalledPackageVersion(import_node_path.default.join(current, "package.json"));
487
- if (packageVersion) {
488
- return packageVersion;
489
- }
490
- const sourceVersion = readVersionFile(import_node_path.default.join(current, "VERSION.txt"));
491
- if (sourceVersion && import_node_path.default.basename(current) === "packages") {
492
- return sourceVersion;
493
- }
494
- const parent = import_node_path.default.dirname(current);
495
- if (parent === current) {
496
- break;
497
- }
498
- current = parent;
499
- }
500
- return "unknown";
501
- }
502
455
  function printVersion() {
503
- process.stdout.write(`r5d-worker ${getWorkerVersion()}
456
+ process.stdout.write(`r5d-worker ${import_runtime_version.WORKER_RUNTIME_VERSION}
504
457
  `);
505
458
  }
506
459
  function requireValue(value, message) {
@@ -3670,7 +3623,7 @@ function websocketUrl(baseUrl, label) {
3670
3623
  const url = new URL("/worker/ws", baseUrl);
3671
3624
  url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
3672
3625
  url.searchParams.set("label", label);
3673
- url.searchParams.set("version", getWorkerVersion());
3626
+ url.searchParams.set("version", import_runtime_version.WORKER_RUNTIME_VERSION);
3674
3627
  return url.toString();
3675
3628
  }
3676
3629
  async function startWorker(options, projectRuntime = {
@@ -5812,7 +5765,7 @@ async function startWorker(options, projectRuntime = {
5812
5765
  platform: process.platform,
5813
5766
  arch: process.arch,
5814
5767
  pid: process.pid,
5815
- version: getWorkerVersion(),
5768
+ version: import_runtime_version.WORKER_RUNTIME_VERSION,
5816
5769
  r5dctlVersion: (0, import_cli_update.readInstalledCliVersion)("r5dctl"),
5817
5770
  capabilities: {
5818
5771
  updateClis: true,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.115",
3
+ "version": "0.0.117",
4
4
  "type": "commonjs"
5
5
  }
@@ -59,6 +59,7 @@ class WorkerRecoveryStore {
59
59
  CREATE TABLE IF NOT EXISTS metadata (key TEXT PRIMARY KEY, value TEXT NOT NULL);
60
60
  CREATE TABLE IF NOT EXISTS operations (request_id TEXT PRIMARY KEY, run_id TEXT, session_id TEXT, request_type TEXT NOT NULL, digest TEXT NOT NULL, state TEXT NOT NULL, response TEXT);
61
61
  CREATE UNIQUE INDEX IF NOT EXISTS operation_run_start ON operations(run_id) WHERE request_type='exec_start';
62
+ CREATE INDEX IF NOT EXISTS operation_run_state ON operations(run_id,state);
62
63
  CREATE TABLE IF NOT EXISTS aliases (request_id TEXT PRIMARY KEY, operation_id TEXT NOT NULL);
63
64
  CREATE TABLE IF NOT EXISTS streams (run_id TEXT NOT NULL, stream TEXT NOT NULL, bytes INTEGER NOT NULL DEFAULT 0, acknowledged INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(run_id, stream));
64
65
  CREATE TABLE IF NOT EXISTS output (run_id TEXT NOT NULL, stream TEXT NOT NULL, offset INTEGER NOT NULL, bytes INTEGER NOT NULL, data TEXT NOT NULL, PRIMARY KEY(run_id, stream, offset));
@@ -176,7 +177,8 @@ class WorkerRecoveryStore {
176
177
  }
177
178
  *output(offsets = []) {
178
179
  const cursors = new Map(offsets.map((offset) => [offset.runId, offset]));
179
- for (const stream of this.db.query("SELECT * FROM streams WHERE NOT EXISTS (SELECT 1 FROM operations WHERE operations.run_id=streams.run_id AND operations.state='unknown') ORDER BY rowid").all()) {
180
+ const pendingStreamFilter = offsets.length === 0 ? "bytes>acknowledged AND " : "";
181
+ for (const stream of this.db.query(`SELECT * FROM streams WHERE ${pendingStreamFilter}NOT EXISTS (SELECT 1 FROM operations WHERE operations.run_id=streams.run_id AND operations.state='unknown') ORDER BY rowid`).all()) {
180
182
  const supplied = cursors.get(stream.run_id);
181
183
  const cursor = supplied ? stream.stream === "stdout" ? supplied.stdoutBytes : supplied.stderrBytes : stream.acknowledged;
182
184
  if (cursor >= stream.bytes) continue;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var runtime_version_exports = {};
30
+ __export(runtime_version_exports, {
31
+ WORKER_RUNTIME_VERSION: () => WORKER_RUNTIME_VERSION
32
+ });
33
+ module.exports = __toCommonJS(runtime_version_exports);
34
+ var import_node_fs = __toESM(require("node:fs"), 1);
35
+ var import_node_path = __toESM(require("node:path"), 1);
36
+ const WORKER_PACKAGE_NAME = "@ricsam/r5d-worker";
37
+ function readVersionFile(filePath) {
38
+ try {
39
+ const version = import_node_fs.default.readFileSync(filePath, "utf8").trim();
40
+ return version || null;
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+ function readInstalledPackageVersion(packageJsonPath) {
46
+ try {
47
+ const parsed = JSON.parse(import_node_fs.default.readFileSync(packageJsonPath, "utf8"));
48
+ if (parsed.name === WORKER_PACKAGE_NAME && typeof parsed.version === "string" && parsed.version.trim()) {
49
+ return parsed.version.trim();
50
+ }
51
+ } catch {
52
+ return null;
53
+ }
54
+ return null;
55
+ }
56
+ function resolveEntrypointPath(entrypoint) {
57
+ const resolved = import_node_path.default.resolve(entrypoint);
58
+ try {
59
+ return import_node_fs.default.realpathSync.native(resolved);
60
+ } catch {
61
+ return resolved;
62
+ }
63
+ }
64
+ function readWorkerVersion() {
65
+ const entrypoint = process.argv[1] ? resolveEntrypointPath(process.argv[1]) : null;
66
+ let current = entrypoint ? import_node_path.default.dirname(entrypoint) : process.cwd();
67
+ for (let index = 0; index < 12; index += 1) {
68
+ const packageVersion = readInstalledPackageVersion(import_node_path.default.join(current, "package.json"));
69
+ if (packageVersion) {
70
+ return packageVersion;
71
+ }
72
+ const sourceVersion = readVersionFile(import_node_path.default.join(current, "VERSION.txt"));
73
+ if (sourceVersion && import_node_path.default.basename(current) === "packages") {
74
+ return sourceVersion;
75
+ }
76
+ const parent = import_node_path.default.dirname(current);
77
+ if (parent === current) {
78
+ break;
79
+ }
80
+ current = parent;
81
+ }
82
+ return "unknown";
83
+ }
84
+ const WORKER_RUNTIME_VERSION = readWorkerVersion();
85
+ // Annotate the CommonJS export names for ESM import in node:
86
+ 0 && (module.exports = {
87
+ WORKER_RUNTIME_VERSION
88
+ });
package/dist/mjs/main.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env bun
2
+ import { WORKER_RUNTIME_VERSION } from "./runtime-version.mjs";
2
3
  import { getWorkerPlanPath, getWorkerPlansPath, validatePlanId } from "./plan-paths.mjs";
3
4
  import { createPlanMarkdown, updateTaskCheckbox } from "./plan-parser.mjs";
4
5
  import { lintPlans, formatPlanLintErrors } from "./plan-lint.mjs";
@@ -162,7 +163,6 @@ class WorkerServerUnavailableError extends Error {
162
163
  name = "WorkerServerUnavailableError";
163
164
  }
164
165
  const DEFAULT_BASE_URL = "https://r5d.dev";
165
- const WORKER_PACKAGE_NAME = "@ricsam/r5d-worker";
166
166
  const LABEL_RE = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,62}$/;
167
167
  const DEFAULT_READ_LIMIT = 2e3;
168
168
  const DEFAULT_READ_MAX_BYTES = 5e4;
@@ -471,55 +471,8 @@ Options:
471
471
  -h, --help Show this help
472
472
  `);
473
473
  }
474
- function readVersionFile(filePath) {
475
- try {
476
- const version = fs.readFileSync(filePath, "utf8").trim();
477
- return version || null;
478
- } catch {
479
- return null;
480
- }
481
- }
482
- function readInstalledPackageVersion(packageJsonPath) {
483
- try {
484
- const parsed = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
485
- if (parsed.name === WORKER_PACKAGE_NAME && typeof parsed.version === "string" && parsed.version.trim()) {
486
- return parsed.version.trim();
487
- }
488
- } catch {
489
- return null;
490
- }
491
- return null;
492
- }
493
- function resolveEntrypointPath(entrypoint) {
494
- const resolved = path.resolve(entrypoint);
495
- try {
496
- return fs.realpathSync.native(resolved);
497
- } catch {
498
- return resolved;
499
- }
500
- }
501
- function getWorkerVersion() {
502
- const entrypoint = process.argv[1] ? resolveEntrypointPath(process.argv[1]) : null;
503
- let current = entrypoint ? path.dirname(entrypoint) : process.cwd();
504
- for (let index = 0; index < 12; index += 1) {
505
- const packageVersion = readInstalledPackageVersion(path.join(current, "package.json"));
506
- if (packageVersion) {
507
- return packageVersion;
508
- }
509
- const sourceVersion = readVersionFile(path.join(current, "VERSION.txt"));
510
- if (sourceVersion && path.basename(current) === "packages") {
511
- return sourceVersion;
512
- }
513
- const parent = path.dirname(current);
514
- if (parent === current) {
515
- break;
516
- }
517
- current = parent;
518
- }
519
- return "unknown";
520
- }
521
474
  function printVersion() {
522
- process.stdout.write(`r5d-worker ${getWorkerVersion()}
475
+ process.stdout.write(`r5d-worker ${WORKER_RUNTIME_VERSION}
523
476
  `);
524
477
  }
525
478
  function requireValue(value, message) {
@@ -3689,7 +3642,7 @@ function websocketUrl(baseUrl, label) {
3689
3642
  const url = new URL("/worker/ws", baseUrl);
3690
3643
  url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
3691
3644
  url.searchParams.set("label", label);
3692
- url.searchParams.set("version", getWorkerVersion());
3645
+ url.searchParams.set("version", WORKER_RUNTIME_VERSION);
3693
3646
  return url.toString();
3694
3647
  }
3695
3648
  async function startWorker(options, projectRuntime = {
@@ -5831,7 +5784,7 @@ async function startWorker(options, projectRuntime = {
5831
5784
  platform: process.platform,
5832
5785
  arch: process.arch,
5833
5786
  pid: process.pid,
5834
- version: getWorkerVersion(),
5787
+ version: WORKER_RUNTIME_VERSION,
5835
5788
  r5dctlVersion: readInstalledCliVersion("r5dctl"),
5836
5789
  capabilities: {
5837
5790
  updateClis: true,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.115",
3
+ "version": "0.0.117",
4
4
  "type": "module"
5
5
  }
@@ -25,6 +25,7 @@ class WorkerRecoveryStore {
25
25
  CREATE TABLE IF NOT EXISTS metadata (key TEXT PRIMARY KEY, value TEXT NOT NULL);
26
26
  CREATE TABLE IF NOT EXISTS operations (request_id TEXT PRIMARY KEY, run_id TEXT, session_id TEXT, request_type TEXT NOT NULL, digest TEXT NOT NULL, state TEXT NOT NULL, response TEXT);
27
27
  CREATE UNIQUE INDEX IF NOT EXISTS operation_run_start ON operations(run_id) WHERE request_type='exec_start';
28
+ CREATE INDEX IF NOT EXISTS operation_run_state ON operations(run_id,state);
28
29
  CREATE TABLE IF NOT EXISTS aliases (request_id TEXT PRIMARY KEY, operation_id TEXT NOT NULL);
29
30
  CREATE TABLE IF NOT EXISTS streams (run_id TEXT NOT NULL, stream TEXT NOT NULL, bytes INTEGER NOT NULL DEFAULT 0, acknowledged INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(run_id, stream));
30
31
  CREATE TABLE IF NOT EXISTS output (run_id TEXT NOT NULL, stream TEXT NOT NULL, offset INTEGER NOT NULL, bytes INTEGER NOT NULL, data TEXT NOT NULL, PRIMARY KEY(run_id, stream, offset));
@@ -142,7 +143,8 @@ class WorkerRecoveryStore {
142
143
  }
143
144
  *output(offsets = []) {
144
145
  const cursors = new Map(offsets.map((offset) => [offset.runId, offset]));
145
- for (const stream of this.db.query("SELECT * FROM streams WHERE NOT EXISTS (SELECT 1 FROM operations WHERE operations.run_id=streams.run_id AND operations.state='unknown') ORDER BY rowid").all()) {
146
+ const pendingStreamFilter = offsets.length === 0 ? "bytes>acknowledged AND " : "";
147
+ for (const stream of this.db.query(`SELECT * FROM streams WHERE ${pendingStreamFilter}NOT EXISTS (SELECT 1 FROM operations WHERE operations.run_id=streams.run_id AND operations.state='unknown') ORDER BY rowid`).all()) {
146
148
  const supplied = cursors.get(stream.run_id);
147
149
  const cursor = supplied ? stream.stream === "stdout" ? supplied.stdoutBytes : supplied.stderrBytes : stream.acknowledged;
148
150
  if (cursor >= stream.bytes) continue;
@@ -0,0 +1,54 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ const WORKER_PACKAGE_NAME = "@ricsam/r5d-worker";
4
+ function readVersionFile(filePath) {
5
+ try {
6
+ const version = fs.readFileSync(filePath, "utf8").trim();
7
+ return version || null;
8
+ } catch {
9
+ return null;
10
+ }
11
+ }
12
+ function readInstalledPackageVersion(packageJsonPath) {
13
+ try {
14
+ const parsed = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
15
+ if (parsed.name === WORKER_PACKAGE_NAME && typeof parsed.version === "string" && parsed.version.trim()) {
16
+ return parsed.version.trim();
17
+ }
18
+ } catch {
19
+ return null;
20
+ }
21
+ return null;
22
+ }
23
+ function resolveEntrypointPath(entrypoint) {
24
+ const resolved = path.resolve(entrypoint);
25
+ try {
26
+ return fs.realpathSync.native(resolved);
27
+ } catch {
28
+ return resolved;
29
+ }
30
+ }
31
+ function readWorkerVersion() {
32
+ const entrypoint = process.argv[1] ? resolveEntrypointPath(process.argv[1]) : null;
33
+ let current = entrypoint ? path.dirname(entrypoint) : process.cwd();
34
+ for (let index = 0; index < 12; index += 1) {
35
+ const packageVersion = readInstalledPackageVersion(path.join(current, "package.json"));
36
+ if (packageVersion) {
37
+ return packageVersion;
38
+ }
39
+ const sourceVersion = readVersionFile(path.join(current, "VERSION.txt"));
40
+ if (sourceVersion && path.basename(current) === "packages") {
41
+ return sourceVersion;
42
+ }
43
+ const parent = path.dirname(current);
44
+ if (parent === current) {
45
+ break;
46
+ }
47
+ current = parent;
48
+ }
49
+ return "unknown";
50
+ }
51
+ const WORKER_RUNTIME_VERSION = readWorkerVersion();
52
+ export {
53
+ WORKER_RUNTIME_VERSION
54
+ };
@@ -0,0 +1 @@
1
+ export declare const WORKER_RUNTIME_VERSION: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.115",
3
+ "version": "0.0.117",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/main.cjs",
6
6
  "module": "./dist/mjs/main.mjs",