@fungi.computer/oyster 0.0.0-stage → 0.0.1

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 (242) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1082 -2
  3. package/dist/agents.d.ts +114 -0
  4. package/dist/agents.js +322 -0
  5. package/dist/app-builds.d.ts +151 -0
  6. package/dist/app-builds.js +355 -0
  7. package/dist/app-sandbox.d.ts +2 -0
  8. package/dist/app-sandbox.js +2 -0
  9. package/dist/app-sources.d.ts +46 -0
  10. package/dist/app-sources.js +335 -0
  11. package/dist/app-storage.d.ts +141 -0
  12. package/dist/app-storage.js +281 -0
  13. package/dist/apps/gate.d.ts +105 -0
  14. package/dist/apps/gate.js +273 -0
  15. package/dist/apps/index.d.ts +5 -0
  16. package/dist/apps/index.js +5 -0
  17. package/dist/apps/installation.d.ts +248 -0
  18. package/dist/apps/installation.js +307 -0
  19. package/dist/apps/operations.d.ts +8 -0
  20. package/dist/apps/operations.js +98 -0
  21. package/dist/apps/tools.d.ts +33 -0
  22. package/dist/apps/tools.js +17 -0
  23. package/dist/auth.d.ts +11 -0
  24. package/dist/auth.js +93 -0
  25. package/dist/backend-request.d.ts +3 -0
  26. package/dist/backend-request.js +53 -0
  27. package/dist/build/build-jobs.d.ts +128 -0
  28. package/dist/build/build-jobs.js +197 -0
  29. package/dist/build/build-step.d.ts +26 -0
  30. package/dist/build/build-step.js +20 -0
  31. package/dist/build/bundled-html.d.ts +7 -0
  32. package/dist/build/bundled-html.js +64 -0
  33. package/dist/build/definition-emission.d.ts +34 -0
  34. package/dist/build/definition-emission.js +201 -0
  35. package/dist/build/effect.d.ts +231 -0
  36. package/dist/build/effect.js +396 -0
  37. package/dist/build/git-environment.d.ts +6 -0
  38. package/dist/build/git-environment.js +48 -0
  39. package/dist/build/html-recipe.d.ts +20 -0
  40. package/dist/build/html-recipe.js +51 -0
  41. package/dist/build/local-build.d.ts +57 -0
  42. package/dist/build/local-build.js +1078 -0
  43. package/dist/build/r2-artifact-writer.d.ts +31 -0
  44. package/dist/build/r2-artifact-writer.js +179 -0
  45. package/dist/build/snapshot-package-store.d.ts +2 -0
  46. package/dist/build/snapshot-package-store.js +21 -0
  47. package/dist/build/snapshot-program-scripts.d.ts +6 -0
  48. package/dist/build/snapshot-program-scripts.js +2 -0
  49. package/dist/build/submission-contract.d.ts +384 -0
  50. package/dist/build/submission-contract.js +70 -0
  51. package/dist/build/workspace-build.d.ts +50 -0
  52. package/dist/build/workspace-build.js +156 -0
  53. package/dist/build-contract.d.ts +4 -0
  54. package/dist/build-contract.js +4 -0
  55. package/dist/build-jobs.d.ts +2 -0
  56. package/dist/build-jobs.js +2 -0
  57. package/dist/build.d.ts +11 -0
  58. package/dist/build.js +13 -0
  59. package/dist/capability-descriptor.d.ts +46 -0
  60. package/dist/capability-descriptor.js +51 -0
  61. package/dist/catalog-frame.d.ts +39 -0
  62. package/dist/catalog-frame.js +72 -0
  63. package/dist/cause-class.d.ts +19 -0
  64. package/dist/cause-class.js +54 -0
  65. package/dist/cloudflare.d.ts +123 -0
  66. package/dist/cloudflare.js +391 -0
  67. package/dist/computer-terminal.d.ts +38 -0
  68. package/dist/computer-terminal.js +410 -0
  69. package/dist/computer.d.ts +60 -0
  70. package/dist/computer.js +33 -0
  71. package/dist/event-recipients.d.ts +57 -0
  72. package/dist/event-recipients.js +144 -0
  73. package/dist/facet-invocation.d.ts +4 -0
  74. package/dist/facet-invocation.js +45 -0
  75. package/dist/files.d.ts +42 -0
  76. package/dist/files.js +123 -0
  77. package/dist/frame-binding-wire-script.d.ts +2 -0
  78. package/dist/frame-binding-wire-script.js +1 -0
  79. package/dist/frame-binding-wire.d.ts +28 -0
  80. package/dist/frame-binding-wire.js +54 -0
  81. package/dist/git-admission.d.ts +52 -0
  82. package/dist/git-admission.js +188 -0
  83. package/dist/git-auth.d.ts +11 -0
  84. package/dist/git-auth.js +27 -0
  85. package/dist/git-door.d.ts +35 -0
  86. package/dist/git-door.js +108 -0
  87. package/dist/git-journal.d.ts +31 -0
  88. package/dist/git-journal.js +188 -0
  89. package/dist/git.d.ts +31 -0
  90. package/dist/git.js +64 -0
  91. package/dist/guest-policy.d.ts +9 -0
  92. package/dist/guest-policy.js +13 -0
  93. package/dist/host.d.ts +244 -0
  94. package/dist/host.js +1043 -0
  95. package/dist/index.d.ts +166 -0
  96. package/dist/index.js +320 -0
  97. package/dist/installation-consent.d.ts +79 -0
  98. package/dist/installation-consent.js +82 -0
  99. package/dist/internal/background-origins.d.ts +3 -0
  100. package/dist/internal/background-origins.js +8 -0
  101. package/dist/internal/delivery.d.ts +12 -0
  102. package/dist/internal/delivery.js +167 -0
  103. package/dist/internal/host-gate.d.ts +14 -0
  104. package/dist/internal/host-gate.js +62 -0
  105. package/dist/internal/registry-installations.d.ts +6 -0
  106. package/dist/internal/registry-installations.js +12 -0
  107. package/dist/invocation.d.ts +251 -0
  108. package/dist/invocation.js +377 -0
  109. package/dist/keys.d.ts +48 -0
  110. package/dist/keys.js +52 -0
  111. package/dist/mcp-owner.d.ts +27 -0
  112. package/dist/mcp-owner.js +143 -0
  113. package/dist/mcp.d.ts +89 -0
  114. package/dist/mcp.js +429 -0
  115. package/dist/media.d.ts +23 -0
  116. package/dist/media.js +12 -0
  117. package/dist/pack.d.ts +76 -0
  118. package/dist/pack.js +134 -0
  119. package/dist/page/capabilities.d.ts +23 -0
  120. package/dist/page/capabilities.js +256 -0
  121. package/dist/page/computer-handles.d.ts +79 -0
  122. package/dist/page/computer-handles.js +44 -0
  123. package/dist/page/lifetime.d.ts +6 -0
  124. package/dist/page/lifetime.js +19 -0
  125. package/dist/page/mount.d.ts +163 -0
  126. package/dist/page/mount.js +390 -0
  127. package/dist/page/storage.d.ts +3 -0
  128. package/dist/page/storage.js +82 -0
  129. package/dist/page/terminal.d.ts +5 -0
  130. package/dist/page/terminal.js +7 -0
  131. package/dist/page-script.d.ts +14 -0
  132. package/dist/page-script.js +5 -0
  133. package/dist/r2-files.d.ts +3 -0
  134. package/dist/r2-files.js +79 -0
  135. package/dist/release.d.ts +72 -0
  136. package/dist/release.js +164 -0
  137. package/dist/root-data.d.ts +86 -0
  138. package/dist/root-data.js +69 -0
  139. package/dist/shell/app-directory.d.ts +537 -0
  140. package/dist/shell/app-directory.js +195 -0
  141. package/dist/shell/browser-actions.d.ts +7 -0
  142. package/dist/shell/browser-actions.js +56 -0
  143. package/dist/shell/command-host.d.ts +112 -0
  144. package/dist/shell/command-host.js +502 -0
  145. package/dist/shell/commands.d.ts +25 -0
  146. package/dist/shell/commands.js +78 -0
  147. package/dist/shell/index.d.ts +8 -0
  148. package/dist/shell/index.js +8 -0
  149. package/dist/shell/keys.d.ts +41 -0
  150. package/dist/shell/keys.js +134 -0
  151. package/dist/shell/shell-module.d.ts +31 -0
  152. package/dist/shell/shell-module.js +37 -0
  153. package/dist/shell/window-manager.d.ts +124 -0
  154. package/dist/shell/window-manager.js +711 -0
  155. package/dist/shell/workbench-contract.d.ts +68 -0
  156. package/dist/shell/workbench-contract.js +1 -0
  157. package/dist/shell/workbench-host-types.d.ts +96 -0
  158. package/dist/shell/workbench-host-types.js +1 -0
  159. package/dist/shell/workbench-host.d.ts +34 -0
  160. package/dist/shell/workbench-host.js +347 -0
  161. package/dist/shell/workbench-model.d.ts +192 -0
  162. package/dist/shell/workbench-model.js +53 -0
  163. package/dist/shell/workbench.d.ts +4 -0
  164. package/dist/shell/workbench.js +2 -0
  165. package/dist/source/fork.d.ts +27 -0
  166. package/dist/source/fork.js +46 -0
  167. package/dist/source/index.d.ts +11 -0
  168. package/dist/source/index.js +11 -0
  169. package/dist/source/seed-receipt.d.ts +53 -0
  170. package/dist/source/seed-receipt.js +18 -0
  171. package/dist/source/team-source.d.ts +32 -0
  172. package/dist/source/team-source.js +42 -0
  173. package/dist/source/template-repository.d.ts +2 -0
  174. package/dist/source/template-repository.js +8 -0
  175. package/dist/source/template-write.d.ts +69 -0
  176. package/dist/source/template-write.js +344 -0
  177. package/dist/storage-frame.d.ts +64 -0
  178. package/dist/storage-frame.js +28 -0
  179. package/dist/tenant.d.ts +81 -0
  180. package/dist/tenant.js +71 -0
  181. package/dist/testing/capabilities.d.ts +29 -0
  182. package/dist/testing/capabilities.js +130 -0
  183. package/dist/testing/catalog.d.ts +3 -0
  184. package/dist/testing/catalog.js +53 -0
  185. package/dist/testing/index.d.ts +10 -0
  186. package/dist/testing/index.js +10 -0
  187. package/dist/testing/installation.d.ts +11 -0
  188. package/dist/testing/installation.js +25 -0
  189. package/dist/testing/native-worker.d.ts +21 -0
  190. package/dist/testing/native-worker.js +106 -0
  191. package/dist/testing/worker.d.ts +18 -0
  192. package/dist/testing/worker.js +67 -0
  193. package/dist/testing/workers-runtime.d.ts +24 -0
  194. package/dist/testing/workers-runtime.js +33 -0
  195. package/dist/webhook-delivery.d.ts +14 -0
  196. package/dist/webhook-delivery.js +17 -0
  197. package/dist/workers-git.d.ts +285 -0
  198. package/dist/workers-git.js +1174 -0
  199. package/dist/workers.d.ts +352 -0
  200. package/dist/workers.js +866 -0
  201. package/docs/api/README.md +82 -0
  202. package/docs/api/agents.md +332 -0
  203. package/docs/api/app-storage.md +209 -0
  204. package/docs/api/apps.md +1375 -0
  205. package/docs/api/auth.md +63 -0
  206. package/docs/api/build-contract.md +507 -0
  207. package/docs/api/build.md +1476 -0
  208. package/docs/api/cloudflare.md +421 -0
  209. package/docs/api/computer.md +203 -0
  210. package/docs/api/files.md +117 -0
  211. package/docs/api/git.md +470 -0
  212. package/docs/api/host.md +1328 -0
  213. package/docs/api/index.md +430 -0
  214. package/docs/api/invocation.md +738 -0
  215. package/docs/api/keys.md +175 -0
  216. package/docs/api/mcp.md +301 -0
  217. package/docs/api/media.md +40 -0
  218. package/docs/api/modules.md +42 -0
  219. package/docs/api/pack.md +138 -0
  220. package/docs/api/page/mount.md +298 -0
  221. package/docs/api/root-data.md +111 -0
  222. package/docs/api/shell/app-directory.md +332 -0
  223. package/docs/api/shell/command-host.md +224 -0
  224. package/docs/api/shell/commands.md +197 -0
  225. package/docs/api/shell/keys.md +183 -0
  226. package/docs/api/shell/shell-module.md +143 -0
  227. package/docs/api/shell/window-manager.md +267 -0
  228. package/docs/api/shell/workbench.md +186 -0
  229. package/docs/api/shell.md +1788 -0
  230. package/docs/api/tenant.md +194 -0
  231. package/docs/api/workers-git.md +1527 -0
  232. package/docs/api/workers.md +1678 -0
  233. package/docs/media/architecture.mmd +32 -0
  234. package/docs/media/architecture.png +0 -0
  235. package/docs/media/bonsai-network.png +0 -0
  236. package/docs/media/bonsai-repository.png +0 -0
  237. package/docs/site/agent-quickstart.mdx +349 -0
  238. package/examples/git/worker.ts +39 -0
  239. package/examples/git/wrangler.jsonc +32 -0
  240. package/examples/standalone/worker.ts +14 -0
  241. package/examples/standalone/wrangler.jsonc +21 -0
  242. package/package.json +249 -3
@@ -0,0 +1,5 @@
1
+ /** Installation records and pure admission rules; tenant state and commits stay host-owned. */
2
+ export { applyInstallationCommand, approvedGrantSchema, declaredOperationsSchema, installationCommandSchema, installationIdSchema, installationRefusalSchema, installationSchema, } from "./installation.js";
3
+ export { admits, admitMethodMetadata, appInvocationCeiling, approvedBindingGrants, scopedBindings, grantsFor, gateRefusalSchema, gateRequestSchema, invocationGrantsSchema, } from "./gate.js";
4
+ export { deriveDeclaredOperations, appCallOperation } from "./operations.js";
5
+ export { appToolPolicySchema, } from "./tools.js";
@@ -0,0 +1,248 @@
1
+ import * as z from "zod";
2
+ import * as Schema from "effect/Schema";
3
+ import * as Result from "effect/Result";
4
+ import { type AppDeclaration, type HostActor } from "@fungi.computer/puffball/app";
5
+ import type { CapabilityDescriptor } from "../capability-descriptor.js";
6
+ /** Reuse the owning Effect codec at a JSON/storage boundary. */
7
+ export declare function ownerCodec<A, I>(schema: Schema.Codec<A, I>): z.ZodPipe<z.ZodUnknown, z.ZodTransform<Awaited<A>, unknown>>;
8
+ /** Installation names cannot contain the tenant separator. */
9
+ export declare const installationIdSchema: z.ZodString;
10
+ export type ResourceRef = Exclude<ApprovedGrant["resources"], "tenant-scope">[number];
11
+ /**
12
+ * Consent approves capabilities, with explicit operation and resource ceilings.
13
+ * A Release adding a method that requires an already-approved capability needs
14
+ * no new consent; a new capability does. Release selection intersects stored
15
+ * operations instead of adding approvals. Each Worker call is still restricted
16
+ * to that method's declared requires, never the App's complete capability set.
17
+ * Only an approved install or set-grants command mints capabilityApproved.
18
+ * Empty operation rows retain that consent; removing the capability revokes it.
19
+ * route:app.methods rows record approval detail, not a method allowlist: narrowing
20
+ * them in set-grants does not restrict methods using an approved capability.
21
+ * Exact operation rows are enforced for capability routes and backend bindings.
22
+ */
23
+ export declare const approvedGrantSchema: z.ZodReadonly<z.ZodObject<{
24
+ capability: z.ZodPipe<z.ZodUnknown, z.ZodTransform<"agent.list" | "agent.manage" | "agent.rename" | "agent.toolPolicy" | "agent.read" | "agent.send" | "computer.list" | "computer.readFile" | "computer.writeFile" | "computer.execute" | "llm.generation" | "media.generation" | "games:servers" | "git.repository" | "apps.open" | "apps.call" | "app.storage" | "app.events.emit" | "team.workbenchHost" | "marketplace.browse" | "marketplace.purchase" | "workflow.run" | "workflow.publish" | "workflow.arm" | "world" | "channels.read" | "channels.personal.manage", unknown>>;
25
+ capabilityApproved: z.ZodLiteral<true>;
26
+ operations: z.ZodReadonly<z.ZodArray<z.ZodString>>;
27
+ resources: z.ZodUnion<readonly [z.ZodLiteral<"tenant-scope">, z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
28
+ kind: z.ZodString;
29
+ id: z.ZodString;
30
+ }, z.core.$strict>>>>]>;
31
+ }, z.core.$strict>>;
32
+ export type ApprovedGrant = Readonly<z.output<typeof approvedGrantSchema>>;
33
+ export type GrantRequest = Extract<InstallationCommand, {
34
+ kind: "install";
35
+ }>["grants"];
36
+ export type Approval = NonNullable<Installation["approval"]>;
37
+ /** Immutable operation facts derived from descriptor methods and the binding registry. */
38
+ export declare const declaredOperationsSchema: z.ZodObject<{
39
+ artifactDigest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
40
+ digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
41
+ anonymousMethods: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
42
+ name: z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>;
43
+ digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
44
+ }, z.core.$strict>>>>;
45
+ capabilities: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
46
+ capability: z.ZodPipe<z.ZodUnknown, z.ZodTransform<"agent.list" | "agent.manage" | "agent.rename" | "agent.toolPolicy" | "agent.read" | "agent.send" | "computer.list" | "computer.readFile" | "computer.writeFile" | "computer.execute" | "llm.generation" | "media.generation" | "games:servers" | "git.repository" | "apps.open" | "apps.call" | "app.storage" | "app.events.emit" | "team.workbenchHost" | "marketplace.browse" | "marketplace.purchase" | "workflow.run" | "workflow.publish" | "workflow.arm" | "world" | "channels.read" | "channels.personal.manage", unknown>>;
47
+ operations: z.ZodReadonly<z.ZodArray<z.ZodString>>;
48
+ }, z.core.$strict>>>>;
49
+ }, z.core.$strict>;
50
+ export type DeclaredOperations = Readonly<z.output<typeof declaredOperationsSchema>>;
51
+ /** Use the existing adapter's permission and methods; hosts author no operation lists. */
52
+ export type CapabilityOperationDescriptor = Pick<CapabilityDescriptor, "id" | "methods"> & Readonly<{
53
+ permission: ApprovedGrant["capability"];
54
+ }>;
55
+ /** Resolved immutable Release plus host-owned installation and approval state. */
56
+ export declare const installationSchema: z.ZodObject<{
57
+ installationId: z.ZodString;
58
+ instanceId: z.ZodNullable<z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("../index.js").InstanceId, unknown>>>;
59
+ appId: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").AppId, unknown>>;
60
+ release: z.ZodPipe<z.ZodUnknown, z.ZodTransform<{
61
+ readonly releaseId: import("@fungi.computer/puffball").ReleaseId;
62
+ readonly appId: import("@fungi.computer/puffball").AppId;
63
+ readonly artifact: {
64
+ readonly artifactRef: import("@fungi.computer/puffball").ArtifactRef;
65
+ readonly artifactDigest: import("@fungi.computer/puffball").Sha256Digest;
66
+ readonly provenance: {
67
+ readonly sourceRevision: import("@fungi.computer/puffball").SourceRevision;
68
+ readonly sourceTreeDigest: import("@fungi.computer/puffball").Sha256Digest;
69
+ readonly buildRecipeDigest: import("@fungi.computer/puffball").Sha256Digest;
70
+ };
71
+ };
72
+ }, unknown>>;
73
+ releasePin: z.ZodEnum<{
74
+ pinned: "pinned";
75
+ floating: "floating";
76
+ }>;
77
+ authorityVersion: z.ZodNumber;
78
+ status: z.ZodEnum<{
79
+ enabled: "enabled";
80
+ disabled: "disabled";
81
+ deleted: "deleted";
82
+ }>;
83
+ visibility: z.ZodEnum<{
84
+ private: "private";
85
+ public: "public";
86
+ }>;
87
+ declaredOperationsDigest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
88
+ approvedGrants: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
89
+ capability: z.ZodPipe<z.ZodUnknown, z.ZodTransform<"agent.list" | "agent.manage" | "agent.rename" | "agent.toolPolicy" | "agent.read" | "agent.send" | "computer.list" | "computer.readFile" | "computer.writeFile" | "computer.execute" | "llm.generation" | "media.generation" | "games:servers" | "git.repository" | "apps.open" | "apps.call" | "app.storage" | "app.events.emit" | "team.workbenchHost" | "marketplace.browse" | "marketplace.purchase" | "workflow.run" | "workflow.publish" | "workflow.arm" | "world" | "channels.read" | "channels.personal.manage", unknown>>;
90
+ capabilityApproved: z.ZodLiteral<true>;
91
+ operations: z.ZodReadonly<z.ZodArray<z.ZodString>>;
92
+ resources: z.ZodUnion<readonly [z.ZodLiteral<"tenant-scope">, z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
93
+ kind: z.ZodString;
94
+ id: z.ZodString;
95
+ }, z.core.$strict>>>>]>;
96
+ }, z.core.$strict>>>>;
97
+ approval: z.ZodNullable<z.ZodReadonly<z.ZodObject<{
98
+ by: import("zod/mini").ZodMiniDiscriminatedUnion<[import("zod/mini").ZodMiniObject<{
99
+ kind: import("zod/mini").ZodMiniLiteral<"member">;
100
+ memberId: import("zod/mini").ZodMiniString<string>;
101
+ }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
102
+ kind: import("zod/mini").ZodMiniLiteral<"agent">;
103
+ agentId: import("zod/mini").ZodMiniString<string>;
104
+ }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
105
+ kind: import("zod/mini").ZodMiniLiteral<"team-key">;
106
+ keyId: import("zod/mini").ZodMiniString<string>;
107
+ }, z.core.$strip>], "kind">;
108
+ at: z.ZodNumber;
109
+ authorityVersion: z.ZodNumber;
110
+ }, z.core.$strict>>>;
111
+ approvedAnonymousMethods: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
112
+ name: z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>;
113
+ digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
114
+ }, z.core.$strict>>>>;
115
+ anonymousApproval: z.ZodNullable<z.ZodReadonly<z.ZodObject<{
116
+ by: import("zod/mini").ZodMiniDiscriminatedUnion<[import("zod/mini").ZodMiniObject<{
117
+ kind: import("zod/mini").ZodMiniLiteral<"member">;
118
+ memberId: import("zod/mini").ZodMiniString<string>;
119
+ }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
120
+ kind: import("zod/mini").ZodMiniLiteral<"agent">;
121
+ agentId: import("zod/mini").ZodMiniString<string>;
122
+ }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
123
+ kind: import("zod/mini").ZodMiniLiteral<"team-key">;
124
+ keyId: import("zod/mini").ZodMiniString<string>;
125
+ }, z.core.$strip>], "kind">;
126
+ at: z.ZodNumber;
127
+ authorityVersion: z.ZodNumber;
128
+ }, z.core.$strict>>>;
129
+ }, z.core.$strict>;
130
+ export type Installation = Readonly<z.output<typeof installationSchema>>;
131
+ /** Host commands; floating targets have already been resolved to immutable Releases. */
132
+ export declare const installationCommandSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
133
+ grants: z.ZodUnion<readonly [z.ZodLiteral<"none">, z.ZodLiteral<"declared">, z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
134
+ capability: z.ZodPipe<z.ZodUnknown, z.ZodTransform<"agent.list" | "agent.manage" | "agent.rename" | "agent.toolPolicy" | "agent.read" | "agent.send" | "computer.list" | "computer.readFile" | "computer.writeFile" | "computer.execute" | "llm.generation" | "media.generation" | "games:servers" | "git.repository" | "apps.open" | "apps.call" | "app.storage" | "app.events.emit" | "team.workbenchHost" | "marketplace.browse" | "marketplace.purchase" | "workflow.run" | "workflow.publish" | "workflow.arm" | "world" | "channels.read" | "channels.personal.manage", unknown>>;
135
+ operations: z.ZodReadonly<z.ZodArray<z.ZodString>>;
136
+ resources: z.ZodUnion<readonly [z.ZodLiteral<"tenant-scope">, z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
137
+ kind: z.ZodString;
138
+ id: z.ZodString;
139
+ }, z.core.$strict>>>>]>;
140
+ }, z.core.$strict>>>>]>;
141
+ release: z.ZodPipe<z.ZodUnknown, z.ZodTransform<{
142
+ readonly releaseId: import("@fungi.computer/puffball").ReleaseId;
143
+ readonly appId: import("@fungi.computer/puffball").AppId;
144
+ readonly artifact: {
145
+ readonly artifactRef: import("@fungi.computer/puffball").ArtifactRef;
146
+ readonly artifactDigest: import("@fungi.computer/puffball").Sha256Digest;
147
+ readonly provenance: {
148
+ readonly sourceRevision: import("@fungi.computer/puffball").SourceRevision;
149
+ readonly sourceTreeDigest: import("@fungi.computer/puffball").Sha256Digest;
150
+ readonly buildRecipeDigest: import("@fungi.computer/puffball").Sha256Digest;
151
+ };
152
+ };
153
+ }, unknown>>;
154
+ releasePin: z.ZodEnum<{
155
+ pinned: "pinned";
156
+ floating: "floating";
157
+ }>;
158
+ kind: z.ZodLiteral<"install">;
159
+ }, z.core.$strict>, z.ZodObject<{
160
+ release: z.ZodPipe<z.ZodUnknown, z.ZodTransform<{
161
+ readonly releaseId: import("@fungi.computer/puffball").ReleaseId;
162
+ readonly appId: import("@fungi.computer/puffball").AppId;
163
+ readonly artifact: {
164
+ readonly artifactRef: import("@fungi.computer/puffball").ArtifactRef;
165
+ readonly artifactDigest: import("@fungi.computer/puffball").Sha256Digest;
166
+ readonly provenance: {
167
+ readonly sourceRevision: import("@fungi.computer/puffball").SourceRevision;
168
+ readonly sourceTreeDigest: import("@fungi.computer/puffball").Sha256Digest;
169
+ readonly buildRecipeDigest: import("@fungi.computer/puffball").Sha256Digest;
170
+ };
171
+ };
172
+ }, unknown>>;
173
+ releasePin: z.ZodEnum<{
174
+ pinned: "pinned";
175
+ floating: "floating";
176
+ }>;
177
+ kind: z.ZodLiteral<"select-release">;
178
+ }, z.core.$strict>, z.ZodObject<{
179
+ kind: z.ZodLiteral<"set-status">;
180
+ status: z.ZodEnum<{
181
+ enabled: "enabled";
182
+ disabled: "disabled";
183
+ deleted: "deleted";
184
+ }>;
185
+ }, z.core.$strict>, z.ZodObject<{
186
+ kind: z.ZodLiteral<"set-visibility">;
187
+ visibility: z.ZodEnum<{
188
+ private: "private";
189
+ public: "public";
190
+ }>;
191
+ }, z.core.$strict>, z.ZodObject<{
192
+ kind: z.ZodLiteral<"set-grants">;
193
+ grants: z.ZodUnion<readonly [z.ZodLiteral<"none">, z.ZodLiteral<"declared">, z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
194
+ capability: z.ZodPipe<z.ZodUnknown, z.ZodTransform<"agent.list" | "agent.manage" | "agent.rename" | "agent.toolPolicy" | "agent.read" | "agent.send" | "computer.list" | "computer.readFile" | "computer.writeFile" | "computer.execute" | "llm.generation" | "media.generation" | "games:servers" | "git.repository" | "apps.open" | "apps.call" | "app.storage" | "app.events.emit" | "team.workbenchHost" | "marketplace.browse" | "marketplace.purchase" | "workflow.run" | "workflow.publish" | "workflow.arm" | "world" | "channels.read" | "channels.personal.manage", unknown>>;
195
+ operations: z.ZodReadonly<z.ZodArray<z.ZodString>>;
196
+ resources: z.ZodUnion<readonly [z.ZodLiteral<"tenant-scope">, z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
197
+ kind: z.ZodString;
198
+ id: z.ZodString;
199
+ }, z.core.$strict>>>>]>;
200
+ }, z.core.$strict>>>>]>;
201
+ }, z.core.$strict>, z.ZodObject<{
202
+ kind: z.ZodLiteral<"approve-anonymous">;
203
+ methods: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
204
+ name: z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>;
205
+ digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
206
+ }, z.core.$strict>>>>;
207
+ }, z.core.$strict>], "kind">;
208
+ export type InstallationCommand = z.output<typeof installationCommandSchema>;
209
+ /** Values captured by the host; identities and time are never generated by the reducer. */
210
+ export type InstallationContext = Readonly<{
211
+ by: HostActor;
212
+ role: "owner" | "member" | null;
213
+ expectedVersion: number | null;
214
+ declaration: AppDeclaration;
215
+ declaredOperations: DeclaredOperations;
216
+ now: number;
217
+ identity: Pick<Installation, "installationId" | "instanceId">;
218
+ /** Trusted interactive consent; never copied from a command or inferred from a key's owner role. */
219
+ anonymousConsent?: Readonly<{
220
+ memberId: string;
221
+ }>;
222
+ }>;
223
+ /** Closed refusals: failed commands produce no installation update. */
224
+ export declare const installationRefusalSchema: z.ZodObject<{
225
+ kind: z.ZodLiteral<"refused">;
226
+ code: z.ZodEnum<{
227
+ unavailable: "unavailable";
228
+ stale_authority: "stale_authority";
229
+ not_declared: "not_declared";
230
+ release_app_mismatch: "release_app_mismatch";
231
+ not_owner: "not_owner";
232
+ invalid_command: "invalid_command";
233
+ }>;
234
+ }, z.core.$strict>;
235
+ export type InstallationRefusal = z.output<typeof installationRefusalSchema>;
236
+ export type InstallationUpdate = Readonly<{
237
+ next: Installation;
238
+ authorityChanged: boolean;
239
+ }>;
240
+ /** Read port for one host tenant; commits, receipts and revocation stay with that host. */
241
+ export type InstallationReader = Readonly<{
242
+ /** Read every installation in the selected tenant. */
243
+ list(): Promise<readonly Installation[]>;
244
+ /** Read one installation in the same selected tenant. */
245
+ get(id: string): Promise<Installation | null>;
246
+ }>;
247
+ /** Pure transition; hosts retain transactions, receipts, epochs and delivery. */
248
+ export declare function applyInstallationCommand(current: Installation | null, rawCommand: InstallationCommand, context: InstallationContext): Result.Result<InstallationUpdate, InstallationRefusal>;
@@ -0,0 +1,307 @@
1
+ import * as z from "zod";
2
+ import * as Schema from "effect/Schema";
3
+ import * as Result from "effect/Result";
4
+ import { appCapabilityIdSchema, appApiMethodNameSchema, } from "@fungi.computer/puffball/app";
5
+ import { hostActorSchema } from "@fungi.computer/puffball/app-method-runtime";
6
+ import { appIdSchema, releaseSchema, sha256DigestSchema, } from "@fungi.computer/puffball/effect";
7
+ import { instanceIdSchema } from "../index.js";
8
+ /** Reuse the owning Effect codec at a JSON/storage boundary. */
9
+ export function ownerCodec(schema) {
10
+ const decode = Schema.decodeUnknownResult(schema, {
11
+ onExcessProperty: "error",
12
+ });
13
+ return z.unknown().transform((value, ctx) => {
14
+ const result = decode(value);
15
+ if (Result.isFailure(result)) {
16
+ ctx.addIssue({ code: "custom", message: "Invalid owner-defined value" });
17
+ return z.NEVER;
18
+ }
19
+ return result.success;
20
+ });
21
+ }
22
+ /** Installation names cannot contain the tenant separator. */
23
+ export const installationIdSchema = z
24
+ .string()
25
+ .regex(/^[a-z](?:[a-z0-9]|-(?!-)){0,62}$/u);
26
+ /** A resource identity minted and resolved by the host, never by this module. */
27
+ const resourceRefSchema = z
28
+ .strictObject({
29
+ kind: z.string().min(1),
30
+ id: z.string().min(1),
31
+ })
32
+ .readonly();
33
+ const operationsSchema = z
34
+ .array(z.string().min(1))
35
+ .refine((values) => new Set(values).size === values.length, "Operations must be unique")
36
+ .readonly();
37
+ /**
38
+ * Consent approves capabilities, with explicit operation and resource ceilings.
39
+ * A Release adding a method that requires an already-approved capability needs
40
+ * no new consent; a new capability does. Release selection intersects stored
41
+ * operations instead of adding approvals. Each Worker call is still restricted
42
+ * to that method's declared requires, never the App's complete capability set.
43
+ * Only an approved install or set-grants command mints capabilityApproved.
44
+ * Empty operation rows retain that consent; removing the capability revokes it.
45
+ * route:app.methods rows record approval detail, not a method allowlist: narrowing
46
+ * them in set-grants does not restrict methods using an approved capability.
47
+ * Exact operation rows are enforced for capability routes and backend bindings.
48
+ */
49
+ export const approvedGrantSchema = z
50
+ .strictObject({
51
+ capability: ownerCodec(appCapabilityIdSchema),
52
+ capabilityApproved: z.literal(true),
53
+ operations: operationsSchema,
54
+ resources: z.union([
55
+ z.literal("tenant-scope"),
56
+ z.array(resourceRefSchema).readonly(),
57
+ ]),
58
+ })
59
+ .readonly();
60
+ /** One grant per capability keeps operation/resource pairs unambiguous. */
61
+ const approvedGrantsSchema = z
62
+ .array(approvedGrantSchema)
63
+ .refine((grants) => new Set(grants.map((grant) => grant.capability)).size === grants.length, "Capabilities must be unique")
64
+ .readonly();
65
+ /** Shorthand is accepted only on a command and expanded at approval time. */
66
+ const grantRequestSchema = z.union([
67
+ z.literal("none"),
68
+ z.literal("declared"),
69
+ z
70
+ .array(approvedGrantSchema
71
+ .unwrap()
72
+ .omit({ capabilityApproved: true })
73
+ .readonly())
74
+ .refine((grants) => new Set(grants.map((grant) => grant.capability)).size === grants.length, "Capabilities must be unique")
75
+ .readonly(),
76
+ ]);
77
+ const versionSchema = z.number().int().positive();
78
+ /** An owner's approval of a fixed operation snapshot. */
79
+ const approvalSchema = z
80
+ .strictObject({
81
+ by: hostActorSchema,
82
+ at: z.number().int().nonnegative(),
83
+ authorityVersion: versionSchema,
84
+ })
85
+ .readonly();
86
+ const anonymousMethodsSchema = z
87
+ .array(z
88
+ .strictObject({
89
+ name: ownerCodec(appApiMethodNameSchema),
90
+ digest: ownerCodec(sha256DigestSchema),
91
+ })
92
+ .readonly())
93
+ .refine((methods) => methods.every((method, index) => {
94
+ const previous = methods[index - 1];
95
+ return previous === undefined || previous.name < method.name;
96
+ }), "Anonymous method identities must have unique, sorted names")
97
+ .readonly();
98
+ /** Immutable operation facts derived from descriptor methods and the binding registry. */
99
+ export const declaredOperationsSchema = z.strictObject({
100
+ artifactDigest: ownerCodec(sha256DigestSchema),
101
+ digest: ownerCodec(sha256DigestSchema),
102
+ anonymousMethods: anonymousMethodsSchema,
103
+ capabilities: z
104
+ .array(z
105
+ .strictObject({
106
+ capability: ownerCodec(appCapabilityIdSchema),
107
+ operations: operationsSchema,
108
+ })
109
+ .readonly())
110
+ .readonly(),
111
+ });
112
+ /** Resolved immutable Release plus host-owned installation and approval state. */
113
+ export const installationSchema = z
114
+ .strictObject({
115
+ installationId: installationIdSchema,
116
+ instanceId: ownerCodec(instanceIdSchema).nullable(),
117
+ appId: ownerCodec(appIdSchema),
118
+ release: ownerCodec(releaseSchema),
119
+ releasePin: z.enum(["pinned", "floating"]),
120
+ authorityVersion: versionSchema,
121
+ status: z.enum(["enabled", "disabled", "deleted"]),
122
+ visibility: z.enum(["private", "public"]),
123
+ declaredOperationsDigest: ownerCodec(sha256DigestSchema),
124
+ approvedGrants: approvedGrantsSchema,
125
+ approval: approvalSchema.nullable(),
126
+ approvedAnonymousMethods: anonymousMethodsSchema,
127
+ anonymousApproval: approvalSchema.nullable(),
128
+ })
129
+ .refine((installation) => installation.appId === installation.release.appId, "Release must belong to the installation's App")
130
+ .refine((installation) => {
131
+ const approval = installation.anonymousApproval;
132
+ return approval === null
133
+ ? installation.approvedAnonymousMethods.length === 0
134
+ : installation.status !== "deleted" &&
135
+ installation.approvedAnonymousMethods.length > 0 &&
136
+ approval.by.kind === "member" &&
137
+ approval.authorityVersion <= installation.authorityVersion;
138
+ }, "Anonymous authority requires a retained human approval receipt");
139
+ const releaseFields = {
140
+ release: ownerCodec(releaseSchema),
141
+ releasePin: installationSchema.shape.releasePin,
142
+ };
143
+ /** Host commands; floating targets have already been resolved to immutable Releases. */
144
+ export const installationCommandSchema = z.discriminatedUnion("kind", [
145
+ z.strictObject({
146
+ kind: z.literal("install"),
147
+ ...releaseFields,
148
+ grants: grantRequestSchema,
149
+ }),
150
+ z.strictObject({ kind: z.literal("select-release"), ...releaseFields }),
151
+ z.strictObject({
152
+ kind: z.literal("set-status"),
153
+ status: installationSchema.shape.status,
154
+ }),
155
+ z.strictObject({
156
+ kind: z.literal("set-visibility"),
157
+ visibility: installationSchema.shape.visibility,
158
+ }),
159
+ z.strictObject({ kind: z.literal("set-grants"), grants: grantRequestSchema }),
160
+ z.strictObject({
161
+ kind: z.literal("approve-anonymous"),
162
+ methods: anonymousMethodsSchema,
163
+ }),
164
+ ]);
165
+ /** Closed refusals: failed commands produce no installation update. */
166
+ export const installationRefusalSchema = z.strictObject({
167
+ kind: z.literal("refused"),
168
+ code: z.enum([
169
+ "unavailable",
170
+ "stale_authority",
171
+ "not_owner",
172
+ "not_declared",
173
+ "release_app_mismatch",
174
+ "invalid_command",
175
+ ]),
176
+ });
177
+ /** Pure transition; hosts retain transactions, receipts, epochs and delivery. */
178
+ export function applyInstallationCommand(current, rawCommand, context) {
179
+ const refuse = (code) => Result.fail({ kind: "refused", code });
180
+ const parsed = installationCommandSchema.safeParse(rawCommand);
181
+ if (!parsed.success)
182
+ return refuse("invalid_command");
183
+ const command = parsed.data;
184
+ if (context.role === null || current?.status === "deleted")
185
+ return refuse("unavailable");
186
+ if (current === null) {
187
+ if (command.kind !== "install")
188
+ return refuse("unavailable");
189
+ if (context.expectedVersion !== null && context.expectedVersion !== 0)
190
+ return refuse("stale_authority");
191
+ }
192
+ else if (command.kind === "install" ||
193
+ context.expectedVersion !== current.authorityVersion) {
194
+ return refuse("stale_authority");
195
+ }
196
+ if ((command.kind === "set-grants" ||
197
+ (command.kind === "install" && command.grants !== "none")) &&
198
+ context.role !== "owner")
199
+ return refuse("not_owner");
200
+ const anonymousConsent = context.by.kind === "member" &&
201
+ context.anonymousConsent?.memberId === context.by.memberId;
202
+ if (command.kind === "approve-anonymous") {
203
+ const narrows = command.methods.every((method) => current?.approvedAnonymousMethods.some((approved) => approved.name === method.name && approved.digest === method.digest));
204
+ if (context.role !== "owner" || (!narrows && !anonymousConsent))
205
+ return refuse("not_owner");
206
+ }
207
+ const release = command.kind === "install" || command.kind === "select-release"
208
+ ? command.release
209
+ : current?.release;
210
+ if (release === undefined)
211
+ return refuse("unavailable");
212
+ if (current !== null && release.appId !== current.appId)
213
+ return refuse("release_app_mismatch");
214
+ if (context.declaredOperations.artifactDigest !==
215
+ release.artifact.artifactDigest ||
216
+ (current !== null &&
217
+ command.kind !== "select-release" &&
218
+ context.declaredOperations.digest !== current.declaredOperationsDigest))
219
+ return refuse("stale_authority");
220
+ const declared = (capability) => context.declaration.capabilities.includes(capability)
221
+ ? (context.declaredOperations.capabilities.find((row) => row.capability === capability)?.operations ?? [])
222
+ : [];
223
+ const version = (current?.authorityVersion ?? 0) + 1;
224
+ let grants = current?.approvedGrants ?? [];
225
+ let approval = current?.approval ?? null;
226
+ let anonymousMethods = current?.approvedAnonymousMethods ?? [];
227
+ let anonymousApproval = current?.anonymousApproval ?? null;
228
+ if (command.kind === "approve-anonymous") {
229
+ if (command.methods.some((method) => !context.declaredOperations.anonymousMethods.some((declared) => declared.name === method.name &&
230
+ declared.digest === method.digest)))
231
+ return refuse("not_declared");
232
+ anonymousMethods = command.methods;
233
+ if (command.methods.length === 0)
234
+ anonymousApproval = null;
235
+ else if (anonymousConsent)
236
+ anonymousApproval = {
237
+ by: context.by,
238
+ at: context.now,
239
+ authorityVersion: version,
240
+ };
241
+ }
242
+ if (command.kind === "install" || command.kind === "set-grants") {
243
+ grants =
244
+ command.grants === "none"
245
+ ? []
246
+ : command.grants === "declared"
247
+ ? context.declaredOperations.capabilities
248
+ .filter((row) => context.declaration.capabilities.includes(row.capability))
249
+ .map((row) => ({
250
+ ...row,
251
+ capabilityApproved: true,
252
+ resources: "tenant-scope",
253
+ }))
254
+ : command.grants.map((grant) => ({
255
+ ...grant,
256
+ capabilityApproved: true,
257
+ }));
258
+ if (grants.some((grant) => !context.declaration.capabilities.includes(grant.capability) ||
259
+ grant.operations.some((operation) => !declared(grant.capability).includes(operation))))
260
+ return refuse("not_declared");
261
+ approval =
262
+ command.grants === "none"
263
+ ? null
264
+ : { by: context.by, at: context.now, authorityVersion: version };
265
+ }
266
+ if (command.kind === "select-release") {
267
+ anonymousMethods = anonymousMethods.filter((method) => context.declaredOperations.anonymousMethods.some((declared) => declared.name === method.name && declared.digest === method.digest));
268
+ if (anonymousMethods.length === 0)
269
+ anonymousApproval = null;
270
+ grants = grants
271
+ .map((grant) => ({
272
+ ...grant,
273
+ operations: grant.operations.filter((operation) => declared(grant.capability).includes(operation)),
274
+ }))
275
+ .filter((grant) => context.declaration.capabilities.includes(grant.capability));
276
+ }
277
+ if (command.kind === "set-status" && command.status === "deleted") {
278
+ grants = [];
279
+ approval = null;
280
+ anonymousMethods = [];
281
+ anonymousApproval = null;
282
+ }
283
+ const next = installationSchema.safeParse({
284
+ installationId: current?.installationId ?? context.identity.installationId,
285
+ instanceId: current === null ? context.identity.instanceId : current.instanceId,
286
+ appId: release.appId,
287
+ release,
288
+ releasePin: command.kind === "install" || command.kind === "select-release"
289
+ ? command.releasePin
290
+ : current?.releasePin,
291
+ authorityVersion: version,
292
+ status: command.kind === "set-status"
293
+ ? command.status
294
+ : (current?.status ?? "enabled"),
295
+ visibility: command.kind === "set-visibility"
296
+ ? command.visibility
297
+ : (current?.visibility ?? "private"),
298
+ declaredOperationsDigest: context.declaredOperations.digest,
299
+ approvedGrants: grants,
300
+ approval,
301
+ approvedAnonymousMethods: anonymousMethods,
302
+ anonymousApproval,
303
+ });
304
+ return next.success
305
+ ? Result.succeed({ next: next.data, authorityChanged: true })
306
+ : refuse("invalid_command");
307
+ }
@@ -0,0 +1,8 @@
1
+ import { type AppBackendBindingName, type AppDeclaration } from "@fungi.computer/puffball/app";
2
+ import { type CapabilityOperationDescriptor, type DeclaredOperations, type Installation } from "./installation.js";
3
+ /** Qualify backend operations so a same-named browser method grants no backend authority. */
4
+ export declare function bindingOperationKey(binding: AppBackendBindingName, operation: string): string;
5
+ /** Approval identity for one UI-side target method; upgrades intersect these exact pairs. */
6
+ export declare function appCallOperation(appId: string, method: string): string;
7
+ /** Sole derivation owner. Inputs are the verified Release declaration and existing host adapters. */
8
+ export declare function deriveDeclaredOperations(release: Installation["release"], declaration: AppDeclaration, descriptors: readonly CapabilityOperationDescriptor[]): Promise<DeclaredOperations>;