import { readFile, writeFile } from "node:fs/promises"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; // The runtime context parser rejects unknown fields. Its accepted field lists // come from the schema so the pack contract digest, which fingerprints the // schema, changes whenever the parser's contract does. const root = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const schema = JSON.parse( await readFile(resolve(root, "protocol/provider-schemas/runtime-context.schema.json"), "utf8"), ); const objects = ["runtimeContext", "prompt", "instructions", "workingCopy", "skill", "mcp", "connectionInstructions", "asset"]; const fields = objects.map((name) => { const definition = schema.$defs?.[name]; if (definition?.type !== "object" || definition.additionalProperties !== false || !definition.properties) { throw new Error(`runtime context ${name} must be a closed object schema`); } return [name, Object.keys(definition.properties)]; }); const typescript = `// Generated by scripts/generate-runtime-context-contract.mjs. Do not edit. export const GENERATED_RUNTIME_CONTEXT_FIELDS = { ${fields.map(([name, keys]) => ` ${name}: [${keys.map((key) => JSON.stringify(key)).join(", ")}],`).join("\n")} } as const; `; const output = resolve(root, "src/contracts/generated-runtime-context-contract.ts"); if (process.argv.includes("--check")) { if ((await readFile(output, "utf8").catch(() => "")) !== typescript) { process.stderr.write(`Generated runtime context contract is stale: ${output}\n`); process.exitCode = 1; } else process.stdout.write("Generated runtime context contract matches the schema.\n"); } else { await writeFile(output, typescript); process.stdout.write("Generated runtime context contract.\n"); }