@ricsam/r5d-worker 0.0.162 → 0.0.163

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.162",
3
+ "version": "0.0.163",
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.162";
20608
+ if (true) return "0.0.163";
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.162" : "development"}`);
10
+ console.log(`r5d-worker ${true ? "0.0.163" : "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.162" : "development"
18
+ true ? "0.0.163" : "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.162",
3
+ "version": "0.0.163",
4
4
  "type": "module"
5
5
  }
@@ -103,7 +103,7 @@ async function startPersonalWorker(options, version) {
103
103
  platform: process.platform,
104
104
  arch: process.arch,
105
105
  hostname: os.hostname(),
106
- capabilities: { updateClis: true, durableWorkspace: true }
106
+ capabilities: { updateClis: true, durableWorkspace: true, outerWorkspace: true }
107
107
  });
108
108
  const grant = PersonalWorkerGrant.parse(
109
109
  await request("/api/personal/resources/register", { kind: "worker", ...identity, label: options.label, metadata: metadata() })
@@ -116,7 +116,7 @@ async function startPersonalWorker(options, version) {
116
116
  try {
117
117
  const response = await request(`${endpoint}/storage`, {
118
118
  instanceId: identity.instanceId,
119
- request: { sessionId, envelope: { protocol: 1, installationId: grant.installationId, request: storageRequest } }
119
+ request: { ...sessionId === null ? { scope: "account" } : { sessionId }, envelope: { protocol: 1, installationId: grant.installationId, request: storageRequest } }
120
120
  });
121
121
  if (!response || !("result" in response)) throw new Error("Invalid scoped storage response");
122
122
  return response.result;
@@ -1,8 +1,16 @@
1
1
  const CONFLICT_PUBLICATION_CODES = /* @__PURE__ */ new Set([
2
2
  "conflict",
3
+ "sync_conflict",
3
4
  "workbench_conflict",
4
5
  "workbench_operation_in_progress"
5
6
  ]);
7
+ const TRANSIENT_PUBLICATION_CODES = /* @__PURE__ */ new Set([
8
+ "not_initialized",
9
+ "apply_refused",
10
+ "mirror_advanced",
11
+ "authority_closed",
12
+ "busy"
13
+ ]);
6
14
  const BLOCKED_PUBLICATION_CODES = /* @__PURE__ */ new Set([
7
15
  "secret_or_runtime_path",
8
16
  "secret_content",
@@ -33,6 +41,7 @@ export {
33
41
  OPERATION_IN_PROGRESS_CODE,
34
42
  PUBLICATION_OBSERVATION_THRESHOLDS,
35
43
  STUCK_OPERATION_OBSERVATIONS,
44
+ TRANSIENT_PUBLICATION_CODES,
36
45
  publicationRefusalElects,
37
46
  publicationRefusalOutcome
38
47
  };
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { publicationRefusalElects, publicationRefusalOutcome } from "./publication-refusal.mjs";
2
+ import { publicationRefusalElects, publicationRefusalOutcome, TRANSIENT_PUBLICATION_CODES } from "./publication-refusal.mjs";
3
3
  import { PersonalTcpManager } from "./tcp.mjs";
4
4
  import { createHash, randomBytes, randomUUID } from "node:crypto";
5
5
  import { promises as fs } from "node:fs";
@@ -12,6 +12,7 @@ import { EXECUTOR_CAPABILITY, ExecutorCredential } from "../runtime/protocol.mjs
12
12
  import { privateDirectory, readPrivateJson } from "../runtime/storage.mjs";
13
13
  import { WorkspaceAuthority } from "../runtime/workspace/authority.mjs";
14
14
  import { ApprovedWorkbench, WorkspaceError } from "../runtime/workspace/contracts.mjs";
15
+ import { OuterSnapshotRefusal } from "../runtime/workspace/outer.mjs";
15
16
  import { WorkspaceStorageClient } from "../runtime/workspace/storage-client.mjs";
16
17
  import { BranchName, GitOid, StorageId } from "../runtime/workspace/storage-wire.mjs";
17
18
  const PersonalWorkerGrant = z.object({
@@ -204,7 +205,11 @@ exec ${quote(executable)} ${quote(cli)} "$@"
204
205
  config: { installationId: grant.installationId, root: authorityRoot, workspaceRoot, dynamicWorkbenches: true, workbenches: [] },
205
206
  executor: async () => ({ client, workerFence: current.workerFence }),
206
207
  resolveWorkbench: async (identity) => identity.userId === grant.userId ? manifests.get(routes.get(identity.sessionId) ?? "") ?? null : null,
207
- storage: async (identity) => new WorkspaceStorageClient(grant.installationId, options.storage(identity.sessionId), async () => current.workerFence)
208
+ storage: async (identity) => new WorkspaceStorageClient(grant.installationId, options.storage(identity.sessionId), async () => current.workerFence),
209
+ accountStorage: async (userId) => {
210
+ if (userId !== grant.userId) throw new Error("Wrong personal worker principal");
211
+ return new WorkspaceStorageClient(grant.installationId, options.storage(null), async () => current.workerFence);
212
+ }
208
213
  });
209
214
  async function approve(input) {
210
215
  const cwd = input.rootProfile === "account" ? workspaceRoot : input.namespace && input.projectName ? path.join(workspaceRoot, "projects", input.namespace, input.projectName, ...input.branch.split("/")) : path.join(workspaceRoot, "workbenches", input.id);
@@ -260,51 +265,40 @@ exec ${quote(executable)} ${quote(cli)} "$@"
260
265
  if (publication) return publication;
261
266
  const next = (async () => {
262
267
  const results = [];
263
- for (const [workbenchId, sessionId] of publicationSessions) {
264
- if (closing) break;
265
- const manifest = manifests.get(workbenchId);
266
- if (!manifest || manifest.rootProfile !== "project") continue;
267
- try {
268
- const result = await authority.publish({ userId: grant.userId, sessionId });
269
- failedPublicationAttempts.delete(workbenchId);
270
- results.push({ workbenchId, ...result });
271
- } catch (error) {
272
- const code = error instanceof WorkspaceError ? error.code : "publication_failed";
273
- results.push({ workbenchId, error: code });
274
- if (code !== "not_initialized" && options.publicationReport) {
275
- let startingHead = null;
276
- try {
277
- const status = await authority.status({ userId: grant.userId, sessionId });
278
- startingHead = status.head;
279
- } catch {
280
- }
281
- const outcome = publicationRefusalOutcome(code);
282
- const fingerprint = canonicalJson({ code, startingHead, outcome });
283
- let attempt = failedPublicationAttempts.get(workbenchId);
284
- if (!attempt || attempt.fingerprint !== fingerprint) {
285
- attempt = { fingerprint, attemptId: randomUUID(), observations: 1 };
286
- failedPublicationAttempts.set(workbenchId, attempt);
287
- } else {
288
- attempt.observations++;
289
- }
290
- let workbenchHasLiveRun = false;
291
- try {
292
- workbenchHasLiveRun = await authority.workbenchHasLiveRun({ userId: grant.userId, sessionId });
293
- } catch {
294
- }
295
- if (!publicationRefusalElects(code, attempt.observations, { workbenchHasLiveRun })) continue;
268
+ try {
269
+ const outer = await authority.synchronizeOuter(grant.userId);
270
+ failedPublicationAttempts.delete("workspace");
271
+ results.push({ workbenchId: "workspace", ...outer });
272
+ } catch (error) {
273
+ const code = error instanceof WorkspaceError ? error.code : "publication_failed";
274
+ results.push({ workbenchId: "workspace", error: code });
275
+ if (!TRANSIENT_PUBLICATION_CODES.has(code) && options.publicationReport) {
276
+ let startingHead = null;
277
+ try {
278
+ startingHead = (await authority.outerStatus(grant.userId)).publishedHead;
279
+ } catch {
280
+ }
281
+ const outcome = publicationRefusalOutcome(code);
282
+ const paths = error instanceof OuterSnapshotRefusal ? error.paths : [];
283
+ const fingerprint = canonicalJson({ code, startingHead, outcome, paths });
284
+ let attempt = failedPublicationAttempts.get("workspace");
285
+ if (!attempt || attempt.fingerprint !== fingerprint) {
286
+ attempt = { fingerprint, attemptId: randomUUID(), observations: 1 };
287
+ failedPublicationAttempts.set("workspace", attempt);
288
+ } else {
289
+ attempt.observations++;
290
+ }
291
+ if (publicationRefusalElects(code, attempt.observations)) {
296
292
  try {
297
293
  await options.publicationReport(WorkspacePublicationReport.parse({
298
294
  protocol: 1,
299
295
  attemptId: attempt.attemptId,
300
- sessionId,
301
- workbenchId,
302
- repositoryId: manifest.repositoryId,
303
- branch: manifest.branch,
296
+ scope: "workspace",
304
297
  outcome,
305
298
  startingHead,
306
299
  diffSizeBytes: outcome === "large_diff_blocked" && code === "too_large" ? 5 * 1024 * 1024 + 1 : 0,
307
- error: code
300
+ error: code,
301
+ ...paths.length ? { paths } : {}
308
302
  }));
309
303
  } catch (reportError) {
310
304
  process.stderr.write(`[r5d-worker] publication report deferred: ${reportError instanceof WorkspaceError ? reportError.code : "publication_report_failed"}
@@ -313,13 +307,24 @@ exec ${quote(executable)} ${quote(cli)} "$@"
313
307
  }
314
308
  }
315
309
  }
310
+ for (const [workbenchId, sessionId] of publicationSessions) {
311
+ if (closing) break;
312
+ const manifest = manifests.get(workbenchId);
313
+ if (!manifest || manifest.rootProfile !== "project") continue;
314
+ try {
315
+ results.push({ workbenchId, ...await authority.mirror({ userId: grant.userId, sessionId }) });
316
+ } catch (error) {
317
+ results.push({ workbenchId, error: error instanceof WorkspaceError ? error.code : "mirror_failed" });
318
+ }
319
+ }
316
320
  return results;
317
321
  })();
318
322
  publication = next;
319
323
  void next.then(
320
324
  (results) => {
321
- if (results.some((result) => result.error && result.error !== "not_initialized"))
322
- process.stderr.write(`[r5d-worker] periodic workspace publication deferred: ${results.filter((result) => result.error).map((result) => `${result.workbenchId}:${result.error}`).join(", ")}
325
+ const deferred = results.filter((result) => result.error && !TRANSIENT_PUBLICATION_CODES.has(result.error));
326
+ if (deferred.length)
327
+ process.stderr.write(`[r5d-worker] periodic workspace publication deferred: ${deferred.map((result) => `${result.workbenchId}:${result.error}`).join(", ")}
323
328
  `);
324
329
  if (publication === next) publication = null;
325
330
  },