@ricsam/r5d-worker 0.0.145 → 0.0.146

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
@@ -8,3 +8,5 @@ r5d-worker manager /absolute/private/manager-config.json
8
8
  ```
9
9
 
10
10
  The executor retains processes and durable receipts independently of application releases. The manager authenticates the approved workspace keeper and retains old adapters until their delivery receipts are settled. Configuration and host roots belong exclusively to one installation. Never share an executor's state directory, credentials or workspace root between environments.
11
+
12
+ The published worker has an exact-version dependency on `@ricsam/r5dctl`. A personal worker resolves that bundled CLI before the host `PATH`, verifies that its package version equals the running worker version, and exposes it to agent shells through a private worker-owned wrapper. Each worker remains bound to one server origin and installation, so stage, production, and review workers cannot accidentally select another environment's global CLI or credentials.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.145",
3
+ "version": "0.0.146",
4
4
  "type": "commonjs"
5
5
  }
package/dist/mjs/main.mjs CHANGED
@@ -7,7 +7,7 @@ import { startManagerRpc } from "./runtime/releases/rpc-main.mjs";
7
7
  import { ManagerRpcConfig } from "./runtime/releases/rpc-protocol.mjs";
8
8
  const args = process.argv.slice(2);
9
9
  if (args.includes("--version")) {
10
- console.log(`r5d-worker ${true ? "0.0.145" : "development"}`);
10
+ console.log(`r5d-worker ${true ? "0.0.146" : "development"}`);
11
11
  } else if (!args.length || args.includes("--help")) {
12
12
  console.log(
13
13
  "Usage: r5d-worker start --label <label> [--root <dir>] [--base-url <url>] [--token <worker-token>]\n r5d-worker executor /absolute/private/config.json\n r5d-worker manager /absolute/private/config.json\n\nRun independently supervised durable runtime services. Provision configuration with r5dinfra."
@@ -15,7 +15,7 @@ if (args.includes("--version")) {
15
15
  } else if (args[0] === "start") {
16
16
  const runtime = await startPersonalWorker(
17
17
  parsePersonalWorkerOptions(args.slice(1)),
18
- true ? "0.0.145" : "development"
18
+ true ? "0.0.146" : "development"
19
19
  );
20
20
  console.log(`Worker connected: ${runtime.resourceId}`);
21
21
  let closing = false;
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.145",
3
+ "version": "0.0.146",
4
4
  "type": "module"
5
5
  }
@@ -0,0 +1,59 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { promises as fs } from "node:fs";
4
+ const PACKAGE_NAME = "@ricsam/r5dctl";
5
+ function packageMain(moduleUrl) {
6
+ const module = fileURLToPath(moduleUrl);
7
+ return path.join(path.dirname(module), path.extname(module) === ".cjs" ? "main.cjs" : "main.mjs");
8
+ }
9
+ async function inspectPackage(candidate) {
10
+ const entrypoint = await fs.realpath(candidate);
11
+ const entrypointStat = await fs.lstat(entrypoint);
12
+ if (!entrypointStat.isFile() || entrypointStat.nlink !== 1 || entrypointStat.mode & 18 || ![0, process.getuid?.()].includes(entrypointStat.uid))
13
+ throw new Error("Untrusted installed r5dctl entrypoint");
14
+ let directory = path.dirname(entrypoint);
15
+ for (let depth = 0; depth < 6; depth += 1) {
16
+ const manifest = path.join(directory, "package.json");
17
+ try {
18
+ const stat = await fs.lstat(manifest);
19
+ if (!stat.isFile() || stat.nlink !== 1 || stat.mode & 18 || ![0, process.getuid?.()].includes(stat.uid))
20
+ throw new Error("Untrusted installed r5dctl package manifest");
21
+ const parsed = JSON.parse(await fs.readFile(manifest, "utf8"));
22
+ if (parsed?.name === PACKAGE_NAME) {
23
+ if (typeof parsed.version !== "string" || !/^\d+\.\d+\.\d+(?:-[a-zA-Z0-9.-]+)?$/.test(parsed.version))
24
+ throw new Error("Invalid installed r5dctl package version");
25
+ return { entrypoint, version: parsed.version };
26
+ }
27
+ } catch (error) {
28
+ if (error.code !== "ENOENT") throw error;
29
+ }
30
+ const parent = path.dirname(directory);
31
+ if (parent === directory) break;
32
+ directory = parent;
33
+ }
34
+ throw new Error("Resolved r5dctl entrypoint is not inside an @ricsam/r5dctl package");
35
+ }
36
+ async function resolvePersonalCliEntrypoint(explicit, workerVersion, dependencies = {}) {
37
+ let source, candidate;
38
+ if (explicit) {
39
+ source = "explicit";
40
+ candidate = explicit;
41
+ } else {
42
+ try {
43
+ const resolvePackage = dependencies.resolvePackage ?? ((specifier) => import.meta.resolve(specifier));
44
+ candidate = packageMain(resolvePackage(`${PACKAGE_NAME}/cli`));
45
+ source = "bundled";
46
+ } catch {
47
+ source = "path";
48
+ candidate = (dependencies.which ?? Bun.which)("r5dctl");
49
+ }
50
+ }
51
+ if (!candidate) throw new Error("Install @ricsam/r5dctl before starting this worker");
52
+ const inspected = await inspectPackage(candidate);
53
+ if (workerVersion !== "development" && inspected.version !== workerVersion)
54
+ throw new Error(`r5d-worker ${workerVersion} requires its bundled r5dctl ${workerVersion}; resolved ${inspected.version}`);
55
+ return { ...inspected, source };
56
+ }
57
+ export {
58
+ resolvePersonalCliEntrypoint
59
+ };
@@ -1,5 +1,4 @@
1
1
  import path from "node:path";
2
- import { fileURLToPath } from "node:url";
3
2
  import os from "node:os";
4
3
  import { createHash, randomUUID } from "node:crypto";
5
4
  import { promises as fs, openSync, closeSync } from "node:fs";
@@ -7,9 +6,10 @@ import { Database } from "bun:sqlite";
7
6
  import { canonicalJson } from "@ricsam/r5d-api/runtime-protocol";
8
7
  import { privateDirectory, readPrivateJson } from "../runtime/storage.mjs";
9
8
  import { openPersonalWorkerRuntime, PersonalWorkerGrant } from "./runtime.mjs";
10
- import { installCliUpdate, readInstalledCliVersion } from "../cli-update.mjs";
9
+ import { installCliUpdate } from "../cli-update.mjs";
11
10
  import { WorkspaceError } from "../runtime/workspace/contracts.mjs";
12
11
  import { PersonalActionScheduler } from "./action-scheduler.mjs";
12
+ import { resolvePersonalCliEntrypoint } from "./cli-entrypoint.mjs";
13
13
  class PersonalResponseError extends Error {
14
14
  constructor(code, status, rejectedBeforeAdmission) {
15
15
  super(`Personal worker request failed (${status})`);
@@ -95,9 +95,11 @@ async function startPersonalWorker(options, version) {
95
95
  }
96
96
  return data;
97
97
  }
98
+ const cli = await resolvePersonalCliEntrypoint(options.cliEntrypoint, version);
98
99
  const metadata = () => ({
99
100
  version,
100
- r5dctlVersion: readInstalledCliVersion("r5dctl"),
101
+ r5dctlVersion: cli.version,
102
+ r5dctlSource: cli.source,
101
103
  platform: process.platform,
102
104
  arch: process.arch,
103
105
  hostname: os.hostname(),
@@ -106,18 +108,9 @@ async function startPersonalWorker(options, version) {
106
108
  const grant = PersonalWorkerGrant.parse(
107
109
  await request("/api/personal/resources/register", { kind: "worker", ...identity, label: options.label, metadata: metadata() })
108
110
  );
109
- let cliEntrypoint = options.cliEntrypoint ?? Bun.which("r5dctl");
110
- if (!cliEntrypoint) {
111
- try {
112
- const module = fileURLToPath(import.meta.resolve("@ricsam/r5dctl/cli"));
113
- cliEntrypoint = path.join(path.dirname(module), path.extname(module) === ".cjs" ? "main.cjs" : "main.mjs");
114
- } catch {
115
- }
116
- }
117
- if (!cliEntrypoint) throw new Error("Install @ricsam/r5dctl before starting this worker");
118
111
  const runtime = await openPersonalWorkerRuntime({
119
112
  root,
120
- cliEntrypoint,
113
+ cliEntrypoint: cli.entrypoint,
121
114
  grant,
122
115
  storage: (sessionId) => async (storageRequest) => {
123
116
  try {
@@ -0,0 +1,13 @@
1
+ type ResolutionDependencies = {
2
+ resolvePackage?: (specifier: string) => string;
3
+ which?: (command: string) => string | null;
4
+ };
5
+ export type PersonalCliEntrypoint = {
6
+ entrypoint: string;
7
+ version: string;
8
+ source: "explicit" | "bundled" | "path";
9
+ };
10
+ /** Resolve the CLI that agent shells receive. Published workers always prefer
11
+ * their exact npm dependency; PATH is only a development/source fallback. */
12
+ export declare function resolvePersonalCliEntrypoint(explicit: string | undefined, workerVersion: string, dependencies?: ResolutionDependencies): Promise<PersonalCliEntrypoint>;
13
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.145",
3
+ "version": "0.0.146",
4
4
  "type": "module",
5
5
  "main": "./dist/mjs/main.mjs",
6
6
  "module": "./dist/mjs/main.mjs",
@@ -21,8 +21,8 @@
21
21
  "r5d-worker": "dist/mjs/main.mjs"
22
22
  },
23
23
  "dependencies": {
24
- "@ricsam/r5d-api": "^0.0.145",
25
- "@ricsam/r5dctl": "0.0.145",
24
+ "@ricsam/r5d-api": "^0.0.146",
25
+ "@ricsam/r5dctl": "0.0.146",
26
26
  "node-pty": "1.1.0",
27
27
  "zod": "^4.1.13",
28
28
  "picomatch": "^4.0.3"