@ricsam/r5d-worker 0.0.153 → 0.0.155

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.153",
3
+ "version": "0.0.155",
4
4
  "type": "commonjs"
5
5
  }
@@ -20605,7 +20605,7 @@ function resolveEntrypointPath(entrypoint) {
20605
20605
  }
20606
20606
  }
20607
20607
  function getR5dctlVersion() {
20608
- if (true) return "0.0.153";
20608
+ if (true) return "0.0.155";
20609
20609
  const entrypoint = process.argv[1] ? resolveEntrypointPath(process.argv[1]) : null;
20610
20610
  let current = entrypoint ? import_node_path2.default.dirname(entrypoint) : process.cwd();
20611
20611
  for (let index = 0; index < 12; index += 1) {
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.153" : "development"}`);
10
+ console.log(`r5d-worker ${true ? "0.0.155" : "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.153" : "development"
18
+ true ? "0.0.155" : "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.153",
3
+ "version": "0.0.155",
4
4
  "type": "module"
5
5
  }
@@ -12,7 +12,7 @@ import { privateDirectory, readPrivateJson } from "../runtime/storage.mjs";
12
12
  import { WorkspaceAuthority } from "../runtime/workspace/authority.mjs";
13
13
  import { ApprovedWorkbench, WorkspaceError } from "../runtime/workspace/contracts.mjs";
14
14
  import { WorkspaceStorageClient } from "../runtime/workspace/storage-client.mjs";
15
- import { BranchName, StorageId } from "../runtime/workspace/storage-wire.mjs";
15
+ import { BranchName, GitOid, StorageId } from "../runtime/workspace/storage-wire.mjs";
16
16
  const PersonalWorkerGrant = z.object({
17
17
  installationId: RuntimeId,
18
18
  userId: RuntimeId,
@@ -30,7 +30,8 @@ const Workbench = z.object({
30
30
  sessionId: RuntimeId,
31
31
  rootProfile: z.enum(["account", "project"]).default("project"),
32
32
  namespace: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/).optional(),
33
- projectName: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/).optional()
33
+ projectName: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/).optional(),
34
+ baseCommitHash: GitOid.optional()
34
35
  }).strict();
35
36
  const PersonalWorkspaceRequest = z.object({
36
37
  protocol: z.literal(1),
@@ -111,6 +112,7 @@ async function openPersonalWorkerRuntime(options) {
111
112
  privateDirectory(cliDirectory);
112
113
  const quote = (value) => "'" + value.replace(/'/g, "'\\''") + "'";
113
114
  const wrapper = `#!/bin/sh
115
+ export BUN_RUNTIME_TRANSPILER_CACHE_PATH=0
114
116
  exec ${quote(executable)} ${quote(cli)} "$@"
115
117
  `;
116
118
  const file = path.join(cliDirectory, "r5dctl"), temporary = `${file}.${randomBytes(8).toString("hex")}.next`;
@@ -212,7 +214,7 @@ exec ${quote(executable)} ${quote(cli)} "$@"
212
214
  });
213
215
  const previous = manifests.get(input.id);
214
216
  const stable = (value) => {
215
- const { sessionId: _sessionId, sharedSessionId: _sharedSessionId, ...binding } = value;
217
+ const { sessionId: _sessionId, sharedSessionId: _sharedSessionId, baseCommitHash: _baseCommitHash, ...binding } = value;
216
218
  return binding;
217
219
  };
218
220
  if (previous && canonicalJson(stable(previous)) !== canonicalJson(stable(row))) throw new Error("Personal workbench identity changed");
@@ -30,6 +30,7 @@ import {
30
30
  sourceBytes,
31
31
  verifyIgnore
32
32
  } from "./files.mjs";
33
+ const GITHUB_CREDENTIAL_HELPER = '!f() { if [ "$1" = get ] && [ -n "$R5D_GIT_CREDENTIAL" ]; then git credential-store --file="$R5D_GIT_CREDENTIAL" get; else return 0; fi; }; f';
33
34
  class WorkspaceAuthority {
34
35
  constructor(options) {
35
36
  this.options = options;
@@ -54,7 +55,7 @@ class WorkspaceAuthority {
54
55
  return path.join(workspaceRoot, "workbenches", config.id);
55
56
  }
56
57
  stableBinding(config) {
57
- const { sessionId: _sessionId, sharedSessionId: _sharedSessionId, ...binding } = config;
58
+ const { sessionId: _sessionId, sharedSessionId: _sharedSessionId, baseCommitHash: _baseCommitHash, ...binding } = config;
58
59
  return binding;
59
60
  }
60
61
  repositoryPath(config, directory) {
@@ -99,6 +100,9 @@ class WorkspaceAuthority {
99
100
  throw new WorkspaceError("invalid_config", "Project checkout is missing its GitHub repository identity");
100
101
  return `https://github.com/${b.config.namespace}/${b.config.projectName}.git`;
101
102
  }
103
+ canonicalRef(b) {
104
+ return `refs/r5d/canonical/${b.config.branch}`;
105
+ }
102
106
  resolveHostPath(value) {
103
107
  const expanded = value === "~" ? os.homedir() : value.startsWith("~/") ? path.join(os.homedir(), value.slice(2)) : value;
104
108
  if (!path.isAbsolute(expanded) || expanded.includes("\0")) throw new WorkspaceError("unsafe_path", "Host path must be absolute or home-relative");
@@ -443,8 +447,9 @@ class WorkspaceAuthority {
443
447
  throw new WorkspaceError("dirty_workbench", "Source differs from its canonical base; preserve and publish or reconcile edits first");
444
448
  if (this.linkedWorkbench(b)) {
445
449
  const localHead2 = (await git(b.config.cwd, ["rev-parse", "HEAD"])).toString().trim();
446
- if (localHead2 !== expectedBase || (await git(b.config.cwd, ["write-tree"])).toString().trim() !== expectedTree)
447
- throw new WorkspaceError("dirty_workbench", "Local branch or index changed; preserve and reconcile it before refreshing");
450
+ const localTree = (await git(b.repo, ["rev-parse", `${localHead2}^{tree}`])).toString().trim();
451
+ if ((await git(b.config.cwd, ["write-tree"])).toString().trim() !== localTree)
452
+ throw new WorkspaceError("dirty_workbench", "Staged Git changes are retained; preserve or reconcile them before refreshing");
448
453
  return;
449
454
  }
450
455
  const metadata = path.join(b.config.cwd, ".git");
@@ -565,8 +570,9 @@ class WorkspaceAuthority {
565
570
  await this.installGitPolicy(b, head);
566
571
  } else if (this.linkedWorkbench(b)) {
567
572
  await this.cleanRefreshBase(b, expectedBase);
568
- await git(b.repo, ["update-ref", `refs/heads/${b.config.branch}`, head, expectedBase]);
573
+ const visibleHead = (await git(b.config.cwd, ["rev-parse", "HEAD"])).toString().trim();
569
574
  await git(b.config.cwd, ["reset", "--hard", head]);
575
+ await git(b.config.cwd, ["reset", "--mixed", visibleHead]);
570
576
  await fs.rm(staged, { recursive: true });
571
577
  } else {
572
578
  await this.cleanRefreshBase(b, expectedBase);
@@ -775,7 +781,7 @@ class WorkspaceAuthority {
775
781
  return result;
776
782
  });
777
783
  }
778
- async installGitPolicy(b, head) {
784
+ async installGitPolicy(b, head, initialVisibleHead = b.config.baseCommitHash ?? head) {
779
785
  delete b.state.publishedMetadata;
780
786
  if (this.linkedWorkbench(b)) {
781
787
  const destination2 = path.join(b.config.cwd, ".git");
@@ -783,10 +789,17 @@ class WorkspaceAuthority {
783
789
  if (error.code === "ENOENT") return false;
784
790
  throw error;
785
791
  });
786
- await git(b.repo, ["update-ref", `refs/heads/${b.config.branch}`, head]);
792
+ await git(b.repo, ["update-ref", this.canonicalRef(b), head]);
787
793
  if (!exists) {
788
794
  if ((await fs.readdir(b.config.cwd)).length) throw new WorkspaceError("nonempty_workbench", "Linked worktree destination must be empty");
795
+ const visibleHead = initialVisibleHead;
796
+ await git(b.repo, ["cat-file", "-e", `${visibleHead}^{commit}`]);
797
+ await git(b.repo, ["update-ref", `refs/heads/${b.config.branch}`, visibleHead]);
789
798
  await git(b.repo, ["worktree", "add", "--force", b.config.cwd, b.config.branch]);
799
+ if (visibleHead !== head) {
800
+ await git(b.config.cwd, ["reset", "--hard", head]);
801
+ await git(b.config.cwd, ["reset", "--mixed", visibleHead]);
802
+ }
790
803
  }
791
804
  const origin = this.githubOrigin(b);
792
805
  await git(b.repo, ["config", "--local", "--replace-all", "remote.origin.url", origin]);
@@ -796,8 +809,11 @@ class WorkspaceAuthority {
796
809
  await git(b.repo, ["config", "--local", "--remove-section", "remote.canonical"]);
797
810
  await git(b.repo, ["config", "--local", "--replace-all", `branch.${b.config.branch}.remote`, "origin"]);
798
811
  await git(b.repo, ["config", "--local", "--replace-all", `branch.${b.config.branch}.merge`, `refs/heads/${b.config.branch}`]);
812
+ if (b.config.baseCommitHash)
813
+ await git(b.repo, ["update-ref", `refs/remotes/origin/${b.config.branch}`, b.config.baseCommitHash]);
799
814
  await git(b.repo, ["config", "--local", "--replace-all", "push.default", "upstream"]);
800
815
  await git(b.repo, ["config", "--local", "--replace-all", "credential.helper", ""]);
816
+ await git(b.repo, ["config", "--local", "--add", "credential.helper", GITHUB_CREDENTIAL_HELPER]);
801
817
  await git(b.repo, ["config", "--local", "--replace-all", "credential.useHttpPath", "false"]);
802
818
  await git(b.repo, ["config", "protocol.allow", "never"]);
803
819
  await git(b.repo, ["config", "protocol.https.allow", "always"]);
@@ -1013,6 +1029,7 @@ class WorkspaceAuthority {
1013
1029
  }));
1014
1030
  }
1015
1031
  async importCommit(b, head, id, identity) {
1032
+ const visibleHead = b.config.baseCommitHash ?? head;
1016
1033
  const entries = await selectedTree(b.repo, head);
1017
1034
  const staged = path.join(b.directory, `import-${randomUUID()}`);
1018
1035
  await fs.mkdir(staged, { mode: 448 });
@@ -1042,9 +1059,9 @@ ${DEFAULT_WORKSPACE_IGNORE}`, { mode: 384 });
1042
1059
  existing = { head: null };
1043
1060
  }
1044
1061
  if (existing.head) throw new WorkspaceError("conflict", "Branch already exists in canonical storage");
1045
- await git(b.repo, ["update-ref", `refs/heads/${b.config.branch}`, head]);
1062
+ await git(b.repo, ["update-ref", this.canonicalRef(b), head]);
1046
1063
  const file = path.join(b.directory, `publish-${randomUUID()}.bundle`);
1047
- await git(b.repo, ["bundle", "create", file, `refs/heads/${b.config.branch}`]);
1064
+ await git(b.repo, ["bundle", "create", file, this.canonicalRef(b)]);
1048
1065
  const bytes = await readRegular(file, STORAGE_LIMITS.blobBytes), blobId = operationId;
1049
1066
  b.state.blocked = { code: "publication_unknown", operationId, commit: head, message: "Inspect the original import publication receipt" };
1050
1067
  await this.save(b);
@@ -1056,7 +1073,7 @@ ${DEFAULT_WORKSPACE_IGNORE}`, { mode: 384 });
1056
1073
  if (result.head !== head) throw new WorkspaceError("invalid_receipt", "Import receipt does not match selected commit");
1057
1074
  if (this.linkedWorkbench(b)) await fs.rm(staged, { recursive: true });
1058
1075
  else for (const name of await fs.readdir(staged)) await fs.rename(path.join(staged, name), path.join(b.config.cwd, name));
1059
- await this.installGitPolicy(b, head);
1076
+ await this.installGitPolicy(b, head, visibleHead);
1060
1077
  b.state.initialized = true;
1061
1078
  b.state.head = head;
1062
1079
  b.state.blocked = null;
@@ -1069,7 +1086,6 @@ ${DEFAULT_WORKSPACE_IGNORE}`, { mode: 384 });
1069
1086
  return this.serial(b, () => this.productAction(b, input.id, { method: "commit", expectedHead: input.expectedHead, message: input.message }, async () => {
1070
1087
  const result = await this.publishIdle(b, identity, input.message);
1071
1088
  if (this.linkedWorkbench(b)) {
1072
- await git(b.config.cwd, ["reset", "--mixed", result.head]);
1073
1089
  return { ...result, treeHash: (await git(b.repo, ["rev-parse", `${result.head}^{tree}`])).toString().trim() };
1074
1090
  }
1075
1091
  const metadata = path.join(b.config.cwd, ".git");
@@ -1180,9 +1196,9 @@ ${DEFAULT_WORKSPACE_IGNORE}`, { mode: 384 });
1180
1196
  return { head: b.state.head, unchanged: true };
1181
1197
  const commit = (await git(b.repo, ["commit-tree", tree, ...b.state.head ? ["-p", b.state.head] : []], `${message}
1182
1198
  `)).toString().trim();
1183
- await git(b.repo, ["update-ref", `refs/heads/${b.config.branch}`, commit]);
1199
+ await git(b.repo, ["update-ref", this.canonicalRef(b), commit]);
1184
1200
  const bundleFile = path.join(b.directory, `${randomUUID()}.bundle`);
1185
- await git(b.repo, ["bundle", "create", bundleFile, `refs/heads/${b.config.branch}`]);
1201
+ await git(b.repo, ["bundle", "create", bundleFile, this.canonicalRef(b)]);
1186
1202
  const bytes = await readRegular(bundleFile, STORAGE_LIMITS.blobBytes);
1187
1203
  const operationId = `ws-${randomUUID()}`, blobId = `ws-${randomUUID()}`;
1188
1204
  try {
@@ -1236,10 +1252,6 @@ ${DEFAULT_WORKSPACE_IGNORE}`, { mode: 384 });
1236
1252
  });
1237
1253
  if (result.head !== commit)
1238
1254
  throw new WorkspaceError("invalid_response", "Publication receipt head mismatch; inspect original operation");
1239
- if (this.linkedWorkbench(b) && await fs.lstat(path.join(b.config.cwd, ".git")).then(() => true, (error) => {
1240
- if (error.code === "ENOENT") return false;
1241
- throw error;
1242
- })) await git(b.config.cwd, ["reset", "--mixed", commit]);
1243
1255
  b.state.head = commit;
1244
1256
  b.state.blocked = null;
1245
1257
  await this.save(b);
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { z } from "zod";
3
3
  import { RuntimeId } from "@ricsam/r5d-api/runtime-protocol";
4
- import { BranchName, StorageId } from "./storage-wire.mjs";
4
+ import { BranchName, GitOid, StorageId } from "./storage-wire.mjs";
5
5
  const ApprovedWorkbench = z.object({
6
6
  id: StorageId,
7
7
  userId: RuntimeId,
@@ -13,6 +13,8 @@ const ApprovedWorkbench = z.object({
13
13
  rootProfile: z.enum(["account", "project"]).default("project"),
14
14
  namespace: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/).optional(),
15
15
  projectName: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/).optional(),
16
+ /** GitHub/local branch tip; canonical workspace snapshots never replace it. */
17
+ baseCommitHash: GitOid.optional(),
16
18
  // Supervisor-owned exact argv approvals, NOT an agent-supplied read-only flag.
17
19
  // Approved servers must keep generated/cache files ignored and never write source.
18
20
  nonmutatingArgv: z.array(z.array(z.string()).min(1)).max(20).default([])
@@ -50,6 +52,7 @@ dist/
50
52
  build/
51
53
  coverage/
52
54
  .cache/
55
+ .bun/
53
56
  *.pem
54
57
  *.key
55
58
  `;
@@ -52,6 +52,7 @@ export declare const PersonalWorkspaceRequest: z.ZodObject<{
52
52
  }>>;
53
53
  namespace: z.ZodOptional<z.ZodString>;
54
54
  projectName: z.ZodOptional<z.ZodString>;
55
+ baseCommitHash: z.ZodOptional<z.ZodString>;
55
56
  }, z.core.$strict>;
56
57
  sourceWorkbench: z.ZodOptional<z.ZodObject<{
57
58
  id: z.ZodString;
@@ -64,6 +65,7 @@ export declare const PersonalWorkspaceRequest: z.ZodObject<{
64
65
  }>>;
65
66
  namespace: z.ZodOptional<z.ZodString>;
66
67
  projectName: z.ZodOptional<z.ZodString>;
68
+ baseCommitHash: z.ZodOptional<z.ZodString>;
67
69
  }, z.core.$strict>>;
68
70
  sharedWorkbenches: z.ZodOptional<z.ZodArray<z.ZodObject<{
69
71
  id: z.ZodString;
@@ -76,6 +78,7 @@ export declare const PersonalWorkspaceRequest: z.ZodObject<{
76
78
  }>>;
77
79
  namespace: z.ZodOptional<z.ZodString>;
78
80
  projectName: z.ZodOptional<z.ZodString>;
81
+ baseCommitHash: z.ZodOptional<z.ZodString>;
79
82
  }, z.core.$strict>>>;
80
83
  kind: z.ZodEnum<{
81
84
  inspect: "inspect";
@@ -34,6 +34,7 @@ export declare class WorkspaceAuthority {
34
34
  private assertNonOverlappingCheckout;
35
35
  private linkedWorkbench;
36
36
  private githubOrigin;
37
+ private canonicalRef;
37
38
  private resolveHostPath;
38
39
  static open(options: WorkspaceAuthorityOptions): Promise<WorkspaceAuthority>;
39
40
  private readonly registrations;
@@ -223,6 +224,7 @@ export declare class WorkspaceAuthority {
223
224
  sharedSessionId?: string | undefined;
224
225
  namespace?: string | undefined;
225
226
  projectName?: string | undefined;
227
+ baseCommitHash?: string | undefined;
226
228
  };
227
229
  }>;
228
230
  private cleanRefreshBase;
@@ -15,6 +15,7 @@ export declare const ApprovedWorkbench: z.ZodObject<{
15
15
  }>>;
16
16
  namespace: z.ZodOptional<z.ZodString>;
17
17
  projectName: z.ZodOptional<z.ZodString>;
18
+ baseCommitHash: z.ZodOptional<z.ZodString>;
18
19
  nonmutatingArgv: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodString>>>;
19
20
  }, z.core.$strict>;
20
21
  export type ApprovedWorkbench = z.input<typeof ApprovedWorkbench>;
@@ -36,6 +37,7 @@ export declare const WorkspaceConfig: z.ZodObject<{
36
37
  }>>;
37
38
  namespace: z.ZodOptional<z.ZodString>;
38
39
  projectName: z.ZodOptional<z.ZodString>;
40
+ baseCommitHash: z.ZodOptional<z.ZodString>;
39
41
  nonmutatingArgv: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodString>>>;
40
42
  }, z.core.$strict>>;
41
43
  dynamicWorkbenches: z.ZodOptional<z.ZodBoolean>;
@@ -85,4 +87,4 @@ export type WorkspaceState = {
85
87
  completedAt?: string;
86
88
  }>;
87
89
  };
88
- export declare const DEFAULT_WORKSPACE_IGNORE = "# Required destination workbench exclusions; keep these rules effective.\n.env\n.env.*\n!.env.example\nnode_modules/\n.r5d/\n.r5d-next/\n.ssh/\n.kube/\nsecrets/\ncredentials/\n.next/\ndist/\nbuild/\ncoverage/\n.cache/\n*.pem\n*.key\n";
90
+ export declare const DEFAULT_WORKSPACE_IGNORE = "# Required destination workbench exclusions; keep these rules effective.\n.env\n.env.*\n!.env.example\nnode_modules/\n.r5d/\n.r5d-next/\n.ssh/\n.kube/\nsecrets/\ncredentials/\n.next/\ndist/\nbuild/\ncoverage/\n.cache/\n.bun/\n*.pem\n*.key\n";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.153",
3
+ "version": "0.0.155",
4
4
  "type": "module",
5
5
  "main": "./dist/mjs/main.mjs",
6
6
  "module": "./dist/mjs/main.mjs",
@@ -21,7 +21,7 @@
21
21
  "r5d-worker": "dist/mjs/main.mjs"
22
22
  },
23
23
  "dependencies": {
24
- "@ricsam/r5d-api": "^0.0.153",
24
+ "@ricsam/r5d-api": "^0.0.155",
25
25
  "node-pty": "1.1.0",
26
26
  "zod": "^4.1.13",
27
27
  "picomatch": "^4.0.3"