Skip to content
Source
Sign inSign up
Open History to see the last commit

managedProviders.ts

607 lines24.0 KB

1import {
2 AntigravitySettings,
3 ClaudeSettings,
4 CodexSettings,
5 defaultInstanceIdForDriver,
6 disallowedProviderMessage,
7 type ManagedProviderInstance,
8 type ManagedProviderLimits,
9 type ManagedPolicySummary,
10 ProviderDriverKind,
11 type ProviderInstanceConfig,
12 type ProviderInstanceEnvironmentVariable,
13 type ProviderInstanceId,
14 resolveProviderInstanceEnabled,
15 type RuntimeMode,
16 type ServerSettings,
17} from "@t3tools/contracts";
18import { acpRegistryClient } from "@t3tools/provider-acp-registry/client";
19import { cursorClient } from "@t3tools/provider-cursor/client";
20import { fxClient } from "@t3tools/provider-fx/client";
21import { grokClient } from "@t3tools/provider-grok/client";
22import { kiroClient } from "@t3tools/provider-kiro/client";
23import { museClient } from "@t3tools/provider-muse/client";
24import { openCodeClient } from "@t3tools/provider-opencode/client";
25import { piClient } from "@t3tools/provider-pi/client";
26import type { ProviderClientDefinition } from "@t3tools/provider-core/client";
27​
28interface KnownDriver {
29 readonly label: string;
30 readonly configFields: ReadonlySet<string>;
31 readonly hasDefaultInstance: boolean;
33​
34const known = (
35 driverKind: string,
36 label: string,
37 settingsSchema: { readonly fields: object },
38 hasDefaultInstance = true,
39) =>
40 [
41 ProviderDriverKind.make(driverKind),
42 { label, configFields: new Set(Object.keys(settingsSchema.fields)), hasDefaultInstance },
43 ] as const;
44​
45const PACKAGED_CLIENTS: ReadonlyArray<ProviderClientDefinition> = [
46 cursorClient,
47 grokClient,
48 fxClient,
49 kiroClient,
50 museClient,
51 openCodeClient,
52 piClient,
53 acpRegistryClient,
54];
55​
56/**
57 * Fields that make an instance launch without the settings a pin sets: managed Codex runs T3's own
58 * runtime against OpenAI, and a server URL points OpenCode at a server it does not start. A policy
59 * that owns a pin owns these too, holding each at the value given here, which clients still send
60 * when they add an instance.
61 */
62const PIN_BYPASS_FIELDS: ReadonlyMap<
63 ProviderDriverKind,
64 Readonly<Record<string, unknown>>
65> = new Map<ProviderDriverKind, Readonly<Record<string, unknown>>>([
66 [ProviderDriverKind.make("codex"), { setupMode: "existing" }],
67 [ProviderDriverKind.make("opencode"), { serverUrl: "" }],
68]);
69​
70/** Drivers this build knows, by the names users see; a policy naming any other is refused. */
71const KNOWN_DRIVERS: ReadonlyMap<ProviderDriverKind, KnownDriver> = new Map([
72 known("codex", "Codex", CodexSettings),
73 known("claudeAgent", "Claude", ClaudeSettings),
74 known("antigravity", "Antigravity", AntigravitySettings),
75 ...PACKAGED_CLIENTS.map((client) =>
76 known(client.driverKind, client.label, client.settingsSchema, client.hasDefaultInstance),
77 ),
78]);
79​
80/** Each driver a policy may name, with the label users see. */
81export const knownDriverLabels = () =>
82 Array.from(KNOWN_DRIVERS, ([driver, { label }]) => ({ driver, label }));
83​
84const PINNED_CONFIG_FIELDS = ["binaryPath", "launchArgs"] as const;
85type PinnedConfigField = (typeof PINNED_CONFIG_FIELDS)[number];
86​
87/**
88 * Fields that decide what a provider starts and reads on startup; a driver has those its schema
89 * has. Under a ceiling they are the administrator's: a wrapper binary, a skip-permissions flag, or
90 * a config directory with allow rules each outrank T3 Code's mode, and no flag filter stops a wrapper.
91 */
92const LAUNCH_CONFIG_FIELDS = [
93 "binaryPath",
94 "launchArgs",
95 "homePath",
96 "shadowHomePath",
97 "commandPath",
98 "commandArgs",
99 "serverUrl",
100] as const;
101​
102export type ProviderWriteVerdict =
103 | { readonly _tag: "Allowed"; readonly settings: ServerSettings }
104 | { readonly _tag: "Locked"; readonly path: string }
105 | {
106 readonly _tag: "Disallowed";
107 readonly instanceId: ProviderInstanceId;
108 readonly label: string;
109 };
110​
111/**
112 * How a policy's `limits.providers` shapes provider instances. Like settings locks, pinned
113 * fields are laid over instances on every read and never written to the user's settings file.
114 */
115export interface ManagedProviders {
116 readonly isDriverAllowed: (driver: ProviderDriverKind) => boolean;
117 /** Lay pinned fields over every instance of a pinned driver, its default instance included. */
118 readonly applyLimits: (settings: ServerSettings) => ServerSettings;
119 /**
120 * Vet a write from stored `current` to `next`. Refuses enabling a disallowed driver and moving
121 * a locked field off its pinned value. Pinned values a client sends back are dropped, so the
122 * user's own values return once the policy is gone.
123 */
124 readonly vetWrite: (current: ServerSettings, next: ServerSettings) => ProviderWriteVerdict;
125 /**
126 * Per driver, the instance fields users cannot change, for clients. `environment` alone means
127 * every variable.
128 */
129 readonly lockedInstanceFields: Readonly<Record<string, ReadonlyArray<string>>>;
131​
132export const unmanagedProviders: ManagedProviders = {
133 isDriverAllowed: () => true,
134 applyLimits: (settings) => settings,
135 vetWrite: (_current, next) => ({ _tag: "Allowed", settings: next }),
136 lockedInstanceFields: {},
137};
138​
139const labelFor = (driver: ProviderDriverKind) => KNOWN_DRIVERS.get(driver)?.label ?? driver;
140​
141/** Why the policy clients were told about keeps `driver` from running, or undefined when it may run. */
142export const disallowedDriverReason = (
143 summary: ManagedPolicySummary | undefined,
144 driver: ProviderDriverKind,
145): string | undefined => {
146 const allowed = summary?.limits?.providers?.allowedDrivers;
147 return allowed === undefined || allowed.includes(driver)
148 ? undefined
149 : disallowedProviderMessage(summary?.organization, labelFor(driver));
150};
151​
152/**
153 * Variables that replace a provider's launch configuration. From the environment that starts the
154 * server they are ignored under any policy, which can lock launch arguments in more than one way;
155 * from an instance's own variables they are ignored when the policy locks launch arguments.
156 */
157const LAUNCH_OVERRIDE_VARIABLES = ["T3CODE_CODEX_LAUNCH_ARGS"] as const;
158​
159/** The environment provider drivers launch from under the policy clients were told about. */
160export const providerHostEnvironment = (
161 summary: ManagedPolicySummary | undefined,
162 environment: NodeJS.ProcessEnv,
163): NodeJS.ProcessEnv => {
164 if (summary === undefined) return environment;
165 const next = { ...environment };
166 for (const name of Object.keys(next)) {
167 if (LAUNCH_OVERRIDE_VARIABLES.some((override) => override === name.toUpperCase())) {
168 delete next[name];
169 }
170 }
171 return next;
172};
173​
174/** Each driver's home variable, and the settings that rewrite it at launch. */
175const HOME_VARIABLES: ReadonlyMap<
176 ProviderDriverKind,
177 { readonly variable: string; readonly fields: ReadonlyArray<string> }
178> = new Map([
179 [ProviderDriverKind.make("claudeAgent"), { variable: "CLAUDE_CONFIG_DIR", fields: ["homePath"] }],
180 [
181 ProviderDriverKind.make("codex"),
182 { variable: "CODEX_HOME", fields: ["homePath", "shadowHomePath"] },
183 ],
184]);
185​
186const configRecord = (instance: ProviderInstanceConfig | undefined): Record<string, unknown> =>
187 instance?.config !== null &&
188 typeof instance?.config === "object" &&
189 !Array.isArray(instance.config)
190 ? (instance.config as Record<string, unknown>)
191 : {};
192​
193/** Empty strings and empty lists are how clients clear a field, so they count as unset. */
194const configValue = (instance: ProviderInstanceConfig | undefined, field: string) => {
195 const value = configRecord(instance)[field];
196 return value === "" || (Array.isArray(value) && value.length === 0) ? undefined : value;
197};
198​
199const sameValue = (left: unknown, right: unknown) =>
200 left === right || JSON.stringify(left) === JSON.stringify(right);
201​
202const sameVariable = (
203 left: ProviderInstanceEnvironmentVariable | undefined,
204 right: ProviderInstanceEnvironmentVariable | undefined,
205) =>
206 left === right ||
207 (left !== undefined &&
208 right !== undefined &&
209 // A secret reference is an object, and a decoded copy is never the stored one.
210 sameValue(left.value, right.value) &&
211 left.sensitive === right.sensitive);
212​
213const pinnedFields = (pin: ManagedProviderInstance): ReadonlyArray<PinnedConfigField> =>
214 PINNED_CONFIG_FIELDS.filter((field) => pin[field] !== undefined);
215​
216/**
217 * What the administrator owns on one driver's instances. `config` fields take the pin or the
218 * default, never the user's value; `fill` fields take the pin only where the user left them empty.
219 * `environment`: `all` runs with only pinned variables, `locked` lets them win, `fill` fills gaps.
220 */
221interface DriverRule {
222 readonly pin: ManagedProviderInstance | undefined;
223 readonly config: ReadonlyArray<string>;
224 readonly fill: ReadonlyArray<PinnedConfigField>;
225 readonly environment: "all" | "locked" | "fill";
227​
228const pinnedValue = (pin: ManagedProviderInstance | undefined, field: string): unknown =>
229 pin === undefined ? undefined : (pin as Record<string, unknown>)[field];
230​
231const overlayInstance = (
232 instance: ProviderInstanceConfig,
233 rule: DriverRule,
234 variableKey: (name: string) => string,
235): ProviderInstanceConfig => {
236 const config = { ...configRecord(instance) };
237 for (const field of rule.config) {
238 const value = pinnedValue(rule.pin, field);
239 if (value === undefined) delete config[field];
240 else config[field] = value;
241 }
242 for (const field of rule.fill) {
243 if (configValue(instance, field) === undefined) config[field] = rule.pin?.[field];
244 }
245 const pinnedEnvironment = (rule.pin?.environment ?? []).map((variable) => ({
246 name: variable.name,
247 value: variable.value,
248 sensitive: false,
249 }));
250 const pinnedByName = new Map(
251 pinnedEnvironment.map((variable) => [variableKey(variable.name), variable]),
252 );
253 const environment: Array<NonNullable<ProviderInstanceConfig["environment"]>[number]> =
254 rule.environment === "all" ? pinnedEnvironment : [];
255 if (rule.environment !== "all") {
256 const seen = new Set<string>();
257 // A user's override would replace launch arguments the administrator locked.
258 const overrides = rule.config.includes("launchArgs")
259 ? new Set(LAUNCH_OVERRIDE_VARIABLES.map(variableKey))
260 : new Set<string>();
261 // The last entry for a name wins at launch, so a lock replaces every entry with that name.
262 for (const variable of instance.environment ?? []) {
263 const key = variableKey(variable.name);
264 if (overrides.has(key)) continue;
265 const pinned = rule.environment === "locked" ? pinnedByName.get(key) : undefined;
266 if (pinned === undefined) environment.push(variable);
267 else if (!seen.has(key)) environment.push(pinned);
268 seen.add(key);
269 }
270 for (const pinned of pinnedEnvironment) {
271 if (!seen.has(variableKey(pinned.name))) environment.push(pinned);
272 }
273 }
274 const { config: _config, environment: _environment, ...envelope } = instance;
275 return {
276 ...envelope,
277 ...(instance.config === undefined && Object.keys(config).length === 0 ? {} : { config }),
278 ...(environment.length === 0 ? {} : { environment }),
279 };
280};
281​
282/**
283 * Build the overlay for `limits.providers` and `limits.maxRuntimeMode`, or say why it cannot be
284 * enforced. A ceiling takes each driver's launch fields and its whole `environment`, which can
285 * carry `CLAUDE_CONFIG_DIR`, `PATH`, or `NODE_OPTIONS`; a deny list would miss the next one.
286 */
287export function makeManagedProviders(
288 limits: ManagedProviderLimits,
289 maxRuntimeMode: RuntimeMode | undefined,
290 platform: NodeJS.Platform,
291):
292 | { readonly _tag: "Valid"; readonly providers: ManagedProviders }
293 | { readonly _tag: "Invalid"; readonly reason: string } {
294 // Windows reads a variable name without regard to case, so there a lock covers every spelling.
295 const variableKey = (name: string) => (platform === "win32" ? name.toUpperCase() : name);
296 for (const driver of limits.allowedDrivers ?? []) {
297 if (!KNOWN_DRIVERS.has(driver)) {
298 return { _tag: "Invalid", reason: `"${driver}" in allowedDrivers is not a known provider` };
299 }
300 }
301 const pins = new Map<ProviderDriverKind, ManagedProviderInstance>();
302 for (const [rawDriver, pin] of Object.entries(limits.instances ?? {})) {
303 const driver = ProviderDriverKind.make(rawDriver);
304 const known = KNOWN_DRIVERS.get(driver);
305 if (known === undefined) {
306 return { _tag: "Invalid", reason: `"${driver}" in instances is not a known provider` };
307 }
308 const unsupported = pinnedFields(pin).find((field) => !known.configFields.has(field));
309 if (unsupported !== undefined) {
310 return {
311 _tag: "Invalid",
312 reason: `the ${known.label} provider has no ${unsupported} setting`,
313 };
314 }
315 const names = (pin.environment ?? []).map((variable) => variableKey(variable.name));
316 if (new Set(names).size !== names.length) {
317 return { _tag: "Invalid", reason: `instances.${driver}.environment repeats a variable` };
318 }
319 pins.set(driver, pin);
320 }
321​
322 const rules = new Map<ProviderDriverKind, DriverRule>();
323 for (const [driver, known] of KNOWN_DRIVERS) {
324 const pin = pins.get(driver);
325 const bypasses = pin === undefined ? [] : Object.keys(PIN_BYPASS_FIELDS.get(driver) ?? {});
326 if (maxRuntimeMode !== undefined) {
327 rules.set(driver, {
328 pin,
329 config: [
330 ...LAUNCH_CONFIG_FIELDS.filter((field) => known.configFields.has(field)),
331 ...bypasses,
332 ],
333 fill: [],
334 environment: "all",
335 });
336 } else if (pin !== undefined) {
337 // A home path setting rewrites the provider's home variable at launch, so a lock on that
338 // variable owns the setting too.
339 const home = HOME_VARIABLES.get(driver);
340 const homeLocked =
341 home !== undefined &&
342 (pin.environment ?? []).some(
343 (variable) => variableKey(variable.name) === variableKey(home.variable),
344 );
345 rules.set(driver, {
346 pin,
347 config: pin.locked
348 ? [...pinnedFields(pin), ...bypasses, ...(homeLocked ? home.fields : [])]
349 : [],
350 fill: pin.locked ? [] : pinnedFields(pin),
351 environment: pin.locked ? "locked" : "fill",
352 });
353 }
354 }
355​
356 const allowed =
357 limits.allowedDrivers === undefined ? undefined : new Set<string>(limits.allowedDrivers);
358 const isDriverAllowed = (driver: ProviderDriverKind) => allowed?.has(driver) ?? true;
359 const allowedSelection = (
360 settings: ServerSettings,
361 selection: { readonly instanceId: ProviderInstanceId } | null | undefined,
362 ) =>
363 selection == null ||
364 isDriverAllowed(
365 settings.providerInstances[selection.instanceId]?.driver ??
366 ProviderDriverKind.make(selection.instanceId),
367 );
368​
369 const cache = new WeakMap<ServerSettings, ServerSettings>();
370 const applyLimits = (settings: ServerSettings): ServerSettings => {
371 const cached = cache.get(settings);
372 if (cached !== undefined) return cached;
373 const providerInstances: Record<string, ProviderInstanceConfig> = {
374 ...settings.providerInstances,
375 };
376 // Default instances exist without a settings entry until the user edits one; pin them too.
377 for (const driver of pins.keys()) {
378 const instanceId = defaultInstanceIdForDriver(driver);
379 if (KNOWN_DRIVERS.get(driver)?.hasDefaultInstance === false) continue;
380 if (providerInstances[instanceId] === undefined) providerInstances[instanceId] = { driver };
381 }
382 for (const [instanceId, instance] of Object.entries(providerInstances)) {
383 const rule = rules.get(instance.driver);
384 if (rule !== undefined) {
385 providerInstances[instanceId] = overlayInstance(instance, rule, variableKey);
386 }
387 }
388 // A cleared selection falls back to the text generation model, which is resolved afterwards.
389 const result: ServerSettings = {
390 ...settings,
391 providerInstances: providerInstances as ServerSettings["providerInstances"],
392 // An allow-list names agent drivers; text generators would send code to any other API.
393 ...(allowed === undefined ? {} : { textGenerators: {} }),
394 sourceControlWriterModelSelection: allowedSelection(
395 settings,
396 settings.sourceControlWriterModelSelection,
397 )
398 ? settings.sourceControlWriterModelSelection
399 : null,
400 reviewGuideModelSelection: allowedSelection(settings, settings.reviewGuideModelSelection)
401 ? settings.reviewGuideModelSelection
402 : null,
403 };
404 cache.set(settings, result);
405 return result;
406 };
407​
408 type Locked = { readonly _tag: "Locked"; readonly path: string };
409​
410 /**
411 * Vet the environment a client sent. Pinned values it echoes back are dropped; anything else
412 * the administrator owns must match what is stored. Returns the environment to store.
413 */
414 const vetEnvironment = (
415 instanceId: string,
416 stored: ProviderInstanceConfig | undefined,
417 next: ProviderInstanceConfig,
418 rule: DriverRule,
419 ):
420 | Locked
421 | {
422 readonly _tag: "Kept";
423 readonly environment: ReadonlyArray<ProviderInstanceEnvironmentVariable>;
424 } => {
425 const pinned = rule.pin?.environment ?? [];
426 const storedVariable = (name: string) =>
427 stored?.environment?.find((existing) => variableKey(existing.name) === variableKey(name));
428 const storedUnchanged = (variable: ProviderInstanceEnvironmentVariable) =>
429 (stored?.environment ?? []).some((existing) => sameVariable(variable, existing));
430 const echoed = (variable: ProviderInstanceEnvironmentVariable) =>
431 !variable.sensitive &&
432 pinned.some((pin) => pin.name === variable.name && pin.value === variable.value);
433 if (rule.environment === "all") {
434 // Only the pinned variables were shown, so the user's own stay as stored.
435 for (const variable of next.environment ?? []) {
436 if (echoed(variable) || storedUnchanged(variable)) continue;
437 return { _tag: "Locked", path: `providerInstances.${instanceId}.environment` };
438 }
439 return { _tag: "Kept", environment: stored?.environment ?? [] };
440 }
441 let environment: ReadonlyArray<ProviderInstanceEnvironmentVariable> = next.environment ?? [];
442 for (const variable of pinned) {
443 const sent = environment.find(
444 (existing) => variableKey(existing.name) === variableKey(variable.name),
445 );
446 const kept = storedVariable(variable.name);
447 if ((sent !== undefined && echoed(sent)) || sameVariable(sent, kept)) {
448 // Put the user's own entries back where it was sent, or drop the echoed policy value.
449 const storedEntries = (stored?.environment ?? []).filter(
450 (existing) => variableKey(existing.name) === variableKey(variable.name),
451 );
452 let restored = false;
453 environment = environment.flatMap((existing) => {
454 if (variableKey(existing.name) !== variableKey(variable.name)) return [existing];
455 if (restored) return [];
456 restored = true;
457 return storedEntries;
458 });
459 } else if (rule.environment === "locked") {
460 return {
461 _tag: "Locked",
462 path: `providerInstances.${instanceId}.environment.${variable.name}`,
463 };
464 }
465 }
466 // The client never saw a launch override the lock hides, so keep the stored one.
467 if (rule.config.includes("launchArgs")) {
468 const overrides = new Set(LAUNCH_OVERRIDE_VARIABLES.map(variableKey));
469 environment = [
470 ...environment.filter((variable) => !overrides.has(variableKey(variable.name))),
471 ...(stored?.environment ?? []).filter((variable) =>
472 overrides.has(variableKey(variable.name)),
473 ),
474 ];
475 }
476 return { _tag: "Kept", environment };
477 };
478​
479 const vetInstance = (
480 instanceId: string,
481 stored: ProviderInstanceConfig | undefined,
482 next: ProviderInstanceConfig,
483 rule: DriverRule,
484 ): Locked | { readonly _tag: "Kept"; readonly instance: ProviderInstanceConfig | undefined } => {
485 const config = { ...configRecord(next) };
486 const harmless = PIN_BYPASS_FIELDS.get(next.driver) ?? {};
487 for (const field of [...rule.config, ...rule.fill]) {
488 const value = configValue(next, field);
489 const storedValue = configValue(stored, field);
490 if (
491 sameValue(value, pinnedValue(rule.pin, field)) ||
492 sameValue(value, storedValue) ||
493 (field in harmless && sameValue(value, harmless[field]))
494 ) {
495 if (storedValue === undefined) delete config[field];
496 else config[field] = configRecord(stored)[field];
497 } else if (rule.config.includes(field)) {
498 return { _tag: "Locked", path: `providerInstances.${instanceId}.config.${field}` };
499 }
500 }
501 const vettedEnvironment = vetEnvironment(instanceId, stored, next, rule);
502 if (vettedEnvironment._tag === "Locked") return vettedEnvironment;
503 const { environment } = vettedEnvironment;
504 const { config: _config, environment: _environment, ...envelope } = next;
505 const kept: ProviderInstanceConfig = {
506 ...envelope,
507 ...(Object.keys(config).length === 0 && stored?.config === undefined ? {} : { config }),
508 ...(environment.length === 0 ? {} : { environment }),
509 };
510 // A default instance the client echoed back with only pinned values stays implicit.
511 const echoedDefault =
512 stored === undefined &&
513 instanceId === defaultInstanceIdForDriver(next.driver) &&
514 Object.keys(kept).length === 1;
515 return { _tag: "Kept", instance: echoedDefault ? undefined : kept };
516 };
517​
518 const vetWrite = (current: ServerSettings, next: ServerSettings): ProviderWriteVerdict => {
519 const providerInstances: Record<string, ProviderInstanceConfig> = {};
520 for (const [instanceId, instance] of Object.entries(next.providerInstances)) {
521 const stored = current.providerInstances[instanceId as ProviderInstanceId];
522 // Clients echo instances with policy fields laid over them, so compare only whether it runs.
523 // A driver's default instance runs before it is configured.
524 const wasEnabled =
525 stored !== undefined
526 ? stored.driver === instance.driver && resolveProviderInstanceEnabled(stored)
527 : instanceId === defaultInstanceIdForDriver(instance.driver);
528 if (
529 !isDriverAllowed(instance.driver) &&
530 resolveProviderInstanceEnabled(instance) &&
531 !wasEnabled
532 ) {
533 return {
534 _tag: "Disallowed",
535 instanceId: instanceId as ProviderInstanceId,
536 label: labelFor(instance.driver),
537 };
538 }
539 const rule = rules.get(instance.driver);
540 if (rule === undefined) {
541 providerInstances[instanceId] = instance;
542 continue;
543 }
544 const vetted = vetInstance(
545 instanceId,
546 stored?.driver === instance.driver ? stored : undefined,
547 instance,
548 rule,
549 );
550 if (vetted._tag === "Locked") return vetted;
551 if (vetted.instance !== undefined) providerInstances[instanceId] = vetted.instance;
552 }
553 // A project can switch an instance on for itself; that is enabling it too.
554 for (const [projectId, overrides] of Object.entries(next.projectSettingsOverrides)) {
555 const storedOverrides =
556 current.projectSettingsOverrides[projectId as keyof typeof current.projectSettingsOverrides]
557 ?.providerInstances;
558 for (const [instanceId, override] of Object.entries(overrides.providerInstances ?? {})) {
559 const driver =
560 next.providerInstances[instanceId as ProviderInstanceId]?.driver ??
561 ProviderDriverKind.make(instanceId);
562 if (
563 override.enabled === true &&
564 storedOverrides?.[instanceId as ProviderInstanceId]?.enabled !== true &&
565 !isDriverAllowed(driver)
566 ) {
567 return {
568 _tag: "Disallowed",
569 instanceId: instanceId as ProviderInstanceId,
570 label: labelFor(driver),
571 };
572 }
573 }
574 }
575 return {
576 _tag: "Allowed",
577 settings: {
578 ...next,
579 providerInstances: providerInstances as ServerSettings["providerInstances"],
580 },
581 };
582 };
583​
584 const lockedInstanceFields = Object.fromEntries(
585 [...rules].flatMap(([driver, rule]) => {
586 const fields = [
587 ...rule.config.map((field) => `config.${field}`),
588 ...(rule.environment === "all"
589 ? ["environment"]
590 : rule.environment === "locked"
591 ? (rule.pin?.environment ?? []).map((variable) => `environment.${variable.name}`)
592 : []),
593 ];
594 return fields.length === 0 ? [] : [[driver, fields] as const];
595 }),
596 );
597​
598 return {
599 _tag: "Valid",
600 providers: {
601 isDriverAllowed,
602 applyLimits,
603 vetWrite,
604 lockedInstanceFields,
605 },
606 };