Fork Horse repository, listed by its owner. Publisher snapshot.
scripts/deployment-config.ts
Snapshot at 34e3175
1// The two shapes `deploy.ts` sits between: `deployment.jsonc` on the way in, and the
2// `wrangler.prod.jsonc` files it generates on the way out.
3//
4// The wrangler side reuses the submodule's own declarations rather than redeclaring them, so a
5// base-config change upstream surfaces here as a type error during `pnpm types:scripts` instead of
6// as a silently dropped key at deploy time. The imports are type-only, which under
7// `verbatimModuleSyntax` erase completely -- `node` never resolves them, so the submodule's
8// runtime dependencies are not this script's problem.
9
10import type { CoreDeployment } from "./core-deployment.ts";
11import type { ForkhorseGatekeeperDeployment } from "./gatekeeper-deployment.ts";
12import type {
13 BindingDecl,
14 ObservabilityConfig,
15 WranglerConfig,
16} from "../cloudflare-os/scripts/release/manifest-lib.ts";
17
18/** A model provider the Workshop can serve through AI Gateway with deployment-managed keys. */
19export type AiGatewayProvider = "anthropic" | "openai" | "google" | "cloudflare";
20
21/** Every provider {@link AiGatewayProvider} allows, for validation and for error messages. */
22export const AI_GATEWAY_PROVIDERS: readonly AiGatewayProvider[] =
23 ["anthropic", "openai", "google", "cloudflare"];
24
25/**
26 * The public address of the router Worker. Exactly one field is set; `validateConfig` enforces
27 * that, since wrangler would otherwise happily deploy both a custom domain and a workers.dev route.
28 */
29export interface RouterRoute {
30 /** Evaluation route on the account's workers.dev subdomain. */
31 workersDev?: boolean;
32 /** Production hostname in an active Cloudflare zone. Wrangler creates DNS and TLS for it. */
33 customDomain?: string;
34}
35
36/** Cloudflare Access trust boundary and the `/admin` allowlist. */
37export interface AccessConfig {
38 /** Access team origin, HTTPS with no path. */
39 issuer: string;
40 /** The self-hosted Access application's AUD tag. */
41 audience: string;
42 /** Access-verified emails allowed into `/admin`. */
43 admins: string[];
44}
45
46/**
47 * Deployment-managed model catalog, served through Cloudflare AI Gateway.
48 *
49 * Transport is derived rather than configured: the Workshop reaches the gateway over its
50 * `WORKERS_AI` binding, which is pre-authenticated inside the Worker's own account. Only a gateway
51 * in a *different* account, or the `google` provider, needs `CF_AI_GATEWAY_API_TOKEN` -- see
52 * `AiGatewayConfig` in cloudflare-os/packages/workshop-backend/src/ai-gateway.ts, whose constructor
53 * throws this script mirrors.
54 */
55export interface AiGatewayConfigInput {
56 /** Whether the Workshop advertises a deployment-managed catalog at all. */
57 enabled: boolean;
58 /** Gateway name. `"default"` is the one Cloudflare creates for an account on first use. */
59 name?: string;
60 /** Account owning the gateway. `null` reuses the deployment's own `accountId`. */
61 accountId?: string | null;
62 /** Providers to advertise. Must be non-empty when enabled. */
63 providers?: AiGatewayProvider[];
64 /**
65 * No longer configurable: Workers AI rides the same gateway route as every other provider.
66 * Declared only so `validateConfig` can reject a leftover key loudly -- silently ignoring one
67 * produces a deploy that succeeds with an empty model picker.
68 */
69 workersAi?: never;
70}
71
72/** Context Gatekeeper storage and the sharing boundary its data is scoped to. */
73export interface ContextConfig {
74 /**
75 * Stable label isolating Context data belonging to this deployment. `null` scopes it to the
76 * deployment's public origin, which is what the hosted deploy does.
77 */
78 sharingDomain: string | null;
79 /** Existing snapshot KV namespace, or `null` for Wrangler automatic provisioning. */
80 kvNamespaceId: string | null;
81 /** Optional Git-compatible collection storage. Absent or `{}` means disabled. */
82 artifacts?: { enabled?: boolean; namespace?: string };
83}
84
85/** Worker telemetry. Maps onto wrangler's `observability` block. */
86export interface DeploymentObservabilityConfig {
87 enabled: boolean;
88 headSamplingRate: number;
89 logs: { invocationLogs: boolean };
90 traces: { enabled: boolean; headSamplingRate: number };
91}
92
93/**
94 * `deployment.jsonc`, parsed.
95 *
96 * This describes the *valid* shape. The file is hand-edited JSONC with no schema behind it, so
97 * every field is still checked at runtime by `validateConfig` -- the type is what makes the
98 * generation code readable, not a guarantee about what is on disk.
99 */
100export interface DeploymentConfig {
101 /** Cloudflare account owning every Worker and provisioned resource. 32 hex characters. */
102 accountId: string;
103 /**
104 * The deployment's public origin: HTTPS, no path, no trailing slash. `null` derives it from
105 * `workers.router.route.customDomain`, and is invalid on a workers.dev route -- the account's
106 * workers.dev subdomain is not in this file and wrangler exposes no way to look it up.
107 */
108 publicBaseUrl: string | null;
109 /** Permanent Worker service identities. Each must be unique within the account. */
110 workers: {
111 /** Owns the public route, serves the frontend, and proxies to every other Worker. */
112 router: { name: string; route: RouterRoute };
113 workshop: { name: string };
114 context: { name: string };
115 scheduler: { name: string };
116 /** Upstream connector for user-supplied MCP endpoints; deployed by default. */
117 mcp: { name: string };
118 /** Legacy sample setting; ignored because the agent fork vendor replaces it. */
119 customGatekeeper?: { name: string };
120 /** Only required when `errorReporting.enabled`. */
121 errorReporter?: { name: string };
122 };
123 core?: CoreDeployment;
124 forkhorseGatekeeper?: ForkhorseGatekeeperDeployment;
125 access: AccessConfig;
126 aiGateway: AiGatewayConfigInput;
127 context: ContextConfig;
128 /** Legacy sample setting; ignored. The package is retained only as a reference. */
129 customGatekeeper?: { name: string; message: string };
130 /** Private explicit-issue destination. */
131 errorReporting: { enabled: boolean; environment?: string; release?: string | null };
132 /** Workshop KV/R2. `null` requests Wrangler automatic provisioning. */
133 resources: {
134 blueprintsKvNamespaceId: string | null;
135 avatarsKvNamespaceId: string | null;
136 blueprintContentBucket: string | null;
137 };
138 observability: DeploymentObservabilityConfig;
139}
140
141/**
142 * `ObservabilityConfig` plus the traces block. Upstream's type does not declare it, though the
143 * backend's own `wrangler.jsonc` sets it and this script writes it for every Worker.
144 */
145export interface ProdObservabilityConfig extends ObservabilityConfig {
146 traces?: { enabled?: boolean; head_sampling_rate?: number };
147}
148
149/**
150 * A generated `wrangler.prod.jsonc`: the subset of wrangler config upstream's `WranglerConfig`
151 * declares, plus the keys this script writes that it does not.
152 *
153 * Four keys are replaced rather than added to. Upstream's observability type is deliberately closed
154 * and has no `traces`; its `artifacts` is a single `BindingDecl`, while wrangler takes an array and
155 * the Context Gatekeeper's entry carries a `namespace`; and its KV/R2 bindings are bare
156 * `BindingDecl`s, because the release manifest replaces every id with a placeholder while a
157 * generated config either names the resource or leaves it for automatic provisioning.
158 *
159 * `assets` adds the core Worker-first gate; Queue consumers and Durable Objects are core bindings.
160 */
161export type ProdWranglerConfig =
162 Omit<WranglerConfig, "observability" | "artifacts" | "kv_namespaces" | "r2_buckets" | "assets" | "queues">
163 & {
164 durable_objects?: { bindings: { name: string; class_name: string }[] };
165 assets?: {
166 directory?: string;
167 binding?: string;
168 run_worker_first?: boolean | string[];
169 html_handling?: "none";
170 not_found_handling?: "none";
171 };
172 queues?: {
173 consumers: {
174 queue: string;
175 dead_letter_queue: string;
176 max_retries: number;
177 max_batch_size: number;
178 }[];
179 };
180 /** KV bindings. `id` absent requests Wrangler automatic provisioning. */
181 kv_namespaces?: (BindingDecl & { id?: string })[];
182 /** R2 bindings. `bucket_name` absent requests Wrangler automatic provisioning. */
183 r2_buckets?: (BindingDecl & { bucket_name?: string })[];
184 /** The deployment's account, pinned so a stray `CLOUDFLARE_ACCOUNT_ID` cannot redirect it. */
185 account_id?: string;
186 /** Whether the Worker answers on the account's workers.dev subdomain. */
187 workers_dev?: boolean;
188 /** Custom-domain routes. Wrangler creates DNS and TLS for each. */
189 routes?: { pattern: string; custom_domain: boolean }[];
190 /** Turned off on every Worker: a preview URL is an unauthenticated path around Access. */
191 preview_urls?: boolean;
192 observability?: ProdObservabilityConfig;
193 /** Workers AI. Both the AI Gateway transport and what webFetch's `toMarkdown()` runs on. */
194 ai?: BindingDecl;
195 /** Secrets wrangler refuses to deploy without. Emitted only when one is genuinely needed. */
196 secrets?: { required: string[] };
197 /** Artifacts namespaces. An array, unlike upstream's single-binding declaration. */
198 artifacts?: { binding: string; namespace: string }[];
199 };
200
201/** The generated configs, keyed as `deployment.jsonc` keys them. */
202export interface GeneratedConfigs {
203 forkhorseGatekeeper?: ProdWranglerConfig;
204 core?: ProdWranglerConfig;
205 router: ProdWranglerConfig;
206 workshop: ProdWranglerConfig;
207 context: ProdWranglerConfig;
208 scheduler: ProdWranglerConfig;
209 mcp: ProdWranglerConfig;
210 /** Absent when `errorReporting.enabled` is false. */
211 errorReporter?: ProdWranglerConfig;
212}
213
214/** The upstream base configs the generated ones are derived from. */
215export interface BaseConfigs {
216 forkhorseGatekeeper?: ProdWranglerConfig;
217 core?: ProdWranglerConfig;
218 router: ProdWranglerConfig;
219 workshop: ProdWranglerConfig;
220 context: ProdWranglerConfig;
221 scheduler: ProdWranglerConfig;
222 mcp: ProdWranglerConfig;
223 customGatekeeper?: ProdWranglerConfig;
224 errorReporter: ProdWranglerConfig;
225}
226
227/** One build step `deploy.ts` runs before deploying. See `buildCommands`. */
228export interface BuildCommand {
229 /** Arguments passed to `pnpm`, from the repository root. */
230 args: string[];
231 /**
232 * Variables set on top of the ambient environment for this step alone. Explicit rather than
233 * inherited: a build-time flag that arrives by inheritance is one a cached `vp` run would strip.
234 */
235 env?: Record<string, string>;
236}
237