@fungi.computer/oyster 0.0.1 → 0.2.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 (195) hide show
  1. package/README.md +228 -176
  2. package/dist/agent-avatars.d.ts +103 -0
  3. package/dist/agent-avatars.js +66 -0
  4. package/dist/agents.d.ts +12 -2
  5. package/dist/agents.js +39 -2
  6. package/dist/app-builds.d.ts +76 -17
  7. package/dist/app-builds.js +207 -63
  8. package/dist/app-sources.d.ts +7 -22
  9. package/dist/app-sources.js +49 -14
  10. package/dist/apps/gate.d.ts +7 -7
  11. package/dist/apps/gate.js +6 -6
  12. package/dist/apps/installation.d.ts +35 -35
  13. package/dist/apps/installation.js +20 -19
  14. package/dist/build/artifact-writer.d.ts +40 -0
  15. package/dist/build/artifact-writer.js +181 -0
  16. package/dist/build/build-jobs.js +7 -1
  17. package/dist/build/build-policy.d.ts +10 -0
  18. package/dist/build/build-policy.js +10 -0
  19. package/dist/build/build-step.d.ts +12 -1
  20. package/dist/build/build-step.js +9 -1
  21. package/dist/build/candidate-builder.d.ts +68 -0
  22. package/dist/build/candidate-builder.js +91 -0
  23. package/dist/build/candidate-cli.d.ts +1 -0
  24. package/dist/build/candidate-cli.js +119 -0
  25. package/dist/build/candidate-cli.mjs +28220 -0
  26. package/dist/build/content-objects.d.ts +37 -0
  27. package/dist/build/content-objects.js +53 -0
  28. package/dist/build/effect.d.ts +18 -0
  29. package/dist/build/effect.js +52 -12
  30. package/dist/build/git-environment.d.ts +3 -0
  31. package/dist/build/git-environment.js +21 -9
  32. package/dist/build/local-build.d.ts +13 -3
  33. package/dist/build/local-build.js +149 -99
  34. package/dist/build/snapshot-package-store.d.ts +10 -0
  35. package/dist/build/snapshot-package-store.js +15 -0
  36. package/dist/build/snapshot-program-scripts.js +1 -1
  37. package/dist/build/snapshot-programs/snapshot-path.d.ts +2 -0
  38. package/dist/build/snapshot-programs/snapshot-path.js +6 -0
  39. package/dist/build/submission-contract.d.ts +461 -17
  40. package/dist/build/submission-contract.js +136 -26
  41. package/dist/build/workspace-build.d.ts +7 -2
  42. package/dist/build/workspace-build.js +55 -11
  43. package/dist/build-contract.d.ts +3 -0
  44. package/dist/build-contract.js +3 -0
  45. package/dist/build-egress.d.ts +62 -0
  46. package/dist/build-egress.js +148 -0
  47. package/dist/build.d.ts +1 -0
  48. package/dist/build.js +1 -0
  49. package/dist/cloudflare.js +4 -4
  50. package/dist/frame-binding-wire-script.js +1 -1
  51. package/dist/frame-binding-wire.d.ts +1 -1
  52. package/dist/frame-binding-wire.js +1 -1
  53. package/dist/git-journal.js +4 -4
  54. package/dist/index.d.ts +1 -0
  55. package/dist/index.js +1 -0
  56. package/dist/installation-consent.d.ts +3 -1
  57. package/dist/installation-consent.js +2 -0
  58. package/dist/mcp-owner.js +20 -8
  59. package/dist/pack.d.ts +6 -3
  60. package/dist/pack.js +28 -11
  61. package/dist/page/gill-observation.d.ts +4 -0
  62. package/dist/page/gill-observation.js +31 -0
  63. package/dist/page/mount.d.ts +4 -8
  64. package/dist/page/window-launch.d.ts +18 -0
  65. package/dist/page/window-launch.js +11 -0
  66. package/dist/page-script.js +4 -4
  67. package/dist/release.d.ts +11 -0
  68. package/dist/release.js +4 -1
  69. package/dist/shell/app-directory.d.ts +50 -45
  70. package/dist/shell/app-directory.js +19 -18
  71. package/dist/shell/shell-module.d.ts +26 -2
  72. package/dist/shell/shell-module.js +51 -2
  73. package/dist/shell/window-manager.d.ts +5 -1
  74. package/dist/shell/window-manager.js +21 -2
  75. package/dist/shell/workbench-directory.d.ts +332 -0
  76. package/dist/shell/workbench-directory.js +35 -0
  77. package/dist/shell/workbench-host-types.d.ts +1 -1
  78. package/dist/shell/workbench-host.js +5 -0
  79. package/dist/shell/workbench-model.d.ts +3 -1
  80. package/dist/source/index.d.ts +6 -7
  81. package/dist/source/index.js +6 -7
  82. package/dist/source/template-push.d.ts +33 -0
  83. package/dist/source/template-push.js +223 -0
  84. package/dist/source/template-repository.d.ts +2 -0
  85. package/dist/source/template-repository.js +13 -0
  86. package/dist/source/{template-write.d.ts → template-seed.d.ts} +19 -20
  87. package/dist/source/template-seed.js +80 -0
  88. package/dist/standalone/agent-directory.d.ts +4 -0
  89. package/dist/standalone/agent-directory.js +4 -0
  90. package/dist/standalone/agents.d.ts +227 -0
  91. package/dist/standalone/agents.js +834 -0
  92. package/dist/standalone/app-builds-binding.d.ts +36 -0
  93. package/dist/standalone/app-builds-binding.js +119 -0
  94. package/dist/standalone/app-set.d.ts +91 -0
  95. package/dist/standalone/app-set.js +395 -0
  96. package/dist/standalone/app-source.d.ts +41 -0
  97. package/dist/standalone/app-source.js +59 -0
  98. package/dist/standalone/builds.d.ts +18 -0
  99. package/dist/standalone/builds.js +101 -0
  100. package/dist/standalone/computer-egress.d.ts +37 -0
  101. package/dist/standalone/computer-egress.js +111 -0
  102. package/dist/standalone/computer.d.ts +122 -0
  103. package/dist/standalone/computer.js +714 -0
  104. package/dist/standalone/install-apps-cli.d.ts +2 -0
  105. package/dist/standalone/install-apps-cli.js +126 -0
  106. package/dist/standalone/instance.d.ts +35 -0
  107. package/dist/standalone/instance.js +74 -0
  108. package/dist/standalone/media.d.ts +76 -0
  109. package/dist/standalone/media.js +227 -0
  110. package/dist/standalone/preview.d.ts +51 -0
  111. package/dist/standalone/preview.js +167 -0
  112. package/dist/standalone/registry.d.ts +102 -0
  113. package/dist/standalone/registry.js +193 -0
  114. package/dist/standalone/tenancy.d.ts +42 -0
  115. package/dist/standalone/tenancy.js +241 -0
  116. package/dist/standalone/worker.d.ts +55 -0
  117. package/dist/standalone/worker.js +147 -0
  118. package/dist/standalone/workflow-authority-readers.d.ts +16 -0
  119. package/dist/standalone/workflow-authority-readers.js +35 -0
  120. package/dist/standalone/workflow-binding.d.ts +216 -0
  121. package/dist/standalone/workflow-binding.js +217 -0
  122. package/dist/standalone/workflow-events.d.ts +46 -0
  123. package/dist/standalone/workflow-events.js +39 -0
  124. package/dist/standalone/workflow-git-capture.d.ts +7 -0
  125. package/dist/standalone/workflow-git-capture.js +62 -0
  126. package/dist/standalone/workflow-host.d.ts +955 -0
  127. package/dist/standalone/workflow-host.js +778 -0
  128. package/dist/standalone/workflow-node-capture.d.ts +13 -0
  129. package/dist/standalone/workflow-node-capture.js +72 -0
  130. package/dist/standalone/workflow-root-authority.d.ts +27 -0
  131. package/dist/standalone/workflow-root-authority.js +87 -0
  132. package/dist/standalone/workflow-schedules.d.ts +52 -0
  133. package/dist/standalone/workflow-schedules.js +40 -0
  134. package/dist/standalone/workflow-trigger-host.d.ts +1716 -0
  135. package/dist/standalone/workflow-trigger-host.js +52 -0
  136. package/dist/standalone/workflows.d.ts +713 -0
  137. package/dist/standalone/workflows.js +554 -0
  138. package/dist/tenancy/auth0.d.ts +64 -0
  139. package/dist/tenancy/auth0.js +198 -0
  140. package/dist/tenancy/cloudflare/rate-limit.d.ts +8 -0
  141. package/dist/tenancy/cloudflare/rate-limit.js +19 -0
  142. package/dist/tenancy/cloudflare/registry.d.ts +50 -0
  143. package/dist/tenancy/cloudflare/registry.js +301 -0
  144. package/dist/tenancy/cloudflare.d.ts +2 -0
  145. package/dist/tenancy/cloudflare.js +2 -0
  146. package/dist/tenancy/cold-directory.d.ts +13 -0
  147. package/dist/tenancy/cold-directory.js +43 -0
  148. package/dist/tenancy/contracts.d.ts +195 -0
  149. package/dist/tenancy/contracts.js +1 -0
  150. package/dist/tenancy/index.d.ts +19 -0
  151. package/dist/tenancy/index.js +156 -0
  152. package/dist/tenancy/owner-key.d.ts +26 -0
  153. package/dist/tenancy/owner-key.js +63 -0
  154. package/dist/tenancy/oyster.d.ts +4 -0
  155. package/dist/tenancy/oyster.js +25 -0
  156. package/dist/tenancy/registry-record.d.ts +7 -0
  157. package/dist/tenancy/registry-record.js +44 -0
  158. package/dist/tenancy/registry.d.ts +27 -0
  159. package/dist/tenancy/registry.js +138 -0
  160. package/dist/tenancy/resolve.d.ts +7 -0
  161. package/dist/tenancy/resolve.js +64 -0
  162. package/dist/workers-git.js +1 -4
  163. package/dist/workers.d.ts +2 -2
  164. package/dist/workers.js +3 -1
  165. package/docs/api/build-contract.md +829 -32
  166. package/docs/api/build.md +253 -17
  167. package/docs/api/modules.md +40 -32
  168. package/docs/api/pack.md +6 -3
  169. package/docs/api/shell/app-directory.md +267 -0
  170. package/docs/api/shell/shell-module.md +64 -3
  171. package/docs/api/shell/workbench-directory.md +119 -0
  172. package/docs/api/shell/workbench.md +19 -0
  173. package/docs/api/shell.md +127 -0
  174. package/docs/api/tenancy/auth0.md +110 -0
  175. package/docs/api/tenancy/cloudflare.md +221 -0
  176. package/docs/api/tenancy/cold-directory.md +46 -0
  177. package/docs/api/tenancy/owner-key.md +63 -0
  178. package/docs/api/tenancy/registry.md +77 -0
  179. package/docs/api/tenancy/resolve.md +69 -0
  180. package/docs/api/tenancy.md +430 -0
  181. package/docs/api/workers-git.md +140 -21
  182. package/docs/api/workers.md +15 -2
  183. package/docs/site/agent-quickstart.mdx +52 -55
  184. package/examples/standalone/worker.ts +7 -14
  185. package/examples/standalone/wrangler.jsonc +67 -6
  186. package/package.json +78 -25
  187. package/dist/build/r2-artifact-writer.d.ts +0 -31
  188. package/dist/build/r2-artifact-writer.js +0 -179
  189. package/dist/source/template-write.js +0 -344
  190. package/docs/media/architecture.mmd +0 -32
  191. package/docs/media/architecture.png +0 -0
  192. package/docs/media/bonsai-network.png +0 -0
  193. package/docs/media/bonsai-repository.png +0 -0
  194. package/examples/git/worker.ts +0 -39
  195. package/examples/git/wrangler.jsonc +0 -32
@@ -6,7 +6,7 @@ import type { CapabilityDescriptor } from "../capability-descriptor.js";
6
6
  /** Reuse the owning Effect codec at a JSON/storage boundary. */
7
7
  export declare function ownerCodec<A, I>(schema: Schema.Codec<A, I>): z.ZodPipe<z.ZodUnknown, z.ZodTransform<Awaited<A>, unknown>>;
8
8
  /** Installation names cannot contain the tenant separator. */
9
- export declare const installationIdSchema: z.ZodString;
9
+ export declare const installationIdSchema: z.ZodLazy<z.ZodString>;
10
10
  export type ResourceRef = Exclude<ApprovedGrant["resources"], "tenant-scope">[number];
11
11
  /**
12
12
  * Consent approves capabilities, with explicit operation and resource ceilings.
@@ -21,13 +21,13 @@ export type ResourceRef = Exclude<ApprovedGrant["resources"], "tenant-scope">[nu
21
21
  * Exact operation rows are enforced for capability routes and backend bindings.
22
22
  */
23
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>>;
24
+ capability: z.ZodPipe<z.ZodUnknown, z.ZodTransform<"agent.list" | "agent.manage" | "agent.rename" | "agent.avatar" | "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
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<{
26
+ operations: z.ZodLazy<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
27
+ resources: z.ZodUnion<readonly [z.ZodLiteral<"tenant-scope">, z.ZodReadonly<z.ZodArray<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
28
28
  kind: z.ZodString;
29
29
  id: z.ZodString;
30
- }, z.core.$strict>>>>]>;
30
+ }, z.core.$strict>>>>>]>;
31
31
  }, z.core.$strict>>;
32
32
  export type ApprovedGrant = Readonly<z.output<typeof approvedGrantSchema>>;
33
33
  export type GrantRequest = Extract<InstallationCommand, {
@@ -35,7 +35,7 @@ export type GrantRequest = Extract<InstallationCommand, {
35
35
  }>["grants"];
36
36
  export type Approval = NonNullable<Installation["approval"]>;
37
37
  /** Immutable operation facts derived from descriptor methods and the binding registry. */
38
- export declare const declaredOperationsSchema: z.ZodObject<{
38
+ export declare const declaredOperationsSchema: z.ZodLazy<z.ZodObject<{
39
39
  artifactDigest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
40
40
  digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
41
41
  anonymousMethods: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
@@ -43,10 +43,10 @@ export declare const declaredOperationsSchema: z.ZodObject<{
43
43
  digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
44
44
  }, z.core.$strict>>>>;
45
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>>;
46
+ capability: z.ZodPipe<z.ZodUnknown, z.ZodTransform<"agent.list" | "agent.manage" | "agent.rename" | "agent.avatar" | "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.ZodLazy<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
48
48
  }, z.core.$strict>>>>;
49
- }, z.core.$strict>;
49
+ }, z.core.$strict>>;
50
50
  export type DeclaredOperations = Readonly<z.output<typeof declaredOperationsSchema>>;
51
51
  /** Use the existing adapter's permission and methods; hosts author no operation lists. */
52
52
  export type CapabilityOperationDescriptor = Pick<CapabilityDescriptor, "id" | "methods"> & Readonly<{
@@ -54,7 +54,7 @@ export type CapabilityOperationDescriptor = Pick<CapabilityDescriptor, "id" | "m
54
54
  }>;
55
55
  /** Resolved immutable Release plus host-owned installation and approval state. */
56
56
  export declare const installationSchema: z.ZodObject<{
57
- installationId: z.ZodString;
57
+ installationId: z.ZodLazy<z.ZodString>;
58
58
  instanceId: z.ZodNullable<z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("../index.js").InstanceId, unknown>>>;
59
59
  appId: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").AppId, unknown>>;
60
60
  release: z.ZodPipe<z.ZodUnknown, z.ZodTransform<{
@@ -74,7 +74,7 @@ export declare const installationSchema: z.ZodObject<{
74
74
  pinned: "pinned";
75
75
  floating: "floating";
76
76
  }>;
77
- authorityVersion: z.ZodNumber;
77
+ authorityVersion: z.ZodLazy<z.ZodNumber>;
78
78
  status: z.ZodEnum<{
79
79
  enabled: "enabled";
80
80
  disabled: "disabled";
@@ -85,16 +85,16 @@ export declare const installationSchema: z.ZodObject<{
85
85
  public: "public";
86
86
  }>;
87
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>>;
88
+ approvedGrants: z.ZodLazy<z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
89
+ capability: z.ZodPipe<z.ZodUnknown, z.ZodTransform<"agent.list" | "agent.manage" | "agent.rename" | "agent.avatar" | "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
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<{
91
+ operations: z.ZodLazy<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
92
+ resources: z.ZodUnion<readonly [z.ZodLiteral<"tenant-scope">, z.ZodReadonly<z.ZodArray<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
93
93
  kind: z.ZodString;
94
94
  id: z.ZodString;
95
- }, z.core.$strict>>>>]>;
96
- }, z.core.$strict>>>>;
97
- approval: z.ZodNullable<z.ZodReadonly<z.ZodObject<{
95
+ }, z.core.$strict>>>>>]>;
96
+ }, z.core.$strict>>>>>;
97
+ approval: z.ZodNullable<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
98
98
  by: import("zod/mini").ZodMiniDiscriminatedUnion<[import("zod/mini").ZodMiniObject<{
99
99
  kind: import("zod/mini").ZodMiniLiteral<"member">;
100
100
  memberId: import("zod/mini").ZodMiniString<string>;
@@ -106,13 +106,13 @@ export declare const installationSchema: z.ZodObject<{
106
106
  keyId: import("zod/mini").ZodMiniString<string>;
107
107
  }, z.core.$strip>], "kind">;
108
108
  at: z.ZodNumber;
109
- authorityVersion: z.ZodNumber;
110
- }, z.core.$strict>>>;
109
+ authorityVersion: z.ZodLazy<z.ZodNumber>;
110
+ }, z.core.$strict>>>>;
111
111
  approvedAnonymousMethods: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
112
112
  name: z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>;
113
113
  digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
114
114
  }, z.core.$strict>>>>;
115
- anonymousApproval: z.ZodNullable<z.ZodReadonly<z.ZodObject<{
115
+ anonymousApproval: z.ZodNullable<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
116
116
  by: import("zod/mini").ZodMiniDiscriminatedUnion<[import("zod/mini").ZodMiniObject<{
117
117
  kind: import("zod/mini").ZodMiniLiteral<"member">;
118
118
  memberId: import("zod/mini").ZodMiniString<string>;
@@ -124,19 +124,19 @@ export declare const installationSchema: z.ZodObject<{
124
124
  keyId: import("zod/mini").ZodMiniString<string>;
125
125
  }, z.core.$strip>], "kind">;
126
126
  at: z.ZodNumber;
127
- authorityVersion: z.ZodNumber;
128
- }, z.core.$strict>>>;
127
+ authorityVersion: z.ZodLazy<z.ZodNumber>;
128
+ }, z.core.$strict>>>>;
129
129
  }, z.core.$strict>;
130
130
  export type Installation = Readonly<z.output<typeof installationSchema>>;
131
131
  /** Host commands; floating targets have already been resolved to immutable Releases. */
132
- export declare const installationCommandSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
132
+ export declare const installationCommandSchema: z.ZodLazy<z.ZodDiscriminatedUnion<[z.ZodObject<{
133
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<{
134
+ capability: z.ZodPipe<z.ZodUnknown, z.ZodTransform<"agent.list" | "agent.manage" | "agent.rename" | "agent.avatar" | "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.ZodLazy<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
136
+ resources: z.ZodUnion<readonly [z.ZodLiteral<"tenant-scope">, z.ZodReadonly<z.ZodArray<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
137
137
  kind: z.ZodString;
138
138
  id: z.ZodString;
139
- }, z.core.$strict>>>>]>;
139
+ }, z.core.$strict>>>>>]>;
140
140
  }, z.core.$strict>>>>]>;
141
141
  release: z.ZodPipe<z.ZodUnknown, z.ZodTransform<{
142
142
  readonly releaseId: import("@fungi.computer/puffball").ReleaseId;
@@ -191,12 +191,12 @@ export declare const installationCommandSchema: z.ZodDiscriminatedUnion<[z.ZodOb
191
191
  }, z.core.$strict>, z.ZodObject<{
192
192
  kind: z.ZodLiteral<"set-grants">;
193
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<{
194
+ capability: z.ZodPipe<z.ZodUnknown, z.ZodTransform<"agent.list" | "agent.manage" | "agent.rename" | "agent.avatar" | "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.ZodLazy<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
196
+ resources: z.ZodUnion<readonly [z.ZodLiteral<"tenant-scope">, z.ZodReadonly<z.ZodArray<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
197
197
  kind: z.ZodString;
198
198
  id: z.ZodString;
199
- }, z.core.$strict>>>>]>;
199
+ }, z.core.$strict>>>>>]>;
200
200
  }, z.core.$strict>>>>]>;
201
201
  }, z.core.$strict>, z.ZodObject<{
202
202
  kind: z.ZodLiteral<"approve-anonymous">;
@@ -204,7 +204,7 @@ export declare const installationCommandSchema: z.ZodDiscriminatedUnion<[z.ZodOb
204
204
  name: z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>;
205
205
  digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
206
206
  }, z.core.$strict>>>>;
207
- }, z.core.$strict>], "kind">;
207
+ }, z.core.$strict>], "kind">>;
208
208
  export type InstallationCommand = z.output<typeof installationCommandSchema>;
209
209
  /** Values captured by the host; identities and time are never generated by the reducer. */
210
210
  export type InstallationContext = Readonly<{
@@ -221,7 +221,7 @@ export type InstallationContext = Readonly<{
221
221
  }>;
222
222
  }>;
223
223
  /** Closed refusals: failed commands produce no installation update. */
224
- export declare const installationRefusalSchema: z.ZodObject<{
224
+ export declare const installationRefusalSchema: z.ZodLazy<z.ZodObject<{
225
225
  kind: z.ZodLiteral<"refused">;
226
226
  code: z.ZodEnum<{
227
227
  unavailable: "unavailable";
@@ -231,7 +231,7 @@ export declare const installationRefusalSchema: z.ZodObject<{
231
231
  not_owner: "not_owner";
232
232
  invalid_command: "invalid_command";
233
233
  }>;
234
- }, z.core.$strict>;
234
+ }, z.core.$strict>>;
235
235
  export type InstallationRefusal = z.output<typeof installationRefusalSchema>;
236
236
  export type InstallationUpdate = Readonly<{
237
237
  next: Installation;
@@ -20,20 +20,18 @@ export function ownerCodec(schema) {
20
20
  });
21
21
  }
22
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);
23
+ export const installationIdSchema = z.lazy(() => z.string().regex(/^[a-z](?:[a-z0-9]|-(?!-)){0,62}$/u));
26
24
  /** A resource identity minted and resolved by the host, never by this module. */
27
- const resourceRefSchema = z
25
+ const resourceRefSchema = z.lazy(() => z
28
26
  .strictObject({
29
27
  kind: z.string().min(1),
30
28
  id: z.string().min(1),
31
29
  })
32
- .readonly();
33
- const operationsSchema = z
30
+ .readonly());
31
+ const operationsSchema = z.lazy(() => z
34
32
  .array(z.string().min(1))
35
33
  .refine((values) => new Set(values).size === values.length, "Operations must be unique")
36
- .readonly();
34
+ .readonly());
37
35
  /**
38
36
  * Consent approves capabilities, with explicit operation and resource ceilings.
39
37
  * A Release adding a method that requires an already-approved capability needs
@@ -58,10 +56,10 @@ export const approvedGrantSchema = z
58
56
  })
59
57
  .readonly();
60
58
  /** One grant per capability keeps operation/resource pairs unambiguous. */
61
- const approvedGrantsSchema = z
59
+ const approvedGrantsSchema = z.lazy(() => z
62
60
  .array(approvedGrantSchema)
63
61
  .refine((grants) => new Set(grants.map((grant) => grant.capability)).size === grants.length, "Capabilities must be unique")
64
- .readonly();
62
+ .readonly());
65
63
  /** Shorthand is accepted only on a command and expanded at approval time. */
66
64
  const grantRequestSchema = z.union([
67
65
  z.literal("none"),
@@ -74,15 +72,15 @@ const grantRequestSchema = z.union([
74
72
  .refine((grants) => new Set(grants.map((grant) => grant.capability)).size === grants.length, "Capabilities must be unique")
75
73
  .readonly(),
76
74
  ]);
77
- const versionSchema = z.number().int().positive();
75
+ const versionSchema = z.lazy(() => z.number().int().positive());
78
76
  /** An owner's approval of a fixed operation snapshot. */
79
- const approvalSchema = z
77
+ const approvalSchema = z.lazy(() => z
80
78
  .strictObject({
81
79
  by: hostActorSchema,
82
80
  at: z.number().int().nonnegative(),
83
81
  authorityVersion: versionSchema,
84
82
  })
85
- .readonly();
83
+ .readonly());
86
84
  const anonymousMethodsSchema = z
87
85
  .array(z
88
86
  .strictObject({
@@ -96,7 +94,7 @@ const anonymousMethodsSchema = z
96
94
  }), "Anonymous method identities must have unique, sorted names")
97
95
  .readonly();
98
96
  /** Immutable operation facts derived from descriptor methods and the binding registry. */
99
- export const declaredOperationsSchema = z.strictObject({
97
+ export const declaredOperationsSchema = z.lazy(() => z.strictObject({
100
98
  artifactDigest: ownerCodec(sha256DigestSchema),
101
99
  digest: ownerCodec(sha256DigestSchema),
102
100
  anonymousMethods: anonymousMethodsSchema,
@@ -108,7 +106,7 @@ export const declaredOperationsSchema = z.strictObject({
108
106
  })
109
107
  .readonly())
110
108
  .readonly(),
111
- });
109
+ }));
112
110
  /** Resolved immutable Release plus host-owned installation and approval state. */
113
111
  export const installationSchema = z
114
112
  .strictObject({
@@ -141,7 +139,7 @@ const releaseFields = {
141
139
  releasePin: installationSchema.shape.releasePin,
142
140
  };
143
141
  /** Host commands; floating targets have already been resolved to immutable Releases. */
144
- export const installationCommandSchema = z.discriminatedUnion("kind", [
142
+ export const installationCommandSchema = z.lazy(() => z.discriminatedUnion("kind", [
145
143
  z.strictObject({
146
144
  kind: z.literal("install"),
147
145
  ...releaseFields,
@@ -156,14 +154,17 @@ export const installationCommandSchema = z.discriminatedUnion("kind", [
156
154
  kind: z.literal("set-visibility"),
157
155
  visibility: installationSchema.shape.visibility,
158
156
  }),
159
- z.strictObject({ kind: z.literal("set-grants"), grants: grantRequestSchema }),
157
+ z.strictObject({
158
+ kind: z.literal("set-grants"),
159
+ grants: grantRequestSchema,
160
+ }),
160
161
  z.strictObject({
161
162
  kind: z.literal("approve-anonymous"),
162
163
  methods: anonymousMethodsSchema,
163
164
  }),
164
- ]);
165
+ ]));
165
166
  /** Closed refusals: failed commands produce no installation update. */
166
- export const installationRefusalSchema = z.strictObject({
167
+ export const installationRefusalSchema = z.lazy(() => z.strictObject({
167
168
  kind: z.literal("refused"),
168
169
  code: z.enum([
169
170
  "unavailable",
@@ -173,7 +174,7 @@ export const installationRefusalSchema = z.strictObject({
173
174
  "release_app_mismatch",
174
175
  "invalid_command",
175
176
  ]),
176
- });
177
+ }));
177
178
  /** Pure transition; hosts retain transactions, receipts, epochs and delivery. */
178
179
  export function applyInstallationCommand(current, rawCommand, context) {
179
180
  const refuse = (code) => Result.fail({ kind: "refused", code });
@@ -0,0 +1,40 @@
1
+ import { type AssetBuildPolicy } from "@fungi.computer/puffball/assets";
2
+ import { type ArtifactRef, type Sha256Digest } from "@fungi.computer/puffball";
3
+ import type { BuildOutput, StoredArtifact } from "./effect.js";
4
+ import type { CandidateOutputManifest } from "./submission-contract.js";
5
+ /** Write-once object storage for one artifact root. */
6
+ export type ImmutableArtifactObjects = Readonly<{
7
+ /** Create the object, or answer `exists` when the reference is taken. */
8
+ create(artifactRef: ArtifactRef, bytes: Uint8Array): Promise<"created" | "exists">;
9
+ /** Read the object's bytes, or `null` when absent. */
10
+ read(artifactRef: ArtifactRef): Promise<Uint8Array | null>;
11
+ }>;
12
+ /** One object ready to store under its reference. */
13
+ export type StagedArtifactObject = Readonly<{
14
+ artifactRef: ArtifactRef;
15
+ bytes: Uint8Array;
16
+ digest: Sha256Digest;
17
+ }>;
18
+ /** A build's output manifest and each distinct object it names. */
19
+ export type StagedBuildOutput = Readonly<{
20
+ manifest: CandidateOutputManifest;
21
+ objects: readonly StagedArtifactObject[];
22
+ }>;
23
+ /**
24
+ * Stage a build's bytes: the output manifest that names each object by its
25
+ * digest and size, and each distinct object once.
26
+ */
27
+ export declare function stageBuildOutput(output: BuildOutput): StagedBuildOutput;
28
+ /**
29
+ * Write the asset manifest and App root an output manifest names. The
30
+ * objects it names are already stored, and checked, by the caller.
31
+ */
32
+ export declare function writeArtifactRoot(manifest: CandidateOutputManifest, objects: ImmutableArtifactObjects, options: Readonly<{
33
+ rootArtifactRef: ArtifactRef;
34
+ assetPolicy: AssetBuildPolicy | undefined;
35
+ }>): Promise<StoredArtifact>;
36
+ /** Persist one build's UI, backend and root; an existing object must hold the same bytes. */
37
+ export declare function writeImmutableArtifact(output: BuildOutput, objects: ImmutableArtifactObjects, options: Readonly<{
38
+ rootArtifactRef: ArtifactRef;
39
+ assetPolicy: AssetBuildPolicy | undefined;
40
+ }>): Promise<StoredArtifact>;
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Oyster's one immutable artifact representation: digest-addressed bytes
3
+ * keyed by artifact reference, with media types carried by the App build
4
+ * and asset manifests (never by the store). The local packer writes it to a
5
+ * directory, and a host's build owner writes it to that host's artifact store,
6
+ * so a Release built on the host and one packed and uploaded share objects.
7
+ *
8
+ * It has two halves. Staging turns a build's bytes into content-addressed
9
+ * objects and the output manifest that names them by digest and size. The
10
+ * root half turns an output manifest into the small objects only the store's
11
+ * owner writes: the asset manifest and the App root. The local packer runs
12
+ * both; a host whose builder uploaded the objects itself runs only the root
13
+ * half, over the same manifest, so both write identical root bytes.
14
+ *
15
+ * Every object is derived and checked before the first is stored: an asset
16
+ * build that its policy refuses, or a root over its bound, stores nothing.
17
+ */
18
+ import { createHash } from "node:crypto";
19
+ import mime from "mime";
20
+ import * as Schema from "effect/Schema";
21
+ import { assetBuildJsonSchema, assetPathSchema, decodeAssetBuild, } from "@fungi.computer/puffball/assets";
22
+ import { appBuildJsonSchema, createAppBuild, } from "@fungi.computer/puffball/app";
23
+ import { parseArtifactRef, parseSha256Digest, } from "@fungi.computer/puffball";
24
+ function sha256(bytes) {
25
+ return parseSha256Digest(`sha256:${createHash("sha256").update(bytes).digest("hex")}`);
26
+ }
27
+ /** The content address of an object: `artifact:sha256-<hex>`. */
28
+ function contentArtifactRef(digest) {
29
+ return parseArtifactRef(`artifact:sha256-${digest.slice("sha256:".length)}`);
30
+ }
31
+ /**
32
+ * The root bound for an App whose recipe declares no asset policy: this
33
+ * writer's private fallback, not recipe policy. An asset recipe's own
34
+ * `maxManifestBytes` bounds its root as it bounds its asset manifest.
35
+ */
36
+ const fallbackRootBytes = 1024 * 1024;
37
+ /**
38
+ * Stage a build's bytes: the output manifest that names each object by its
39
+ * digest and size, and each distinct object once.
40
+ */
41
+ export function stageBuildOutput(output) {
42
+ const objects = [];
43
+ const stage = (bytes) => {
44
+ const digest = sha256(bytes);
45
+ if (!objects.some((object) => object.digest === digest))
46
+ objects.push({
47
+ artifactRef: contentArtifactRef(digest),
48
+ bytes: bytes.slice(),
49
+ digest,
50
+ });
51
+ return { digest, byteLength: bytes.byteLength };
52
+ };
53
+ const declared = output.declaration === undefined ? {} : { declaration: output.declaration };
54
+ const ui = (prepared) => prepared.content.kind === "html"
55
+ ? {
56
+ mediaType: "text/html; charset=utf-8",
57
+ content: { kind: "html", ...stage(prepared.content.bytes) },
58
+ }
59
+ : {
60
+ mediaType: "application/vnd.fungi.app-assets+json",
61
+ content: {
62
+ kind: "assets",
63
+ base: prepared.content.base,
64
+ assets: prepared.content.assets.map((asset) => ({
65
+ path: asset.path,
66
+ ...stage(asset.bytes),
67
+ })),
68
+ },
69
+ };
70
+ const manifest = output.kind === "ui"
71
+ ? { kind: "ui", ui: ui(output.ui), ...declared }
72
+ : output.kind === "backend"
73
+ ? { kind: "backend", backend: stage(output.backend), ...declared }
74
+ : {
75
+ kind: "app",
76
+ ui: ui(output.ui),
77
+ backend: stage(output.backend),
78
+ ...declared,
79
+ };
80
+ return { manifest, objects };
81
+ }
82
+ /**
83
+ * Derive the asset manifest (for an asset UI) and the App root an output
84
+ * manifest names, each checked against the policy, without storing either.
85
+ */
86
+ function artifactRootObjects(manifest, options) {
87
+ const objects = [];
88
+ const referenced = (object) => ({
89
+ artifactRef: contentArtifactRef(object.digest),
90
+ digest: object.digest,
91
+ version: object.digest,
92
+ byteLength: object.byteLength,
93
+ });
94
+ const stage = (bytes, ref) => {
95
+ const digest = sha256(bytes);
96
+ const artifactRef = ref ?? contentArtifactRef(digest);
97
+ objects.push({ artifactRef, bytes, digest });
98
+ return {
99
+ artifactRef,
100
+ digest,
101
+ version: digest,
102
+ byteLength: bytes.byteLength,
103
+ };
104
+ };
105
+ const ui = manifest.kind === "backend" ? null : manifest.ui;
106
+ const storedUi = (() => {
107
+ if (ui === null)
108
+ return null;
109
+ if (ui.content.kind === "html")
110
+ return { ...referenced(ui.content), mediaType: ui.mediaType };
111
+ if (!options.assetPolicy)
112
+ throw new Error("Asset writer requires explicit policy");
113
+ const byPath = new Map();
114
+ for (const asset of ui.content.assets) {
115
+ const path = Schema.decodeUnknownSync(assetPathSchema)(asset.path);
116
+ if (byPath.has(path))
117
+ throw new Error("Duplicate build path");
118
+ byPath.set(path, asset);
119
+ }
120
+ const assets = [];
121
+ for (const [path, asset] of [...byPath].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)) {
122
+ const mediaType = mime.getType(path);
123
+ const contentType = mediaType?.startsWith("text/")
124
+ ? `${mediaType}; charset=utf-8`
125
+ : mediaType;
126
+ if (!contentType)
127
+ throw new Error("Build asset has no maintained content type");
128
+ assets.push({ path, ...referenced(asset), contentType });
129
+ }
130
+ const assetManifest = new TextEncoder().encode(Schema.encodeUnknownSync(assetBuildJsonSchema)({
131
+ format: "fungi-assets-v1",
132
+ base: ui.content.base,
133
+ assets,
134
+ }));
135
+ decodeAssetBuild(assetManifest, options.assetPolicy);
136
+ return { ...stage(assetManifest), mediaType: ui.mediaType };
137
+ })();
138
+ const storedBackend = manifest.kind === "ui"
139
+ ? null
140
+ : {
141
+ ...referenced(manifest.backend),
142
+ mediaType: "application/vnd.fungi.app-backend+json",
143
+ };
144
+ const rootBytes = new TextEncoder().encode(Schema.encodeUnknownSync(appBuildJsonSchema)(createAppBuild(storedUi, storedBackend, manifest.declaration)));
145
+ if (rootBytes.byteLength >
146
+ (options.assetPolicy?.maxManifestBytes ?? fallbackRootBytes))
147
+ throw new Error("App root exceeds its manifest bound");
148
+ const root = stage(rootBytes, options.rootArtifactRef);
149
+ return {
150
+ objects,
151
+ root: Object.freeze({
152
+ artifactRef: root.artifactRef,
153
+ artifactDigest: root.digest,
154
+ }),
155
+ };
156
+ }
157
+ /** Store each object once; an existing object must hold the same bytes. */
158
+ async function storeObjects(staged, objects) {
159
+ for (const { artifactRef, bytes, digest } of staged)
160
+ if ((await objects.create(artifactRef, bytes)) === "exists") {
161
+ const existing = await objects.read(artifactRef);
162
+ if (existing === null || sha256(existing) !== digest)
163
+ throw new Error("Immutable artifact collision");
164
+ }
165
+ }
166
+ /**
167
+ * Write the asset manifest and App root an output manifest names. The
168
+ * objects it names are already stored, and checked, by the caller.
169
+ */
170
+ export async function writeArtifactRoot(manifest, objects, options) {
171
+ const derived = artifactRootObjects(manifest, options);
172
+ await storeObjects(derived.objects, objects);
173
+ return derived.root;
174
+ }
175
+ /** Persist one build's UI, backend and root; an existing object must hold the same bytes. */
176
+ export async function writeImmutableArtifact(output, objects, options) {
177
+ const staged = stageBuildOutput(output);
178
+ const derived = artifactRootObjects(staged.manifest, options);
179
+ await storeObjects([...staged.objects, ...derived.objects], objects);
180
+ return derived.root;
181
+ }
@@ -52,6 +52,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
52
52
  });
53
53
  import * as Schema from "effect/Schema";
54
54
  import { base64url } from "jose";
55
+ import { workspaceBuildPolicy } from "./build-policy.js";
55
56
  /** The secret-free Team job; no provider token enters publication. */
56
57
  const jobSchema = Schema.Struct({
57
58
  jobId: Schema.NonEmptyString,
@@ -177,6 +178,8 @@ export function createTeamBuildPublisher(options) {
177
178
  export function teamAppBuildCommand(configuration, uploadGrant) {
178
179
  const encoded = base64url.encode(JSON.stringify({
179
180
  uploadGrant,
181
+ // A builder that predates this policy refuses the whole request.
182
+ buildPolicy: workspaceBuildPolicy,
180
183
  appId: configuration.appId,
181
184
  sourceRevision: configuration.sourceRevision,
182
185
  recipe: configuration.source.recipe,
@@ -191,7 +194,10 @@ export function teamAppBuildCommand(configuration, uploadGrant) {
191
194
  `trap 'rm -rf "$work"' EXIT`,
192
195
  'mkdir "$work/tmp"',
193
196
  'export TMPDIR="$work/tmp"',
197
+ "clone_started=$(date +%s%3N)",
194
198
  'git clone -q --no-checkout -- "$TEAM_BUILD_REMOTE" "$work/source" 2>/dev/null',
195
- `node /opt/fungi/team-candidate-builder.mjs '${encoded}' "$work/source" "$work/candidate.json"`,
199
+ // The builder reports each phase's duration with its outcome.
200
+ "export TEAM_BUILD_CLONE_MS=$(( $(date +%s%3N) - clone_started ))",
201
+ `node /opt/fungi/team-candidate-builder.mjs '${encoded}' "$work/source" "$work/candidate"`,
196
202
  ].join("\n");
197
203
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The pinned workspace build policy: how a captured workspace's manifests
3
+ * build (`package.json#fungi.build`). v2 lets a dependency name its own
4
+ * package build script, which only an admitted Computer build runs.
5
+ *
6
+ * It is part of every workspace HTML recipe's toolchain, so its digest moves
7
+ * with it, and the Computer build request carries it at the top level, where
8
+ * a builder compiled before it existed refuses the request outright.
9
+ */
10
+ export declare const workspaceBuildPolicy = "package-json-fungi-build-v2";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The pinned workspace build policy: how a captured workspace's manifests
3
+ * build (`package.json#fungi.build`). v2 lets a dependency name its own
4
+ * package build script, which only an admitted Computer build runs.
5
+ *
6
+ * It is part of every workspace HTML recipe's toolchain, so its digest moves
7
+ * with it, and the Computer build request carries it at the top level, where
8
+ * a builder compiled before it existed refuses the request outright.
9
+ */
10
+ export const workspaceBuildPolicy = "package-json-fungi-build-v2";
@@ -3,7 +3,10 @@ import * as Schema from "effect/Schema";
3
3
  * One captured dependency build step. Paths are relative to the captured
4
4
  * snapshot root. A TypeScript step compiles one config; a Tailwind step
5
5
  * compiles one stylesheet input into its output (the same path for an App's
6
- * own stylesheet, which compiles in place).
6
+ * own stylesheet, which compiles in place). A script step runs the one
7
+ * `package.json` script a workspace dependency names as its build
8
+ * (`fungi.build.script`), with pnpm, in that package's directory; only an
9
+ * admitted Computer build executes it.
7
10
  */
8
11
  export declare const workspaceBuildStepSchema: Schema.Union<readonly [Schema.Struct<{
9
12
  readonly kind: Schema.Literal<"typescript">;
@@ -12,6 +15,10 @@ export declare const workspaceBuildStepSchema: Schema.Union<readonly [Schema.Str
12
15
  readonly kind: Schema.Literal<"tailwind">;
13
16
  readonly input: Schema.String;
14
17
  readonly output: Schema.String;
18
+ }>, Schema.Struct<{
19
+ readonly kind: Schema.Literal<"script">;
20
+ readonly directory: Schema.String;
21
+ readonly script: Schema.String;
15
22
  }>]>;
16
23
  /** One captured dependency build step. */
17
24
  export type WorkspaceBuildStep = Schema.Schema.Type<typeof workspaceBuildStepSchema>;
@@ -23,4 +30,8 @@ export declare const workspaceBuildStepsJsonSchema: Schema.fromJsonString<Schema
23
30
  readonly kind: Schema.Literal<"tailwind">;
24
31
  readonly input: Schema.String;
25
32
  readonly output: Schema.String;
33
+ }>, Schema.Struct<{
34
+ readonly kind: Schema.Literal<"script">;
35
+ readonly directory: Schema.String;
36
+ readonly script: Schema.String;
26
37
  }>]>>>;
@@ -3,7 +3,10 @@ import * as Schema from "effect/Schema";
3
3
  * One captured dependency build step. Paths are relative to the captured
4
4
  * snapshot root. A TypeScript step compiles one config; a Tailwind step
5
5
  * compiles one stylesheet input into its output (the same path for an App's
6
- * own stylesheet, which compiles in place).
6
+ * own stylesheet, which compiles in place). A script step runs the one
7
+ * `package.json` script a workspace dependency names as its build
8
+ * (`fungi.build.script`), with pnpm, in that package's directory; only an
9
+ * admitted Computer build executes it.
7
10
  */
8
11
  export const workspaceBuildStepSchema = Schema.Union([
9
12
  Schema.Struct({
@@ -15,6 +18,11 @@ export const workspaceBuildStepSchema = Schema.Union([
15
18
  input: Schema.String,
16
19
  output: Schema.String,
17
20
  }),
21
+ Schema.Struct({
22
+ kind: Schema.Literal("script"),
23
+ directory: Schema.String,
24
+ script: Schema.String,
25
+ }),
18
26
  ]);
19
27
  /** The steps one dependency-build process runs, encoded as one argument. */
20
28
  export const workspaceBuildStepsJsonSchema = Schema.fromJsonString(Schema.Array(workspaceBuildStepSchema));