@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
@@ -0,0 +1,37 @@
1
+ /**
2
+ * A build's uploaded objects in an R2-shaped store: content addressed
3
+ * (`artifact:sha256-<hex>`), written once from a stream with the store
4
+ * itself verifying the digest, and described by `head()` without a read.
5
+ * The bytes never pass through an isolate's memory as a whole.
6
+ */
7
+ import { type ArtifactRef, type Sha256Digest } from "@fungi.computer/puffball";
8
+ import type { ArtifactHead } from "../release.js";
9
+ import type { CandidateObject, CandidateObjectMediaType } from "./submission-contract.js";
10
+ /** The parts of an R2 bucket a content-object store uses. */
11
+ export type ContentObjectBucket = Readonly<{
12
+ /** Describe the object at `key` without its body, or `null` when absent. */
13
+ head(key: string): Promise<Readonly<{
14
+ size: number;
15
+ checksums: Readonly<{
16
+ sha256?: ArrayBuffer;
17
+ }>;
18
+ }> | null>;
19
+ /** Store `value` at `key`; the bucket refuses bytes whose SHA-256 differs. */
20
+ put(key: string, value: ReadableStream<Uint8Array> | Uint8Array, options: Readonly<{
21
+ sha256: string;
22
+ httpMetadata: Readonly<{
23
+ contentType: CandidateObjectMediaType;
24
+ }>;
25
+ }>): Promise<object | null>;
26
+ }>;
27
+ /** The store key of one content-addressed object. */
28
+ export declare function contentObjectRef(digest: Sha256Digest): ArtifactRef;
29
+ /** Describe a stored object by its size and the SHA-256 the store verified. */
30
+ export declare function headContentObject(bucket: ContentObjectBucket, artifactRef: ArtifactRef): Promise<ArtifactHead | null>;
31
+ /**
32
+ * Stream one object into the store under its digest. The store refuses a
33
+ * body whose SHA-256 differs; an object already stored with the same
34
+ * verified digest and size is kept (write-once: a stored version may be
35
+ * pinned by a Release) and the body is discarded, never stored.
36
+ */
37
+ export declare function putContentObject(bucket: ContentObjectBucket, object: CandidateObject, body: ReadableStream<Uint8Array> | null, mediaType: CandidateObjectMediaType): Promise<"stored" | "refused">;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * A build's uploaded objects in an R2-shaped store: content addressed
3
+ * (`artifact:sha256-<hex>`), written once from a stream with the store
4
+ * itself verifying the digest, and described by `head()` without a read.
5
+ * The bytes never pass through an isolate's memory as a whole.
6
+ */
7
+ import { parseArtifactRef, parseSha256Digest, } from "@fungi.computer/puffball";
8
+ /** The store key of one content-addressed object. */
9
+ export function contentObjectRef(digest) {
10
+ return parseArtifactRef(`artifact:sha256-${digest.slice("sha256:".length)}`);
11
+ }
12
+ function hex(bytes) {
13
+ return Array.from(new Uint8Array(bytes), (byte) => byte.toString(16).padStart(2, "0")).join("");
14
+ }
15
+ /** Describe a stored object by its size and the SHA-256 the store verified. */
16
+ export async function headContentObject(bucket, artifactRef) {
17
+ const object = await bucket.head(artifactRef);
18
+ if (object === null)
19
+ return null;
20
+ const sha256 = object.checksums.sha256;
21
+ return {
22
+ byteLength: object.size,
23
+ digest: sha256 === undefined ? null : parseSha256Digest(`sha256:${hex(sha256)}`),
24
+ };
25
+ }
26
+ /**
27
+ * Stream one object into the store under its digest. The store refuses a
28
+ * body whose SHA-256 differs; an object already stored with the same
29
+ * verified digest and size is kept (write-once: a stored version may be
30
+ * pinned by a Release) and the body is discarded, never stored.
31
+ */
32
+ export async function putContentObject(bucket, object, body, mediaType) {
33
+ const artifactRef = contentObjectRef(object.digest);
34
+ const existing = await headContentObject(bucket, artifactRef);
35
+ if (existing !== null &&
36
+ existing.digest === object.digest &&
37
+ existing.byteLength === object.byteLength) {
38
+ // Read the body through (discarding it) rather than cancel it: a
39
+ // cancelled request body resets the uploader's connection mid-send.
40
+ await body?.pipeTo(new WritableStream());
41
+ return "stored";
42
+ }
43
+ try {
44
+ await bucket.put(artifactRef, body ?? new Uint8Array(0), {
45
+ sha256: object.digest.slice("sha256:".length),
46
+ httpMetadata: { contentType: mediaType },
47
+ });
48
+ }
49
+ catch {
50
+ return "refused";
51
+ }
52
+ return "stored";
53
+ }
@@ -3,6 +3,7 @@ import type { AppDeclaration } from "@fungi.computer/puffball";
3
3
  import * as Effect from "effect/Effect";
4
4
  import * as Schema from "effect/Schema";
5
5
  import type * as Scope from "effect/Scope";
6
+ import type { CandidateOutputManifest } from "./submission-contract.js";
6
7
  /** Canonical recipe policy shared by core and the local adapter. */
7
8
  export declare const pinnedRecipeSchema: Schema.Struct<{
8
9
  readonly kind: Schema.optional<Schema.Literals<readonly ["bundled-html", "source-html"]>>;
@@ -211,6 +212,12 @@ export type CapturedConfig = Readonly<{
211
212
  readonly recipe: PinnedRecipe;
212
213
  readonly buildRecipeDigest: Sha256Digest;
213
214
  }>;
215
+ /**
216
+ * Whether a pinned recipe pins this Publisher's current toolchain (its build
217
+ * policy included). An executor refuses any other: a recipe pinned under
218
+ * another policy describes a build this code does not perform.
219
+ */
220
+ export declare function pinsCurrentToolchain(recipe: PinnedRecipe): boolean;
214
221
  /** Hash every captured build and output-policy input in the Publisher recipe. */
215
222
  export declare function digestPinnedRecipe(recipe: PinnedRecipe): Sha256Digest;
216
223
  /** Decode and normalize Publisher-owned recipe policy for durable seed proof. */
@@ -221,6 +228,17 @@ export declare function normalizeAppPublisherRecipe(input: {
221
228
  recipe: PinnedRecipe;
222
229
  buildRecipeDigest: Sha256Digest;
223
230
  }>;
231
+ /**
232
+ * The largest single object a build under this recipe may upload: no larger
233
+ * than the largest output bound the recipe could assign it under.
234
+ */
235
+ export declare function pinnedRecipeObjectBound(recipe: PinnedRecipe): number;
236
+ /**
237
+ * Whether output built elsewhere (a Computer's candidate manifest) is one
238
+ * this recipe could assign: the same surfaces and `maxOutputBytes` bound the
239
+ * preparer applies here. Asset sizes stay the artifact writer's policy.
240
+ */
241
+ export declare function fitsPinnedRecipe(output: CandidateOutputManifest, recipe: PinnedRecipe): boolean;
224
242
  /** Construct one private App Publisher with copied core configuration and roles. */
225
243
  export declare function createAppCandidatePreparer(options: Pick<AppPublisherOptions, "config" | "sourceCapture" | "buildExecutor">): AppCandidatePreparer;
226
244
  /** Prepare a candidate from a previously captured recipe. */
@@ -1,5 +1,6 @@
1
1
  import { assetBuildPolicySchema, assetBaseSchema, } from "@fungi.computer/puffball/assets";
2
2
  import { boundedBundlePolicy, bundledHtmlRecipe } from "./html-recipe.js";
3
+ import { workspaceBuildPolicy } from "./build-policy.js";
3
4
  import * as crypto from "node:crypto";
4
5
  import { backendJsonSchema, durableBackendSchema, } from "@fungi.computer/puffball/backend";
5
6
  import { parseSha256Digest, } from "@fungi.computer/puffball";
@@ -63,7 +64,7 @@ const workspaceHtmlToolchain = Object.freeze({
63
64
  tailwindOxide: "4.2.1",
64
65
  tailwindResolver: "5.24.5",
65
66
  typography: "0.5.19",
66
- buildPolicy: "package-json-fungi-build-v1",
67
+ buildPolicy: workspaceBuildPolicy,
67
68
  recipe: "captured-workspace-html-v1",
68
69
  });
69
70
  const workspaceHtmlBackendToolchain = Object.freeze({
@@ -104,6 +105,29 @@ export class AppPublisherError extends Error {
104
105
  function sha256Digest(value) {
105
106
  return parseSha256Digest(`sha256:${crypto.createHash("sha256").update(value, "utf8").digest("hex")}`);
106
107
  }
108
+ /** The toolchain this Publisher pins for a recipe of this kind, entry and backend. */
109
+ function currentToolchain(recipe) {
110
+ if (recipe.kind === "source-html")
111
+ return sourceHtmlToolchain;
112
+ if (recipe.kind === bundledHtmlRecipe.kind)
113
+ return recipe.backend === null
114
+ ? bundledHtmlRecipe.toolchain
115
+ : { ...bundledHtmlRecipe.toolchain, ...esmToolchain, pnpm: "11.24.0" };
116
+ if (recipe.entry.endsWith(".html"))
117
+ return recipe.backend === null
118
+ ? workspaceHtmlToolchain
119
+ : workspaceHtmlBackendToolchain;
120
+ return esmToolchain;
121
+ }
122
+ /**
123
+ * Whether a pinned recipe pins this Publisher's current toolchain (its build
124
+ * policy included). An executor refuses any other: a recipe pinned under
125
+ * another policy describes a build this code does not perform.
126
+ */
127
+ export function pinsCurrentToolchain(recipe) {
128
+ const entries = (toolchain) => JSON.stringify(Object.entries(toolchain).sort(([a], [b]) => a.localeCompare(b)));
129
+ return entries(currentToolchain(recipe)) === entries(recipe.toolchain);
130
+ }
107
131
  function captureConfig(input) {
108
132
  let decoded;
109
133
  try {
@@ -148,17 +172,7 @@ function captureConfig(input) {
148
172
  : workspaceHtml
149
173
  ? "html"
150
174
  : "esm",
151
- toolchain: sourceHtml
152
- ? sourceHtmlToolchain
153
- : bundled
154
- ? decoded.recipe.backend === null
155
- ? bundledHtmlRecipe.toolchain
156
- : { ...bundledHtmlRecipe.toolchain, ...esmToolchain, pnpm: "11.24.0" }
157
- : workspaceHtml
158
- ? decoded.recipe.backend === null
159
- ? workspaceHtmlToolchain
160
- : workspaceHtmlBackendToolchain
161
- : esmToolchain,
175
+ toolchain: currentToolchain(decoded.recipe),
162
176
  maxOutputBytes: decoded.recipe.maxOutputBytes,
163
177
  platform: decoded.recipe.backend === null
164
178
  ? "browser"
@@ -303,6 +317,32 @@ function encodeBackendOutput(bytes, recipe) {
303
317
  code: new TextDecoder().decode(bytes),
304
318
  }).pipe(Effect.map((encoded) => new TextEncoder().encode(encoded)), Effect.filterOrFail((encoded) => encoded.byteLength <= recipe.maxOutputBytes, () => stageFailure("build")), Effect.mapError(() => stageFailure("build")));
305
319
  }
320
+ /**
321
+ * The largest single object a build under this recipe may upload: no larger
322
+ * than the largest output bound the recipe could assign it under.
323
+ */
324
+ export function pinnedRecipeObjectBound(recipe) {
325
+ return Math.max(recipe.maxOutputBytes, recipe.assets?.maxObjectBytes ?? 0);
326
+ }
327
+ /**
328
+ * Whether output built elsewhere (a Computer's candidate manifest) is one
329
+ * this recipe could assign: the same surfaces and `maxOutputBytes` bound the
330
+ * preparer applies here. Asset sizes stay the artifact writer's policy.
331
+ */
332
+ export function fitsPinnedRecipe(output, recipe) {
333
+ const uiFits = (ui) => ui.content.kind === "html"
334
+ ? ui.content.byteLength <= recipe.maxOutputBytes
335
+ : recipe.assets !== undefined && ui.content.base === recipe.assetBase;
336
+ const backendFits = (backend) => recipe.backend !== null && backend.byteLength <= recipe.maxOutputBytes;
337
+ switch (output.kind) {
338
+ case "ui":
339
+ return recipe.backend === null && uiFits(output.ui);
340
+ case "backend":
341
+ return backendFits(output.backend);
342
+ case "app":
343
+ return uiFits(output.ui) && backendFits(output.backend);
344
+ }
345
+ }
306
346
  function assignAppOutput(built, recipe) {
307
347
  switch (built.kind) {
308
348
  case "bytes":
@@ -1,5 +1,8 @@
1
1
  /** Run a nested repository without inheriting the caller's Git checkout. */
2
2
  export declare function repositoryEnvironment(): NodeJS.ProcessEnv;
3
+ /** Git environment carrying host-held request headers (credentials) only in
4
+ * Git's own config environment: never argv, a URL or a trace. */
5
+ export declare function authenticatedGitEnvironment(headers: Readonly<Record<string, string>>): NodeJS.ProcessEnv;
3
6
  /** Git environment for one authenticated Artifacts fetch; caller validates token. */
4
7
  export declare function artifactsGitEnvironment(token: string): NodeJS.ProcessEnv;
5
8
  /** Credential-free private route used by the hosted Publisher Git client. */
@@ -2,8 +2,9 @@
2
2
  export function repositoryEnvironment() {
3
3
  return Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.startsWith("GIT_")));
4
4
  }
5
- /** Git environment for one authenticated Artifacts fetch; caller validates token. */
6
- export function artifactsGitEnvironment(token) {
5
+ /** Git environment carrying host-held request headers (credentials) only in
6
+ * Git's own config environment: never argv, a URL or a trace. */
7
+ export function authenticatedGitEnvironment(headers) {
7
8
  const excludedEnvironmentNames = new Set([
8
9
  "GIT_CURL_VERBOSE",
9
10
  "CLOUDFLARE_API_TOKEN",
@@ -12,19 +13,30 @@ export function artifactsGitEnvironment(token) {
12
13
  "ARTIFACTS_TOKEN",
13
14
  ]);
14
15
  const environment = Object.fromEntries(Object.entries(repositoryEnvironment()).filter(([name]) => !name.startsWith("GIT_TRACE") && !excludedEnvironmentNames.has(name)));
16
+ for (const [name, value] of Object.entries(headers))
17
+ if (!/^[A-Za-z0-9-]+$/u.test(name) || /[\r\n\0]/u.test(value))
18
+ throw new Error("Git request header is invalid");
19
+ // An empty first value resets any inherited extra headers.
20
+ const config = [
21
+ ["http.extraHeader", ""],
22
+ ...Object.entries(headers).map(([name, value]) => ["http.extraHeader", `${name}: ${value}`]),
23
+ ["http.followRedirects", "false"],
24
+ ];
15
25
  return {
16
26
  ...environment,
17
- GIT_CONFIG_KEY_0: "http.extraHeader",
18
- GIT_CONFIG_VALUE_0: "",
19
- GIT_CONFIG_KEY_1: "http.extraHeader",
20
- GIT_CONFIG_VALUE_1: `Authorization: Bearer ${token}`,
21
- GIT_CONFIG_KEY_2: "http.followRedirects",
22
- GIT_CONFIG_VALUE_2: "false",
23
- GIT_CONFIG_COUNT: "3",
27
+ ...Object.fromEntries(config.flatMap(([key, value], index) => [
28
+ [`GIT_CONFIG_KEY_${String(index)}`, key],
29
+ [`GIT_CONFIG_VALUE_${String(index)}`, value],
30
+ ])),
31
+ GIT_CONFIG_COUNT: String(config.length),
24
32
  GIT_TRACE: "0",
25
33
  GIT_TRACE_CURL: "0",
26
34
  };
27
35
  }
36
+ /** Git environment for one authenticated Artifacts fetch; caller validates token. */
37
+ export function artifactsGitEnvironment(token) {
38
+ return authenticatedGitEnvironment({ Authorization: `Bearer ${token}` });
39
+ }
28
40
  /** Credential-free private route used by the hosted Publisher Git client. */
29
41
  export function hostedArtifactsGitRemote(endpoint, repository) {
30
42
  if (!/^[a-zA-Z0-9._-]{1,128}$/.test(repository))
@@ -40,12 +40,22 @@ export declare function createFirstPartyAppSourceDistribution(options: {
40
40
  readonly source: CapturedSource;
41
41
  readonly entry: string;
42
42
  }): Effect.Effect<FirstPartyAppSourceDistribution, unknown>;
43
+ /** How one build executor is composed; never part of a recipe or request. */
44
+ export type LocalBuildExecutorOptions = Readonly<{
45
+ packageStoreDirectory?: string;
46
+ /**
47
+ * `run` executes a dependency's declared `fungi.build.script`. Only the
48
+ * admitted Computer build sets it; every other composition refuses a source
49
+ * that declares one, before installing anything.
50
+ */
51
+ packageScripts?: "run";
52
+ /** Hears how long the dependency install and the compile each took. */
53
+ onPhase?: (phase: "install" | "build", milliseconds: number) => void;
54
+ }>;
43
55
  /** Exact pinned esbuild options shared by local builds and artifact-budget proofs. */
44
56
  export declare function localSnapshotBuildOptions(source: CapturedSource, recipe: PinnedRecipe, emission: DefinitionEmission): esbuild.BuildOptions;
45
57
  /** Local proving adapter; it builds only the captured extracted snapshot. */
46
- export declare function createLocalBuildExecutor(options?: Readonly<{
47
- packageStoreDirectory?: string;
48
- }>): BuildExecutor;
58
+ export declare function createLocalBuildExecutor(options?: LocalBuildExecutorOptions): BuildExecutor;
49
59
  /** Local proving adapter; the configured root is private and write-once. */
50
60
  export declare function createLocalArtifactWriter(artifactRoot: string, assetPolicy?: AssetBuildPolicy): ArtifactWriter;
51
61
  /** Local Git, recipe and artifact directory configuration with a Release catalog. */