UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

149 lines • 6.86 kB
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= import { z } from 'zod'; // The sources a source's own config may read from: those that take no // config themselves, so the table bottoms out instead of needing a // dependency graph. Everything else declares its credentials here and // is reached through this block. Kept sorted: it is rendered into the // refusal, which Python renders from a set it sorts. export const BOOTSTRAP_SOURCES = ['dotenv', 'env']; /** * One entry of the env map: a literal value or a managed pointer. * * The env block is one map, name -> entry. A bare string in the map is * the literal short form and never reaches this schema; a mapping is * validated through it. `value` and `from` are mutually exclusive and * one is required: `export` belongs to a literal entry, `ref`/`key`/ * `fetch` to a managed one, and `readonly` to either -- a managed * credential the agent cannot overwrite is filled all the same, * because the fetch writes the record itself rather than going through * the shell's assignment gate. The wire key is `from:` in both * languages; Python exposes it as `provider` in code only because * `from` is a keyword there. */ export const EnvVar = z .strictObject({ value: z.string().optional(), readonly: z.boolean().default(false), export: z.boolean().default(true), from: z.string().optional(), ref: z.string().default(''), key: z.string().optional(), fetch: z.enum(['lazy', 'eager']).default('lazy'), }) .superRefine((entry, ctx) => { if (entry.value !== undefined && entry.from !== undefined) { ctx.addIssue({ code: 'custom', message: "an env entry takes 'value' or 'from', not both" }); return; } if (entry.value === undefined && entry.from === undefined) { ctx.addIssue({ code: 'custom', message: "an env entry needs 'value' or 'from'" }); return; } if (entry.from !== undefined) { if (!entry.export) { ctx.addIssue({ code: 'custom', message: "'export' is for literal entries; a managed variable is always exported", }); } } else if (entry.ref !== '' || entry.key !== undefined || entry.fetch !== 'lazy') { ctx.addIssue({ code: 'custom', message: "'ref', 'key' and 'fetch' are for managed entries ('from')", }); } }); /** * A source-config value read from a bootstrap source. * * The config plane's pointer, spelled with the same three keys the env * plane's managed entry uses, so one grammar covers both. Only a * bootstrap source may back one: those take no config of their own, * which is what stops the table from needing a dependency graph. */ export const SecretRef = z.strictObject({ from: z.string(), ref: z.string().default(''), key: z.string(), }); /** * One declared source instance: which source, and its config. * * The `secrets:` block is one map, name -> source, spelled the way * `mounts:` and `clis:` are: a type beside a config. The instance * name is what a managed env entry's `from:` names, so two accounts of * one platform are two instances, and an instance named after its * source reads as that source configured. * * The config map is untyped here, the way `mounts.*.config` is: each * source owns its own model, and this model only has to tell a pointer * from a literal. It does that the way the env plane does, by the * presence of `from`, so a mapping carrying one is validated as a * `SecretRef` and every other value passes through to the source's * model. */ export const SecretSource = z.strictObject({ source: z.string(), // A custom check rather than `z.record`, which builds its output by // keyed assignment and so DROPS a `__proto__` key outright -- the // deployment's own spelling would vanish instead of reaching the // source's model, where python reports it as an unknown field. config: z .custom((value) => typeof value === 'object' && value !== null && !Array.isArray(value), { message: 'config must be a mapping' }) .default({}) .transform((config, ctx) => { const out = []; for (const [name, item] of Object.entries(config)) { const pointer = typeof item === 'object' && item !== null && !Array.isArray(item) && 'from' in item; if (!pointer) { out.push([name, item]); continue; } // Reported, never thrown: a throw from inside a transform // escapes safeParse, and the config loader's own wrapper (which // names the instance) would never see the failure. const parsed = SecretRef.safeParse(item); if (!parsed.success) { for (const issue of parsed.error.issues) { ctx.addIssue({ code: 'custom', message: issue.message, path: [name, ...issue.path] }); } continue; } // The restriction lives here, not on `SecretRef`: it is // this model's reason, not the pointer's. A source that needs // config of its own cannot bootstrap another without a // dependency graph, while a mount reads from any declared // instance because they are all built before it. if (!BOOTSTRAP_SOURCES.includes(parsed.data.from)) { ctx.addIssue({ code: 'custom', path: [name], message: `a source config reads from ${BOOTSTRAP_SOURCES.join(', ')}, ` + 'not that source; only a source that needs no config of its own can ' + 'bootstrap another', }); continue; } out.push([name, parsed.data]); } // Object.fromEntries, not keyed assignment: a config key named // `__proto__` would otherwise assign through the prototype // setter and never reach the source's own model, where python's // dict passes it through like any other. return Object.fromEntries(out); }), }); //# sourceMappingURL=config.js.map