import { z } from 'zod'; export { z } from 'zod'; import { Hono } from 'hono'; /** * UI visualization configuration for MCP Apps */ interface UiConfig { /** Type of visualization to render */ type?: "table" | "chart" | "auto"; /** Chart type when type is "chart" */ chartType?: "line" | "bar"; /** Field to use for x-axis in charts */ xAxis?: string; /** Field(s) for left Y-axis */ leftYAxis?: string | string[]; /** Field(s) for right Y-axis */ rightYAxis?: string | string[]; /** @deprecated Use leftYAxis instead */ yAxis?: string; } /** * Configuration for an environment (dev, test, prod) */ interface EnvironmentConfig { /** Enable debug mode */ debug?: boolean; /** Any additional environment-specific settings */ [key: string]: unknown; } /** * Configuration for an access group */ interface AccessGroupConfig { /** Description of this access group */ description: string; } /** * Definition of an entity in the ontology */ interface EntityDefinition { /** Human-readable description of this entity */ description: string; } /** * Option returned by functions used as field sources. * Functions referenced by `fieldFrom()` should return an array of these. */ interface FieldOption { /** The stored value */ value: string; /** Human-readable label for display */ label: string; } /** * Context passed to resolvers */ interface ResolverContext { /** Current environment name */ env: string; /** Environment configuration */ envConfig: EnvironmentConfig; /** Logger instance */ logger: { info: (message: string, ...args: unknown[]) => void; warn: (message: string, ...args: unknown[]) => void; error: (message: string, ...args: unknown[]) => void; debug: (message: string, ...args: unknown[]) => void; }; /** Access groups for the current request */ accessGroups: string[]; } /** * Resolver function signature */ type ResolverFunction = (ctx: ResolverContext, args: TArgs) => Promise | TResult; /** * Definition of a function in the ontology */ interface FunctionDefinition, TOutputs extends z.ZodType = z.ZodType> { /** Human-readable description of what this function does */ description: string; /** Which access groups can call this function */ access: TGroups[]; /** Which entities this function relates to (use empty array [] if none) */ entities: TEntities[]; /** Zod schema for input validation */ inputs: TInputs; /** Zod schema for output validation */ outputs: TOutputs; /** Resolver function that handles this function's logic */ resolver: ResolverFunction, z.infer>; /** Enable UI visualization via MCP Apps. Set to true for auto-detection or provide config. */ ui?: boolean | UiConfig; /** Whether this function is read-only (query) or has side effects (mutation) */ isReadOnly: boolean; } /** * Result returned by the auth function */ interface AuthResult { /** Access groups for the current request */ groups: string[]; /** Optional user identity for row-level access control */ user?: Record; /** Optional organization context for multi-tenant applications */ organization?: Record; } /** * Cloud service configuration */ interface CloudConfig { /** Override the hosted URL (for testing or self-hosted) */ url?: string; } /** * Auth function that determines access groups for a request. * Can return either: * - `string[]` - just group names (backwards compatible) * - `AuthResult` - groups plus optional user identity */ type AuthFunction = (req: Request) => Promise | string[] | AuthResult; /** * The main Ontology configuration object */ interface OntologyConfig> = Record>> { /** Name of this ontology/API */ name: string; /** * Unique identifier for this ontology instance. * Auto-generated by `ont-run init`. Used for cloud service registration. */ uuid?: string; /** * Enable connection to ont-run.com cloud services. * Set to `true` to connect with defaults, or provide config object. * The cloud provides AI agent access, version history, and team features. */ cloud?: boolean | CloudConfig; /** Environment configurations */ environments: Record; /** Pluggable auth function */ auth: AuthFunction; /** Access group definitions */ accessGroups: Record; /** Entity definitions for categorization */ entities?: Record; /** Function definitions */ functions: TFunctions; } /** * Define an Ontology configuration with full type inference. * * @example * ```ts * import { defineOntology, fieldFrom } from 'ont-run'; * import { z } from 'zod'; * import { getUser } from './resolvers/getUser.js'; * * export default defineOntology({ * name: 'my-api', * environments: { * dev: { debug: true }, * prod: { debug: false }, * }, * auth: async (req) => { * const token = req.headers.get('Authorization'); * return token ? ['admin'] : ['public']; * }, * accessGroups: { * public: { description: 'Unauthenticated users' }, * admin: { description: 'Administrators' }, * }, * entities: { * User: { description: 'A user account' }, * }, * functions: { * getUser: { * description: 'Get a user by ID', * access: ['public', 'admin'], * entities: ['User'], * inputs: z.object({ id: z.string() }), * resolver: getUser, // Direct function reference for type safety * }, * }, * }); * ``` */ declare function defineOntology>>(config: { name: string; environments: Record; auth: AuthFunction; accessGroups: Record; entities?: Record; functions: TFunctions; }): OntologyConfig; /** * Define a function with full type inference for resolver type safety. * * This helper ensures that the resolver function's return type matches * the outputs Zod schema at compile time. * * @example * ```ts * import { defineFunction, z } from 'ont-run'; * import type { ResolverContext } from 'ont-run'; * * const getUser = defineFunction({ * description: 'Get a user by ID', * access: ['public', 'admin'] as const, * entities: ['User'] as const, * inputs: z.object({ id: z.string() }), * outputs: z.object({ id: z.string(), name: z.string() }), * resolver: async (ctx, args) => { * // TypeScript knows args is { id: string } * // TypeScript enforces return type is { id: string, name: string } * return { id: args.id, name: 'Example User' }; * }, * }); * ``` */ declare function defineFunction(config: { description: string; access: readonly TGroups[]; entities: readonly TEntities[]; inputs: TInputs; outputs: TOutputs; resolver: ResolverFunction, z.infer>; /** Enable UI visualization via MCP Apps. Set to true for auto-detection or provide config. */ ui?: boolean | UiConfig; /** Whether this function is read-only (query) or has side effects (mutation) */ isReadOnly: boolean; }): FunctionDefinition; /** * Symbol for storing fieldFrom metadata on Zod schemas */ declare const FIELD_FROM_METADATA: unique symbol; /** * Symbol for storing userContext metadata on Zod schemas */ declare const USER_CONTEXT_METADATA: unique symbol; /** * Symbol for storing organizationContext metadata on Zod schemas */ declare const ORGANIZATION_CONTEXT_METADATA: unique symbol; /** * Metadata stored on fieldFrom Zod schemas */ interface FieldFromMetadata { /** Name of the function that provides options for this field */ functionName: string; } /** * Type for a Zod string with fieldFrom metadata */ type FieldFromString = z.ZodString & { [FIELD_FROM_METADATA]: FieldFromMetadata; }; /** * Create a string field that gets its options from another function. * * The referenced function should return `{ value: string, label: string }[]`. * - If the function has empty inputs `z.object({})`, it's treated as **bulk** (all options fetched at once) * - If the function has a `query` input, it's treated as **autocomplete** (options searched) * * @param functionName - Name of the function that provides options * * @example * ```ts * defineOntology({ * functions: { * // Bulk options source (empty inputs) * getUserStatuses: { * description: 'Get available user statuses', * access: ['admin'], * entities: [], * inputs: z.object({}), * outputs: z.array(z.object({ value: z.string(), label: z.string() })), * resolver: './resolvers/options/userStatuses.ts', * }, * * // Autocomplete source (has query input) * searchTeams: { * description: 'Search for teams', * access: ['admin'], * entities: [], * inputs: z.object({ query: z.string() }), * outputs: z.array(z.object({ value: z.string(), label: z.string() })), * resolver: './resolvers/options/searchTeams.ts', * }, * * // Function using fieldFrom * createUser: { * description: 'Create a user', * access: ['admin'], * entities: ['User'], * inputs: z.object({ * name: z.string(), * status: fieldFrom('getUserStatuses'), * team: fieldFrom('searchTeams'), * }), * resolver: './resolvers/createUser.ts', * }, * }, * }) * ``` */ declare function fieldFrom(functionName: string): FieldFromString; /** * Type for a Zod schema with userContext metadata */ type UserContextSchema = T & { [USER_CONTEXT_METADATA]: true; }; /** * Mark a schema as user context that will be injected at runtime. * * Fields marked with `userContext()` are: * - **Injected**: Populated from `auth()` result's `user` field * - **Hidden**: Not exposed in public API/MCP schemas * - **Type-safe**: Resolver receives typed user object * * @param schema - Zod schema for the user context shape * * @example * ```ts * defineOntology({ * auth: async (req) => { * const user = await verifyToken(req); * return { * groups: user.isAdmin ? ['admin'] : ['user'], * user: { id: user.id, email: user.email } * }; * }, * * functions: { * editPost: { * description: 'Edit a post', * access: ['user', 'admin'], * entities: ['Post'], * inputs: z.object({ * postId: z.string(), * title: z.string(), * currentUser: userContext(z.object({ * id: z.string(), * email: z.string(), * })), * }), * resolver: './resolvers/editPost.ts', * }, * }, * }) * ``` */ declare function userContext(schema: T): UserContextSchema; /** * Type for a Zod schema with organizationContext metadata */ type OrganizationContextSchema = T & { [ORGANIZATION_CONTEXT_METADATA]: true; }; /** * Mark a schema as organization context that will be injected at runtime. * * Fields marked with `organizationContext()` are: * - **Injected**: Populated from `auth()` result's `organization` field * - **Hidden**: Not exposed in public API/MCP schemas * - **Type-safe**: Resolver receives typed organization object * * @param schema - Zod schema for the organization context shape * * @example * ```ts * defineOntology({ * auth: async (req) => { * const orgId = new URL(req.url).searchParams.get('org_id'); * if (!orgId) return { groups: ['public'] }; * * const user = await verifyToken(req); * const org = await db.organizations.findById(orgId); * * // Verify user membership in organization * const isMember = await db.organizationMembers.exists({ userId: user.id, orgId }); * if (!isMember) throw new Error('Not a member of this organization'); * * return { * groups: ['member'], * organization: { id: org.id, name: org.name } * }; * }, * * functions: { * createProject: { * description: 'Create a project in the organization', * access: ['member'], * entities: ['Project'], * inputs: z.object({ * name: z.string(), * description: z.string(), * currentOrg: organizationContext(z.object({ * id: z.string(), * name: z.string(), * })), * }), * resolver: './resolvers/createProject.ts', * }, * }, * }) * ``` */ declare function organizationContext(schema: T): OrganizationContextSchema; interface ServerHandle { port: number; stop: () => void | Promise; } interface StartOntOptions { /** Port for API server (default: 3000) */ port?: number; /** Port for MCP server (default: 3001) */ mcpPort?: number; /** Environment to use (default: 'dev') */ env?: string; /** Mode: 'development' warns on lockfile issues, 'production' fails. Defaults to 'production' unless NODE_ENV is explicitly 'development'. */ mode?: "development" | "production"; /** Set to true to only start the API server */ apiOnly?: boolean; /** Set to true to only start the MCP server */ mcpOnly?: boolean; } interface StartOntResult { api?: ServerHandle; mcp?: { port: number; }; } /** * Start the ont API and MCP servers. * * Automatically discovers ontology.config.ts and handles lockfile validation. * * @example * ```ts * import { startOnt } from 'ont-run'; * * await startOnt({ * port: 3000, * mcpPort: 3001, * }); * ``` */ declare function startOnt(options?: StartOntOptions): Promise; /** * Context variables added by middleware */ interface OntologyVariables { resolverContext: ResolverContext; accessGroups: string[]; authResult: AuthResult; } interface ApiServerOptions { /** The ontology configuration */ config: OntologyConfig; /** Environment to use (e.g., 'dev', 'prod') */ env: string; /** Enable CORS (default: true) */ cors?: boolean; /** Config directory for lockfile validation. If provided, validates lockfile and auto-launches review UI in dev mode. */ configDir?: string; /** Path to the ontology.config.ts file (used for review UI source display) */ configPath?: string; } /** * Create the Hono API app from an OntologyConfig */ declare function createApiApp(options: ApiServerOptions): Hono<{ Variables: OntologyVariables; }>; interface CreateMcpAppOptions { /** The ontology configuration */ config: OntologyConfig; /** Environment to use */ env: string; } /** * Create an MCP Hono app without starting a server. * Use this to mount MCP on an existing server alongside other routes. */ declare function createMcpApp(options: CreateMcpAppOptions): Promise; /** * Find the ontology config file in the given directory or its parents */ declare function findConfigFile(startDir?: string): string | null; /** * Load the ontology config from a file */ declare function loadConfig(configPath?: string): Promise<{ config: OntologyConfig; configDir: string; configPath: string; }>; /** * A field that references another function for its options */ interface FieldReference { /** Path to the field in the schema, e.g., "status" or "filters.country" */ path: string; /** Name of the function that provides options for this field */ functionName: string; } /** * Snapshot of a function's shape (what matters for security review) */ interface FunctionShape { /** Description of the function */ description: string; /** Sorted list of access groups */ access: string[]; /** Sorted list of entities this function relates to */ entities: string[]; /** JSON Schema representation of the input schema */ inputsSchema: Record; /** JSON Schema representation of the output schema */ outputsSchema?: Record; /** Fields that reference other functions for their options */ fieldReferences?: FieldReference[]; /** Whether this function uses userContext() for row-level access control */ usesUserContext?: boolean; /** Whether this function uses organizationContext() for multi-tenant access control */ usesOrganizationContext?: boolean; } /** * Complete snapshot of the ontology */ interface OntologySnapshot { /** Name of the ontology */ name: string; /** Sorted list of access group names */ accessGroups: string[]; /** Sorted list of entity names */ entities?: string[]; /** Function shapes keyed by name */ functions: Record; } /** * A single change in a function */ interface FunctionChange { name: string; type: "added" | "removed" | "modified"; oldAccess?: string[]; newAccess?: string[]; oldDescription?: string; newDescription?: string; inputsChanged?: boolean; outputsChanged?: boolean; entitiesChanged?: boolean; oldEntities?: string[]; newEntities?: string[]; fieldReferencesChanged?: boolean; userContextChanged?: boolean; usesUserContext?: boolean; organizationContextChanged?: boolean; usesOrganizationContext?: boolean; } /** * Diff between old and new ontology */ interface OntologyDiff { /** Whether there are any changes */ hasChanges: boolean; /** Added access groups */ addedGroups: string[]; /** Removed access groups */ removedGroups: string[]; /** Added entities */ addedEntities: string[]; /** Removed entities */ removedEntities: string[]; /** Function changes */ functions: FunctionChange[]; /** The new ontology (for writing to lockfile on approve) */ newOntology: OntologySnapshot; /** The new hash */ newHash: string; } interface LaunchReviewOptions { config: OntologyConfig; diff: OntologyDiff; configDir: string; configPath?: string; } /** * Launch the review UI in the background (non-blocking). * * This is a fire-and-forget function that starts the browser server * and opens the review UI without blocking the caller. Useful for * auto-launching the review UI when ontology changes are detected * in dev mode. * * @example * ```ts * // Auto-launch review UI when changes are detected * if (diff.hasChanges && isDev) { * launchReviewInBackground({ config, diff, configDir }); * } * ``` */ declare function launchReviewInBackground(options: LaunchReviewOptions): Promise; /** * Response from the register endpoint */ interface RegisterResponse { success: boolean; hash: string; versionId?: string; limitReached?: boolean; message?: string; } /** * Chat message format */ interface ChatMessage { role: "user" | "assistant"; content: string; } /** * Parameters for chat endpoint */ interface ChatParams { uuid: string; messages: ChatMessage[]; /** Optional context about current state */ context?: Record; } /** * Response from chat endpoint */ interface ChatResponse { success: boolean; message?: string; toolCalls?: Array<{ name: string; arguments: Record; result?: unknown; }>; limitReached?: boolean; } /** * Version history entry */ interface VersionEntry { id: string; hash: string; createdAt: string; verified: boolean; status: "pending" | "approved" | "rejected"; } /** * Response from versions endpoint */ interface VersionsResponse { success: boolean; versions: VersionEntry[]; } /** * Review action parameters */ interface ReviewParams { uuid: string; versionId: string; action: "approve" | "reject"; comment?: string; } /** * Response from review endpoint */ interface ReviewResponse { success: boolean; message?: string; } /** * Options for CloudClient */ interface CloudClientOptions { /** Override the hosted URL (for testing) */ baseUrl?: string; /** API key for verified access */ apiKey?: string; } /** * Client for communicating with ont-run.com hosted services. * * Handles: * - Registering ontology with the cloud * - AI chat with tool execution * - Version history retrieval * - Version approval/rejection * * @example * ```ts * const client = new CloudClient(); * * // Register ontology * const result = await client.register({ * uuid: config.uuid, * ontology, * hash, * }); * * // Chat with AI agent * const chatResult = await client.chat({ * uuid: config.uuid, * messages: [{ role: 'user', content: 'Hello' }], * }); * ``` */ declare class CloudClient { private baseUrl; private apiKey?; constructor(options?: CloudClientOptions); /** * Get headers for requests, including API key if available */ private getHeaders; /** * Register ontology with the cloud service. * This is called on server startup to sync the ontology. */ register(params: { uuid: string; ontology: OntologySnapshot; hash: string; }): Promise; /** * Send a chat message to the AI agent. */ chat(params: ChatParams): Promise; /** * Get version history for a UUID. */ versions(uuid: string): Promise; /** * Approve or reject a version. */ review(params: ReviewParams): Promise; /** * Check if the client has an API key configured. */ hasApiKey(): boolean; /** * Get the base URL for the cloud service. */ getBaseUrl(): string; } /** * Result of cloud registration */ interface RegistrationResult { success: boolean; hash: string; versionId?: string; limitReached?: boolean; verified: boolean; message?: string; } /** * Register the ontology with the ont-run.com cloud service. * * This function: * 1. Extracts the ontology definition from config * 2. Computes a hash of the ontology * 3. Sends it to the cloud for registration * 4. Returns the registration result * * @param config - The ontology configuration * @returns Registration result with success status and hash * * @example * ```ts * const result = await registerWithCloud(config); * if (result.success) { * console.log(`Registered with hash: ${result.hash}`); * if (result.verified) { * console.log('Running in verified mode'); * } * } * ``` */ declare function registerWithCloud(config: OntologyConfig): Promise; /** * Attempt cloud registration and log the result. * This is a fire-and-forget helper for use in server startup. * * @param config - The ontology configuration * @returns Promise that resolves when registration completes */ declare function tryRegisterWithCloud(config: OntologyConfig): Promise; export { type AccessGroupConfig, type ApiServerOptions, type AuthFunction, type AuthResult, type ChatMessage, type ChatParams, type ChatResponse, CloudClient, type CloudClientOptions, type CloudConfig, type CreateMcpAppOptions, type EntityDefinition, type EnvironmentConfig, type FieldOption, type FunctionDefinition, type LaunchReviewOptions, type OntologyConfig, type RegisterResponse, type RegistrationResult, type ResolverContext, type ResolverFunction, type StartOntOptions, type StartOntResult, type UiConfig, createApiApp, createMcpApp, defineFunction, defineOntology, fieldFrom, findConfigFile, launchReviewInBackground, loadConfig, organizationContext, registerWithCloud, startOnt, tryRegisterWithCloud, userContext };