@fungi.computer/oyster 0.6.0 → 0.7.0

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.
Files changed (163) hide show
  1. package/README.md +132 -78
  2. package/dist/agent-archive-projection.d.ts +8 -0
  3. package/dist/agent-archive-projection.js +49 -0
  4. package/dist/agent-schedules.d.ts +50 -0
  5. package/dist/agent-schedules.js +17 -0
  6. package/dist/agents.d.ts +35 -14
  7. package/dist/agents.js +93 -144
  8. package/dist/app-builds.d.ts +7 -2
  9. package/dist/app-builds.js +19 -2
  10. package/dist/approval-capability.d.ts +1 -1
  11. package/dist/approval-capability.js +11 -16
  12. package/dist/approval-home.d.ts +2 -1
  13. package/dist/approval-home.js +5 -3
  14. package/dist/apps/catalog.js +1 -1
  15. package/dist/apps/gate.d.ts +4 -7
  16. package/dist/apps/gate.js +14 -29
  17. package/dist/apps/index.d.ts +1 -1
  18. package/dist/apps/installation.d.ts +4 -42
  19. package/dist/apps/installation.js +7 -75
  20. package/dist/apps/operations.js +0 -10
  21. package/dist/apps/permissions.d.ts +18 -8
  22. package/dist/apps/permissions.js +7 -1
  23. package/dist/auth.d.ts +1 -24
  24. package/dist/auth.js +9 -39
  25. package/dist/authorization.d.ts +307 -0
  26. package/dist/authorization.js +542 -0
  27. package/dist/boot-host-script.js +1 -1
  28. package/dist/build/build-jobs.d.ts +2 -2
  29. package/dist/build/candidate-builder.d.ts +2 -0
  30. package/dist/build/candidate-builder.js +5 -1
  31. package/dist/build/candidate-cli.js +3 -0
  32. package/dist/build/candidate-cli.mjs +59 -21
  33. package/dist/build/job-log.d.ts +23 -0
  34. package/dist/build/job-log.js +88 -0
  35. package/dist/build/local-build.d.ts +2 -0
  36. package/dist/build/local-build.js +11 -9
  37. package/dist/cloudflare.d.ts +4 -1
  38. package/dist/cloudflare.js +1 -1
  39. package/dist/files.js +9 -8
  40. package/dist/git-admission.d.ts +3 -3
  41. package/dist/git-admission.js +4 -6
  42. package/dist/git-door.d.ts +8 -7
  43. package/dist/git-door.js +18 -17
  44. package/dist/git.d.ts +1 -1
  45. package/dist/host.d.ts +33 -40
  46. package/dist/host.js +153 -272
  47. package/dist/installation-execution.js +10 -5
  48. package/dist/installation-operations.d.ts +4 -180
  49. package/dist/installation-operations.js +1 -5
  50. package/dist/installation-store-generation.d.ts +4 -1
  51. package/dist/installation-store-generation.js +59 -6
  52. package/dist/internal/delivery.js +0 -1
  53. package/dist/internal/host-gate.d.ts +3 -2
  54. package/dist/internal/host-gate.js +5 -18
  55. package/dist/internal/invocation-timing.d.ts +16 -0
  56. package/dist/internal/invocation-timing.js +23 -0
  57. package/dist/internal/registry-installations.js +1 -0
  58. package/dist/invocation.d.ts +15 -14
  59. package/dist/invocation.js +58 -26
  60. package/dist/keys.d.ts +2 -19
  61. package/dist/keys.js +1 -37
  62. package/dist/mcp.d.ts +15 -27
  63. package/dist/mcp.js +105 -155
  64. package/dist/page/agent-browser.d.ts +15 -0
  65. package/dist/page/agent-browser.js +162 -0
  66. package/dist/page/app-permissions-port.d.ts +3 -0
  67. package/dist/page/app-permissions-port.js +43 -0
  68. package/dist/page/app-permissions.d.ts +11 -0
  69. package/dist/page/app-permissions.js +296 -0
  70. package/dist/page/isolated-page-theme.d.ts +2 -0
  71. package/dist/page/isolated-page-theme.js +11 -0
  72. package/dist/page/mount.d.ts +2 -13
  73. package/dist/page/mount.js +7 -93
  74. package/dist/page-script.d.ts +0 -3
  75. package/dist/page-script.js +3 -4
  76. package/dist/standalone/agent-archive-owner.d.ts +42 -0
  77. package/dist/standalone/agent-archive-owner.js +116 -0
  78. package/dist/standalone/agent-archive-schedules.d.ts +6 -0
  79. package/dist/standalone/agent-archive-schedules.js +76 -0
  80. package/dist/standalone/agent-archive.d.ts +31 -0
  81. package/dist/standalone/agent-archive.js +67 -0
  82. package/dist/standalone/agent-computers.d.ts +7 -6
  83. package/dist/standalone/agent-computers.js +4 -7
  84. package/dist/standalone/agent-mail.d.ts +2 -2
  85. package/dist/standalone/agents.d.ts +157 -20
  86. package/dist/standalone/agents.js +234 -30
  87. package/dist/standalone/app-builds-binding.js +1 -1
  88. package/dist/standalone/app-set.js +2 -54
  89. package/dist/standalone/app-source.d.ts +3 -4
  90. package/dist/standalone/app-source.js +12 -9
  91. package/dist/standalone/builds.d.ts +2 -14
  92. package/dist/standalone/builds.js +15 -13
  93. package/dist/standalone/computer-fleet.d.ts +4 -4
  94. package/dist/standalone/computer-fleet.js +13 -42
  95. package/dist/standalone/computer.d.ts +4 -3
  96. package/dist/standalone/computer.js +52 -53
  97. package/dist/standalone/dashboard-approval.d.ts +10 -0
  98. package/dist/standalone/dashboard-approval.js +66 -0
  99. package/dist/standalone/machine-link.d.ts +4 -221
  100. package/dist/standalone/machine-link.js +21 -15
  101. package/dist/standalone/media.d.ts +1 -0
  102. package/dist/standalone/media.js +20 -14
  103. package/dist/standalone/owner-routes.d.ts +16 -0
  104. package/dist/standalone/owner-routes.js +21 -0
  105. package/dist/standalone/permission-resources.d.ts +5 -0
  106. package/dist/standalone/permission-resources.js +31 -0
  107. package/dist/standalone/registry.d.ts +7 -0
  108. package/dist/standalone/registry.js +3 -0
  109. package/dist/standalone/tenancy.d.ts +0 -3
  110. package/dist/standalone/tenancy.js +2 -5
  111. package/dist/standalone/worker-routes.d.ts +19 -0
  112. package/dist/standalone/worker-routes.js +9 -0
  113. package/dist/standalone/worker.d.ts +1 -1
  114. package/dist/standalone/worker.js +27 -35
  115. package/dist/standalone/workflow-binding.d.ts +8 -3
  116. package/dist/standalone/workflow-binding.js +14 -5
  117. package/dist/standalone/workflow-git-capture.js +2 -1
  118. package/dist/standalone/workflow-host.d.ts +8 -103
  119. package/dist/standalone/workflow-host.js +1 -1
  120. package/dist/standalone/workflow-node-capture.js +2 -2
  121. package/dist/standalone/workflow-root-authority.d.ts +1 -1
  122. package/dist/standalone/workflow-root-authority.js +1 -1
  123. package/dist/standalone/workflow-schedules.d.ts +14 -5
  124. package/dist/standalone/workflow-schedules.js +11 -5
  125. package/dist/standalone/workflow-webhooks.d.ts +4 -18
  126. package/dist/standalone/workflow-webhooks.js +5 -5
  127. package/dist/standalone/workflows.d.ts +36 -28
  128. package/dist/standalone/workflows.js +6 -3
  129. package/dist/storage-frame.d.ts +6 -6
  130. package/dist/testing/capabilities.d.ts +7 -6
  131. package/dist/testing/capabilities.js +3 -6
  132. package/dist/testing/installation.d.ts +0 -1
  133. package/dist/testing/installation.js +2 -5
  134. package/dist/workers-git.d.ts +12 -4
  135. package/dist/workers-git.js +38 -35
  136. package/dist/workers.d.ts +9 -1
  137. package/dist/workers.js +44 -8
  138. package/dist/workflows.d.ts +2 -0
  139. package/dist/workflows.js +1 -0
  140. package/docs/api/README.md +7 -0
  141. package/docs/api/agent-schedules.md +175 -0
  142. package/docs/api/agents.md +34 -13
  143. package/docs/api/app-storage.md +1 -1
  144. package/docs/api/apps.md +323 -225
  145. package/docs/api/auth.md +0 -55
  146. package/docs/api/authorization.md +1300 -0
  147. package/docs/api/build.md +40 -37
  148. package/docs/api/cloudflare.md +3 -3
  149. package/docs/api/git.md +20 -11
  150. package/docs/api/host.md +64 -192
  151. package/docs/api/index.md +1 -1
  152. package/docs/api/invocation.md +14 -14
  153. package/docs/api/keys.md +1 -98
  154. package/docs/api/mcp.md +43 -40
  155. package/docs/api/modules.md +2 -0
  156. package/docs/api/page/mount.md +16 -19
  157. package/docs/api/workers-git.md +46 -12
  158. package/docs/api/workers.md +124 -235
  159. package/package.json +39 -20
  160. package/dist/page/computer-target.d.ts +0 -3
  161. package/dist/page/computer-target.js +0 -9
  162. package/dist/standalone/channel-session-door.d.ts +0 -4
  163. package/dist/standalone/channel-session-door.js +0 -72
@@ -5,10 +5,10 @@ import { GraphWorkflow, type GraphRunEnding, type GraphWorkflowBindings } from "
5
5
  import { type GraphReview, type GraphParams, type NodeCall, type NodeEvent, type NodeDispatcher } from "@fungi.computer/braid";
6
6
  import { type StandaloneTenancyEnv } from "./tenancy.js";
7
7
  import type { AppEventFact } from "@fungi.computer/puffball/app-event-facts";
8
+ import { type ScheduleListInput } from "@fungi.computer/workflows-wire";
8
9
  import type { InvocationFacts } from "../invocation.js";
9
- import type { OysterPrincipal } from "../host.js";
10
10
  import type { OysterGitWorkerEnv, OysterGitWorkerHost } from "../workers-git.js";
11
- import { type TenantScope } from "../tenant.js";
11
+ import { type TenantPrincipal, type TenantScope } from "../tenant.js";
12
12
  import type { GitWriter, RepositoryRef } from "@fungi.computer/agar/git";
13
13
  import { type WorkflowGitCredential, type WorkflowGitPassGrant } from "@fungi.computer/braid/runtime";
14
14
  import { reviewKey, type EventDisarmInput, type WorkflowGitPassRequest, type TriggerArmTarget, type ScheduleDisarmInput } from "./workflow-binding.js";
@@ -86,10 +86,16 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
86
86
  } | {
87
87
  error: string;
88
88
  }>;
89
- schedules(facts: InvocationFacts): Promise<{
90
- error: string;
91
- schedules?: undefined;
89
+ pauseSchedule(facts: InvocationFacts, input: {
90
+ scheduleId: string;
91
+ }): Promise<{
92
+ kind: "not_found";
92
93
  } | {
94
+ kind: "paused";
95
+ } | {
96
+ error: string;
97
+ }>;
98
+ schedules(facts: InvocationFacts, input: ScheduleListInput): Promise<{
93
99
  schedules: {
94
100
  scheduleId: string;
95
101
  installationId: string;
@@ -99,7 +105,9 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
99
105
  state: "active" | "finished" | "paused" | "cancelled";
100
106
  lastError: string | null;
101
107
  }[];
102
- error?: undefined;
108
+ nextCursor: string | null;
109
+ } | {
110
+ error: string;
103
111
  }>;
104
112
  armEventTrigger(facts: InvocationFacts, input: TriggerArmTarget): Promise<{
105
113
  kind: "armed";
@@ -143,7 +151,7 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
143
151
  runId: string;
144
152
  workflowId: string | undefined;
145
153
  commit: string | undefined;
146
- source: "key" | "manual" | "agent" | "event" | "schedule";
154
+ source: "key" | "manual" | "agent" | "schedule" | "event";
147
155
  initiatedBy: Readonly<{
148
156
  kind: "member";
149
157
  memberId: string;
@@ -162,7 +170,7 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
162
170
  runId: string;
163
171
  workflowId: string | undefined;
164
172
  commit: string | undefined;
165
- source: "key" | "manual" | "agent" | "event" | "schedule";
173
+ source: "key" | "manual" | "agent" | "schedule" | "event";
166
174
  initiatedBy: Readonly<{
167
175
  kind: "member";
168
176
  memberId: string;
@@ -183,7 +191,7 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
183
191
  /** Signed traffic uses the native fetch interface; no sender principal is accepted. */
184
192
  fetch(request: Request): Promise<Response>;
185
193
  /** Root-authenticated owner RPC; command data cannot nominate an actor or pin. */
186
- webhookControl(principal: OysterPrincipal, installationId: string, raw: unknown): Promise<Response>;
194
+ webhookControl(principal: TenantPrincipal, installationId: string, raw: unknown): Promise<Response>;
187
195
  /**
188
196
  * The emitting App's Instance delivers each accepted event here. The
189
197
  * emitter's live Release must declare the event and its payload schema
@@ -271,13 +279,13 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
271
279
  readonly input: {
272
280
  readonly [x: string]: S.Json;
273
281
  };
282
+ readonly title: string;
274
283
  readonly apps: {
275
284
  readonly [x: string]: {
276
285
  readonly appId: string;
277
286
  readonly role?: string | undefined;
278
287
  };
279
288
  };
280
- readonly title: string;
281
289
  readonly nodes: readonly ({
282
290
  readonly id: string;
283
291
  readonly kind: "code";
@@ -315,19 +323,19 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
315
323
  } | {
316
324
  readonly id: string;
317
325
  readonly kind: "review";
318
- readonly timeout: number;
319
326
  readonly title: string;
327
+ readonly timeout: number;
320
328
  readonly reviewers: "runner" | "owners";
321
329
  readonly show: readonly {
322
330
  readonly node: string;
323
331
  readonly port: string;
324
332
  }[];
333
+ readonly detail?: string | undefined;
325
334
  readonly choices?: readonly {
326
335
  readonly label: string;
327
336
  readonly id: string;
328
337
  readonly description?: string | undefined;
329
338
  }[] | undefined;
330
- readonly detail?: string | undefined;
331
339
  } | {
332
340
  readonly id: string;
333
341
  readonly kind: "condition";
@@ -412,19 +420,19 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
412
420
  } | {
413
421
  readonly id: string;
414
422
  readonly kind: "review";
415
- readonly timeout: number;
416
423
  readonly title: string;
424
+ readonly timeout: number;
417
425
  readonly reviewers: "runner" | "owners";
418
426
  readonly show: readonly {
419
427
  readonly node: string;
420
428
  readonly port: string;
421
429
  }[];
430
+ readonly detail?: string | undefined;
422
431
  readonly choices?: readonly {
423
432
  readonly label: string;
424
433
  readonly id: string;
425
434
  readonly description?: string | undefined;
426
435
  }[] | undefined;
427
- readonly detail?: string | undefined;
428
436
  } | {
429
437
  readonly id: string;
430
438
  readonly kind: "condition";
@@ -477,10 +485,10 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
477
485
  readonly triggers?: readonly ({
478
486
  readonly id: string;
479
487
  readonly on: "webhook";
488
+ readonly when?: import("@fungi.computer/braid").Predicate | undefined;
480
489
  readonly input?: {
481
490
  readonly [x: string]: import("@fungi.computer/braid").Mapping;
482
491
  } | undefined;
483
- readonly when?: import("@fungi.computer/braid").Predicate | undefined;
484
492
  readonly title?: string | undefined;
485
493
  } | {
486
494
  readonly id: string;
@@ -495,14 +503,14 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
495
503
  readonly title?: string | undefined;
496
504
  } | {
497
505
  readonly id: string;
506
+ readonly app: string;
498
507
  readonly on: "event";
499
508
  readonly event: string;
500
- readonly app: string;
501
509
  readonly eventDigest: string;
510
+ readonly when?: import("@fungi.computer/braid").Predicate | undefined;
502
511
  readonly input?: {
503
512
  readonly [x: string]: import("@fungi.computer/braid").Mapping;
504
513
  } | undefined;
505
- readonly when?: import("@fungi.computer/braid").Predicate | undefined;
506
514
  readonly repository?: import("@fungi.computer/braid").Mapping | undefined;
507
515
  readonly title?: string | undefined;
508
516
  })[] | undefined;
@@ -581,7 +589,7 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
581
589
  readonly title: string;
582
590
  readonly status?: {
583
591
  readonly label: string;
584
- readonly tone: "success" | "error" | "info" | "warning" | "neutral" | "primary" | "secondary" | "accent";
592
+ readonly tone: "success" | "error" | "neutral" | "primary" | "secondary" | "accent" | "info" | "warning";
585
593
  } | undefined;
586
594
  readonly media?: {
587
595
  readonly path: string;
@@ -594,7 +602,7 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
594
602
  }[];
595
603
  } | undefined;
596
604
  readonly detail?: string | undefined;
597
- readonly tone?: "error" | "running" | "settled" | "attention" | "muted" | undefined;
605
+ readonly tone?: "error" | "attention" | "running" | "settled" | "muted" | undefined;
598
606
  } | undefined;
599
607
  } | {
600
608
  readonly kind: "refused";
@@ -628,8 +636,8 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
628
636
  outcome: "active" | "terminal" | "cancelled" | null;
629
637
  events: readonly {
630
638
  readonly runId: string;
631
- readonly state: "skipped" | "queued" | "running" | "failed" | "complete" | "waiting";
632
639
  readonly nodeId: string;
640
+ readonly state: "failed" | "running" | "complete" | "skipped" | "queued" | "waiting";
633
641
  readonly iteration: string;
634
642
  readonly attempt: number;
635
643
  readonly output?: {
@@ -647,7 +655,7 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
647
655
  readonly title: string;
648
656
  readonly status?: {
649
657
  readonly label: string;
650
- readonly tone: "success" | "error" | "info" | "warning" | "neutral" | "primary" | "secondary" | "accent";
658
+ readonly tone: "success" | "error" | "neutral" | "primary" | "secondary" | "accent" | "info" | "warning";
651
659
  } | undefined;
652
660
  readonly media?: {
653
661
  readonly path: string;
@@ -660,7 +668,7 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
660
668
  }[];
661
669
  } | undefined;
662
670
  readonly detail?: string | undefined;
663
- readonly tone?: "error" | "running" | "settled" | "attention" | "muted" | undefined;
671
+ readonly tone?: "error" | "attention" | "running" | "settled" | "muted" | undefined;
664
672
  } | undefined;
665
673
  readonly graphId?: string | undefined;
666
674
  readonly poll?: number | undefined;
@@ -693,7 +701,7 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
693
701
  openedAt: number;
694
702
  expiresAt: number;
695
703
  }[];
696
- status: "unknown" | "paused" | "queued" | "running" | "errored" | "terminated" | "complete" | "waiting" | "waitingForPause";
704
+ status: "unknown" | "running" | "complete" | "paused" | "queued" | "errored" | "terminated" | "waiting" | "waitingForPause";
697
705
  output: {} | null;
698
706
  error: {
699
707
  name: string;
@@ -712,15 +720,15 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
712
720
  reason: string;
713
721
  }>;
714
722
  /** Host-only MCP control reopens the configured owner's authority on every call. */
715
- control(principal: OysterPrincipal, operation: "status" | "cancel", runId: string): Promise<{
723
+ control(principal: TenantPrincipal, operation: "status" | "cancel", runId: string): Promise<{
716
724
  runId: string;
717
725
  planDigest: string;
718
726
  title: string;
719
727
  outcome: "active" | "terminal" | "cancelled" | null;
720
728
  events: readonly {
721
729
  readonly runId: string;
722
- readonly state: "skipped" | "queued" | "running" | "failed" | "complete" | "waiting";
723
730
  readonly nodeId: string;
731
+ readonly state: "failed" | "running" | "complete" | "skipped" | "queued" | "waiting";
724
732
  readonly iteration: string;
725
733
  readonly attempt: number;
726
734
  readonly output?: {
@@ -738,7 +746,7 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
738
746
  readonly title: string;
739
747
  readonly status?: {
740
748
  readonly label: string;
741
- readonly tone: "success" | "error" | "info" | "warning" | "neutral" | "primary" | "secondary" | "accent";
749
+ readonly tone: "success" | "error" | "neutral" | "primary" | "secondary" | "accent" | "info" | "warning";
742
750
  } | undefined;
743
751
  readonly media?: {
744
752
  readonly path: string;
@@ -751,7 +759,7 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
751
759
  }[];
752
760
  } | undefined;
753
761
  readonly detail?: string | undefined;
754
- readonly tone?: "error" | "running" | "settled" | "attention" | "muted" | undefined;
762
+ readonly tone?: "error" | "attention" | "running" | "settled" | "muted" | undefined;
755
763
  } | undefined;
756
764
  readonly graphId?: string | undefined;
757
765
  readonly poll?: number | undefined;
@@ -784,7 +792,7 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
784
792
  openedAt: number;
785
793
  expiresAt: number;
786
794
  }[];
787
- status: "unknown" | "paused" | "queued" | "running" | "errored" | "terminated" | "complete" | "waiting" | "waitingForPause";
795
+ status: "unknown" | "running" | "complete" | "paused" | "queued" | "errored" | "terminated" | "waiting" | "waitingForPause";
788
796
  output: {} | null;
789
797
  error: {
790
798
  name: string;
@@ -11,7 +11,7 @@ import { createStandaloneWorkflowEvents, } from "./workflow-events.js";
11
11
  import { createStandaloneWorkflowTriggerHost, standaloneTriggerErrors, } from "./workflow-trigger-host.js";
12
12
  import { createWorkflowPublishedDefinitionPort, readPublishedWorkflow, } from "@fungi.computer/workflows-wire";
13
13
  import { workflowAgentAuthority } from "../workflow-agent-authority.js";
14
- import { defaultTenant, tenantScope } from "../tenant.js";
14
+ import { defaultTenant, tenantScope, } from "../tenant.js";
15
15
  import { AppInstanceError } from "../index.js";
16
16
  import { createWorkflowGitPasses, createWorkflowGitPassStore, workflowGitPassWriter, workflowRunReport, wireRunInitiator, } from "@fungi.computer/braid/runtime";
17
17
  import { canonicalJson } from "@fungi.computer/braid";
@@ -403,8 +403,11 @@ export class StandaloneWorkflowRunsBase extends DurableObject {
403
403
  async disarmSchedule(facts, input) {
404
404
  return (await this.library(facts, "disarmSchedule")).schedules.disarm(facts, input);
405
405
  }
406
- async schedules(facts) {
407
- return (await this.library(facts, "schedules")).schedules.list(facts);
406
+ async pauseSchedule(facts, input) {
407
+ return (await this.library(facts, "pauseSchedule")).schedules.pause(facts, input);
408
+ }
409
+ async schedules(facts, input) {
410
+ return (await this.library(facts, "schedules")).schedules.list(facts, input);
408
411
  }
409
412
  async armEventTrigger(facts, input) {
410
413
  return (await this.library(facts, "armEventTrigger")).events.arm(facts, input);
@@ -8,7 +8,7 @@ export declare const storageFrameMethods: {
8
8
  readonly kind: "accepted";
9
9
  readonly value: {
10
10
  readonly value: Schema.Json;
11
- readonly source: "member" | "team" | "none";
11
+ readonly source: "team" | "member" | "none";
12
12
  readonly team: {
13
13
  readonly value: Schema.Json;
14
14
  readonly revision: number;
@@ -21,7 +21,7 @@ export declare const storageFrameMethods: {
21
21
  };
22
22
  } | {
23
23
  readonly kind: "rejected";
24
- readonly code: "unavailable" | "conflict" | "invalid" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded";
24
+ readonly code: "unavailable" | "invalid" | "conflict" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded";
25
25
  };
26
26
  requiredOperations: () => string[];
27
27
  };
@@ -29,7 +29,7 @@ export declare const storageFrameMethods: {
29
29
  decodeArgs: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => readonly [{
30
30
  readonly value: Schema.Json;
31
31
  readonly key: string;
32
- readonly scope: "member" | "team";
32
+ readonly scope: "team" | "member";
33
33
  readonly basedOn: number | null;
34
34
  }];
35
35
  decodeResult: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => {
@@ -39,14 +39,14 @@ export declare const storageFrameMethods: {
39
39
  };
40
40
  } | {
41
41
  readonly kind: "rejected";
42
- readonly code: "unavailable" | "conflict" | "invalid" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded";
42
+ readonly code: "unavailable" | "invalid" | "conflict" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded";
43
43
  };
44
44
  requiredOperations: () => string[];
45
45
  };
46
46
  reset: {
47
47
  decodeArgs: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => readonly [{
48
48
  readonly key: string;
49
- readonly scope: "member" | "team";
49
+ readonly scope: "team" | "member";
50
50
  readonly basedOn: number | null;
51
51
  }];
52
52
  decodeResult: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => {
@@ -56,7 +56,7 @@ export declare const storageFrameMethods: {
56
56
  };
57
57
  } | {
58
58
  readonly kind: "rejected";
59
- readonly code: "unavailable" | "conflict" | "invalid" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded";
59
+ readonly code: "unavailable" | "invalid" | "conflict" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded";
60
60
  };
61
61
  requiredOperations: () => string[];
62
62
  };
@@ -1,4 +1,3 @@
1
- import { Hono } from "hono";
2
1
  import { type AppCapabilityId } from "@fungi.computer/puffball/app";
3
2
  import { type OysterAgents } from "../agents.js";
4
3
  import type { LiveOysterKey } from "../keys.js";
@@ -16,16 +15,18 @@ export type CapabilityFixtureOptions = Readonly<{
16
15
  fileSource?: FileSource;
17
16
  /** `false` clears the owner's approval after install. */
18
17
  admitted?: boolean;
19
- /** Serve a public installation to an anonymous visitor. */
20
- visitor?: boolean;
21
18
  authenticate?: (request: Request) => Promise<OysterAuthResult>;
22
19
  agents?: OysterAgents;
23
20
  liveKey?: (keyId: string) => Promise<LiveOysterKey | null>;
24
21
  }>;
25
22
  /** One Oyster host with a single installation whose capability grants the options shape. */
26
23
  export declare function capabilityFixture(options?: CapabilityFixtureOptions): Promise<{
27
- app: Hono<import("../host.js").OysterEnv, import("hono/types").BlankSchema, "/">;
28
- page: () => Response | Promise<Response>;
29
- request: () => Response | Promise<Response>;
24
+ app: Readonly<{
25
+ fetch: (request: Request<unknown, CfProperties<unknown>>, env?: unknown, executionCtx?: import("hono").ExecutionContext | undefined) => Promise<Response>;
26
+ request: (input: string | Request<unknown, CfProperties<unknown>> | URL, requestInit?: RequestInit<CfProperties<unknown>> | undefined, Env?: unknown, executionCtx?: import("hono").ExecutionContext | undefined) => Promise<Response>;
27
+ servedPaths: () => readonly string[];
28
+ }>;
29
+ page: () => Promise<Response>;
30
+ request: () => Promise<Response>;
30
31
  removeDeclaration(): Promise<void>;
31
32
  }>;
@@ -1,4 +1,4 @@
1
- import { Hono } from "hono";
1
+ import { computer, routes as routeTable } from "../authorization.js";
2
2
  import * as Schema from "effect/Schema";
3
3
  import { releaseSchema } from "@fungi.computer/puffball/effect";
4
4
  import { parseArtifactRef, parseSha256Digest } from "@fungi.computer/puffball";
@@ -43,7 +43,7 @@ export async function capabilityFixture(options = {}) {
43
43
  },
44
44
  });
45
45
  }
46
- const routes = new Hono().post("/list", (context) => context.json({ entries: [] }));
46
+ const routes = routeTable().post("/list", computer(), (context) => context.json({ entries: [] }));
47
47
  const capabilities = options.adapters ??
48
48
  (options.fileSource
49
49
  ? fileCapabilities(options.fileSource)
@@ -80,16 +80,13 @@ export async function capabilityFixture(options = {}) {
80
80
  approval: null,
81
81
  },
82
82
  };
83
- // An anonymous reader reaches only a public App, as the host's visitor.
84
- if (options.visitor)
85
- installation = { ...installation, visibility: "public" };
86
83
  const readOnly = () => Promise.reject(new Error("Fixture installations are read-only"));
87
84
  let base = {
88
85
  bootUi: () => Promise.resolve({ kind: "passed" }),
89
86
  auth: {
90
87
  resolve: () => Promise.reject(new Error("Capability fixture resolves no actor")),
91
88
  authenticate: options.authenticate ??
92
- ((request) => Promise.resolve(options.visitor || request.headers.get("x-who") === "visitor"
89
+ ((request) => Promise.resolve(request.headers.get("x-who") === "anonymous"
93
90
  ? { kind: "refused", status: 401 }
94
91
  : {
95
92
  kind: "admitted",
@@ -7,5 +7,4 @@ export declare function approvedInstallation(input: Readonly<{
7
7
  installationId: string;
8
8
  instanceId: Installation["instanceId"];
9
9
  descriptors?: readonly CapabilityOperationDescriptor[];
10
- visibility?: Installation["visibility"];
11
10
  }>): Promise<Installation>;
@@ -30,7 +30,7 @@ export async function approvedInstallation(input) {
30
30
  grant: { operations, resources: "tenant-scope" },
31
31
  }));
32
32
  if (changes.length === 0)
33
- return { ...installed, visibility: input.visibility ?? "private" };
33
+ return installed;
34
34
  const approved = applyInstallationCommand(installed, { kind: "change-grants", changes }, {
35
35
  by: { kind: "member", memberId: "fixture-owner" },
36
36
  role: "owner",
@@ -42,8 +42,5 @@ export async function approvedInstallation(input) {
42
42
  });
43
43
  if (Result.isFailure(approved))
44
44
  throw new Error(approved.failure.code);
45
- return {
46
- ...approved.success.next,
47
- visibility: input.visibility ?? "private",
48
- };
45
+ return approved.success.next;
49
46
  }
@@ -6,7 +6,6 @@ import { type OysterTenancy, type ResolvedTenant, type TenantScope } from "./ten
6
6
  */
7
7
  import { DurableObject } from "cloudflare:workers";
8
8
  import { type InvocationFrame } from "./invocation.js";
9
- import { Hono } from "hono";
10
9
  import { type GitBinding } from "@fungi.computer/agar/git";
11
10
  import { type GitDoorTarget, type StandardWebhookPayload } from "./git.js";
12
11
  import { type KeyStore } from "@fungi.computer/ear-tag/keys";
@@ -17,6 +16,7 @@ import { type ArtifactsNamespace } from "@fungi.computer/burrow";
17
16
  import { type HostEventSource, type HostRecipientEvent, type RecipientDelivery } from "./event-recipients.js";
18
17
  export type { HostEventSource, HostRecipientEvent, RecipientDelivery, } from "./event-recipients.js";
19
18
  import { type OysterAuth, type OysterCapability, type OysterEnv, type OysterGitHost, type OysterHost } from "./host.js";
19
+ import { type Routes } from "./authorization.js";
20
20
  import type { GitWriter } from "@fungi.computer/agar/git";
21
21
  import { OysterInstance, type OysterBindingContext, type OysterBindingRow, type OysterBindingTable, type OysterWorkspace, type OysterWorkerEnv, type OysterInvocationHost } from "./workers.js";
22
22
  /** The GIT binding plus the credential only the host's git door may use. */
@@ -167,7 +167,7 @@ declare const OysterBinding_base: abstract new (ctx: ExecutionContext, env: Oyst
167
167
  export declare class OysterBinding extends OysterBinding_base {
168
168
  }
169
169
  /** Owner routes for keys and webhooks; `createOyster` mounts them owner-only. */
170
- export declare function gitAdminRoutes(env: OysterGitEnv, scope: TenantScope): Hono<OysterEnv>;
170
+ export declare function gitAdminRoutes(env: OysterGitEnv, scope: TenantScope): Routes<OysterEnv>;
171
171
  /** A standalone Oyster Worker that also hosts git Apps. */
172
172
  export type OysterGitWorkerEnv = OysterWorkerEnv & OysterGitEnv & Readonly<{
173
173
  /** This Oyster's public origin, for clone URLs (e.g. https://oyster.example). */
@@ -191,6 +191,8 @@ export declare abstract class OysterGitInstance<Env extends OysterGitWorkerEnv =
191
191
  * `oysterGitRoot(options).hostFor`.
192
192
  */
193
193
  export type OysterGitWorkerOptions<Env extends OysterGitWorkerEnv> = Readonly<{
194
+ /** Picker facts through this host's existing, owner-admitted directories. */
195
+ permissionResources?: (env: Env, scope: TenantScope) => NonNullable<OysterHost["permissionResources"]>;
194
196
  /** Host-owned fleet control, admitted on every invocation by its durable owner. */
195
197
  computerControl?: (env: Env, scope: TenantScope) => NonNullable<OysterHost["computerControl"]>;
196
198
  /** Workflow controls supplied by the host's existing run owner. */
@@ -216,7 +218,7 @@ export type OysterGitWorkerOptions<Env extends OysterGitWorkerEnv> = Readonly<{
216
218
  auth: OysterAuth;
217
219
  }>) => OysterWorkspace | Promise<OysterWorkspace>;
218
220
  /** Additional capabilities share the exact workspace selected for the base host. */
219
- capabilities?: (env: Env, workspace: OysterWorkspace, keysLive: OysterKeysLivePort) => readonly OysterCapability[];
221
+ capabilities?: (env: Env, workspace: OysterWorkspace) => readonly OysterCapability[];
220
222
  /** Admit a presented Workflow Git credential for one repository and scope;
221
223
  * omit when this host issues no Workflow Git credentials. */
222
224
  workflowGitPass?: (env: Env, scope: TenantScope, pass: string, target: GitDoorTarget) => Promise<Extract<GitWriter, {
@@ -227,7 +229,7 @@ export type OysterGitWorkerOptions<Env extends OysterGitWorkerEnv> = Readonly<{
227
229
  kind: "actor";
228
230
  }> | null>;
229
231
  /** Further owner-only routes this host serves, after the owner guard. */
230
- ownerRoutes?: (env: Env, scope: TenantScope) => Hono<OysterEnv>;
232
+ ownerRoutes?: (env: Env, scope: TenantScope) => Routes<OysterEnv>;
231
233
  }>;
232
234
  /** A tenant's whole Git host: the App server, `host.git` and the owner routes. */
233
235
  export type OysterGitWorkerHost = OysterHost & Readonly<{
@@ -278,5 +280,11 @@ export declare function oysterWorkerGit<Env extends OysterGitWorkerEnv>(env: Env
278
280
  * `oysterGitRoot(options).hostFor`; a cold directory passes its own resolved tenant.
279
281
  */
280
282
  export declare function oysterGitWorkerHost<Env extends OysterGitWorkerEnv>(env: Env, options: OysterGitWorkerOptions<Env>, selected: ResolvedTenant, request?: Request): Promise<OysterGitWorkerHost>;
283
+ /**
284
+ * A Git host's whole owner table: keys and webhooks, the root's own owner
285
+ * routes, and Agent approval and MCP server custody. The host and the
286
+ * authorization law both compose it here.
287
+ */
288
+ export declare function oysterGitOwnerRoutes(env: OysterGitEnv, scope: TenantScope, hostOwnerRoutes: Routes<OysterEnv> | undefined, agents: OysterAgents | undefined): Routes<OysterEnv>;
281
289
  /** Deploy the MCP owner from the same root as createOysterGitWorker. */
282
290
  export declare function createOysterGitMcpOwner<Env extends OysterGitWorkerEnv>(root: OysterGitRoot<Env>): new (ctx: DurableObjectState, env: Env) => import("./mcp-owner.js").OysterMcpOwner<Env>;
@@ -13,7 +13,6 @@ import { APP_INVOCATION_CEILING_MS, invocationFacts, invocationCallerContext, }
13
13
  import { AppInstanceError, selectionOf } from "./index.js";
14
14
  import { ownerOrKeyAuth } from "./auth.js";
15
15
  import { causeClass } from "./cause-class.js";
16
- import { Hono } from "hono";
17
16
  import { HTTPException } from "hono/http-exception";
18
17
  import * as Schema from "effect/Schema";
19
18
  import { defineIdFamily } from "@fungi.computer/cairn";
@@ -26,7 +25,8 @@ import { agentCapabilities, agentApprovalRoutes, agentMcpServerRoutes, } from ".
26
25
  import { createArtifactsRestNamespace, } from "@fungi.computer/burrow";
27
26
  import { attemptWebhook } from "./webhook-delivery.js";
28
27
  import { createRecipientDeliveries, } from "./event-recipients.js";
29
- import { createOyster, oysterPrincipalIsOwner, } from "./host.js";
28
+ import { createOyster, } from "./host.js";
29
+ import { oysterPrincipalIsOwner, owner as ownerRoute, routes as routeTable, } from "./authorization.js";
30
30
  import { OysterInstance, oysterBindingEntrypoint, oysterBindings, oysterWorkerHost, r2FileSource, r2ArtifactStore, serveOysterBootCapability, } from "./workers.js";
31
31
  import { registryInstallations } from "./internal/registry-installations.js";
32
32
  import { refuseOlderOysterStore } from "./older-oyster-store-refusal.js";
@@ -763,36 +763,29 @@ const subscribeSchema = Schema.Struct({
763
763
  secret: Schema.optional(Schema.String.check(Schema.isMinLength(16))),
764
764
  });
765
765
  const keyNamePattern = /^[a-z0-9][a-z0-9-]{0,39}$/u;
766
- /** `invocation.ts:486` uses `team-key:visitor` for anonymous readers; a key can never claim that name. */
767
- const reservedKeyName = "visitor";
768
766
  const keysWire = keyWire(oysterKeyVocabulary);
769
767
  /** Owner routes for keys and webhooks; `createOyster` mounts them owner-only. */
770
768
  export function gitAdminRoutes(env, scope) {
771
- const routes = new Hono();
769
+ const routes = routeTable();
772
770
  const keys = () => env.KEYS.getByName(scope.durableName("keys"));
773
- routes.get("/api/keys", async (context) => {
774
- const owner = context.get("principal");
775
- if (!oysterPrincipalIsOwner(owner))
776
- return Response.json({ error: "Owner only" }, { status: 403 });
777
- const listed = await keys().list(owner.actor.memberId);
771
+ // Key and webhook custody is the owner's own, never through an App.
772
+ const owner = ownerRoute("direct");
773
+ routes.get("/api/keys", owner, async (_context, { principal }) => {
774
+ const listed = await keys().list(principal.actor.memberId);
778
775
  if (listed.kind === "error")
779
776
  return Response.json({ error: listed.error.code }, { status: 500 });
780
777
  return Response.json({ keys: listed.value.map(keysWire.toSummary) });
781
778
  });
782
- routes.post("/api/keys", async (context) => {
779
+ routes.post("/api/keys", owner, async (context, { principal }) => {
783
780
  const body = keysWire.issue.safeParse(await context.req.json());
784
781
  if (!body.success)
785
782
  return Response.json({ error: "Invalid key" }, { status: 400 });
786
783
  // Named identities and rate policy belong to this host, not the wire grammar.
787
784
  if (!keyNamePattern.test(body.data.name) ||
788
- body.data.name === reservedKeyName ||
789
785
  body.data.keyId !== undefined ||
790
786
  body.data.rateLimit !== undefined)
791
787
  return Response.json({ error: "Invalid key name" }, { status: 400 });
792
- const owner = context.get("principal");
793
- if (!oysterPrincipalIsOwner(owner))
794
- return Response.json({ error: "Owner only" }, { status: 403 });
795
- const issued = await keys().issue(owner.actor.memberId, {
788
+ const issued = await keys().issue(principal.actor.memberId, {
796
789
  name: body.data.name,
797
790
  grants: body.data.grants,
798
791
  expiresAt: body.data.expiresAt,
@@ -804,19 +797,16 @@ export function gitAdminRoutes(env, scope) {
804
797
  key: keysWire.toSummary(issued.value.record),
805
798
  }, { status: 201 });
806
799
  });
807
- routes.post("/api/keys/revoke", async (context) => {
800
+ routes.post("/api/keys/revoke", owner, async (context, { principal }) => {
808
801
  const body = keysWire.revoke.safeParse(await context.req.json());
809
802
  if (!body.success)
810
803
  return Response.json({ error: "Invalid request" }, { status: 400 });
811
- const owner = context.get("principal");
812
- if (!oysterPrincipalIsOwner(owner))
813
- return Response.json({ error: "Owner only" }, { status: 403 });
814
- const revoked = await keys().revoke(owner.actor.memberId, body.data.requestId, body.data.keyId);
804
+ const revoked = await keys().revoke(principal.actor.memberId, body.data.requestId, body.data.keyId);
815
805
  if (revoked.kind === "error")
816
806
  return Response.json({ error: revoked.error.code }, { status: revoked.error.code === "denied" ? 404 : 400 });
817
807
  return Response.json({ key: keysWire.toSummary(revoked.value) });
818
808
  });
819
- routes.post("/api/webhooks", async (context) => {
809
+ routes.post("/api/webhooks", owner, async (context) => {
820
810
  const body = Schema.decodeUnknownOption(subscribeSchema)(await context.req.json());
821
811
  if (body._tag === "None")
822
812
  return Response.json({ error: "Invalid subscription" }, { status: 400 });
@@ -825,9 +815,9 @@ export function gitAdminRoutes(env, scope) {
825
815
  types: body.value.types ?? [],
826
816
  }), { status: 201 });
827
817
  });
828
- routes.get("/api/webhooks", async () => Response.json(await env.WEBHOOKS.getByName(scope.durableName("webhooks")).subscriptions()));
829
- routes.get("/api/webhooks/:id/deliveries", async (context) => Response.json(await env.WEBHOOKS.getByName(scope.durableName("webhooks")).deliveries(context.req.param("id"))));
830
- routes.delete("/api/webhooks/:id", async (context) => (await env.WEBHOOKS.getByName(scope.durableName("webhooks")).unsubscribe(context.req.param("id")))
818
+ routes.get("/api/webhooks", owner, async () => Response.json(await env.WEBHOOKS.getByName(scope.durableName("webhooks")).subscriptions()));
819
+ routes.get("/api/webhooks/:id/deliveries", owner, async (context) => Response.json(await env.WEBHOOKS.getByName(scope.durableName("webhooks")).deliveries(context.req.param("id"))));
820
+ routes.delete("/api/webhooks/:id", owner, async (context) => (await env.WEBHOOKS.getByName(scope.durableName("webhooks")).unsubscribe(context.req.param("id")))
831
821
  ? new Response(null, { status: 204 })
832
822
  : Response.json({ error: "Not found" }, { status: 404 }));
833
823
  return routes;
@@ -1136,8 +1126,8 @@ export async function oysterGitWorkerHost(env, options, selected, request) {
1136
1126
  const base = oysterWorkerHost(env, auth, policyOptions);
1137
1127
  const capabilities = [
1138
1128
  ...(base.capabilities ?? []),
1139
- ...(agents === undefined ? [] : agentCapabilities(agents, keysLive)),
1140
- ...(options.capabilities?.(env, workspace, keysLive) ?? []),
1129
+ ...(agents === undefined ? [] : agentCapabilities(agents)),
1130
+ ...(options.capabilities?.(env, workspace) ?? []),
1141
1131
  ];
1142
1132
  let gitOptions = { scope };
1143
1133
  if (options.workflowGitPass !== undefined)
@@ -1145,14 +1135,7 @@ export async function oysterGitWorkerHost(env, options, selected, request) {
1145
1135
  if (options.buildGitPass !== undefined)
1146
1136
  gitOptions = { ...gitOptions, buildGitPass: options.buildGitPass };
1147
1137
  const git = oysterWorkerGit(env, gitOptions);
1148
- const ownerRoutes = gitAdminRoutes(env, scope);
1149
- const hostOwnerRoutes = options.ownerRoutes?.(env, scope);
1150
- if (hostOwnerRoutes !== undefined)
1151
- ownerRoutes.route("/", hostOwnerRoutes);
1152
- if (agents !== undefined) {
1153
- ownerRoutes.route("/", agentApprovalRoutes(agents));
1154
- ownerRoutes.route("/", agentMcpServerRoutes(agents));
1155
- }
1138
+ const ownerRoutes = oysterGitOwnerRoutes(env, scope, options.ownerRoutes?.(env, scope), agents);
1156
1139
  let host = {
1157
1140
  ...base,
1158
1141
  capabilities,
@@ -1168,8 +1151,28 @@ export async function oysterGitWorkerHost(env, options, selected, request) {
1168
1151
  host = { ...host, computerControl: options.computerControl(env, scope) };
1169
1152
  if (options.homeInstallationId !== undefined)
1170
1153
  host = { ...host, homeInstallationId: options.homeInstallationId(env) };
1154
+ if (options.permissionResources !== undefined)
1155
+ host = {
1156
+ ...host,
1157
+ permissionResources: options.permissionResources(env, scope),
1158
+ };
1171
1159
  return host;
1172
1160
  }
1161
+ /**
1162
+ * A Git host's whole owner table: keys and webhooks, the root's own owner
1163
+ * routes, and Agent approval and MCP server custody. The host and the
1164
+ * authorization law both compose it here.
1165
+ */
1166
+ export function oysterGitOwnerRoutes(env, scope, hostOwnerRoutes, agents) {
1167
+ const ownerRoutes = gitAdminRoutes(env, scope);
1168
+ if (hostOwnerRoutes !== undefined)
1169
+ ownerRoutes.include(hostOwnerRoutes);
1170
+ if (agents !== undefined) {
1171
+ ownerRoutes.include(agentApprovalRoutes(agents));
1172
+ ownerRoutes.include(agentMcpServerRoutes(agents));
1173
+ }
1174
+ return ownerRoutes;
1175
+ }
1173
1176
  /**
1174
1177
  * Authenticate against the pinned tenant. A request admitted at the Worker's
1175
1178
  * top reuses that one admission; any other entry resolves its own.