@ricsam/r5d-worker 0.0.135 → 0.0.137

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,28 +1,42 @@
1
- class WorkspaceHydrationPreBlobLedger {
1
+ class WorkspaceHydrationLedger {
2
2
  mounts = /* @__PURE__ */ new Map();
3
3
  static key(mount) {
4
4
  return `${mount.hydrationIncarnationKey}\0${mount.id}`;
5
5
  }
6
- /** Blob ids by mount-relative path for the mount, or an empty map. */
6
+ /** Entries by mount-relative path for the mount, or an empty map. */
7
+ entries(mount) {
8
+ return this.mounts.get(WorkspaceHydrationLedger.key(mount)) ?? /* @__PURE__ */ new Map();
9
+ }
10
+ /** Blob ids by mount-relative path for the mount (the §3.3 detector's input), or an empty map. */
7
11
  preBlobs(mount) {
8
- return this.mounts.get(WorkspaceHydrationPreBlobLedger.key(mount)) ?? /* @__PURE__ */ new Map();
12
+ return new Map([...this.entries(mount)].map(([relativePath, entry]) => [relativePath, entry.preBlob]));
9
13
  }
10
- /** Record the pre-hydration blob of every path a merge hydration replaced; earlier entries for other paths are kept. */
11
- record(mount, preBlobs) {
12
- const entries = preBlobs instanceof Map ? [...preBlobs] : Object.entries(preBlobs);
13
- if (entries.length === 0) return;
14
- const key = WorkspaceHydrationPreBlobLedger.key(mount);
14
+ /**
15
+ * Record what a merge hydration replaced; earlier entries for other paths
16
+ * are kept. A path that already carries a live entry keeps its `preBlob`
17
+ * while the new hydration also ran across a live process: the process's
18
+ * read predates both hydrations, so what it read is still the first blob.
19
+ */
20
+ record(mount, entries, options = {}) {
21
+ const incoming = entries instanceof Map ? [...entries] : Object.entries(entries);
22
+ if (incoming.length === 0) return;
23
+ const replace = new Set(options.replacePreBlobFor ?? []);
24
+ const key = WorkspaceHydrationLedger.key(mount);
15
25
  const current = this.mounts.get(key) ?? /* @__PURE__ */ new Map();
16
- for (const [relativePath, blob] of entries) current.set(relativePath, blob);
26
+ for (const [relativePath, entry] of incoming) {
27
+ const previous = current.get(relativePath);
28
+ const preBlob = previous && previous.processLiveAcrossHydration && entry.processLiveAcrossHydration && !replace.has(relativePath) ? previous.preBlob : entry.preBlob;
29
+ current.set(relativePath, { ...entry, preBlob });
30
+ }
17
31
  this.mounts.set(key, current);
18
32
  }
19
33
  /** Forget the mount: its checkout was rewritten by something this ledger did not observe. */
20
34
  clearMount(mount) {
21
- this.mounts.delete(WorkspaceHydrationPreBlobLedger.key(mount));
35
+ this.mounts.delete(WorkspaceHydrationLedger.key(mount));
22
36
  }
23
37
  /** Forget paths whose checkout content moved on from the hydrated bytes: a later return to the old bytes is a deliberate edit. */
24
38
  forget(mount, paths) {
25
- const key = WorkspaceHydrationPreBlobLedger.key(mount);
39
+ const key = WorkspaceHydrationLedger.key(mount);
26
40
  const current = this.mounts.get(key);
27
41
  if (!current) return;
28
42
  for (const relativePath of paths) current.delete(relativePath);
@@ -37,6 +51,8 @@ class WorkspaceHydrationPreBlobLedger {
37
51
  return total;
38
52
  }
39
53
  }
54
+ const WorkspaceHydrationPreBlobLedger = WorkspaceHydrationLedger;
40
55
  export {
56
+ WorkspaceHydrationLedger,
41
57
  WorkspaceHydrationPreBlobLedger
42
58
  };
@@ -87,6 +87,8 @@ function decideWorkspaceHydrationMerge(input) {
87
87
  const overrides = /* @__PURE__ */ new Map();
88
88
  const expectations = /* @__PURE__ */ new Map();
89
89
  const preBlobs = /* @__PURE__ */ new Map();
90
+ const postBlobs = /* @__PURE__ */ new Map();
91
+ const blobsToStore = /* @__PURE__ */ new Map();
90
92
  const mergedPaths = [];
91
93
  const keptPaths = /* @__PURE__ */ new Set();
92
94
  const desiredForIndex = new Map(desired);
@@ -139,13 +141,17 @@ function decideWorkspaceHydrationMerge(input) {
139
141
  remove.add(relativePath);
140
142
  expectations.set(relativePath, oursStat);
141
143
  }
142
- if (change.base && isBlobMode(change.base.mode)) preBlobs.set(relativePath, change.base.objectId);
144
+ if (change.base && isBlobMode(change.base.mode)) {
145
+ preBlobs.set(relativePath, change.base.objectId);
146
+ postBlobs.set(relativePath, null);
147
+ }
143
148
  continue;
144
149
  }
145
150
  write.add(relativePath);
146
151
  expectations.set(relativePath, oursStat ?? currentTargetStat(targetRoot, relativePath));
147
152
  if (change.base && isBlobMode(change.base.mode) && change.base.objectId !== change.theirs.objectId) {
148
153
  preBlobs.set(relativePath, change.base.objectId);
154
+ postBlobs.set(relativePath, isBlobMode(change.theirs.mode) ? change.theirs.objectId : null);
149
155
  }
150
156
  continue;
151
157
  }
@@ -259,7 +265,14 @@ function decideWorkspaceHydrationMerge(input) {
259
265
  overrides.set(relativePath, { content: merged.content, mode });
260
266
  write.add(relativePath);
261
267
  expectations.set(relativePath, candidate.oursStat);
262
- if (!contentUnchanged) preBlobs.set(relativePath, hashOf(oursBytes));
268
+ if (!contentUnchanged) {
269
+ const preBlob = hashOf(oursBytes);
270
+ const postBlob = hashOf(merged.content);
271
+ preBlobs.set(relativePath, preBlob);
272
+ postBlobs.set(relativePath, postBlob);
273
+ blobsToStore.set(preBlob, oursBytes);
274
+ blobsToStore.set(postBlob, merged.content);
275
+ }
263
276
  mergedPaths.push(relativePath);
264
277
  }
265
278
  }
@@ -333,6 +346,8 @@ function decideWorkspaceHydrationMerge(input) {
333
346
  overrides,
334
347
  expectations,
335
348
  preBlobs,
349
+ postBlobs,
350
+ blobsToStore,
336
351
  mergedPaths: mergedPaths.sort(),
337
352
  keptPaths: [...keptPaths].sort()
338
353
  };
@@ -218,9 +218,12 @@ function synthesizeMountTree(input) {
218
218
  removeTemporaryIndex(indexPath);
219
219
  }
220
220
  }
221
- function staleRewritePaths(input) {
222
- const entries = Object.entries(input.staleRewriteBlobs ?? {});
223
- if (entries.length === 0) return { stale: [], cleared: [] };
221
+ function ledgerStatMatches(recorded, projected) {
222
+ return recorded !== null && projected?.kind === "file" && projected.ino === recorded.ino && projected.size === recorded.size && projected.mode === recorded.mode && projected.mtimeMs === recorded.mtimeMs && projected.ctimeMs === recorded.ctimeMs;
223
+ }
224
+ function classifyHydrationLedger(input) {
225
+ const entries = Object.entries(input.hydrationLedger ?? {});
226
+ if (entries.length === 0) return { stale: [], merge: [], cleared: [] };
224
227
  const probed = gitText(input.workspacePath, ["cat-file", "--batch-check"], "resolve workspace projection basis subtree", {
225
228
  stdin: Buffer.from(`${input.basisHead}:${input.workspaceRelativePath}
226
229
  `)
@@ -241,21 +244,116 @@ function staleRewritePaths(input) {
241
244
  for (let index = 0; index + 1 < records.length; index += 2) changed.add(records[index + 1].toString());
242
245
  }
243
246
  const stale = [];
247
+ const merge = [];
244
248
  const cleared = [];
245
- for (const [relativePath, blob] of entries) {
249
+ for (const [relativePath, entry] of entries) {
246
250
  const synthesized = input.blobs.get(relativePath);
247
251
  if (synthesized === void 0) {
252
+ if (entry.postBlob === null) continue;
253
+ cleared.push(relativePath);
254
+ continue;
255
+ }
256
+ if (ledgerStatMatches(entry.stat, input.projectedFiles[relativePath]) || synthesized === entry.postBlob) continue;
257
+ if (!changed.has(relativePath)) {
248
258
  cleared.push(relativePath);
249
259
  continue;
250
260
  }
251
- if (synthesized === blob) {
252
- if (changed.has(relativePath)) stale.push(relativePath);
253
- else cleared.push(relativePath);
261
+ if (entry.processLiveAcrossHydration) {
262
+ merge.push({ relativePath, preBlob: entry.preBlob, postBlob: entry.postBlob });
254
263
  continue;
255
264
  }
256
- if (changed.has(relativePath)) cleared.push(relativePath);
265
+ if (synthesized === entry.preBlob) {
266
+ stale.push(relativePath);
267
+ continue;
268
+ }
269
+ cleared.push(relativePath);
270
+ }
271
+ if (merge.length > 0) {
272
+ const ids = [...new Set(merge.flatMap(({ preBlob, postBlob }) => postBlob ? [preBlob, postBlob] : [preBlob]))];
273
+ const checked = gitText(input.workspacePath, ["cat-file", "--batch-check"], "verify hydration ledger blobs", {
274
+ stdin: Buffer.from(`${ids.join("\n")}
275
+ `)
276
+ });
277
+ const missing = new Set(
278
+ checked.split("\n").filter((line) => / missing$/u.test(line)).map((line) => line.split(" ")[0])
279
+ );
280
+ if (missing.size > 0) {
281
+ for (let index = merge.length - 1; index >= 0; index -= 1) {
282
+ const { relativePath, preBlob, postBlob } = merge[index];
283
+ if (missing.has(preBlob) || postBlob !== null && missing.has(postBlob)) {
284
+ merge.splice(index, 1);
285
+ cleared.push(relativePath);
286
+ }
287
+ }
288
+ }
289
+ }
290
+ return { stale: stale.sort(), merge: merge.sort((left, right) => left.relativePath.localeCompare(right.relativePath)), cleared: cleared.sort() };
291
+ }
292
+ function hydrationLedgerMergeBase(input) {
293
+ if (input.merges.length === 0) return input.basisHead;
294
+ return commitWithLedgerPaths({
295
+ workspacePath: input.workspacePath,
296
+ workspaceRelativePath: input.workspaceRelativePath,
297
+ parent: input.basisHead,
298
+ paths: input.merges.map(({ relativePath, preBlob }) => ({ relativePath, blob: preBlob })),
299
+ message: JSON.stringify({
300
+ type: "workspace_hydration_ledger_basis",
301
+ mountId: input.mountId,
302
+ attemptId: input.attemptId,
303
+ paths: input.merges.map(({ relativePath }) => relativePath)
304
+ }),
305
+ action: "hydration ledger merge base"
306
+ });
307
+ }
308
+ function hydrationLedgerTheirs(input) {
309
+ const paths = input.merges.flatMap(({ relativePath, postBlob }) => postBlob ? [{ relativePath, blob: postBlob }] : []);
310
+ if (paths.length === 0) return input.currentHead;
311
+ return commitWithLedgerPaths({
312
+ workspacePath: input.workspacePath,
313
+ workspaceRelativePath: input.workspaceRelativePath,
314
+ parent: input.currentHead,
315
+ paths,
316
+ message: JSON.stringify({
317
+ type: "workspace_hydration_ledger_theirs",
318
+ mountId: input.mountId,
319
+ attemptId: input.attemptId,
320
+ paths: paths.map(({ relativePath }) => relativePath)
321
+ }),
322
+ action: "hydration ledger theirs"
323
+ });
324
+ }
325
+ function commitWithLedgerPaths(input) {
326
+ const indexPath = temporaryIndexPath(input.workspacePath);
327
+ const environment = temporaryIndexEnvironment(indexPath);
328
+ try {
329
+ git(input.workspacePath, ["read-tree", input.parent], `read workspace tree for the ${input.action}`, { environment });
330
+ const paths = input.paths.map(({ relativePath }) => `${input.workspaceRelativePath}/${relativePath}`);
331
+ const listed = git(
332
+ input.workspacePath,
333
+ ["ls-files", "-z", "--stage", "--", ...paths.map((entry) => `:(literal)${entry}`)],
334
+ `inspect modes for the ${input.action}`,
335
+ { environment }
336
+ );
337
+ const modes = /* @__PURE__ */ new Map();
338
+ for (const record of nulRecords(listed)) {
339
+ const match = /^([0-7]{6}) [0-9a-f]{40,64} [0-3]\t(.+)$/su.exec(record.toString());
340
+ if (match) modes.set(match[2], match[1]);
341
+ }
342
+ const records = input.paths.map(
343
+ ({ relativePath, blob }, index) => indexInfoRecord(modes.get(paths[index]) ?? "100644", requireObjectId(blob, `hydration ledger blob for ${relativePath}`), paths[index])
344
+ );
345
+ git(input.workspacePath, ["update-index", "-z", "--index-info"], `rewrite paths for the ${input.action}`, {
346
+ environment,
347
+ stdin: Buffer.concat(records)
348
+ });
349
+ const tree = requireObjectId(gitText(input.workspacePath, ["write-tree"], `write the ${input.action}`, { environment }), `${input.action} tree`);
350
+ return requireObjectId(
351
+ gitText(input.workspacePath, ["commit-tree", tree, "-p", input.parent, "-m", input.message], `commit the ${input.action}`),
352
+ `${input.action} commit`
353
+ );
354
+ } finally {
355
+ removeTemporaryIndex(indexPath);
257
356
  }
258
- return { stale: stale.sort(), cleared: cleared.sort() };
259
357
  }
260
358
  function nulRecords(content) {
261
359
  const records = [];
@@ -289,12 +387,14 @@ function graftMountTree(input) {
289
387
  stdin: removedPaths
290
388
  });
291
389
  }
292
- git(
293
- input.workspacePath,
294
- ["read-tree", "-i", `--prefix=${input.workspaceRelativePath}/`, input.mountTree],
295
- "graft workspace projection mount tree",
296
- { environment }
297
- );
390
+ if (input.mountTree !== null) {
391
+ git(
392
+ input.workspacePath,
393
+ ["read-tree", "-i", `--prefix=${input.workspaceRelativePath}/`, input.mountTree],
394
+ "graft workspace projection mount tree",
395
+ { environment }
396
+ );
397
+ }
298
398
  return requireObjectId(
299
399
  gitText(input.workspacePath, ["write-tree"], "write grafted workspace projection tree", { environment }),
300
400
  "Grafted workspace projection tree"
@@ -303,25 +403,41 @@ function graftMountTree(input) {
303
403
  removeTemporaryIndex(indexPath);
304
404
  }
305
405
  }
406
+ function mountSubtreeObjectId(workspacePath, revision, workspaceRelativePath, label) {
407
+ const spec = `${revision}:${workspaceRelativePath}`;
408
+ if (gitResult(workspacePath, ["cat-file", "-e", spec]).exitCode !== 0) return null;
409
+ const type = gitText(workspacePath, ["cat-file", "-t", spec], `inspect ${label}`);
410
+ if (type !== "tree") throw new Error(`${label} is not a directory at ${workspaceRelativePath}`);
411
+ return requireObjectId(gitText(workspacePath, ["rev-parse", spec], `resolve ${label}`), label);
412
+ }
306
413
  function synthesizeOursCommit(input) {
307
414
  const synthesized = synthesizeMountTree({
308
415
  workspacePath: input.workspacePath,
309
416
  sourcePath: input.mount.sourcePath,
310
417
  sourceMode: input.mount.sourceMode
311
418
  });
312
- const detected = staleRewritePaths({
419
+ const classified = classifyHydrationLedger({
313
420
  workspacePath: input.workspacePath,
314
421
  workspaceRelativePath: input.workspaceRelativePath,
315
422
  basisHead: input.basisHead,
316
423
  mountTree: synthesized.tree,
317
424
  blobs: synthesized.blobs,
318
- staleRewriteBlobs: input.staleRewriteBlobs
425
+ projectedFiles: synthesized.projectedFiles,
426
+ hydrationLedger: input.hydrationLedger
319
427
  });
320
- if (detected.stale.length > 0) return { staleRewritePaths: detected.stale, staleRewriteCleared: detected.cleared };
321
- const rootTree = graftMountTree({
428
+ if (classified.stale.length > 0) return { staleRewritePaths: classified.stale, staleRewriteCleared: classified.cleared };
429
+ const mergeBase = hydrationLedgerMergeBase({
322
430
  workspacePath: input.workspacePath,
323
431
  workspaceRelativePath: input.workspaceRelativePath,
324
432
  basisHead: input.basisHead,
433
+ merges: classified.merge,
434
+ mountId: input.mount.id,
435
+ attemptId: input.attemptId
436
+ });
437
+ const rootTree = graftMountTree({
438
+ workspacePath: input.workspacePath,
439
+ workspaceRelativePath: input.workspaceRelativePath,
440
+ basisHead: mergeBase,
325
441
  mountTree: synthesized.tree
326
442
  });
327
443
  const message = JSON.stringify({
@@ -332,12 +448,18 @@ function synthesizeOursCommit(input) {
332
448
  const oursCommit = requireObjectId(
333
449
  gitText(
334
450
  input.workspacePath,
335
- ["commit-tree", rootTree, "-p", input.basisHead, "-m", message],
451
+ ["commit-tree", rootTree, "-p", mergeBase, "-m", message],
336
452
  `commit synthesized workspace projection for mount ${input.mount.id}`
337
453
  ),
338
454
  `Synthesized workspace projection commit for mount ${input.mount.id}`
339
455
  );
340
- return { oursCommit, projectedFiles: synthesized.projectedFiles, staleRewriteCleared: detected.cleared };
456
+ return {
457
+ oursCommit,
458
+ mergeBase,
459
+ projectedFiles: synthesized.projectedFiles,
460
+ staleRewriteCleared: classified.cleared,
461
+ ledgerMerges: classified.merge
462
+ };
341
463
  }
342
464
  function mergeWorkspaceProjectionMount(input) {
343
465
  const support = workspaceMergeProjectionSupport();
@@ -352,31 +474,69 @@ function mergeWorkspaceProjectionMount(input) {
352
474
  workspaceRelativePath,
353
475
  basisHead,
354
476
  attemptId: input.attemptId,
355
- ...input.staleRewriteBlobs ? { staleRewriteBlobs: input.staleRewriteBlobs } : {}
477
+ ...input.hydrationLedger ? { hydrationLedger: input.hydrationLedger } : {}
356
478
  });
357
479
  if ("staleRewritePaths" in synthesized) {
358
480
  return { kind: "stale_rewrite", paths: synthesized.staleRewritePaths, staleRewriteCleared: synthesized.staleRewriteCleared };
359
481
  }
360
- const { oursCommit, projectedFiles, staleRewriteCleared } = synthesized;
482
+ const { oursCommit, mergeBase, projectedFiles, staleRewriteCleared, ledgerMerges } = synthesized;
483
+ const ledgerMergedPaths = ledgerMerges.map(({ relativePath }) => relativePath);
484
+ const theirsHead = hydrationLedgerTheirs({
485
+ workspacePath,
486
+ workspaceRelativePath,
487
+ currentHead,
488
+ merges: ledgerMerges,
489
+ mountId: input.mount.id,
490
+ attemptId: input.attemptId
491
+ });
361
492
  const readAtMs = Date.now();
493
+ const inboundMountTree = mountSubtreeObjectId(
494
+ workspacePath,
495
+ theirsHead,
496
+ workspaceRelativePath,
497
+ `current workspace head subtree for mount ${input.mount.id}`
498
+ );
499
+ const inboundCommit = requireObjectId(
500
+ gitText(
501
+ workspacePath,
502
+ [
503
+ "commit-tree",
504
+ graftMountTree({ workspacePath, workspaceRelativePath, basisHead: mergeBase, mountTree: inboundMountTree }),
505
+ "-p",
506
+ mergeBase,
507
+ "-m",
508
+ JSON.stringify({ type: "workspace_projection_inbound", mountId: input.mount.id, attemptId: input.attemptId })
509
+ ],
510
+ `commit inbound workspace projection for mount ${input.mount.id}`
511
+ ),
512
+ `Inbound workspace projection commit for mount ${input.mount.id}`
513
+ );
362
514
  const merged = gitResult(workspacePath, [
363
515
  "merge-tree",
364
516
  "--write-tree",
365
- `--merge-base=${basisHead}`,
517
+ `--merge-base=${mergeBase}`,
366
518
  "--name-only",
367
519
  "-z",
368
520
  "--no-messages",
369
521
  oursCommit,
370
- currentHead
522
+ inboundCommit
371
523
  ]);
372
524
  if (merged.exitCode !== 0 && merged.exitCode !== 1) {
373
525
  const detail = merged.stderr.toString().trim() || merged.stdout.toString().trim() || `git exited ${merged.exitCode}`;
374
526
  throw new Error(`Merge workspace projection for mount ${input.mount.id}: ${detail}`);
375
527
  }
376
528
  const records = nulRecords(merged.stdout);
377
- const resultTree = records.shift()?.toString() ?? "";
378
- requireObjectId(resultTree, `Merged workspace projection tree for mount ${input.mount.id}`);
379
- if (merged.exitCode === 0) return { kind: "clean", resultTree, oursCommit, projectedFiles, readAtMs, staleRewriteCleared };
529
+ const mergedTree = records.shift()?.toString() ?? "";
530
+ requireObjectId(mergedTree, `Merged workspace projection tree for mount ${input.mount.id}`);
531
+ if (merged.exitCode === 0) {
532
+ const resultTree = graftMountTree({
533
+ workspacePath,
534
+ workspaceRelativePath,
535
+ basisHead: currentHead,
536
+ mountTree: mountSubtreeObjectId(workspacePath, mergedTree, workspaceRelativePath, `merged subtree for mount ${input.mount.id}`)
537
+ });
538
+ return { kind: "clean", resultTree, oursCommit, projectedFiles, readAtMs, staleRewriteCleared, ledgerMergedPaths };
539
+ }
380
540
  const conflictPaths = records.map((record) => record.toString()).sort();
381
541
  return {
382
542
  kind: "conflict",
@@ -385,7 +545,8 @@ function mergeWorkspaceProjectionMount(input) {
385
545
  error: `Workspace projection for mount ${input.mount.id} conflicted with the current workspace head`,
386
546
  projectedFiles,
387
547
  readAtMs,
388
- staleRewriteCleared
548
+ staleRewriteCleared,
549
+ ledgerMergedPaths
389
550
  };
390
551
  }
391
552
  function materializeWorkspaceProjectionTree(input) {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import type { OperationEnvelope, OwnershipFence } from "@ricsam/r5d-api/runtime-protocol";
2
+ import { AdapterConfig, type ActionReceipt, type ExecutorCommand, type PollResult, type RunIdentity, type RunReceipt } from "./protocol";
3
+ /** Stateless one-request connections: disconnect/timeout never implies the operation did not happen. */
4
+ export declare class HostExecutorClient {
5
+ readonly config: ReturnType<typeof AdapterConfig.parse>;
6
+ constructor(config: AdapterConfig);
7
+ request<T = unknown>(command: ExecutorCommand): Promise<T>;
8
+ status(): Promise<unknown>;
9
+ setAuthority(fence: OwnershipFence, validUntil: number): Promise<unknown>;
10
+ start(operation: OperationEnvelope): Promise<RunReceipt>;
11
+ poll(run: RunIdentity, stdoutOffset?: number, stderrOffset?: number, maxBytes?: number): Promise<PollResult>;
12
+ write(run: RunIdentity, actionId: string, data: string, eof?: boolean): Promise<ActionReceipt>;
13
+ resize(run: RunIdentity, actionId: string, cols: number, rows: number): Promise<ActionReceipt>;
14
+ cancel(run: RunIdentity, actionId: string): Promise<ActionReceipt>;
15
+ }
@@ -0,0 +1,8 @@
1
+ import { HostExecutor } from "./executor";
2
+ import { type ExecutorConfig as Config } from "./protocol";
3
+ /** Start only from an independently supervised daemon entrypoint, never an adapter. */
4
+ export declare function startHostExecutor(input: Config): Promise<{
5
+ executor: HostExecutor;
6
+ socketPath: string;
7
+ close(): Promise<void>;
8
+ }>;
@@ -0,0 +1,30 @@
1
+ import { type RuntimeHello } from "@ricsam/r5d-api/runtime-protocol";
2
+ import { ExecutorConfig, type ResolvedConfig } from "./protocol";
3
+ import { ExecutorStore } from "./storage";
4
+ type PtyModule = typeof import("node-pty");
5
+ export declare const tokenHash: (token: string) => string;
6
+ /** Resource owner, never instantiated in a replaceable adapter. No legacy worker singleton imports. */
7
+ export declare class HostExecutor {
8
+ private readonly nativePty?;
9
+ readonly config: ResolvedConfig;
10
+ readonly store: ExecutorStore;
11
+ readonly hello: RuntimeHello;
12
+ private readonly active;
13
+ private readonly leaseTimer;
14
+ private closing;
15
+ private poisoned;
16
+ private serial;
17
+ constructor(config: ExecutorConfig, nativePty?: PtyModule | undefined);
18
+ /** Single serialized command boundary, including controller promotions and dedupe checks. */
19
+ handle(input: unknown): Promise<unknown>;
20
+ private authenticate;
21
+ private authority;
22
+ private workerFence;
23
+ private authorize;
24
+ private receipt;
25
+ private dispatch;
26
+ private output;
27
+ private finish;
28
+ close(): Promise<void>;
29
+ }
30
+ export {};
@@ -0,0 +1,5 @@
1
+ export { startHostExecutor } from "./daemon";
2
+ export { HostExecutorClient } from "./client";
3
+ export { tokenHash } from "./executor";
4
+ export { AdapterConfig, ExecutorConfig, ExecutorCommand, ExecutorError, EXECUTOR_CAPABILITY, ShellPayload } from "./protocol";
5
+ export type { ActionReceipt, PollResult, RunIdentity, RunReceipt } from "./protocol";