@ricsam/r5d-worker 0.0.157 → 0.0.158

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.157",
3
+ "version": "0.0.158",
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.157";
20608
+ if (true) return "0.0.158";
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.157" : "development"}`);
10
+ console.log(`r5d-worker ${true ? "0.0.158" : "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.157" : "development"
18
+ true ? "0.0.158" : "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.157",
3
+ "version": "0.0.158",
4
4
  "type": "module"
5
5
  }
@@ -125,6 +125,9 @@ async function startPersonalWorker(options, version) {
125
125
  throw new WorkspaceError(error.code, "Scoped storage rejected the request; retain its original operation identity", error.rejectedBeforeAdmission);
126
126
  throw error;
127
127
  }
128
+ },
129
+ publicationReport: async (report) => {
130
+ await request(`${endpoint}/publication`, { instanceId: identity.instanceId, report });
128
131
  }
129
132
  });
130
133
  const ledgerFile = path.join(root, "relay.sqlite");
@@ -1,9 +1,9 @@
1
1
  import path from "node:path";
2
2
  import { PersonalTcpManager } from "./tcp.mjs";
3
- import { createHash, randomBytes } from "node:crypto";
3
+ import { createHash, randomBytes, randomUUID } from "node:crypto";
4
4
  import { promises as fs } from "node:fs";
5
5
  import { z } from "zod";
6
- import { RuntimeHello, RuntimeId, OwnershipFence, OperationEnvelope, canonicalJson } from "@ricsam/r5d-api/runtime-protocol";
6
+ import { RuntimeHello, RuntimeId, OwnershipFence, OperationEnvelope, WorkspacePublicationReport, canonicalJson } from "@ricsam/r5d-api/runtime-protocol";
7
7
  import { startHostExecutor } from "../runtime/daemon.mjs";
8
8
  import { HostExecutorClient } from "../runtime/client.mjs";
9
9
  import { tokenHash } from "../runtime/executor.mjs";
@@ -42,7 +42,8 @@ const PersonalWorkspaceRequest = z.object({
42
42
  sharedWorkbenches: z.array(Workbench).max(32).optional(),
43
43
  kind: z.enum(["inspect", "terminal", "product", "agent", "tcp"]),
44
44
  command: z.record(z.string(), z.unknown()),
45
- credentials: z.array(ExecutorCredential).max(32).optional()
45
+ credentials: z.array(ExecutorCredential).max(32).optional(),
46
+ remediation: z.literal(true).optional()
46
47
  }).strict();
47
48
  async function personalExecutorToken(root, grant) {
48
49
  const identity = { installationId: grant.installationId, userId: grant.userId, resourceId: grant.resourceId, instanceId: grant.instanceId, workerId: grant.workerFence.resourceId };
@@ -252,6 +253,18 @@ exec ${quote(executable)} ${quote(cli)} "$@"
252
253
  }
253
254
  }
254
255
  }
256
+ const blockedPublicationCodes = /* @__PURE__ */ new Set([
257
+ "ignore_policy",
258
+ "secret_or_runtime_path",
259
+ "secret_content",
260
+ "unsafe_file",
261
+ "unsafe_git",
262
+ "unsafe_path",
263
+ "unsafe_tree",
264
+ "too_large"
265
+ ]);
266
+ const conflictPublicationCodes = /* @__PURE__ */ new Set(["conflict", "workbench_conflict"]);
267
+ const failedPublicationAttempts = /* @__PURE__ */ new Map();
255
268
  let publication = null, closing = false;
256
269
  const synchronize = () => {
257
270
  if (publication) return publication;
@@ -263,10 +276,43 @@ exec ${quote(executable)} ${quote(cli)} "$@"
263
276
  if (!manifest || manifest.rootProfile !== "project") continue;
264
277
  try {
265
278
  const result = await authority.publish({ userId: grant.userId, sessionId });
279
+ failedPublicationAttempts.delete(workbenchId);
266
280
  results.push({ workbenchId, ...result });
267
281
  } catch (error) {
268
282
  const code = error instanceof WorkspaceError ? error.code : "publication_failed";
269
283
  results.push({ workbenchId, error: code });
284
+ if (code !== "not_initialized" && options.publicationReport) {
285
+ let startingHead = null;
286
+ try {
287
+ const status = await authority.status({ userId: grant.userId, sessionId });
288
+ startingHead = status.head;
289
+ } catch {
290
+ }
291
+ const outcome = conflictPublicationCodes.has(code) ? "conflict_blocked" : blockedPublicationCodes.has(code) ? "large_diff_blocked" : "failed";
292
+ const fingerprint = canonicalJson({ code, startingHead, outcome });
293
+ let attempt = failedPublicationAttempts.get(workbenchId);
294
+ if (!attempt || attempt.fingerprint !== fingerprint) {
295
+ attempt = { fingerprint, attemptId: randomUUID() };
296
+ failedPublicationAttempts.set(workbenchId, attempt);
297
+ }
298
+ try {
299
+ await options.publicationReport(WorkspacePublicationReport.parse({
300
+ protocol: 1,
301
+ attemptId: attempt.attemptId,
302
+ sessionId,
303
+ workbenchId,
304
+ repositoryId: manifest.repositoryId,
305
+ branch: manifest.branch,
306
+ outcome,
307
+ startingHead,
308
+ diffSizeBytes: outcome === "large_diff_blocked" && code === "too_large" ? 5 * 1024 * 1024 + 1 : 0,
309
+ error: code
310
+ }));
311
+ } catch (reportError) {
312
+ process.stderr.write(`[r5d-worker] publication report deferred: ${reportError instanceof WorkspaceError ? reportError.code : "publication_report_failed"}
313
+ `);
314
+ }
315
+ }
270
316
  }
271
317
  }
272
318
  return results;
@@ -335,7 +381,8 @@ exec ${quote(executable)} ${quote(cli)} "$@"
335
381
  ...command.method === "open" ? { interactive: true, pty: { cols: command.cols, rows: command.rows } } : {},
336
382
  credentials: (request.credentials ?? []).map(({ id, generation }) => ({ id, generation }))
337
383
  }
338
- })
384
+ }),
385
+ request.remediation === true
339
386
  ),
340
387
  workerLabel: grant.label ?? grant.resourceId
341
388
  };
@@ -350,7 +397,7 @@ exec ${quote(executable)} ${quote(cli)} "$@"
350
397
  if (request.kind === "product") {
351
398
  if (command.method === "fileWrite") {
352
399
  await authority.ensureHydrated(identity);
353
- return authority.fileWrite(identity, command);
400
+ return authority.fileWrite(identity, command, request.remediation === true);
354
401
  }
355
402
  if (command.method === "materializeArtifact") return authority.materializeArtifact(identity, command);
356
403
  if (command.method === "seed") return authority.seedReadme(identity, command.readme);
@@ -362,7 +409,10 @@ exec ${quote(executable)} ${quote(cli)} "$@"
362
409
  if (command.method === "commit") return authority.commit(identity, command);
363
410
  if (command.method === "push") return authority.push(identity, command);
364
411
  if (command.method === "reset") return authority.reset(identity, command);
365
- if (command.method === "publish") return authority.publish(identity);
412
+ if (command.method === "publish") return authority.publish(identity, {
413
+ allowLargeDiff: request.remediation === true && command.allowLargeDiff === true,
414
+ allowBlockedConflict: request.remediation === true
415
+ });
366
416
  if (command.method === "reconcilePublication") return authority.reconcilePublication(identity);
367
417
  }
368
418
  if (request.kind === "agent") {
@@ -387,7 +437,7 @@ exec ${quote(executable)} ${quote(cli)} "$@"
387
437
  cwd: manifests.get(routes.get(identity.sessionId)).cwd,
388
438
  credentials: (request.credentials ?? []).map(({ id, generation }) => ({ id, generation }))
389
439
  }
390
- });
440
+ }, request.remediation === true);
391
441
  }
392
442
  if (command.method === "poll")
393
443
  return authority.poll(identity, run(command.run.operationId), command.stdoutOffset, command.stderrOffset, command.maxBytes);
@@ -866,15 +866,18 @@ class WorkspaceAuthority {
866
866
  return { state: "completed", result: receipt.result };
867
867
  });
868
868
  }
869
- async fileWrite(identity, raw) {
869
+ async fileWrite(identity, raw, allowBlockedConflict = false) {
870
870
  const input = WorkspaceFileWrite.parse(raw), bytes = Buffer.from(input.base64, "base64");
871
871
  if (bytes.toString("base64") !== input.base64 || bytes.length > 768 * 1024)
872
872
  throw new WorkspaceError("invalid_input", "Expected canonical base64 for at most 768 KiB", true);
873
873
  const b = await this.bench(identity), target = input.hostPath ? this.resolveHostPath(input.path) : path.join(b.config.cwd, input.path);
874
874
  const fingerprint = { ...input, userId: identity.userId, sessionId: identity.sessionId };
875
875
  let mode = 420;
876
+ let retainedBlock = null;
876
877
  const check = async () => {
877
- if (b.state.blocked) throw new WorkspaceError(b.state.blocked.code, b.state.blocked.message);
878
+ if (b.state.blocked && !(allowBlockedConflict && (b.state.blocked.code === "conflict" || b.state.blocked.code === "workbench_conflict")))
879
+ throw new WorkspaceError(b.state.blocked.code, b.state.blocked.message);
880
+ retainedBlock = b.state.blocked;
878
881
  if (!b.state.initialized) throw new WorkspaceError("uninitialized", "Hydrate this workspace before writing files");
879
882
  let current = input.hostPath ? path.parse(target).root : b.config.cwd;
880
883
  const parentParts = (input.hostPath ? target.slice(current.length) : input.path).split(path.sep).filter(Boolean).slice(0, -1);
@@ -936,7 +939,7 @@ class WorkspaceAuthority {
936
939
  };
937
940
  }, check);
938
941
  if (b.state.blocked?.code === "file_write_unknown" && b.state.blocked.operationId === input.id) {
939
- b.state.blocked = null;
942
+ b.state.blocked = retainedBlock;
940
943
  await this.save(b);
941
944
  }
942
945
  return result;
@@ -1167,15 +1170,16 @@ ${DEFAULT_WORKSPACE_IGNORE}`, { mode: 384 });
1167
1170
  return { ...result, reset: true, retained: true };
1168
1171
  }));
1169
1172
  }
1170
- async publish(identity) {
1173
+ async publish(identity, options = {}) {
1171
1174
  const b = await this.bench(identity);
1172
1175
  if (b.config.rootProfile === "account") return { head: "", unchanged: true };
1173
1176
  return this.serial(b, async () => {
1174
- await this.assertAvailable(b);
1175
- return this.publishIdle(b, identity);
1177
+ const conflictBlocked = b.state.blocked?.code === "conflict" || b.state.blocked?.code === "workbench_conflict";
1178
+ await this.assertAvailable(b, options.allowBlockedConflict === true && conflictBlocked);
1179
+ return this.publishIdle(b, identity, "Destination workspace snapshot", options.allowLargeDiff === true);
1176
1180
  });
1177
1181
  }
1178
- async publishIdle(b, identity, message = "Destination workspace snapshot") {
1182
+ async publishIdle(b, identity, message = "Destination workspace snapshot", allowLargeDiff = false) {
1179
1183
  if (!b.state.initialized) throw new WorkspaceError("not_initialized", "Hydrate or explicitly seed first");
1180
1184
  const storage = await this.storage(b, identity);
1181
1185
  const latest = await storage.read({
@@ -1194,6 +1198,19 @@ ${DEFAULT_WORKSPACE_IGNORE}`, { mode: 384 });
1194
1198
  const tree = await snapshotTree(b.repo, b.config.cwd, b.state.head);
1195
1199
  if (b.state.head && tree === (await git(b.repo, ["rev-parse", `${b.state.head}^{tree}`])).toString().trim())
1196
1200
  return { head: b.state.head, unchanged: true };
1201
+ if (!allowLargeDiff) {
1202
+ const base = b.state.head ?? (await git(b.repo, ["hash-object", "-t", "tree", "--stdin"], "")).toString().trim();
1203
+ let diff;
1204
+ try {
1205
+ diff = await git(b.repo, ["diff", "--binary", "--no-ext-diff", "--no-textconv", base, tree]);
1206
+ } catch (error) {
1207
+ if (error instanceof WorkspaceError && error.code === "git_failed")
1208
+ throw new WorkspaceError("too_large", "Source diff exceeds the 5242880-byte automatic publication limit");
1209
+ throw error;
1210
+ }
1211
+ if (diff.length > 5 * 1024 * 1024)
1212
+ throw new WorkspaceError("too_large", "Source diff exceeds the 5242880-byte automatic publication limit");
1213
+ }
1197
1214
  const commit = (await git(b.repo, ["commit-tree", tree, ...b.state.head ? ["-p", b.state.head] : []], `${message}
1198
1215
  `)).toString().trim();
1199
1216
  await git(b.repo, ["update-ref", this.canonicalRef(b), commit]);
@@ -1312,11 +1329,12 @@ ${DEFAULT_WORKSPACE_IGNORE}`, { mode: 384 });
1312
1329
  return this.serial(b, () => new SessionArtifactStore(this.config.workspaceRoot ?? this.config.root).materialize(identity, input));
1313
1330
  }
1314
1331
  /** Default arbitrary shell is mutating. Exact trusted config approval is the sole exemption. */
1315
- async start(identity, input) {
1332
+ async start(identity, input, allowBlockedConflict = false) {
1316
1333
  const b = await this.bench(identity);
1317
1334
  const operation = OperationEnvelope.parse(JSON.parse(canonicalJson(input)));
1318
1335
  return this.serial(b, async () => {
1319
- if (!b.state.initialized || b.state.blocked)
1336
+ const conflictBlocked = b.state.blocked?.code === "conflict" || b.state.blocked?.code === "workbench_conflict";
1337
+ if (!b.state.initialized || b.state.blocked && !(allowBlockedConflict && conflictBlocked))
1320
1338
  throw new WorkspaceError("workbench_blocked", b.state.blocked?.message ?? "Initialize workbench first");
1321
1339
  if (operation.installationId !== this.config.installationId || operation.userId !== identity.userId || operation.sessionId !== identity.sessionId || operation.kind !== "host.shell")
1322
1340
  throw new WorkspaceError("forbidden", "Shell identity does not match approved workbench");
@@ -254,7 +254,13 @@ async function materializeTree(repo, destination, entries, limit, message) {
254
254
  }
255
255
  }
256
256
  async function verifyIgnore(repo, cwd) {
257
- await readRegular(path.join(cwd, ".gitignore"), 64 * 1024);
257
+ try {
258
+ await readRegular(path.join(cwd, ".gitignore"), 64 * 1024);
259
+ } catch (error) {
260
+ if (error.code === "ENOENT")
261
+ throw new WorkspaceError("ignore_policy", "Restore an effective .gitignore before publishing source");
262
+ throw error;
263
+ }
258
264
  const probes = [
259
265
  ".env",
260
266
  ".env.local",
@@ -276,7 +282,7 @@ async function verifyIgnore(repo, cwd) {
276
282
  if (ignored.length !== probes.length || probes.some((p) => !ignored.includes(p)))
277
283
  throw new WorkspaceError("ignore_policy", "Restore effective .gitignore rules for secrets, dependency, cache and runtime roots");
278
284
  }
279
- async function snapshotTree(repo, cwd, canonicalHead) {
285
+ async function snapshotTree(repo, cwd, canonicalHead, maxBytes = 128 * 1024 * 1024) {
280
286
  const metadata = path.join(cwd, ".git");
281
287
  let workbenchIndex = null;
282
288
  if (await fs.lstat(metadata).then(
@@ -328,15 +334,16 @@ async function snapshotTree(repo, cwd, canonicalHead) {
328
334
  }
329
335
  const canonical = canonicalHead ? await selectedTree(repo, canonicalHead) : [];
330
336
  const indexedGitlinks = /* @__PURE__ */ new Map();
337
+ const indexedFiles = /* @__PURE__ */ new Set();
331
338
  if (workbenchIndex) {
332
339
  const indexed = await git(repo, ["ls-files", "--stage", "-z"], void 0, workbenchIndex);
333
340
  if (!Buffer.from(indexed.toString()).equals(indexed)) throw new WorkspaceError("unsafe_path", "Non-UTF8 index names unsupported");
334
341
  for (const record of indexed.toString().split("\0").filter(Boolean)) {
335
- const match = /^160000 ([0-9a-f]{40}) 0\t(.+)$/.exec(record);
336
- if (record.startsWith("160000 ") && !match) throw new WorkspaceError("unsafe_tree", "Malformed gitlink index entry");
337
- if (!match) continue;
338
- sourcePath(match[2]);
339
- indexedGitlinks.set(match[2], match[1]);
342
+ const match = /^(100644|100755|160000) ([0-9a-f]{40}) 0\t(.+)$/.exec(record);
343
+ if (!match) throw new WorkspaceError("unsafe_tree", "Malformed workbench index entry");
344
+ sourcePath(match[3]);
345
+ if (match[1] === "160000") indexedGitlinks.set(match[3], match[2]);
346
+ else indexedFiles.add(match[3]);
340
347
  }
341
348
  }
342
349
  const canonicalGitlinks = new Map(canonical.filter((entry) => entry.mode === "160000").map((entry) => [entry.file, entry.oid]));
@@ -363,7 +370,6 @@ async function snapshotTree(repo, cwd, canonicalHead) {
363
370
  if (nested) gitlinks.delete(file);
364
371
  else gitlinkRoots.push(file);
365
372
  }
366
- await verifyIgnore(repo, cwd);
367
373
  await git(repo, ["read-tree", "--empty"]);
368
374
  const listing = await git(repo, [
369
375
  `--work-tree=${cwd}`,
@@ -385,7 +391,7 @@ async function snapshotTree(repo, cwd, canonicalHead) {
385
391
  current = parent;
386
392
  }
387
393
  };
388
- const tracked = canonical.filter((entry) => entry.mode !== "160000").map((entry) => entry.file);
394
+ const tracked = [.../* @__PURE__ */ new Set([...canonical.filter((entry) => entry.mode !== "160000").map((entry) => entry.file), ...indexedFiles])];
389
395
  const existingTracked = [];
390
396
  for (const file of tracked) {
391
397
  const exists = await fs.lstat(path.join(cwd, file)).then(() => true, (error) => {
@@ -395,6 +401,8 @@ async function snapshotTree(repo, cwd, canonicalHead) {
395
401
  if (exists) existingTracked.push(file);
396
402
  }
397
403
  const files = [.../* @__PURE__ */ new Set([...listing.toString().split("\0").filter(Boolean), ...existingTracked])].filter((file) => !beneathGitlink(file)).sort();
404
+ if (files.some((file) => file.split("/").some((part) => part.toLowerCase() === "node_modules")))
405
+ throw new WorkspaceError("too_large", "An unignored dependency tree exceeds the automatic publication limit");
398
406
  if (files.length + gitlinks.size > STORAGE_LIMITS.treeEntries) throw new WorkspaceError("too_large", "Too many source files");
399
407
  let size = 0;
400
408
  const records = [...gitlinks].map(([file, oid]) => ({
@@ -405,12 +413,14 @@ async function snapshotTree(repo, cwd, canonicalHead) {
405
413
  sourcePath(file);
406
414
  const bytes = await readRegular(path.join(cwd, file));
407
415
  sourceBytes(bytes, file);
408
- if ((size += bytes.length) > 128 * 1024 * 1024) throw new WorkspaceError("too_large", "Source snapshot exceeds 128 MiB");
416
+ if ((size += bytes.length) > maxBytes)
417
+ throw new WorkspaceError("too_large", `Source snapshot exceeds the ${maxBytes}-byte automatic publication limit`);
409
418
  const st = await fs.lstat(path.join(cwd, file));
410
419
  const oid = (await git(repo, ["hash-object", "-w", "--stdin", "--no-filters"], bytes)).toString().trim();
411
420
  records.push({ file, value: `${st.mode & 73 ? "100755" : "100644"} ${oid} ${file}\0` });
412
421
  }
413
422
  if (!files.includes(".gitignore")) throw new WorkspaceError("ignore_policy", "The .gitignore policy itself must be published");
423
+ await verifyIgnore(repo, cwd);
414
424
  await git(repo, ["update-index", "-z", "--index-info"], records.sort((a, b) => a.file.localeCompare(b.file)).map((record) => record.value).join(""));
415
425
  return (await git(repo, ["write-tree"])).toString().trim();
416
426
  }
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { WorkspacePublicationReport } from "@ricsam/r5d-api/runtime-protocol";
2
3
  import { WorkspaceAuthority } from "../runtime/workspace/authority";
3
4
  import { type StorageTransport } from "../runtime/workspace/storage-client";
4
5
  export declare const PersonalWorkerGrant: z.ZodObject<{
@@ -96,6 +97,7 @@ export declare const PersonalWorkspaceRequest: z.ZodObject<{
96
97
  env: z.ZodRecord<z.ZodString, z.ZodString>;
97
98
  files: z.ZodRecord<z.ZodString, z.ZodString>;
98
99
  }, z.core.$strict>>>;
100
+ remediation: z.ZodOptional<z.ZodLiteral<true>>;
99
101
  }, z.core.$strict>;
100
102
  export type PersonalWorkerRuntime = Awaited<ReturnType<typeof openPersonalWorkerRuntime>>;
101
103
  /** The host owns this keeper independently of every replaceable remote web/gateway generation. */
@@ -106,6 +108,9 @@ export declare function openPersonalWorkerRuntime(options: {
106
108
  cliEntrypoint?: string;
107
109
  /** Internal test/embedding override. Personal workers publish every minute. */
108
110
  publicationIntervalMs?: number;
111
+ /** Authenticated transport for autonomous publication evidence. Failures are
112
+ * retried with the same attempt identity on the next publication cycle. */
113
+ publicationReport?: (report: z.infer<typeof WorkspacePublicationReport>) => Promise<void>;
109
114
  }): Promise<{
110
115
  grant: {
111
116
  [x: string]: unknown;
@@ -256,7 +256,7 @@ export declare class WorkspaceAuthority {
256
256
  state: "completed";
257
257
  result: any;
258
258
  }>;
259
- fileWrite(identity: WorkspaceIdentity, raw: WorkspaceFileWrite): Promise<{
259
+ fileWrite(identity: WorkspaceIdentity, raw: WorkspaceFileWrite, allowBlockedConflict?: boolean): Promise<{
260
260
  intentHash?: string | undefined;
261
261
  method: string;
262
262
  id: string;
@@ -323,7 +323,10 @@ export declare class WorkspaceAuthority {
323
323
  retained: boolean;
324
324
  head: string;
325
325
  }>;
326
- publish(identity: WorkspaceIdentity): Promise<{
326
+ publish(identity: WorkspaceIdentity, options?: {
327
+ allowLargeDiff?: boolean;
328
+ allowBlockedConflict?: boolean;
329
+ }): Promise<{
327
330
  head: string;
328
331
  unchanged?: boolean;
329
332
  }>;
@@ -347,7 +350,7 @@ export declare class WorkspaceAuthority {
347
350
  complete: boolean;
348
351
  }>;
349
352
  /** Default arbitrary shell is mutating. Exact trusted config approval is the sole exemption. */
350
- start(identity: WorkspaceIdentity, input: OperationEnvelope): Promise<import("..").RunReceipt>;
353
+ start(identity: WorkspaceIdentity, input: OperationEnvelope, allowBlockedConflict?: boolean): Promise<import("..").RunReceipt>;
351
354
  private runRoute;
352
355
  private action;
353
356
  poll(identity: WorkspaceIdentity, run: RunIdentity, stdoutOffset?: number, stderrOffset?: number, maxBytes?: number): Promise<PollResult>;
@@ -26,4 +26,4 @@ export type TreeEntry = {
26
26
  export declare function selectedTree(repo: string, commit: string): Promise<TreeEntry[]>;
27
27
  export declare function materializeTree(repo: string, destination: string, entries: TreeEntry[], limit: number, message: string): Promise<void>;
28
28
  export declare function verifyIgnore(repo: string, cwd: string): Promise<void>;
29
- export declare function snapshotTree(repo: string, cwd: string, canonicalHead?: string | null): Promise<string>;
29
+ export declare function snapshotTree(repo: string, cwd: string, canonicalHead?: string | null, maxBytes?: number): Promise<string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.157",
3
+ "version": "0.0.158",
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.157",
24
+ "@ricsam/r5d-api": "^0.0.158",
25
25
  "node-pty": "1.1.0",
26
26
  "zod": "^4.1.13",
27
27
  "picomatch": "^4.0.3"