@ricsam/r5d-worker 0.0.175 → 0.0.177

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.175",
3
+ "version": "0.0.177",
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.175";
20608
+ if (true) return "0.0.177";
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,15 +7,15 @@ 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.175" : "development"}`);
10
+ console.log(`r5d-worker ${true ? "0.0.177" : "development"}`);
11
11
  } else if (!args.length || args.includes("--help")) {
12
12
  console.log(
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."
13
+ "Usage: r5d-worker start --label <label> [--root <dir>] [--base-url <url>] [--token <worker-token>] [--initial-sync merge|reset]\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."
14
14
  );
15
15
  } else if (args[0] === "start") {
16
16
  const runtime = await startPersonalWorker(
17
17
  parsePersonalWorkerOptions(args.slice(1)),
18
- true ? "0.0.175" : "development"
18
+ true ? "0.0.177" : "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.175",
3
+ "version": "0.0.177",
4
4
  "type": "module"
5
5
  }
@@ -27,7 +27,7 @@ class PersonalResponseError extends Error {
27
27
  const storageErrorCodes = /* @__PURE__ */ new Set(["invalid_input", "conflict", "not_found", "busy", "stale_fence", "unknown", "unsafe_path", "git_failed"]);
28
28
  function parsePersonalWorkerOptions(args, environment = process.env) {
29
29
  const values = {};
30
- const allowed = /* @__PURE__ */ new Set(["label", "base-url", "token", "api-key", "config", "root"]);
30
+ const allowed = /* @__PURE__ */ new Set(["label", "base-url", "token", "api-key", "config", "root", "initial-sync"]);
31
31
  for (let index = 0; index < args.length; index++) {
32
32
  const arg = args[index];
33
33
  const match = /^--([^=]+)(?:=(.*))?$/.exec(arg);
@@ -54,17 +54,21 @@ function parsePersonalWorkerOptions(args, environment = process.env) {
54
54
  throw new Error("Worker base URL must use HTTPS or local loopback");
55
55
  const credential = values.token ?? values["api-key"] ?? environment.R5D_TOKEN ?? environment.R5D_API_KEY ?? string(saved.token) ?? string(saved.apiKey);
56
56
  if (!credential) throw new Error(`Authenticate this worker with: r5dctl auth login --worker-label ${label}`);
57
+ const initialSync = values["initial-sync"] ?? "merge";
58
+ if (initialSync !== "merge" && initialSync !== "reset") throw new Error("--initial-sync must be merge or reset");
57
59
  return {
58
60
  label,
59
61
  baseUrl: origin.origin,
60
62
  credential,
61
- root: path.resolve(values.root ?? environment.R5D_ROOT ?? path.join(os.homedir(), ".r5d"))
63
+ root: path.resolve(values.root ?? environment.R5D_ROOT ?? path.join(os.homedir(), ".r5d")),
64
+ initialSync
62
65
  };
63
66
  }
64
67
  function safeError(error) {
65
68
  const value = error.code;
66
69
  return typeof value === "string" && /^[a-zA-Z0-9_-]{1,100}$/.test(value) ? value : "personal_operation_unknown";
67
70
  }
71
+ const resetBatchId = (instanceId) => `${instanceId.slice(0, 16)}-${Date.now().toString(36)}`;
68
72
  const rejectedBeforeAdmission = (error) => error?.rejectedBeforeAdmission === true;
69
73
  const CliUpdateRequest = z.object({ kind: z.literal("update-clis"), userId: z.string(), command: z.object({ version: z.string() }).passthrough() }).passthrough();
70
74
  async function startPersonalWorker(options, version) {
@@ -101,8 +105,10 @@ async function startPersonalWorker(options, version) {
101
105
  return data;
102
106
  }
103
107
  const cli = await resolvePersonalCliEntrypoint(options.cliEntrypoint, version);
108
+ const bootId = randomUUID();
104
109
  const metadata = () => ({
105
110
  version,
111
+ bootId,
106
112
  r5dctlVersion: cli.version,
107
113
  r5dctlSource: cli.source,
108
114
  platform: process.platform,
@@ -137,6 +143,14 @@ async function startPersonalWorker(options, version) {
137
143
  return result && typeof result === "object" && typeof result.incidentId === "string" ? { incidentId: result.incidentId } : void 0;
138
144
  }
139
145
  });
146
+ if (options.initialSync === "reset") {
147
+ const results = await runtime.resetProjectCheckouts(`initial-sync-${resetBatchId(identity.instanceId)}`);
148
+ const reset = results.filter((entry) => !entry.error), refused = results.filter((entry) => entry.error);
149
+ process.stderr.write(`[r5d-worker] initial sync reset: ${reset.length} checkout(s) restored to the server state${reset.length ? ` (${reset.map((entry) => entry.workbenchId).join(", ")}); discarded changes retained beside each checkout's state` : ""}
150
+ `);
151
+ for (const entry of refused) process.stderr.write(`[r5d-worker] initial sync reset skipped ${entry.workbenchId}: ${entry.error}
152
+ `);
153
+ }
140
154
  const ledgerFile = path.join(root, "relay.sqlite");
141
155
  try {
142
156
  closeSync(openSync(ledgerFile, "wx", 384));
@@ -226,8 +226,8 @@ exec ${quote(executable)} ${quote(cli)} "$@"
226
226
  return binding;
227
227
  };
228
228
  if (previous && canonicalJson(stable(previous)) !== canonicalJson(stable(row))) throw new Error("Personal workbench identity changed");
229
- if (!previous) {
230
- manifests.set(input.id, row);
229
+ if (!previous || previous.sessionId !== row.sessionId) {
230
+ manifests.set(input.id, previous ? { ...previous, sessionId: row.sessionId, ...row.sharedSessionId ? { sharedSessionId: row.sharedSessionId } : {}, ...row.baseCommitHash ? { baseCommitHash: row.baseCommitHash } : {} } : row);
231
231
  const tmp = `${manifestFile}.${randomBytes(8).toString("hex")}.next`;
232
232
  const handle = await fs.open(tmp, "wx", 384);
233
233
  try {
@@ -481,6 +481,22 @@ exec ${quote(executable)} ${quote(cli)} "$@"
481
481
  const identity = { userId: grant.userId, sessionId: RuntimeId.parse(input.sessionId) };
482
482
  return authority.streamInput(identity, { ...identity, operationId: RuntimeId.parse(input.operationId), fence: current.workerFence }, input.data, input.resize);
483
483
  }
484
+ async function resetProjectCheckouts(idPrefix) {
485
+ const results = [];
486
+ for (const [workbenchId, sessionId] of publicationSessions) {
487
+ const manifest = manifests.get(workbenchId);
488
+ if (!manifest || manifest.rootProfile !== "project") continue;
489
+ const identity = { userId: grant.userId, sessionId };
490
+ try {
491
+ if (!(await authority.status(identity)).initialized) continue;
492
+ const result = await authority.reset(identity, { id: `${idPrefix}-${workbenchId}` });
493
+ results.push({ workbenchId, head: result.head ?? null });
494
+ } catch (error) {
495
+ results.push({ workbenchId, head: null, error: error instanceof WorkspaceError ? error.code : error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : "reset_failed" });
496
+ }
497
+ }
498
+ return results;
499
+ }
484
500
  return {
485
501
  grant,
486
502
  renew,
@@ -489,6 +505,7 @@ exec ${quote(executable)} ${quote(cli)} "$@"
489
505
  synchronize,
490
506
  subscribeTerminal,
491
507
  terminalInput,
508
+ resetProjectCheckouts,
492
509
  async close() {
493
510
  closing = true;
494
511
  clearInterval(publicationTimer);
@@ -1210,7 +1210,7 @@ class WorkspaceAuthority {
1210
1210
  /** An explicit user reset retains the complete old tree/index before hydration. */
1211
1211
  async reset(identity, input) {
1212
1212
  const b = await this.bench(identity);
1213
- return this.serial(b, () => this.productAction(b, input.id, { method: "reset", expectedHead: input.expectedHead }, async () => {
1213
+ return this.serial(b, () => this.productAction(b, input.id, { method: "reset", expectedHead: input.expectedHead ?? null }, async () => {
1214
1214
  await this.assertAvailable(b, true);
1215
1215
  if (input.expectedHead !== void 0 && input.expectedHead !== b.state.head) throw new WorkspaceError("conflict", "Workbench base changed before reset");
1216
1216
  if (b.state.blocked?.code === "archived") throw new WorkspaceError("archived", "A deleted branch cannot be reset");
@@ -1,9 +1,16 @@
1
+ /** What the first synchronization after `r5d-worker start` does to checkouts
2
+ * that diverged from the server while the worker was away: `merge` integrates
3
+ * them and opens a conflict remediation when needed (the default); `reset`
4
+ * takes the server state and keeps the discarded local changes aside under
5
+ * the checkout's retained directory. */
6
+ export type PersonalWorkerInitialSync = "merge" | "reset";
1
7
  export type PersonalWorkerOptions = {
2
8
  label: string;
3
9
  baseUrl: string;
4
10
  credential: string;
5
11
  root: string;
6
12
  cliEntrypoint?: string;
13
+ initialSync?: PersonalWorkerInitialSync;
7
14
  };
8
15
  export declare function parsePersonalWorkerOptions(args: string[], environment?: Record<string, string | undefined>): PersonalWorkerOptions;
9
16
  /** HTTP reconnects never replace the local source/PTY keeper. */
@@ -179,6 +179,11 @@ export declare function openPersonalWorkerRuntime(options: {
179
179
  rows: number;
180
180
  };
181
181
  }) => Promise<import("../runtime/protocol").InputResult>;
182
+ resetProjectCheckouts: (idPrefix: string) => Promise<{
183
+ workbenchId: string;
184
+ head: string | null;
185
+ error?: string;
186
+ }[]>;
182
187
  close(): Promise<void>;
183
188
  }>;
184
189
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.175",
3
+ "version": "0.0.177",
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.175",
24
+ "@ricsam/r5d-api": "^0.0.177",
25
25
  "node-pty": "1.1.0",
26
26
  "zod": "^4.1.13",
27
27
  "picomatch": "^4.0.3"