/** * Action contract harvester (CLI-side). * * For each `defineAction({ input: })` file in the project, this * computes `z.toJSONSchema(action.input)` plus an optional * `z.toJSONSchema(action.output)` and returns the results keyed by the action's * compiler staging path (`src/features//`). The CLI * ships those maps as `actionInputSchemas` / `actionOutputSchemas`. * * WHY HERE AND NOT IN THE COMPILER: deriving a faithful JSON Schema from a Zod * *expression* (discriminated unions, refinements, recursive schemas, bare- * identifier references) requires evaluating the user's code. The hosted * compiler must NEVER run untrusted user code — doing so inside the shared * build container was a sandbox-escape / cross-tenant exposure surface. The * user's own machine is exactly where their action code legitimately runs, so * the evaluation belongs in the CLI. * * Safety / robustness: * - Each action is bundled in isolation with esbuild (the user's installed * `zod`, drizzle tables, and `~/`-aliased lib all resolve), then evaluated * in a SEPARATE `node` child process with a hard timeout — a hanging or * crashing module can't take down the CLI. * - `execute` is never invoked; only module load + schema conversion runs. * - V1 is best-effort: any failure (no zod 4, bundle error, eval throw, * timeout) drops that action from the map and the compiler falls back to * its static OpenAPI parser. V2 consumes the structured per-action * diagnostics and fails closed with the original bundle/evaluation reason. */ /** * `featureName → absolute authored feature directory`. For an area-nested * feature the authored directory is `/` (e.g. * `events/activities`), NOT `/`. Cross-feature imports in an * action bundle are spelled in the FLAT/global convention (`../../events/events`, * as if every feature were a top-level sibling) so the compiler's flat staging * identity resolves — but esbuild resolves them from the action's PHYSICAL * nested location, where that spelling under-resolves by the area depth. This * index re-resolves such imports by their leading feature-name segment through * the authored directory, independent of nesting depth. */ export type FeatureDirIndex = ReadonlyMap; export interface HarvestFeatureInput { /** * Global LEAF feature identity — drives the compiler staging key * (`src/features//`). For a FLAT feature this also equals * the on-disk directory under `featuresDir`; for a feature nested in an area * it does NOT (see `sourceDir`). */ name: string; /** * The AUTHORED feature directory relative to `featuresDir` (POSIX), exactly * as the file loader discovered it — `todos`, `events`, or * `events/conversations`. The harvest reconstructs the on-disk source path * from THIS, not `name`; nested-area actions live under their area folder and * would otherwise resolve to a directory that does not contain the file * (bare + feature-local imports fail to resolve). Falls back to `name` when * absent (top-level features, where the two are identical). */ sourceDir?: string; /** * Action files keyed by relative path under the feature dir (e.g. * `actions/advance.ts`), matching the compiler's `action.relativePath`. */ actionFiles?: Record; } /** * Return the compiler staging keys that did not produce a JSON Schema. * Contract v2 uses this as a fail-closed gate; v1 keeps the historical * best-effort fallback to the compiler's static schema parser. */ export declare function missingActionInputSchemaKeys(features: HarvestFeatureInput[], harvested: Record): string[]; export interface ActionHarvestDiagnostic { /** Compiler staging path for the action that could not be harvested. */ key: string; /** The isolated phase that failed. */ phase: 'bundle' | 'evaluation'; /** Actionable local error text, bounded before it reaches CLI output. */ message: string; } export interface ActionHarvestResult { /** Backward-compatible input-schema map. */ schemas: Record; /** Optional declared action output schemas, keyed like `schemas`. */ outputSchemas: Record; diagnostics: ActionHarvestDiagnostic[]; } /** * Harvest action input JSON Schemas for every action file across the given * features. Returns a map keyed by staging path; empty when there's nothing * to harvest or harvesting isn't possible in this environment. */ export declare function harvestActionInputSchemas(args: { projectRoot: string; featuresDir: string; features: HarvestFeatureInput[]; /** Additional node_modules roots, such as a nested generated output tree. */ dependencyRoots?: string[]; }): Promise>; /** * Detailed harvest used by the compile command. Keeping the schema-only * wrapper above preserves the existing library surface for callers that use * v1's best-effort behavior. */ export declare function harvestActionInputSchemasDetailed(args: { projectRoot: string; featuresDir: string; features: HarvestFeatureInput[]; dependencyRoots?: string[]; }): Promise; //# sourceMappingURL=action-harvest.d.ts.map