@happyvertical/smrt-dev-mcp 0.54.4 → 0.55.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dev-plane.js CHANGED
@@ -1,7 +1,7 @@
1
- import { a as runtimeMigrationStatus, c as runtimeScheduleHealth, g as loadProjectCore, i as runtimeJobHealth, m as ProjectRuntimeResolutionError, o as runtimeRecentChanges, p as safeErrorMessage, r as runtimeDispatchHealth, s as runtimeRegistryDrift, t as RUNTIME_PROVENANCE } from "./tools-B-CDyCXi.js";
2
- import { i as SERVER_VERSION, r as SERVER_NAME, t as bearerMatches } from "./http-DnmJox6O.js";
1
+ import { a as runtimeMigrationStatus, c as runtimeScheduleHealth, g as loadProjectCore, i as runtimeJobHealth, m as ProjectRuntimeResolutionError, o as runtimeRecentChanges, p as safeErrorMessage, r as runtimeDispatchHealth, s as runtimeRegistryDrift, t as RUNTIME_PROVENANCE } from "./tools-B0uaPTz0.js";
2
+ import { i as SERVER_VERSION, r as SERVER_NAME, t as bearerMatches } from "./http-D-MuW6Ar.js";
3
3
  import { n as TOOLS } from "./tool-catalog-RHq7nyCm.js";
4
- import { i as runtimeSchemaDiff, n as runtimeObject, r as runtimeRegistry } from "./observation-BA287SLA.js";
4
+ import { i as runtimeSchemaDiff, n as runtimeObject, r as runtimeRegistry } from "./observation-CmDrAP9J.js";
5
5
  import { Server, createMcpHandler } from "@modelcontextprotocol/server";
6
6
  //#region src/dev-plane.ts
7
7
  /**
@@ -1,6 +1,6 @@
1
- import { a as runtimeMigrationStatus, c as runtimeScheduleHealth, i as runtimeJobHealth, o as runtimeRecentChanges, r as runtimeDispatchHealth, s as runtimeRegistryDrift } from "./tools-B-CDyCXi.js";
1
+ import { a as runtimeMigrationStatus, c as runtimeScheduleHealth, i as runtimeJobHealth, o as runtimeRecentChanges, r as runtimeDispatchHealth, s as runtimeRegistryDrift } from "./tools-B0uaPTz0.js";
2
2
  import { n as TOOLS } from "./tool-catalog-RHq7nyCm.js";
3
- import { a as bootRuntime, i as runtimeSchemaDiff, n as runtimeObject, r as runtimeRegistry } from "./observation-BA287SLA.js";
3
+ import { a as bootRuntime, i as runtimeSchemaDiff, n as runtimeObject, r as runtimeRegistry } from "./observation-CmDrAP9J.js";
4
4
  import { readFileSync } from "node:fs";
5
5
  import { dirname, join } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
@@ -166,4 +166,4 @@ async function startRuntimeHttpHost(options = {}) {
166
166
  //#endregion
167
167
  export { SERVER_VERSION as i, startRuntimeHttpHost as n, SERVER_NAME as r, bearerMatches as t };
168
168
 
169
- //# sourceMappingURL=http-DnmJox6O.js.map
169
+ //# sourceMappingURL=http-D-MuW6Ar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"http-DnmJox6O.js","names":[],"sources":["../src/server-info.ts","../src/http.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport const SERVER_NAME = 'smrt-dev-mcp';\n\nfunction readPackageVersion(): string {\n try {\n const packageRoot = dirname(fileURLToPath(import.meta.url));\n const packageJsonPath = join(packageRoot, '..', 'package.json');\n const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as {\n version?: unknown;\n };\n return typeof packageJson.version === 'string'\n ? packageJson.version\n : '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\nexport const SERVER_VERSION = readPackageVersion();\n","/**\n * Standalone Level 2 runtime dev-plane host (#1831).\n *\n * Serves a *positive* read-only tool catalog over the stateless 2026-07-28\n * Streamable HTTP transport (#2147): `createMcpHandler` with `legacy: 'reject'`\n * and `maxSubscriptions: 0`, adapted to Node with `toNodeHandler`. No SSE,\n * no session header, no sticky routing.\n *\n * Security boundary (development only):\n * - binds loopback only; SDK localhost Host/Origin validation runs first;\n * - every request needs `Authorization: Bearer <token>` (constant-time\n * compare) — from `SMRT_DEV_MCP_TOKEN` or minted per process;\n * - no authenticated principal exists here, so scope stays fail-closed\n * global-only exactly as in Level 1;\n * - the catalog never includes generated CRUD, custom actions, `do()`, or\n * tool-backed `is()`.\n */\n\nimport { randomBytes, timingSafeEqual } from 'node:crypto';\nimport {\n createServer as createHttpServer,\n type Server as HttpServer,\n type IncomingMessage,\n type ServerResponse,\n} from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport {\n localhostHostValidation,\n localhostOriginValidation,\n toNodeHandler,\n} from '@modelcontextprotocol/node';\nimport type { CallToolResult } from '@modelcontextprotocol/server';\nimport { createMcpHandler, Server } from '@modelcontextprotocol/server';\nimport { SERVER_NAME, SERVER_VERSION } from './server-info.js';\nimport { TOOLS } from './tool-catalog.js';\nimport { bootRuntime, type RuntimeBoot } from './tools/runtime/boot.js';\nimport {\n runtimeObject,\n runtimeRegistry,\n runtimeSchemaDiff,\n} from './tools/runtime/observation.js';\nimport {\n runtimeDispatchHealth,\n runtimeJobHealth,\n runtimeMigrationStatus,\n runtimeRecentChanges,\n runtimeRegistryDrift,\n runtimeScheduleHealth,\n} from './tools/runtime/tools.js';\n\n/**\n * The complete Level 2 catalog. A tool is exposed over HTTP only if it is\n * named here; the static stdio catalog is deliberately not mounted.\n */\nexport const RUNTIME_HTTP_TOOL_NAMES = [\n 'runtime-registry',\n 'runtime-object',\n 'runtime-schema-diff',\n 'migration-status',\n 'job-health',\n 'schedule-health',\n 'dispatch-health',\n 'recent-changes',\n 'registry-drift',\n] as const;\n\nexport type RuntimeHttpToolName = (typeof RUNTIME_HTTP_TOOL_NAMES)[number];\n\nconst RUNTIME_HTTP_HANDLERS: Record<\n RuntimeHttpToolName,\n (args: Record<string, unknown>) => Promise<unknown>\n> = {\n 'runtime-registry': (args) => runtimeRegistry(args as never),\n 'runtime-object': (args) => runtimeObject(args as never),\n 'runtime-schema-diff': (args) => runtimeSchemaDiff(args as never),\n 'migration-status': (args) => runtimeMigrationStatus(args as never),\n 'job-health': (args) => runtimeJobHealth(args as never),\n 'schedule-health': (args) => runtimeScheduleHealth(args as never),\n 'dispatch-health': (args) => runtimeDispatchHealth(args as never),\n 'recent-changes': (args) => runtimeRecentChanges(args as never),\n 'registry-drift': (args) => runtimeRegistryDrift(args as never),\n};\n\n/** Catalog definitions for the HTTP plane, in catalog order. */\nexport function runtimeHttpTools() {\n const names = new Set<string>(RUNTIME_HTTP_TOOL_NAMES);\n return TOOLS.filter((tool) => names.has(tool.name));\n}\n\n/**\n * Build a fresh protocol server per request (stateless transport contract).\n * `projectRoot` is fixed at host start; per-request `projectPath` arguments\n * are ignored so a client cannot re-point the booted process.\n */\nexport function createRuntimeProtocolServer(projectRoot: string): Server {\n const server = new Server(\n { name: `${SERVER_NAME}-runtime`, version: SERVER_VERSION },\n { capabilities: { tools: {} } },\n );\n server.setRequestHandler('tools/list', async () => ({\n tools: runtimeHttpTools(),\n }));\n server.setRequestHandler('tools/call', async (request) => {\n const name = request.params.name as string;\n const handler = (RUNTIME_HTTP_HANDLERS as Record<string, unknown>)[name];\n if (typeof handler !== 'function') {\n throw new Error(`Unknown runtime tool: ${name}`);\n }\n const { projectPath: _ignored, ...args } = (request.params.arguments ??\n {}) as Record<string, unknown>;\n const result = await (\n handler as (a: Record<string, unknown>) => Promise<unknown>\n )({ ...args, projectPath: projectRoot });\n const text = JSON.stringify(result, null, 2);\n return {\n content: [{ type: 'text', text }],\n structuredContent: result as Record<string, unknown>,\n } satisfies CallToolResult;\n });\n return server;\n}\n\nexport interface RuntimeHttpHostOptions {\n projectRoot?: string;\n /** Loopback interface only; anything else is refused at startup. */\n host?: '127.0.0.1' | 'localhost' | '::1';\n /** `0` (default) picks a free port. */\n port?: number;\n /** Bearer token; defaults to `SMRT_DEV_MCP_TOKEN` or a per-process random token. */\n token?: string;\n /** Path the MCP endpoint is mounted on (default `/mcp`). */\n path?: string;\n}\n\nexport interface RuntimeHttpHost {\n url: string;\n token: string;\n boot: RuntimeBoot;\n close(): Promise<void>;\n}\n\nconst LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '::1']);\n\nexport function bearerMatches(\n header: string | undefined,\n token: string,\n): boolean {\n if (!header?.startsWith('Bearer ')) return false;\n const presented = Buffer.from(header.slice('Bearer '.length));\n const expected = Buffer.from(token);\n return (\n presented.length === expected.length && timingSafeEqual(presented, expected)\n );\n}\n\nfunction deny(res: ServerResponse, status: number, message: string): void {\n res.statusCode = status;\n res.setHeader('content-type', 'application/json');\n if (status === 401) {\n res.setHeader('www-authenticate', 'Bearer realm=\"smrt-dev-mcp runtime\"');\n }\n res.end(JSON.stringify({ error: message }));\n}\n\n/** Start the Level 2 host. Boots the confined runtime before listening. */\nexport async function startRuntimeHttpHost(\n options: RuntimeHttpHostOptions = {},\n): Promise<RuntimeHttpHost> {\n const host = options.host ?? '127.0.0.1';\n if (!LOOPBACK_HOSTS.has(host)) {\n throw new Error(\n 'The runtime dev-plane binds loopback only (127.0.0.1, localhost, ::1).',\n );\n }\n const path = options.path ?? '/mcp';\n const token =\n options.token ??\n process.env.SMRT_DEV_MCP_TOKEN?.trim() ??\n randomBytes(24).toString('base64url');\n if (!token) {\n throw new Error('SMRT_DEV_MCP_TOKEN must not be empty.');\n }\n const projectRoot = options.projectRoot ?? process.cwd();\n const boot = await bootRuntime({ projectRoot });\n\n const mcp = toNodeHandler(\n createMcpHandler(() => createRuntimeProtocolServer(projectRoot), {\n legacy: 'reject',\n maxSubscriptions: 0,\n }),\n );\n const hostGuard = localhostHostValidation();\n const originGuard = localhostOriginValidation();\n\n const httpServer: HttpServer = createHttpServer(\n (req: IncomingMessage, res: ServerResponse) => {\n // Guards answer and end the response themselves when they return false.\n if (!hostGuard(req, res)) return;\n if (!originGuard(req, res)) return;\n const url = new URL(req.url ?? '/', 'http://localhost');\n if (url.pathname !== path) {\n deny(res, 404, 'not found');\n return;\n }\n if (!bearerMatches(req.headers.authorization, token)) {\n deny(res, 401, 'missing or invalid bearer token');\n return;\n }\n void mcp(req, res);\n },\n );\n\n await new Promise<void>((resolve, reject) => {\n httpServer.once('error', reject);\n httpServer.listen(options.port ?? 0, host, () => {\n httpServer.off('error', reject);\n resolve();\n });\n });\n const address = httpServer.address() as AddressInfo;\n const urlHost =\n address.family === 'IPv6' ? `[${address.address}]` : address.address;\n return {\n url: `http://${urlHost}:${address.port}${path}`,\n token,\n boot,\n close: () =>\n new Promise<void>((resolve, reject) => {\n httpServer.closeAllConnections?.();\n httpServer.close((error) => (error ? reject(error) : resolve()));\n }),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"http-D-MuW6Ar.js","names":[],"sources":["../src/server-info.ts","../src/http.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport const SERVER_NAME = 'smrt-dev-mcp';\n\nfunction readPackageVersion(): string {\n try {\n const packageRoot = dirname(fileURLToPath(import.meta.url));\n const packageJsonPath = join(packageRoot, '..', 'package.json');\n const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as {\n version?: unknown;\n };\n return typeof packageJson.version === 'string'\n ? packageJson.version\n : '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\nexport const SERVER_VERSION = readPackageVersion();\n","/**\n * Standalone Level 2 runtime dev-plane host (#1831).\n *\n * Serves a *positive* read-only tool catalog over the stateless 2026-07-28\n * Streamable HTTP transport (#2147): `createMcpHandler` with `legacy: 'reject'`\n * and `maxSubscriptions: 0`, adapted to Node with `toNodeHandler`. No SSE,\n * no session header, no sticky routing.\n *\n * Security boundary (development only):\n * - binds loopback only; SDK localhost Host/Origin validation runs first;\n * - every request needs `Authorization: Bearer <token>` (constant-time\n * compare) — from `SMRT_DEV_MCP_TOKEN` or minted per process;\n * - no authenticated principal exists here, so scope stays fail-closed\n * global-only exactly as in Level 1;\n * - the catalog never includes generated CRUD, custom actions, `do()`, or\n * tool-backed `is()`.\n */\n\nimport { randomBytes, timingSafeEqual } from 'node:crypto';\nimport {\n createServer as createHttpServer,\n type Server as HttpServer,\n type IncomingMessage,\n type ServerResponse,\n} from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport {\n localhostHostValidation,\n localhostOriginValidation,\n toNodeHandler,\n} from '@modelcontextprotocol/node';\nimport type { CallToolResult } from '@modelcontextprotocol/server';\nimport { createMcpHandler, Server } from '@modelcontextprotocol/server';\nimport { SERVER_NAME, SERVER_VERSION } from './server-info.js';\nimport { TOOLS } from './tool-catalog.js';\nimport { bootRuntime, type RuntimeBoot } from './tools/runtime/boot.js';\nimport {\n runtimeObject,\n runtimeRegistry,\n runtimeSchemaDiff,\n} from './tools/runtime/observation.js';\nimport {\n runtimeDispatchHealth,\n runtimeJobHealth,\n runtimeMigrationStatus,\n runtimeRecentChanges,\n runtimeRegistryDrift,\n runtimeScheduleHealth,\n} from './tools/runtime/tools.js';\n\n/**\n * The complete Level 2 catalog. A tool is exposed over HTTP only if it is\n * named here; the static stdio catalog is deliberately not mounted.\n */\nexport const RUNTIME_HTTP_TOOL_NAMES = [\n 'runtime-registry',\n 'runtime-object',\n 'runtime-schema-diff',\n 'migration-status',\n 'job-health',\n 'schedule-health',\n 'dispatch-health',\n 'recent-changes',\n 'registry-drift',\n] as const;\n\nexport type RuntimeHttpToolName = (typeof RUNTIME_HTTP_TOOL_NAMES)[number];\n\nconst RUNTIME_HTTP_HANDLERS: Record<\n RuntimeHttpToolName,\n (args: Record<string, unknown>) => Promise<unknown>\n> = {\n 'runtime-registry': (args) => runtimeRegistry(args as never),\n 'runtime-object': (args) => runtimeObject(args as never),\n 'runtime-schema-diff': (args) => runtimeSchemaDiff(args as never),\n 'migration-status': (args) => runtimeMigrationStatus(args as never),\n 'job-health': (args) => runtimeJobHealth(args as never),\n 'schedule-health': (args) => runtimeScheduleHealth(args as never),\n 'dispatch-health': (args) => runtimeDispatchHealth(args as never),\n 'recent-changes': (args) => runtimeRecentChanges(args as never),\n 'registry-drift': (args) => runtimeRegistryDrift(args as never),\n};\n\n/** Catalog definitions for the HTTP plane, in catalog order. */\nexport function runtimeHttpTools() {\n const names = new Set<string>(RUNTIME_HTTP_TOOL_NAMES);\n return TOOLS.filter((tool) => names.has(tool.name));\n}\n\n/**\n * Build a fresh protocol server per request (stateless transport contract).\n * `projectRoot` is fixed at host start; per-request `projectPath` arguments\n * are ignored so a client cannot re-point the booted process.\n */\nexport function createRuntimeProtocolServer(projectRoot: string): Server {\n const server = new Server(\n { name: `${SERVER_NAME}-runtime`, version: SERVER_VERSION },\n { capabilities: { tools: {} } },\n );\n server.setRequestHandler('tools/list', async () => ({\n tools: runtimeHttpTools(),\n }));\n server.setRequestHandler('tools/call', async (request) => {\n const name = request.params.name as string;\n const handler = (RUNTIME_HTTP_HANDLERS as Record<string, unknown>)[name];\n if (typeof handler !== 'function') {\n throw new Error(`Unknown runtime tool: ${name}`);\n }\n const { projectPath: _ignored, ...args } = (request.params.arguments ??\n {}) as Record<string, unknown>;\n const result = await (\n handler as (a: Record<string, unknown>) => Promise<unknown>\n )({ ...args, projectPath: projectRoot });\n const text = JSON.stringify(result, null, 2);\n return {\n content: [{ type: 'text', text }],\n structuredContent: result as Record<string, unknown>,\n } satisfies CallToolResult;\n });\n return server;\n}\n\nexport interface RuntimeHttpHostOptions {\n projectRoot?: string;\n /** Loopback interface only; anything else is refused at startup. */\n host?: '127.0.0.1' | 'localhost' | '::1';\n /** `0` (default) picks a free port. */\n port?: number;\n /** Bearer token; defaults to `SMRT_DEV_MCP_TOKEN` or a per-process random token. */\n token?: string;\n /** Path the MCP endpoint is mounted on (default `/mcp`). */\n path?: string;\n}\n\nexport interface RuntimeHttpHost {\n url: string;\n token: string;\n boot: RuntimeBoot;\n close(): Promise<void>;\n}\n\nconst LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '::1']);\n\nexport function bearerMatches(\n header: string | undefined,\n token: string,\n): boolean {\n if (!header?.startsWith('Bearer ')) return false;\n const presented = Buffer.from(header.slice('Bearer '.length));\n const expected = Buffer.from(token);\n return (\n presented.length === expected.length && timingSafeEqual(presented, expected)\n );\n}\n\nfunction deny(res: ServerResponse, status: number, message: string): void {\n res.statusCode = status;\n res.setHeader('content-type', 'application/json');\n if (status === 401) {\n res.setHeader('www-authenticate', 'Bearer realm=\"smrt-dev-mcp runtime\"');\n }\n res.end(JSON.stringify({ error: message }));\n}\n\n/** Start the Level 2 host. Boots the confined runtime before listening. */\nexport async function startRuntimeHttpHost(\n options: RuntimeHttpHostOptions = {},\n): Promise<RuntimeHttpHost> {\n const host = options.host ?? '127.0.0.1';\n if (!LOOPBACK_HOSTS.has(host)) {\n throw new Error(\n 'The runtime dev-plane binds loopback only (127.0.0.1, localhost, ::1).',\n );\n }\n const path = options.path ?? '/mcp';\n const token =\n options.token ??\n process.env.SMRT_DEV_MCP_TOKEN?.trim() ??\n randomBytes(24).toString('base64url');\n if (!token) {\n throw new Error('SMRT_DEV_MCP_TOKEN must not be empty.');\n }\n const projectRoot = options.projectRoot ?? process.cwd();\n const boot = await bootRuntime({ projectRoot });\n\n const mcp = toNodeHandler(\n createMcpHandler(() => createRuntimeProtocolServer(projectRoot), {\n legacy: 'reject',\n maxSubscriptions: 0,\n }),\n );\n const hostGuard = localhostHostValidation();\n const originGuard = localhostOriginValidation();\n\n const httpServer: HttpServer = createHttpServer(\n (req: IncomingMessage, res: ServerResponse) => {\n // Guards answer and end the response themselves when they return false.\n if (!hostGuard(req, res)) return;\n if (!originGuard(req, res)) return;\n const url = new URL(req.url ?? '/', 'http://localhost');\n if (url.pathname !== path) {\n deny(res, 404, 'not found');\n return;\n }\n if (!bearerMatches(req.headers.authorization, token)) {\n deny(res, 401, 'missing or invalid bearer token');\n return;\n }\n void mcp(req, res);\n },\n );\n\n await new Promise<void>((resolve, reject) => {\n httpServer.once('error', reject);\n httpServer.listen(options.port ?? 0, host, () => {\n httpServer.off('error', reject);\n resolve();\n });\n });\n const address = httpServer.address() as AddressInfo;\n const urlHost =\n address.family === 'IPv6' ? `[${address.address}]` : address.address;\n return {\n url: `http://${urlHost}:${address.port}${path}`,\n token,\n boot,\n close: () =>\n new Promise<void>((resolve, reject) => {\n httpServer.closeAllConnections?.();\n httpServer.close((error) => (error ? reject(error) : resolve()));\n }),\n };\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { f as redactConnectionString, m as ProjectRuntimeResolutionError } from "./tools-B-CDyCXi.js";
3
- import { i as SERVER_VERSION, n as startRuntimeHttpHost, r as SERVER_NAME } from "./http-DnmJox6O.js";
2
+ import { f as redactConnectionString, m as ProjectRuntimeResolutionError } from "./tools-B0uaPTz0.js";
3
+ import { i as SERVER_VERSION, n as startRuntimeHttpHost, r as SERVER_NAME } from "./http-D-MuW6Ar.js";
4
4
  import { n as TOOLS, t as REVIEW_SKILL_NAME } from "./tool-catalog-RHq7nyCm.js";
5
5
  import { a as buildReviewContext, c as compactContextResult, i as buildPackageSpecialistContext, n as buildContext, o as checkKnowledgeFreshness, r as buildKnowledgeIndex, s as checkKnowledgeFreshnessFromIndex, t as buildArchitectureContext } from "./knowledge-CvNlDTLz.js";
6
6
  import { existsSync, readFileSync, realpathSync } from "node:fs";
@@ -919,7 +919,7 @@ function createServer() {
919
919
  result = await generateSmrtClass(args);
920
920
  break;
921
921
  case "introspect-project":
922
- result = await (await import("./introspect-project-DzfPJ-iO.js")).introspectProject(args);
922
+ result = await (await import("./introspect-project-D8bjCeZZ.js")).introspectProject(args);
923
923
  break;
924
924
  case "review-smrt-project":
925
925
  result = await reviewSmrtProject(args);
@@ -992,7 +992,7 @@ function createServer() {
992
992
  case "dispatch-health":
993
993
  case "recent-changes":
994
994
  case "registry-drift": {
995
- const runtime = await import("./tools-B-CDyCXi.js").then((n) => n.l);
995
+ const runtime = await import("./tools-B0uaPTz0.js").then((n) => n.l);
996
996
  const handlers = {
997
997
  "migration-status": runtime.runtimeMigrationStatus,
998
998
  "job-health": runtime.runtimeJobHealth,
@@ -1007,7 +1007,7 @@ function createServer() {
1007
1007
  case "runtime-registry":
1008
1008
  case "runtime-object":
1009
1009
  case "runtime-schema-diff": {
1010
- const observation = await import("./observation-BA287SLA.js").then((n) => n.t);
1010
+ const observation = await import("./observation-CmDrAP9J.js").then((n) => n.t);
1011
1011
  const handlers = {
1012
1012
  "runtime-registry": observation.runtimeRegistry,
1013
1013
  "runtime-object": observation.runtimeObject,
@@ -1,4 +1,4 @@
1
- import { m as ProjectRuntimeResolutionError, v as resolveProjectRuntimeEntry } from "./tools-B-CDyCXi.js";
1
+ import { m as ProjectRuntimeResolutionError, v as resolveProjectRuntimeEntry } from "./tools-B0uaPTz0.js";
2
2
  import { isAbsolute, join, relative, resolve } from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
4
  import { spawn } from "node:child_process";
@@ -487,4 +487,4 @@ function messageFromError(error) {
487
487
  //#endregion
488
488
  export { introspectProject };
489
489
 
490
- //# sourceMappingURL=introspect-project-DzfPJ-iO.js.map
490
+ //# sourceMappingURL=introspect-project-D8bjCeZZ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"introspect-project-DzfPJ-iO.js","names":[],"sources":["../src/tools/introspect-project.ts"],"sourcesContent":["/**\n * Project Introspection Tool\n * Returns a manifest-equivalent inventory of SMRT objects in a project.\n */\n\nimport { spawn } from 'node:child_process';\nimport type { Dirent } from 'node:fs';\nimport { access, readdir, readFile } from 'node:fs/promises';\nimport { isAbsolute, join, relative, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { ScanError } from '@happyvertical/smrt-scanner';\nimport { ManifestAdapter, OxcScanner } from '@happyvertical/smrt-scanner';\nimport {\n ProjectRuntimeResolutionError,\n resolveProjectRuntimeEntry,\n} from './runtime/project-runtime.js';\n\n/**\n * `summary` (default) returns one compact record per object; `full` returns the\n * verbose shape. A 42-object project produced 338,462 characters in the only\n * mode that used to exist, which put every call over tool-result budgets and\n * forced callers to write-to-file and `jq` (#2143).\n */\nexport type IntrospectDetail = 'summary' | 'full';\n\n/** Common shape both detail modes emit, so the two branches share a list. */\ntype IntrospectedObject = Record<string, unknown> & { className: string };\n\ninterface IntrospectProjectArgs {\n directory?: string;\n manifestPath?: string;\n includeFields?: boolean;\n includeRelationships?: boolean;\n includeMethods?: boolean;\n detail?: IntrospectDetail;\n /**\n * Overrides the default character budget for the serialized `objects` array.\n *\n * Project metadata and diagnostics are deliberately exempt: a diagnostic is\n * how discovery reports that it found nothing (#2143), so budgeting it away\n * would hide the very failure the caller needs to see. The full response is\n * therefore somewhat larger than this budget.\n */\n maxChars?: number;\n /**\n * Resume after this `className` (objects are sorted alphabetically). Pass\n * the `nextCursor` from a previous truncated response to read the next page\n * instead of raising `maxChars` (#2779).\n */\n cursor?: string;\n /** Maximum objects per page, applied before the character budget. */\n limit?: number;\n}\n\ntype DiagnosticSeverity = 'error' | 'warning' | 'info';\n\ninterface Diagnostic {\n severity: DiagnosticSeverity;\n message: string;\n filePath?: string;\n line?: number;\n column?: number;\n}\n\ninterface ManifestLike {\n version?: string;\n packageName?: string;\n packageVersion?: string;\n objects?: Record<string, ManifestObject>;\n}\n\ninterface ManifestObject {\n name?: string;\n className: string;\n qualifiedName?: string;\n collection?: string;\n filePath: string;\n packageName?: string;\n packageVersion?: string;\n importPath?: string;\n modulePath?: string;\n exportName?: string;\n collectionExportName?: string;\n fields?: Record<string, ManifestField>;\n methods?: Record<string, ManifestMethod>;\n decoratorConfig?: Record<string, unknown>;\n extends?: string;\n extendsTypeArg?: string;\n schema?: ManifestSchema;\n staticProperties?: Record<string, unknown>;\n validationRules?: Record<string, unknown>;\n}\n\ninterface ManifestField {\n type: string;\n required?: boolean;\n default?: unknown;\n related?: string;\n description?: string;\n _meta?: Record<string, unknown>;\n transient?: boolean;\n}\n\ninterface ManifestMethod {\n name?: string;\n async?: boolean;\n parameters?: Array<{\n name: string;\n type?: string;\n optional?: boolean;\n default?: unknown;\n }>;\n returnType?: string;\n description?: string;\n isStatic?: boolean;\n isPublic?: boolean;\n}\n\ninterface ManifestSchema {\n tableName?: string;\n ddl?: string;\n columns?: Record<string, unknown>;\n indexes?: Array<{\n name?: string;\n columns?: string[];\n unique?: boolean;\n where?: string;\n jsonPath?: unknown;\n }>;\n version?: string;\n}\n\ninterface PackageMetadata {\n name?: string;\n version?: string;\n json?: Record<string, unknown>;\n}\n\nconst DEFAULT_MANIFEST_PATHS = [\n '.smrt/manifest.json',\n 'dist/manifest.json',\n 'src/manifest/manifest.json',\n];\n\nconst SCAN_EXCLUDE = [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/.git/**',\n '**/.smrt/**',\n '**/*.d.ts',\n '**/*.test.ts',\n '**/*.spec.ts',\n '**/__tests__/**',\n];\n\nconst RELATIONSHIP_TYPES = new Set([\n 'foreignKey',\n 'crossPackageRef',\n 'oneToMany',\n 'manyToMany',\n]);\n\n/**\n * Response budget in characters. Generous enough that a 42-object summary never\n * truncates, while still capping a runaway payload instead of emitting one.\n */\nconst DEFAULT_MAX_CHARS = 50_000;\n\nconst DEFAULT_MCP_OPERATIONS = ['list', 'get', 'create', 'update', 'delete'];\n\nexport async function introspectProject(\n args: IntrospectProjectArgs,\n): Promise<string> {\n const {\n directory = process.cwd(),\n includeFields = true,\n includeRelationships = true,\n includeMethods = true,\n detail = 'summary',\n maxChars = DEFAULT_MAX_CHARS,\n cursor,\n limit,\n } = args;\n const projectPath = resolve(directory);\n\n const exists = await pathExists(projectPath);\n if (!exists) {\n return JSON.stringify(\n {\n projectPath,\n manifestSource: 'none',\n objectCount: 0,\n objects: [],\n diagnostics: [\n {\n severity: 'warning',\n message: `Project directory does not exist: ${projectPath}`,\n },\n ],\n },\n null,\n 2,\n );\n }\n\n const packageMetadata = await readPackageMetadata(projectPath);\n const tenantScopes = await scanTenantScopes(projectPath);\n const manifestResult =\n (await loadManifestArtifact(projectPath, args.manifestPath)) ??\n (await scanSourceManifest(projectPath, packageMetadata));\n\n const entries = Object.entries(manifestResult.manifest.objects ?? {});\n const objects: IntrospectedObject[] =\n detail === 'summary'\n ? entries.map(([manifestKey, object]) =>\n summarizeObject({\n manifestKey,\n object,\n projectPath,\n tenantScope: tenantScopes.get(object.className),\n }),\n )\n : entries.map(([manifestKey, object]) =>\n formatObject({\n manifestKey,\n object,\n projectPath,\n includeFields,\n includeRelationships,\n includeMethods,\n tenantScope: tenantScopes.get(object.className),\n }),\n );\n objects.sort((left, right) => left.className.localeCompare(right.className));\n // Paging: skip everything up to and including the cursor, then cap by\n // `limit` before the character budget trims further.\n const afterCursor =\n typeof cursor === 'string' && cursor.length > 0\n ? objects.filter((object) => object.className.localeCompare(cursor) > 0)\n : objects;\n const pageLimit =\n typeof limit === 'number' && Number.isFinite(limit) && limit >= 1\n ? Math.floor(limit)\n : undefined;\n const window =\n pageLimit !== undefined ? afterCursor.slice(0, pageLimit) : afterCursor;\n const { kept, omitted: budgetOmitted } = applyObjectBudget(window, maxChars);\n const omitted = afterCursor.length - kept.length;\n const nextCursor =\n omitted > 0 && kept.length > 0\n ? kept[kept.length - 1].className\n : undefined;\n\n const output = {\n projectPath,\n manifestSource: manifestResult.source,\n manifestPath:\n 'path' in manifestResult\n ? relative(projectPath, manifestResult.path)\n : undefined,\n packageName:\n manifestResult.manifest.packageName ?? packageMetadata.name ?? undefined,\n packageVersion:\n manifestResult.manifest.packageVersion ??\n packageMetadata.version ??\n undefined,\n detail,\n objectCount: objects.length,\n scannedFileCount: manifestResult.scannedFileCount,\n parseTimeMs: manifestResult.parseTimeMs,\n ...(cursor ? { cursor } : {}),\n ...(nextCursor ? { nextCursor } : {}),\n ...(omitted > 0\n ? {\n truncated: {\n returnedObjectCount: kept.length,\n omittedObjectCount: omitted,\n budgetChars: maxChars,\n ...(pageLimit !== undefined ? { limit: pageLimit } : {}),\n guidance:\n budgetOmitted > 0\n ? 'The object list hit its character budget; metadata and diagnostics are still complete. Pass `cursor: nextCursor` to read the next page, narrow the scan with `directory`, keep `detail: \"summary\"`, or raise `maxChars` deliberately. Objects are sorted alphabetically.'\n : 'More objects follow this page. Pass `cursor: nextCursor` to continue.',\n },\n }\n : {}),\n objects: kept,\n diagnostics: manifestResult.diagnostics,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Trims the object list to the character budget, always keeping at least one so\n * a caller sees the shape rather than an empty list.\n */\nfunction applyObjectBudget<T>(\n objects: T[],\n maxChars: number,\n): { kept: T[]; omitted: number } {\n const kept: T[] = [];\n let used = 0;\n\n for (const object of objects) {\n const size = JSON.stringify(object, null, 2).length + 2;\n if (used + size > maxChars && kept.length > 0) break;\n used += size;\n kept.push(object);\n }\n\n return { kept, omitted: objects.length - kept.length };\n}\n\nfunction summarizeObject({\n manifestKey,\n object,\n projectPath,\n tenantScope,\n}: {\n manifestKey: string;\n object: ManifestObject;\n projectPath: string;\n tenantScope?: Record<string, unknown>;\n}) {\n const fields = Object.entries(object.fields ?? {});\n const decoratorConfig = object.decoratorConfig ?? {};\n\n return compactObject({\n manifestKey,\n className: object.className,\n qualifiedName: object.qualifiedName,\n filePath: sanitizePath(projectPath, object.filePath),\n extends: object.extends,\n collection: object.collection,\n tableName:\n object.schema?.tableName ??\n stringFromConfig(decoratorConfig.tableName) ??\n object.collection,\n tenantScope:\n tenantScope ?? normalizeTenantScopedConfig(decoratorConfig.tenantScoped),\n fieldCount: fields.length,\n relationships:\n fields\n .filter(([, field]) => RELATIONSHIP_TYPES.has(field.type))\n .map(\n ([name, field]) =>\n `${name} -> ${field.related ?? ''} (${field.type})`,\n )\n .join(', ') || undefined,\n mcpOperations: mcpOperationsFromConfig(decoratorConfig.mcp),\n });\n}\n\n/**\n * An omitted `mcp` config means full CRUD, not a closed surface — the same rule\n * the knowledge index applies.\n */\nfunction mcpOperationsFromConfig(config: unknown): string[] {\n if (config === false) return [];\n if (typeof config !== 'object' || config === null || Array.isArray(config)) {\n return [...DEFAULT_MCP_OPERATIONS];\n }\n\n const record = config as Record<string, unknown>;\n const include = Array.isArray(record.include)\n ? record.include.filter((item): item is string => typeof item === 'string')\n : DEFAULT_MCP_OPERATIONS;\n const exclude = new Set(\n Array.isArray(record.exclude)\n ? record.exclude.filter(\n (item): item is string => typeof item === 'string',\n )\n : [],\n );\n\n return include.filter((operation) => !exclude.has(operation));\n}\n\nasync function loadManifestArtifact(\n projectPath: string,\n manifestPath: string | undefined,\n): Promise<\n | {\n source: 'manifest';\n path: string;\n manifest: ManifestLike;\n diagnostics: Diagnostic[];\n scannedFileCount?: number;\n parseTimeMs?: number;\n }\n | undefined\n> {\n const candidates = manifestPath\n ? [resolve(projectPath, manifestPath)]\n : DEFAULT_MANIFEST_PATHS.map((candidate) => join(projectPath, candidate));\n\n const diagnostics: Diagnostic[] = [];\n for (const candidate of candidates) {\n if (!(await pathExists(candidate))) {\n continue;\n }\n\n try {\n const parsed = JSON.parse(await readFile(candidate, 'utf-8')) as unknown;\n if (isManifestLike(parsed)) {\n return {\n source: 'manifest',\n path: candidate,\n manifest: parsed,\n diagnostics,\n };\n }\n\n diagnostics.push({\n severity: 'warning',\n filePath: candidate,\n message: 'Manifest artifact is present but does not contain objects.',\n });\n } catch (error) {\n diagnostics.push({\n severity: 'error',\n filePath: candidate,\n message: `Unable to parse manifest artifact: ${messageFromError(error)}`,\n });\n }\n }\n\n return manifestPath\n ? {\n source: 'manifest',\n path: resolve(projectPath, manifestPath),\n manifest: { objects: {} },\n diagnostics: [\n ...diagnostics,\n {\n severity: 'warning',\n filePath: resolve(projectPath, manifestPath),\n message: 'Requested manifest artifact was not found.',\n },\n ],\n }\n : undefined;\n}\n\nasync function scanSourceManifest(\n projectPath: string,\n packageMetadata: PackageMetadata,\n): Promise<{\n source: 'scanner';\n manifest: ManifestLike;\n diagnostics: Diagnostic[];\n scannedFileCount: number;\n parseTimeMs: number;\n}> {\n const scanner = new OxcScanner({\n cwd: projectPath,\n include: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],\n exclude: SCAN_EXCLUDE,\n });\n const { results, resolved } = await scanner.scanAndResolve();\n const adapter = new ManifestAdapter();\n const manifest = adapter.toManifest(\n resolved.filter((classDef) => classDef.hasSmartDecorator),\n {\n packageName: packageMetadata.name,\n packageVersion: packageMetadata.version,\n typeAliases: results.typeAliases,\n },\n ) as ManifestLike;\n await finalizeScannerManifest(manifest, packageMetadata, projectPath);\n\n return {\n source: 'scanner',\n manifest,\n diagnostics: results.errors.map(scanErrorToDiagnostic),\n scannedFileCount: results.fileCount,\n parseTimeMs: Math.round(results.totalParseTimeMs),\n };\n}\n\n/**\n * Enrichment executes only installed framework code in an isolated process.\n * Its stdout/stderr are captured, never forwarded onto the MCP protocol. This\n * also prevents the scanner's registry and config from contaminating the host.\n */\nasync function finalizeScannerManifest(\n manifest: ManifestLike,\n packageMetadata: PackageMetadata,\n projectPath: string,\n): Promise<void> {\n const specifier = '@happyvertical/smrt-core/scanner';\n const entry = resolveProjectRuntimeEntry(projectPath, specifier);\n const enriched = await new Promise<ManifestLike>(\n (resolveManifest, reject) => {\n const child = spawn(\n process.execPath,\n [\n '--input-type=module',\n '-e',\n `\n const answer = (result) => process.send(result, () => process.disconnect());\n process.once('message', async (workerData) => {\n let module;\n try { module = await import(workerData.entry); }\n catch { answer({ unavailable: true }); return; }\n if (typeof module.ManifestGenerator !== 'function') { answer({ unavailable: true }); return; }\n try {\n const generator = new module.ManifestGenerator();\n const manifest = workerData.manifest;\n generator.injectTenantScopedFields(manifest);\n generator.mergeInheritedFields(manifest);\n generator.generateValidationRules(manifest);\n generator.generateSchemas(manifest);\n generator.assertTenantScopedSchemaContract(manifest);\n generator.generateAgentManifests(manifest, workerData.name, workerData.packageJson);\n answer({ manifest });\n } catch { answer({ failed: true }); }\n });\n `,\n ],\n { cwd: projectPath, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] },\n );\n child.send({\n entry: pathToFileURL(entry).href,\n manifest,\n name: packageMetadata.name,\n packageJson: packageMetadata.json,\n });\n const timeout = setTimeout(() => {\n child.kill('SIGKILL');\n reject(\n new Error(\n 'Project runtime manifest enrichment timed out; run the project build to diagnose it.',\n ),\n );\n }, 30_000);\n let answered = false;\n child.once('message', (message) => {\n const result = message as {\n unavailable?: boolean;\n failed?: boolean;\n manifest?: ManifestLike;\n };\n clearTimeout(timeout);\n answered = true;\n child.kill('SIGKILL');\n if (result.unavailable)\n reject(new ProjectRuntimeResolutionError(specifier));\n else if (result.failed)\n reject(\n new Error(\n 'Project runtime manifest enrichment failed. Run the project build to diagnose its manifest; no partial inventory was returned.',\n ),\n );\n else if (result.manifest) resolveManifest(result.manifest);\n else reject(new ProjectRuntimeResolutionError(specifier));\n });\n child.once('error', () => {\n clearTimeout(timeout);\n reject(new ProjectRuntimeResolutionError(specifier));\n });\n child.once('exit', () => {\n clearTimeout(timeout);\n if (!answered) reject(new ProjectRuntimeResolutionError(specifier));\n });\n },\n );\n Object.assign(manifest, enriched);\n}\n\nfunction formatObject({\n manifestKey,\n object,\n projectPath,\n includeFields,\n includeRelationships,\n includeMethods,\n tenantScope,\n}: {\n manifestKey: string;\n object: ManifestObject;\n projectPath: string;\n includeFields: boolean;\n includeRelationships: boolean;\n includeMethods: boolean;\n tenantScope?: Record<string, unknown>;\n}) {\n const fieldDetails = Object.entries(object.fields ?? {}).map(\n ([name, field]) => ({\n name,\n type: field.type,\n ...(field.required !== undefined ? { required: field.required } : {}),\n ...(field.default !== undefined ? { default: field.default } : {}),\n ...(field.related ? { related: field.related } : {}),\n ...(field.description ? { description: field.description } : {}),\n ...(field._meta ? { meta: field._meta } : {}),\n ...(field.transient !== undefined ? { transient: field.transient } : {}),\n }),\n );\n const relationshipDetails = fieldDetails\n .filter((field) => RELATIONSHIP_TYPES.has(field.type))\n .map((field) => ({\n field: field.name,\n relatedClass: field.related ?? '',\n type: field.type,\n ...(field.meta ? { meta: field.meta } : {}),\n }));\n const methodDetails = Object.entries(object.methods ?? {}).map(\n ([name, method]) => ({\n name: method.name ?? name,\n isAsync: method.async === true,\n isStatic: method.isStatic === true,\n isPublic: method.isPublic !== false,\n parameters: method.parameters ?? [],\n returnType: method.returnType ?? 'unknown',\n ...(method.description ? { description: method.description } : {}),\n }),\n );\n\n const decoratorConfig = object.decoratorConfig ?? {};\n const effectiveTenantScope =\n tenantScope ?? normalizeTenantScopedConfig(decoratorConfig.tenantScoped);\n const schema = object.schema;\n\n return compactObject({\n manifestKey,\n name: object.name,\n className: object.className,\n qualifiedName: object.qualifiedName,\n filePath: sanitizePath(projectPath, object.filePath),\n packageName: object.packageName,\n packageVersion: object.packageVersion,\n importPath: object.importPath,\n modulePath: object.modulePath,\n exportName: object.exportName,\n collectionExportName: object.collectionExportName,\n collection: object.collection,\n extends: object.extends,\n extendsTypeArg: object.extendsTypeArg,\n tableName:\n schema?.tableName ??\n stringFromConfig(decoratorConfig.tableName) ??\n object.collection,\n tableStrategy: stringFromConfig(decoratorConfig.tableStrategy) ?? 'cti',\n conflictColumns: arrayFromConfig(decoratorConfig.conflictColumns),\n tenantScope: effectiveTenantScope,\n decoratorConfig,\n schema: schema\n ? {\n tableName: schema.tableName,\n columns: schema.columns,\n indexes: schema.indexes ?? [],\n version: schema.version,\n }\n : undefined,\n indexes: schema?.indexes ?? [],\n staticProperties: object.staticProperties,\n validationRules: object.validationRules,\n ...(includeFields && {\n fields: fieldDetails\n .map((field) => `${field.name}: ${field.type}`)\n .join(', '),\n fieldDetails,\n }),\n ...(includeRelationships &&\n relationshipDetails.length > 0 && {\n relationships: relationshipDetails\n .map(\n (relationship) =>\n `${relationship.field} -> ${relationship.relatedClass} (${relationship.type})`,\n )\n .join(', '),\n relationshipDetails,\n }),\n ...(includeMethods &&\n methodDetails.length > 0 && {\n methods: methodDetails\n .map((method) => `${method.isAsync ? 'async ' : ''}${method.name}()`)\n .join(', '),\n methodDetails,\n }),\n });\n}\n\nasync function scanTenantScopes(\n projectPath: string,\n): Promise<Map<string, Record<string, unknown>>> {\n const files = await listSourceFiles(projectPath);\n const scopes = new Map<string, Record<string, unknown>>();\n\n await Promise.all(\n files.map(async (filePath) => {\n const content = await readFile(filePath, 'utf-8');\n const classMatches = content.matchAll(/class\\s+([A-Za-z_]\\w*)\\b/g);\n for (const match of classMatches) {\n const className = match[1];\n if (!className || match.index === undefined) continue;\n const prefix = content.slice(\n Math.max(0, match.index - 800),\n match.index,\n );\n const tenantMatches = Array.from(\n prefix.matchAll(/@TenantScoped\\s*\\(([\\s\\S]*?)\\)/g),\n );\n const tenantMatch = tenantMatches.at(-1);\n if (!tenantMatch) continue;\n scopes.set(className, {\n source: 'TenantScoped',\n ...parseTenantScopedOptions(tenantMatch[1]),\n });\n }\n }),\n );\n\n return scopes;\n}\n\nasync function listSourceFiles(projectPath: string): Promise<string[]> {\n const files: string[] = [];\n\n async function visit(dir: string): Promise<void> {\n let entries: Dirent[];\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n if (\n [\n 'node_modules',\n 'dist',\n 'build',\n '.git',\n '.smrt',\n '__tests__',\n ].includes(entry.name) ||\n entry.name.startsWith('.')\n ) {\n continue;\n }\n await visit(fullPath);\n continue;\n }\n\n if (\n entry.isFile() &&\n /\\.(tsx?|jsx?)$/.test(entry.name) &&\n !entry.name.endsWith('.d.ts') &&\n !entry.name.endsWith('.test.ts') &&\n !entry.name.endsWith('.spec.ts')\n ) {\n files.push(fullPath);\n }\n }\n }\n\n await visit(projectPath);\n return files;\n}\n\nfunction parseTenantScopedOptions(raw: string): Record<string, unknown> {\n const mode = raw.match(/mode\\s*:\\s*['\"`](required|optional)['\"`]/)?.[1];\n const field = raw.match(/field\\s*:\\s*['\"`]([A-Za-z_]\\w*)['\"`]/)?.[1];\n const allowSuperAdminBypass = raw.match(\n /allowSuperAdminBypass\\s*:\\s*(true|false)/,\n )?.[1];\n const autoFilter = raw.match(/autoFilter\\s*:\\s*(true|false)/)?.[1];\n const autoPopulate = raw.match(/autoPopulate\\s*:\\s*(true|false)/)?.[1];\n\n return compactObject({\n mode: mode ?? 'required',\n field: field ?? 'tenantId',\n autoFilter: autoFilter === undefined ? undefined : autoFilter === 'true',\n autoPopulate:\n autoPopulate === undefined ? undefined : autoPopulate === 'true',\n allowSuperAdminBypass:\n allowSuperAdminBypass === undefined\n ? undefined\n : allowSuperAdminBypass === 'true',\n });\n}\n\nfunction normalizeTenantScopedConfig(\n tenantScoped: unknown,\n): Record<string, unknown> | undefined {\n if (!tenantScoped) return undefined;\n const options =\n typeof tenantScoped === 'object' && !Array.isArray(tenantScoped)\n ? (tenantScoped as Record<string, unknown>)\n : {};\n return {\n source: 'smrt',\n mode: options.mode ?? 'required',\n field: options.field ?? 'tenantId',\n autoFilter: options.autoFilter ?? true,\n autoPopulate: options.autoPopulate ?? true,\n allowSuperAdminBypass: options.allowSuperAdminBypass ?? false,\n };\n}\n\nasync function readPackageMetadata(\n projectPath: string,\n): Promise<PackageMetadata> {\n const packageJsonPath = join(projectPath, 'package.json');\n if (!(await pathExists(packageJsonPath))) return {};\n\n try {\n const json = JSON.parse(await readFile(packageJsonPath, 'utf-8')) as Record<\n string,\n unknown\n >;\n return {\n name: typeof json.name === 'string' ? json.name : undefined,\n version: typeof json.version === 'string' ? json.version : undefined,\n json,\n };\n } catch {\n return {};\n }\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction isManifestLike(value: unknown): value is ManifestLike {\n return (\n !!value &&\n typeof value === 'object' &&\n !!(value as ManifestLike).objects &&\n typeof (value as ManifestLike).objects === 'object'\n );\n}\n\nfunction scanErrorToDiagnostic(error: ScanError): Diagnostic {\n return {\n severity: error.severity,\n message: error.message,\n filePath: error.filePath,\n line: error.line,\n column: error.column,\n };\n}\n\nfunction sanitizePath(projectPath: string, filePath: string): string {\n const absolute = isAbsolute(filePath)\n ? filePath\n : resolve(projectPath, filePath);\n const relativePath = relative(projectPath, absolute);\n if (!relativePath.startsWith('..')) {\n return relativePath || filePath;\n }\n return filePath;\n}\n\nfunction stringFromConfig(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction arrayFromConfig(value: unknown): string[] | undefined {\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === 'string')\n : undefined;\n}\n\nfunction compactObject<T extends Record<string, unknown>>(value: T): T {\n return Object.fromEntries(\n Object.entries(value).filter(([, entry]) => entry !== undefined),\n ) as T;\n}\n\nfunction messageFromError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"introspect-project-D8bjCeZZ.js","names":[],"sources":["../src/tools/introspect-project.ts"],"sourcesContent":["/**\n * Project Introspection Tool\n * Returns a manifest-equivalent inventory of SMRT objects in a project.\n */\n\nimport { spawn } from 'node:child_process';\nimport type { Dirent } from 'node:fs';\nimport { access, readdir, readFile } from 'node:fs/promises';\nimport { isAbsolute, join, relative, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { ScanError } from '@happyvertical/smrt-scanner';\nimport { ManifestAdapter, OxcScanner } from '@happyvertical/smrt-scanner';\nimport {\n ProjectRuntimeResolutionError,\n resolveProjectRuntimeEntry,\n} from './runtime/project-runtime.js';\n\n/**\n * `summary` (default) returns one compact record per object; `full` returns the\n * verbose shape. A 42-object project produced 338,462 characters in the only\n * mode that used to exist, which put every call over tool-result budgets and\n * forced callers to write-to-file and `jq` (#2143).\n */\nexport type IntrospectDetail = 'summary' | 'full';\n\n/** Common shape both detail modes emit, so the two branches share a list. */\ntype IntrospectedObject = Record<string, unknown> & { className: string };\n\ninterface IntrospectProjectArgs {\n directory?: string;\n manifestPath?: string;\n includeFields?: boolean;\n includeRelationships?: boolean;\n includeMethods?: boolean;\n detail?: IntrospectDetail;\n /**\n * Overrides the default character budget for the serialized `objects` array.\n *\n * Project metadata and diagnostics are deliberately exempt: a diagnostic is\n * how discovery reports that it found nothing (#2143), so budgeting it away\n * would hide the very failure the caller needs to see. The full response is\n * therefore somewhat larger than this budget.\n */\n maxChars?: number;\n /**\n * Resume after this `className` (objects are sorted alphabetically). Pass\n * the `nextCursor` from a previous truncated response to read the next page\n * instead of raising `maxChars` (#2779).\n */\n cursor?: string;\n /** Maximum objects per page, applied before the character budget. */\n limit?: number;\n}\n\ntype DiagnosticSeverity = 'error' | 'warning' | 'info';\n\ninterface Diagnostic {\n severity: DiagnosticSeverity;\n message: string;\n filePath?: string;\n line?: number;\n column?: number;\n}\n\ninterface ManifestLike {\n version?: string;\n packageName?: string;\n packageVersion?: string;\n objects?: Record<string, ManifestObject>;\n}\n\ninterface ManifestObject {\n name?: string;\n className: string;\n qualifiedName?: string;\n collection?: string;\n filePath: string;\n packageName?: string;\n packageVersion?: string;\n importPath?: string;\n modulePath?: string;\n exportName?: string;\n collectionExportName?: string;\n fields?: Record<string, ManifestField>;\n methods?: Record<string, ManifestMethod>;\n decoratorConfig?: Record<string, unknown>;\n extends?: string;\n extendsTypeArg?: string;\n schema?: ManifestSchema;\n staticProperties?: Record<string, unknown>;\n validationRules?: Record<string, unknown>;\n}\n\ninterface ManifestField {\n type: string;\n required?: boolean;\n default?: unknown;\n related?: string;\n description?: string;\n _meta?: Record<string, unknown>;\n transient?: boolean;\n}\n\ninterface ManifestMethod {\n name?: string;\n async?: boolean;\n parameters?: Array<{\n name: string;\n type?: string;\n optional?: boolean;\n default?: unknown;\n }>;\n returnType?: string;\n description?: string;\n isStatic?: boolean;\n isPublic?: boolean;\n}\n\ninterface ManifestSchema {\n tableName?: string;\n ddl?: string;\n columns?: Record<string, unknown>;\n indexes?: Array<{\n name?: string;\n columns?: string[];\n unique?: boolean;\n where?: string;\n jsonPath?: unknown;\n }>;\n version?: string;\n}\n\ninterface PackageMetadata {\n name?: string;\n version?: string;\n json?: Record<string, unknown>;\n}\n\nconst DEFAULT_MANIFEST_PATHS = [\n '.smrt/manifest.json',\n 'dist/manifest.json',\n 'src/manifest/manifest.json',\n];\n\nconst SCAN_EXCLUDE = [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/.git/**',\n '**/.smrt/**',\n '**/*.d.ts',\n '**/*.test.ts',\n '**/*.spec.ts',\n '**/__tests__/**',\n];\n\nconst RELATIONSHIP_TYPES = new Set([\n 'foreignKey',\n 'crossPackageRef',\n 'oneToMany',\n 'manyToMany',\n]);\n\n/**\n * Response budget in characters. Generous enough that a 42-object summary never\n * truncates, while still capping a runaway payload instead of emitting one.\n */\nconst DEFAULT_MAX_CHARS = 50_000;\n\nconst DEFAULT_MCP_OPERATIONS = ['list', 'get', 'create', 'update', 'delete'];\n\nexport async function introspectProject(\n args: IntrospectProjectArgs,\n): Promise<string> {\n const {\n directory = process.cwd(),\n includeFields = true,\n includeRelationships = true,\n includeMethods = true,\n detail = 'summary',\n maxChars = DEFAULT_MAX_CHARS,\n cursor,\n limit,\n } = args;\n const projectPath = resolve(directory);\n\n const exists = await pathExists(projectPath);\n if (!exists) {\n return JSON.stringify(\n {\n projectPath,\n manifestSource: 'none',\n objectCount: 0,\n objects: [],\n diagnostics: [\n {\n severity: 'warning',\n message: `Project directory does not exist: ${projectPath}`,\n },\n ],\n },\n null,\n 2,\n );\n }\n\n const packageMetadata = await readPackageMetadata(projectPath);\n const tenantScopes = await scanTenantScopes(projectPath);\n const manifestResult =\n (await loadManifestArtifact(projectPath, args.manifestPath)) ??\n (await scanSourceManifest(projectPath, packageMetadata));\n\n const entries = Object.entries(manifestResult.manifest.objects ?? {});\n const objects: IntrospectedObject[] =\n detail === 'summary'\n ? entries.map(([manifestKey, object]) =>\n summarizeObject({\n manifestKey,\n object,\n projectPath,\n tenantScope: tenantScopes.get(object.className),\n }),\n )\n : entries.map(([manifestKey, object]) =>\n formatObject({\n manifestKey,\n object,\n projectPath,\n includeFields,\n includeRelationships,\n includeMethods,\n tenantScope: tenantScopes.get(object.className),\n }),\n );\n objects.sort((left, right) => left.className.localeCompare(right.className));\n // Paging: skip everything up to and including the cursor, then cap by\n // `limit` before the character budget trims further.\n const afterCursor =\n typeof cursor === 'string' && cursor.length > 0\n ? objects.filter((object) => object.className.localeCompare(cursor) > 0)\n : objects;\n const pageLimit =\n typeof limit === 'number' && Number.isFinite(limit) && limit >= 1\n ? Math.floor(limit)\n : undefined;\n const window =\n pageLimit !== undefined ? afterCursor.slice(0, pageLimit) : afterCursor;\n const { kept, omitted: budgetOmitted } = applyObjectBudget(window, maxChars);\n const omitted = afterCursor.length - kept.length;\n const nextCursor =\n omitted > 0 && kept.length > 0\n ? kept[kept.length - 1].className\n : undefined;\n\n const output = {\n projectPath,\n manifestSource: manifestResult.source,\n manifestPath:\n 'path' in manifestResult\n ? relative(projectPath, manifestResult.path)\n : undefined,\n packageName:\n manifestResult.manifest.packageName ?? packageMetadata.name ?? undefined,\n packageVersion:\n manifestResult.manifest.packageVersion ??\n packageMetadata.version ??\n undefined,\n detail,\n objectCount: objects.length,\n scannedFileCount: manifestResult.scannedFileCount,\n parseTimeMs: manifestResult.parseTimeMs,\n ...(cursor ? { cursor } : {}),\n ...(nextCursor ? { nextCursor } : {}),\n ...(omitted > 0\n ? {\n truncated: {\n returnedObjectCount: kept.length,\n omittedObjectCount: omitted,\n budgetChars: maxChars,\n ...(pageLimit !== undefined ? { limit: pageLimit } : {}),\n guidance:\n budgetOmitted > 0\n ? 'The object list hit its character budget; metadata and diagnostics are still complete. Pass `cursor: nextCursor` to read the next page, narrow the scan with `directory`, keep `detail: \"summary\"`, or raise `maxChars` deliberately. Objects are sorted alphabetically.'\n : 'More objects follow this page. Pass `cursor: nextCursor` to continue.',\n },\n }\n : {}),\n objects: kept,\n diagnostics: manifestResult.diagnostics,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Trims the object list to the character budget, always keeping at least one so\n * a caller sees the shape rather than an empty list.\n */\nfunction applyObjectBudget<T>(\n objects: T[],\n maxChars: number,\n): { kept: T[]; omitted: number } {\n const kept: T[] = [];\n let used = 0;\n\n for (const object of objects) {\n const size = JSON.stringify(object, null, 2).length + 2;\n if (used + size > maxChars && kept.length > 0) break;\n used += size;\n kept.push(object);\n }\n\n return { kept, omitted: objects.length - kept.length };\n}\n\nfunction summarizeObject({\n manifestKey,\n object,\n projectPath,\n tenantScope,\n}: {\n manifestKey: string;\n object: ManifestObject;\n projectPath: string;\n tenantScope?: Record<string, unknown>;\n}) {\n const fields = Object.entries(object.fields ?? {});\n const decoratorConfig = object.decoratorConfig ?? {};\n\n return compactObject({\n manifestKey,\n className: object.className,\n qualifiedName: object.qualifiedName,\n filePath: sanitizePath(projectPath, object.filePath),\n extends: object.extends,\n collection: object.collection,\n tableName:\n object.schema?.tableName ??\n stringFromConfig(decoratorConfig.tableName) ??\n object.collection,\n tenantScope:\n tenantScope ?? normalizeTenantScopedConfig(decoratorConfig.tenantScoped),\n fieldCount: fields.length,\n relationships:\n fields\n .filter(([, field]) => RELATIONSHIP_TYPES.has(field.type))\n .map(\n ([name, field]) =>\n `${name} -> ${field.related ?? ''} (${field.type})`,\n )\n .join(', ') || undefined,\n mcpOperations: mcpOperationsFromConfig(decoratorConfig.mcp),\n });\n}\n\n/**\n * An omitted `mcp` config means full CRUD, not a closed surface — the same rule\n * the knowledge index applies.\n */\nfunction mcpOperationsFromConfig(config: unknown): string[] {\n if (config === false) return [];\n if (typeof config !== 'object' || config === null || Array.isArray(config)) {\n return [...DEFAULT_MCP_OPERATIONS];\n }\n\n const record = config as Record<string, unknown>;\n const include = Array.isArray(record.include)\n ? record.include.filter((item): item is string => typeof item === 'string')\n : DEFAULT_MCP_OPERATIONS;\n const exclude = new Set(\n Array.isArray(record.exclude)\n ? record.exclude.filter(\n (item): item is string => typeof item === 'string',\n )\n : [],\n );\n\n return include.filter((operation) => !exclude.has(operation));\n}\n\nasync function loadManifestArtifact(\n projectPath: string,\n manifestPath: string | undefined,\n): Promise<\n | {\n source: 'manifest';\n path: string;\n manifest: ManifestLike;\n diagnostics: Diagnostic[];\n scannedFileCount?: number;\n parseTimeMs?: number;\n }\n | undefined\n> {\n const candidates = manifestPath\n ? [resolve(projectPath, manifestPath)]\n : DEFAULT_MANIFEST_PATHS.map((candidate) => join(projectPath, candidate));\n\n const diagnostics: Diagnostic[] = [];\n for (const candidate of candidates) {\n if (!(await pathExists(candidate))) {\n continue;\n }\n\n try {\n const parsed = JSON.parse(await readFile(candidate, 'utf-8')) as unknown;\n if (isManifestLike(parsed)) {\n return {\n source: 'manifest',\n path: candidate,\n manifest: parsed,\n diagnostics,\n };\n }\n\n diagnostics.push({\n severity: 'warning',\n filePath: candidate,\n message: 'Manifest artifact is present but does not contain objects.',\n });\n } catch (error) {\n diagnostics.push({\n severity: 'error',\n filePath: candidate,\n message: `Unable to parse manifest artifact: ${messageFromError(error)}`,\n });\n }\n }\n\n return manifestPath\n ? {\n source: 'manifest',\n path: resolve(projectPath, manifestPath),\n manifest: { objects: {} },\n diagnostics: [\n ...diagnostics,\n {\n severity: 'warning',\n filePath: resolve(projectPath, manifestPath),\n message: 'Requested manifest artifact was not found.',\n },\n ],\n }\n : undefined;\n}\n\nasync function scanSourceManifest(\n projectPath: string,\n packageMetadata: PackageMetadata,\n): Promise<{\n source: 'scanner';\n manifest: ManifestLike;\n diagnostics: Diagnostic[];\n scannedFileCount: number;\n parseTimeMs: number;\n}> {\n const scanner = new OxcScanner({\n cwd: projectPath,\n include: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],\n exclude: SCAN_EXCLUDE,\n });\n const { results, resolved } = await scanner.scanAndResolve();\n const adapter = new ManifestAdapter();\n const manifest = adapter.toManifest(\n resolved.filter((classDef) => classDef.hasSmartDecorator),\n {\n packageName: packageMetadata.name,\n packageVersion: packageMetadata.version,\n typeAliases: results.typeAliases,\n },\n ) as ManifestLike;\n await finalizeScannerManifest(manifest, packageMetadata, projectPath);\n\n return {\n source: 'scanner',\n manifest,\n diagnostics: results.errors.map(scanErrorToDiagnostic),\n scannedFileCount: results.fileCount,\n parseTimeMs: Math.round(results.totalParseTimeMs),\n };\n}\n\n/**\n * Enrichment executes only installed framework code in an isolated process.\n * Its stdout/stderr are captured, never forwarded onto the MCP protocol. This\n * also prevents the scanner's registry and config from contaminating the host.\n */\nasync function finalizeScannerManifest(\n manifest: ManifestLike,\n packageMetadata: PackageMetadata,\n projectPath: string,\n): Promise<void> {\n const specifier = '@happyvertical/smrt-core/scanner';\n const entry = resolveProjectRuntimeEntry(projectPath, specifier);\n const enriched = await new Promise<ManifestLike>(\n (resolveManifest, reject) => {\n const child = spawn(\n process.execPath,\n [\n '--input-type=module',\n '-e',\n `\n const answer = (result) => process.send(result, () => process.disconnect());\n process.once('message', async (workerData) => {\n let module;\n try { module = await import(workerData.entry); }\n catch { answer({ unavailable: true }); return; }\n if (typeof module.ManifestGenerator !== 'function') { answer({ unavailable: true }); return; }\n try {\n const generator = new module.ManifestGenerator();\n const manifest = workerData.manifest;\n generator.injectTenantScopedFields(manifest);\n generator.mergeInheritedFields(manifest);\n generator.generateValidationRules(manifest);\n generator.generateSchemas(manifest);\n generator.assertTenantScopedSchemaContract(manifest);\n generator.generateAgentManifests(manifest, workerData.name, workerData.packageJson);\n answer({ manifest });\n } catch { answer({ failed: true }); }\n });\n `,\n ],\n { cwd: projectPath, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] },\n );\n child.send({\n entry: pathToFileURL(entry).href,\n manifest,\n name: packageMetadata.name,\n packageJson: packageMetadata.json,\n });\n const timeout = setTimeout(() => {\n child.kill('SIGKILL');\n reject(\n new Error(\n 'Project runtime manifest enrichment timed out; run the project build to diagnose it.',\n ),\n );\n }, 30_000);\n let answered = false;\n child.once('message', (message) => {\n const result = message as {\n unavailable?: boolean;\n failed?: boolean;\n manifest?: ManifestLike;\n };\n clearTimeout(timeout);\n answered = true;\n child.kill('SIGKILL');\n if (result.unavailable)\n reject(new ProjectRuntimeResolutionError(specifier));\n else if (result.failed)\n reject(\n new Error(\n 'Project runtime manifest enrichment failed. Run the project build to diagnose its manifest; no partial inventory was returned.',\n ),\n );\n else if (result.manifest) resolveManifest(result.manifest);\n else reject(new ProjectRuntimeResolutionError(specifier));\n });\n child.once('error', () => {\n clearTimeout(timeout);\n reject(new ProjectRuntimeResolutionError(specifier));\n });\n child.once('exit', () => {\n clearTimeout(timeout);\n if (!answered) reject(new ProjectRuntimeResolutionError(specifier));\n });\n },\n );\n Object.assign(manifest, enriched);\n}\n\nfunction formatObject({\n manifestKey,\n object,\n projectPath,\n includeFields,\n includeRelationships,\n includeMethods,\n tenantScope,\n}: {\n manifestKey: string;\n object: ManifestObject;\n projectPath: string;\n includeFields: boolean;\n includeRelationships: boolean;\n includeMethods: boolean;\n tenantScope?: Record<string, unknown>;\n}) {\n const fieldDetails = Object.entries(object.fields ?? {}).map(\n ([name, field]) => ({\n name,\n type: field.type,\n ...(field.required !== undefined ? { required: field.required } : {}),\n ...(field.default !== undefined ? { default: field.default } : {}),\n ...(field.related ? { related: field.related } : {}),\n ...(field.description ? { description: field.description } : {}),\n ...(field._meta ? { meta: field._meta } : {}),\n ...(field.transient !== undefined ? { transient: field.transient } : {}),\n }),\n );\n const relationshipDetails = fieldDetails\n .filter((field) => RELATIONSHIP_TYPES.has(field.type))\n .map((field) => ({\n field: field.name,\n relatedClass: field.related ?? '',\n type: field.type,\n ...(field.meta ? { meta: field.meta } : {}),\n }));\n const methodDetails = Object.entries(object.methods ?? {}).map(\n ([name, method]) => ({\n name: method.name ?? name,\n isAsync: method.async === true,\n isStatic: method.isStatic === true,\n isPublic: method.isPublic !== false,\n parameters: method.parameters ?? [],\n returnType: method.returnType ?? 'unknown',\n ...(method.description ? { description: method.description } : {}),\n }),\n );\n\n const decoratorConfig = object.decoratorConfig ?? {};\n const effectiveTenantScope =\n tenantScope ?? normalizeTenantScopedConfig(decoratorConfig.tenantScoped);\n const schema = object.schema;\n\n return compactObject({\n manifestKey,\n name: object.name,\n className: object.className,\n qualifiedName: object.qualifiedName,\n filePath: sanitizePath(projectPath, object.filePath),\n packageName: object.packageName,\n packageVersion: object.packageVersion,\n importPath: object.importPath,\n modulePath: object.modulePath,\n exportName: object.exportName,\n collectionExportName: object.collectionExportName,\n collection: object.collection,\n extends: object.extends,\n extendsTypeArg: object.extendsTypeArg,\n tableName:\n schema?.tableName ??\n stringFromConfig(decoratorConfig.tableName) ??\n object.collection,\n tableStrategy: stringFromConfig(decoratorConfig.tableStrategy) ?? 'cti',\n conflictColumns: arrayFromConfig(decoratorConfig.conflictColumns),\n tenantScope: effectiveTenantScope,\n decoratorConfig,\n schema: schema\n ? {\n tableName: schema.tableName,\n columns: schema.columns,\n indexes: schema.indexes ?? [],\n version: schema.version,\n }\n : undefined,\n indexes: schema?.indexes ?? [],\n staticProperties: object.staticProperties,\n validationRules: object.validationRules,\n ...(includeFields && {\n fields: fieldDetails\n .map((field) => `${field.name}: ${field.type}`)\n .join(', '),\n fieldDetails,\n }),\n ...(includeRelationships &&\n relationshipDetails.length > 0 && {\n relationships: relationshipDetails\n .map(\n (relationship) =>\n `${relationship.field} -> ${relationship.relatedClass} (${relationship.type})`,\n )\n .join(', '),\n relationshipDetails,\n }),\n ...(includeMethods &&\n methodDetails.length > 0 && {\n methods: methodDetails\n .map((method) => `${method.isAsync ? 'async ' : ''}${method.name}()`)\n .join(', '),\n methodDetails,\n }),\n });\n}\n\nasync function scanTenantScopes(\n projectPath: string,\n): Promise<Map<string, Record<string, unknown>>> {\n const files = await listSourceFiles(projectPath);\n const scopes = new Map<string, Record<string, unknown>>();\n\n await Promise.all(\n files.map(async (filePath) => {\n const content = await readFile(filePath, 'utf-8');\n const classMatches = content.matchAll(/class\\s+([A-Za-z_]\\w*)\\b/g);\n for (const match of classMatches) {\n const className = match[1];\n if (!className || match.index === undefined) continue;\n const prefix = content.slice(\n Math.max(0, match.index - 800),\n match.index,\n );\n const tenantMatches = Array.from(\n prefix.matchAll(/@TenantScoped\\s*\\(([\\s\\S]*?)\\)/g),\n );\n const tenantMatch = tenantMatches.at(-1);\n if (!tenantMatch) continue;\n scopes.set(className, {\n source: 'TenantScoped',\n ...parseTenantScopedOptions(tenantMatch[1]),\n });\n }\n }),\n );\n\n return scopes;\n}\n\nasync function listSourceFiles(projectPath: string): Promise<string[]> {\n const files: string[] = [];\n\n async function visit(dir: string): Promise<void> {\n let entries: Dirent[];\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n if (\n [\n 'node_modules',\n 'dist',\n 'build',\n '.git',\n '.smrt',\n '__tests__',\n ].includes(entry.name) ||\n entry.name.startsWith('.')\n ) {\n continue;\n }\n await visit(fullPath);\n continue;\n }\n\n if (\n entry.isFile() &&\n /\\.(tsx?|jsx?)$/.test(entry.name) &&\n !entry.name.endsWith('.d.ts') &&\n !entry.name.endsWith('.test.ts') &&\n !entry.name.endsWith('.spec.ts')\n ) {\n files.push(fullPath);\n }\n }\n }\n\n await visit(projectPath);\n return files;\n}\n\nfunction parseTenantScopedOptions(raw: string): Record<string, unknown> {\n const mode = raw.match(/mode\\s*:\\s*['\"`](required|optional)['\"`]/)?.[1];\n const field = raw.match(/field\\s*:\\s*['\"`]([A-Za-z_]\\w*)['\"`]/)?.[1];\n const allowSuperAdminBypass = raw.match(\n /allowSuperAdminBypass\\s*:\\s*(true|false)/,\n )?.[1];\n const autoFilter = raw.match(/autoFilter\\s*:\\s*(true|false)/)?.[1];\n const autoPopulate = raw.match(/autoPopulate\\s*:\\s*(true|false)/)?.[1];\n\n return compactObject({\n mode: mode ?? 'required',\n field: field ?? 'tenantId',\n autoFilter: autoFilter === undefined ? undefined : autoFilter === 'true',\n autoPopulate:\n autoPopulate === undefined ? undefined : autoPopulate === 'true',\n allowSuperAdminBypass:\n allowSuperAdminBypass === undefined\n ? undefined\n : allowSuperAdminBypass === 'true',\n });\n}\n\nfunction normalizeTenantScopedConfig(\n tenantScoped: unknown,\n): Record<string, unknown> | undefined {\n if (!tenantScoped) return undefined;\n const options =\n typeof tenantScoped === 'object' && !Array.isArray(tenantScoped)\n ? (tenantScoped as Record<string, unknown>)\n : {};\n return {\n source: 'smrt',\n mode: options.mode ?? 'required',\n field: options.field ?? 'tenantId',\n autoFilter: options.autoFilter ?? true,\n autoPopulate: options.autoPopulate ?? true,\n allowSuperAdminBypass: options.allowSuperAdminBypass ?? false,\n };\n}\n\nasync function readPackageMetadata(\n projectPath: string,\n): Promise<PackageMetadata> {\n const packageJsonPath = join(projectPath, 'package.json');\n if (!(await pathExists(packageJsonPath))) return {};\n\n try {\n const json = JSON.parse(await readFile(packageJsonPath, 'utf-8')) as Record<\n string,\n unknown\n >;\n return {\n name: typeof json.name === 'string' ? json.name : undefined,\n version: typeof json.version === 'string' ? json.version : undefined,\n json,\n };\n } catch {\n return {};\n }\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction isManifestLike(value: unknown): value is ManifestLike {\n return (\n !!value &&\n typeof value === 'object' &&\n !!(value as ManifestLike).objects &&\n typeof (value as ManifestLike).objects === 'object'\n );\n}\n\nfunction scanErrorToDiagnostic(error: ScanError): Diagnostic {\n return {\n severity: error.severity,\n message: error.message,\n filePath: error.filePath,\n line: error.line,\n column: error.column,\n };\n}\n\nfunction sanitizePath(projectPath: string, filePath: string): string {\n const absolute = isAbsolute(filePath)\n ? filePath\n : resolve(projectPath, filePath);\n const relativePath = relative(projectPath, absolute);\n if (!relativePath.startsWith('..')) {\n return relativePath || filePath;\n }\n return filePath;\n}\n\nfunction stringFromConfig(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction arrayFromConfig(value: unknown): string[] | undefined {\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === 'string')\n : undefined;\n}\n\nfunction compactObject<T extends Record<string, unknown>>(value: T): T {\n return Object.fromEntries(\n Object.entries(value).filter(([, entry]) => entry !== undefined),\n ) as T;\n}\n\nfunction messageFromError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":";;;;;;;;;;;AA0IA,IAAM,yBAAyB;CAC7B;CACA;CACA;AACF;AAEA,IAAM,eAAe;CACnB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,IAAM,qCAAqB,IAAI,IAAI;CACjC;CACA;CACA;CACA;AACF,CAAC;;;;;AAMD,IAAM,oBAAoB;AAE1B,IAAM,yBAAyB;CAAC;CAAQ;CAAO;CAAU;CAAU;AAAQ;AAE3E,eAAsB,kBACpB,MACiB;CACjB,MAAM,EACJ,YAAY,QAAQ,IAAI,GACxB,gBAAgB,MAChB,uBAAuB,MACvB,iBAAiB,MACjB,SAAS,WACT,WAAW,mBACX,QACA,UACE;CACJ,MAAM,cAAc,QAAQ,SAAS;CAGrC,IAAI,CAAC,MADgB,WAAW,WAAW,GAEzC,OAAO,KAAK,UACV;EACE;EACA,gBAAgB;EAChB,aAAa;EACb,SAAS,CAAC;EACV,aAAa,CACX;GACE,UAAU;GACV,SAAS,qCAAqC;EAChD,CACF;CACF,GACA,MACA,CACF;CAGF,MAAM,kBAAkB,MAAM,oBAAoB,WAAW;CAC7D,MAAM,eAAe,MAAM,iBAAiB,WAAW;CACvD,MAAM,iBACH,MAAM,qBAAqB,aAAa,KAAK,YAAY,KACzD,MAAM,mBAAmB,aAAa,eAAe;CAExD,MAAM,UAAU,OAAO,QAAQ,eAAe,SAAS,WAAW,CAAC,CAAC;CACpE,MAAM,UACJ,WAAW,YACP,QAAQ,KAAK,CAAC,aAAa,YACzB,gBAAgB;EACd;EACA;EACA;EACA,aAAa,aAAa,IAAI,OAAO,SAAS;CAChD,CAAC,CACH,IACA,QAAQ,KAAK,CAAC,aAAa,YACzB,aAAa;EACX;EACA;EACA;EACA;EACA;EACA;EACA,aAAa,aAAa,IAAI,OAAO,SAAS;CAChD,CAAC,CACH;CACN,QAAQ,MAAM,MAAM,UAAU,KAAK,UAAU,cAAc,MAAM,SAAS,CAAC;CAG3E,MAAM,cACJ,OAAO,WAAW,YAAY,OAAO,SAAS,IAC1C,QAAQ,QAAQ,WAAW,OAAO,UAAU,cAAc,MAAM,IAAI,CAAC,IACrE;CACN,MAAM,YACJ,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KAAK,SAAS,IAC5D,KAAK,MAAM,KAAK,IAChB,KAAA;CAGN,MAAM,EAAE,MAAM,SAAS,kBAAkB,kBADvC,cAAc,KAAA,IAAY,YAAY,MAAM,GAAG,SAAS,IAAI,aACK,QAAQ;CAC3E,MAAM,UAAU,YAAY,SAAS,KAAK;CAC1C,MAAM,aACJ,UAAU,KAAK,KAAK,SAAS,IACzB,KAAK,KAAK,SAAS,EAAE,CAAC,YACtB,KAAA;CAEN,MAAM,SAAS;EACb;EACA,gBAAgB,eAAe;EAC/B,cACE,UAAU,iBACN,SAAS,aAAa,eAAe,IAAI,IACzC,KAAA;EACN,aACE,eAAe,SAAS,eAAe,gBAAgB,QAAQ,KAAA;EACjE,gBACE,eAAe,SAAS,kBACxB,gBAAgB,WAChB,KAAA;EACF;EACA,aAAa,QAAQ;EACrB,kBAAkB,eAAe;EACjC,aAAa,eAAe;EAC5B,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAC3B,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;EACnC,GAAI,UAAU,IACV,EACE,WAAW;GACT,qBAAqB,KAAK;GAC1B,oBAAoB;GACpB,aAAa;GACb,GAAI,cAAc,KAAA,IAAY,EAAE,OAAO,UAAU,IAAI,CAAC;GACtD,UACE,gBAAgB,IACZ,+QACA;EACR,EACF,IACA,CAAC;EACL,SAAS;EACT,aAAa,eAAe;CAC9B;CAEA,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;AACvC;;;;;AAMA,SAAS,kBACP,SACA,UACgC;CAChC,MAAM,OAAY,CAAC;CACnB,IAAI,OAAO;CAEX,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC,CAAC,SAAS;EACtD,IAAI,OAAO,OAAO,YAAY,KAAK,SAAS,GAAG;EAC/C,QAAQ;EACR,KAAK,KAAK,MAAM;CAClB;CAEA,OAAO;EAAE;EAAM,SAAS,QAAQ,SAAS,KAAK;CAAO;AACvD;AAEA,SAAS,gBAAgB,EACvB,aACA,QACA,aACA,eAMC;CACD,MAAM,SAAS,OAAO,QAAQ,OAAO,UAAU,CAAC,CAAC;CACjD,MAAM,kBAAkB,OAAO,mBAAmB,CAAC;CAEnD,OAAO,cAAc;EACnB;EACA,WAAW,OAAO;EAClB,eAAe,OAAO;EACtB,UAAU,aAAa,aAAa,OAAO,QAAQ;EACnD,SAAS,OAAO;EAChB,YAAY,OAAO;EACnB,WACE,OAAO,QAAQ,aACf,iBAAiB,gBAAgB,SAAS,KAC1C,OAAO;EACT,aACE,eAAe,4BAA4B,gBAAgB,YAAY;EACzE,YAAY,OAAO;EACnB,eACE,OACG,QAAQ,GAAG,WAAW,mBAAmB,IAAI,MAAM,IAAI,CAAC,CAAC,CACzD,KACE,CAAC,MAAM,WACN,GAAG,KAAK,MAAM,MAAM,WAAW,GAAG,IAAI,MAAM,KAAK,EACrD,CAAC,CACA,KAAK,IAAI,KAAK,KAAA;EACnB,eAAe,wBAAwB,gBAAgB,GAAG;CAC5D,CAAC;AACH;;;;;AAMA,SAAS,wBAAwB,QAA2B;CAC1D,IAAI,WAAW,OAAO,OAAO,CAAC;CAC9B,IAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,MAAM,QAAQ,MAAM,GACvE,OAAO,CAAC,GAAG,sBAAsB;CAGnC,MAAM,SAAS;CACf,MAAM,UAAU,MAAM,QAAQ,OAAO,OAAO,IACxC,OAAO,QAAQ,QAAQ,SAAyB,OAAO,SAAS,QAAQ,IACxE;CACJ,MAAM,UAAU,IAAI,IAClB,MAAM,QAAQ,OAAO,OAAO,IACxB,OAAO,QAAQ,QACZ,SAAyB,OAAO,SAAS,QAC5C,IACA,CAAC,CACP;CAEA,OAAO,QAAQ,QAAQ,cAAc,CAAC,QAAQ,IAAI,SAAS,CAAC;AAC9D;AAEA,eAAe,qBACb,aACA,cAWA;CACA,MAAM,aAAa,eACf,CAAC,QAAQ,aAAa,YAAY,CAAC,IACnC,uBAAuB,KAAK,cAAc,KAAK,aAAa,SAAS,CAAC;CAE1E,MAAM,cAA4B,CAAC;CACnC,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,CAAE,MAAM,WAAW,SAAS,GAC9B;EAGF,IAAI;GACF,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,WAAW,OAAO,CAAC;GAC5D,IAAI,eAAe,MAAM,GACvB,OAAO;IACL,QAAQ;IACR,MAAM;IACN,UAAU;IACV;GACF;GAGF,YAAY,KAAK;IACf,UAAU;IACV,UAAU;IACV,SAAS;GACX,CAAC;EACH,SAAS,OAAO;GACd,YAAY,KAAK;IACf,UAAU;IACV,UAAU;IACV,SAAS,sCAAsC,iBAAiB,KAAK;GACvE,CAAC;EACH;CACF;CAEA,OAAO,eACH;EACE,QAAQ;EACR,MAAM,QAAQ,aAAa,YAAY;EACvC,UAAU,EAAE,SAAS,CAAC,EAAE;EACxB,aAAa,CACX,GAAG,aACH;GACE,UAAU;GACV,UAAU,QAAQ,aAAa,YAAY;GAC3C,SAAS;EACX,CACF;CACF,IACA,KAAA;AACN;AAEA,eAAe,mBACb,aACA,iBAOC;CAMD,MAAM,EAAE,SAAS,aAAa,MAAM,IALhB,WAAW;EAC7B,KAAK;EACL,SAAS;GAAC;GAAW;GAAY;GAAW;EAAU;EACtD,SAAS;CACX,CACoC,CAAA,CAAQ,eAAe;CAE3D,MAAM,WAAW,IADG,gBACH,CAAA,CAAQ,WACvB,SAAS,QAAQ,aAAa,SAAS,iBAAiB,GACxD;EACE,aAAa,gBAAgB;EAC7B,gBAAgB,gBAAgB;EAChC,aAAa,QAAQ;CACvB,CACF;CACA,MAAM,wBAAwB,UAAU,iBAAiB,WAAW;CAEpE,OAAO;EACL,QAAQ;EACR;EACA,aAAa,QAAQ,OAAO,IAAI,qBAAqB;EACrD,kBAAkB,QAAQ;EAC1B,aAAa,KAAK,MAAM,QAAQ,gBAAgB;CAClD;AACF;;;;;;AAOA,eAAe,wBACb,UACA,iBACA,aACe;CACf,MAAM,YAAY;CAClB,MAAM,QAAQ,2BAA2B,aAAa,SAAS;CAC/D,MAAM,WAAW,MAAM,IAAI,SACxB,iBAAiB,WAAW;EAC3B,MAAM,QAAQ,MACZ,QAAQ,UACR;GACE;GACA;GACA;;;;;;;;;;;;;;;;;;;;EAoBF,GACA;GAAE,KAAK;GAAa,OAAO;IAAC;IAAU;IAAU;IAAU;GAAK;EAAE,CACnE;EACA,MAAM,KAAK;GACT,OAAO,cAAc,KAAK,CAAC,CAAC;GAC5B;GACA,MAAM,gBAAgB;GACtB,aAAa,gBAAgB;EAC/B,CAAC;EACD,MAAM,UAAU,iBAAiB;GAC/B,MAAM,KAAK,SAAS;GACpB,uBACE,IAAI,MACF,sFACF,CACF;EACF,GAAG,GAAM;EACT,IAAI,WAAW;EACf,MAAM,KAAK,YAAY,YAAY;GACjC,MAAM,SAAS;GAKf,aAAa,OAAO;GACpB,WAAW;GACX,MAAM,KAAK,SAAS;GACpB,IAAI,OAAO,aACT,OAAO,IAAI,8BAA8B,SAAS,CAAC;QAChD,IAAI,OAAO,QACd,uBACE,IAAI,MACF,gIACF,CACF;QACG,IAAI,OAAO,UAAU,gBAAgB,OAAO,QAAQ;QACpD,OAAO,IAAI,8BAA8B,SAAS,CAAC;EAC1D,CAAC;EACD,MAAM,KAAK,eAAe;GACxB,aAAa,OAAO;GACpB,OAAO,IAAI,8BAA8B,SAAS,CAAC;EACrD,CAAC;EACD,MAAM,KAAK,cAAc;GACvB,aAAa,OAAO;GACpB,IAAI,CAAC,UAAU,OAAO,IAAI,8BAA8B,SAAS,CAAC;EACpE,CAAC;CACH,CACF;CACA,OAAO,OAAO,UAAU,QAAQ;AAClC;AAEA,SAAS,aAAa,EACpB,aACA,QACA,aACA,eACA,sBACA,gBACA,eASC;CACD,MAAM,eAAe,OAAO,QAAQ,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,KACtD,CAAC,MAAM,YAAY;EAClB;EACA,MAAM,MAAM;EACZ,GAAI,MAAM,aAAa,KAAA,IAAY,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;EACnE,GAAI,MAAM,YAAY,KAAA,IAAY,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;EAChE,GAAI,MAAM,UAAU,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;EAClD,GAAI,MAAM,cAAc,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;EAC9D,GAAI,MAAM,QAAQ,EAAE,MAAM,MAAM,MAAM,IAAI,CAAC;EAC3C,GAAI,MAAM,cAAc,KAAA,IAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;CACxE,EACF;CACA,MAAM,sBAAsB,aACzB,QAAQ,UAAU,mBAAmB,IAAI,MAAM,IAAI,CAAC,CAAC,CACrD,KAAK,WAAW;EACf,OAAO,MAAM;EACb,cAAc,MAAM,WAAW;EAC/B,MAAM,MAAM;EACZ,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;CAC3C,EAAE;CACJ,MAAM,gBAAgB,OAAO,QAAQ,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,KACxD,CAAC,MAAM,aAAa;EACnB,MAAM,OAAO,QAAQ;EACrB,SAAS,OAAO,UAAU;EAC1B,UAAU,OAAO,aAAa;EAC9B,UAAU,OAAO,aAAa;EAC9B,YAAY,OAAO,cAAc,CAAC;EAClC,YAAY,OAAO,cAAc;EACjC,GAAI,OAAO,cAAc,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;CAClE,EACF;CAEA,MAAM,kBAAkB,OAAO,mBAAmB,CAAC;CACnD,MAAM,uBACJ,eAAe,4BAA4B,gBAAgB,YAAY;CACzE,MAAM,SAAS,OAAO;CAEtB,OAAO,cAAc;EACnB;EACA,MAAM,OAAO;EACb,WAAW,OAAO;EAClB,eAAe,OAAO;EACtB,UAAU,aAAa,aAAa,OAAO,QAAQ;EACnD,aAAa,OAAO;EACpB,gBAAgB,OAAO;EACvB,YAAY,OAAO;EACnB,YAAY,OAAO;EACnB,YAAY,OAAO;EACnB,sBAAsB,OAAO;EAC7B,YAAY,OAAO;EACnB,SAAS,OAAO;EAChB,gBAAgB,OAAO;EACvB,WACE,QAAQ,aACR,iBAAiB,gBAAgB,SAAS,KAC1C,OAAO;EACT,eAAe,iBAAiB,gBAAgB,aAAa,KAAK;EAClE,iBAAiB,gBAAgB,gBAAgB,eAAe;EAChE,aAAa;EACb;EACA,QAAQ,SACJ;GACE,WAAW,OAAO;GAClB,SAAS,OAAO;GAChB,SAAS,OAAO,WAAW,CAAC;GAC5B,SAAS,OAAO;EAClB,IACA,KAAA;EACJ,SAAS,QAAQ,WAAW,CAAC;EAC7B,kBAAkB,OAAO;EACzB,iBAAiB,OAAO;EACxB,GAAI,iBAAiB;GACnB,QAAQ,aACL,KAAK,UAAU,GAAG,MAAM,KAAK,IAAI,MAAM,MAAM,CAAC,CAC9C,KAAK,IAAI;GACZ;EACF;EACA,GAAI,wBACF,oBAAoB,SAAS,KAAK;GAChC,eAAe,oBACZ,KACE,iBACC,GAAG,aAAa,MAAM,MAAM,aAAa,aAAa,IAAI,aAAa,KAAK,EAChF,CAAC,CACA,KAAK,IAAI;GACZ;EACF;EACF,GAAI,kBACF,cAAc,SAAS,KAAK;GAC1B,SAAS,cACN,KAAK,WAAW,GAAG,OAAO,UAAU,WAAW,KAAK,OAAO,KAAK,GAAG,CAAC,CACpE,KAAK,IAAI;GACZ;EACF;CACJ,CAAC;AACH;AAEA,eAAe,iBACb,aAC+C;CAC/C,MAAM,QAAQ,MAAM,gBAAgB,WAAW;CAC/C,MAAM,yBAAS,IAAI,IAAqC;CAExD,MAAM,QAAQ,IACZ,MAAM,IAAI,OAAO,aAAa;EAC5B,MAAM,UAAU,MAAM,SAAS,UAAU,OAAO;EAChD,MAAM,eAAe,QAAQ,SAAS,2BAA2B;EACjE,KAAK,MAAM,SAAS,cAAc;GAChC,MAAM,YAAY,MAAM;GACxB,IAAI,CAAC,aAAa,MAAM,UAAU,KAAA,GAAW;GAC7C,MAAM,SAAS,QAAQ,MACrB,KAAK,IAAI,GAAG,MAAM,QAAQ,GAAG,GAC7B,MAAM,KACR;GAIA,MAAM,cAHgB,MAAM,KAC1B,OAAO,SAAS,iCAAiC,CAE/B,CAAA,CAAc,GAAG,EAAE;GACvC,IAAI,CAAC,aAAa;GAClB,OAAO,IAAI,WAAW;IACpB,QAAQ;IACR,GAAG,yBAAyB,YAAY,EAAE;GAC5C,CAAC;EACH;CACF,CAAC,CACH;CAEA,OAAO;AACT;AAEA,eAAe,gBAAgB,aAAwC;CACrE,MAAM,QAAkB,CAAC;CAEzB,eAAe,MAAM,KAA4B;EAC/C,IAAI;EACJ,IAAI;GACF,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;EACtD,QAAQ;GACN;EACF;EAEA,KAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,WAAW,KAAK,KAAK,MAAM,IAAI;GACrC,IAAI,MAAM,YAAY,GAAG;IACvB,IACE;KACE;KACA;KACA;KACA;KACA;KACA;IACF,CAAC,CAAC,SAAS,MAAM,IAAI,KACrB,MAAM,KAAK,WAAW,GAAG,GAEzB;IAEF,MAAM,MAAM,QAAQ;IACpB;GACF;GAEA,IACE,MAAM,OAAO,KACb,iBAAiB,KAAK,MAAM,IAAI,KAChC,CAAC,MAAM,KAAK,SAAS,OAAO,KAC5B,CAAC,MAAM,KAAK,SAAS,UAAU,KAC/B,CAAC,MAAM,KAAK,SAAS,UAAU,GAE/B,MAAM,KAAK,QAAQ;EAEvB;CACF;CAEA,MAAM,MAAM,WAAW;CACvB,OAAO;AACT;AAEA,SAAS,yBAAyB,KAAsC;CACtE,MAAM,OAAO,IAAI,MAAM,0CAA0C,CAAC,GAAG;CACrE,MAAM,QAAQ,IAAI,MAAM,sCAAsC,CAAC,GAAG;CAClE,MAAM,wBAAwB,IAAI,MAChC,0CACF,CAAC,GAAG;CACJ,MAAM,aAAa,IAAI,MAAM,+BAA+B,CAAC,GAAG;CAChE,MAAM,eAAe,IAAI,MAAM,iCAAiC,CAAC,GAAG;CAEpE,OAAO,cAAc;EACnB,MAAM,QAAQ;EACd,OAAO,SAAS;EAChB,YAAY,eAAe,KAAA,IAAY,KAAA,IAAY,eAAe;EAClE,cACE,iBAAiB,KAAA,IAAY,KAAA,IAAY,iBAAiB;EAC5D,uBACE,0BAA0B,KAAA,IACtB,KAAA,IACA,0BAA0B;CAClC,CAAC;AACH;AAEA,SAAS,4BACP,cACqC;CACrC,IAAI,CAAC,cAAc,OAAO,KAAA;CAC1B,MAAM,UACJ,OAAO,iBAAiB,YAAY,CAAC,MAAM,QAAQ,YAAY,IAC1D,eACD,CAAC;CACP,OAAO;EACL,QAAQ;EACR,MAAM,QAAQ,QAAQ;EACtB,OAAO,QAAQ,SAAS;EACxB,YAAY,QAAQ,cAAc;EAClC,cAAc,QAAQ,gBAAgB;EACtC,uBAAuB,QAAQ,yBAAyB;CAC1D;AACF;AAEA,eAAe,oBACb,aAC0B;CAC1B,MAAM,kBAAkB,KAAK,aAAa,cAAc;CACxD,IAAI,CAAE,MAAM,WAAW,eAAe,GAAI,OAAO,CAAC;CAElD,IAAI;EACF,MAAM,OAAO,KAAK,MAAM,MAAM,SAAS,iBAAiB,OAAO,CAAC;EAIhE,OAAO;GACL,MAAM,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,KAAA;GAClD,SAAS,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU,KAAA;GAC3D;EACF;CACF,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,eAAe,WAAW,MAAgC;CACxD,IAAI;EACF,MAAM,OAAO,IAAI;EACjB,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,eAAe,OAAuC;CAC7D,OACE,CAAC,CAAC,SACF,OAAO,UAAU,YACjB,CAAC,CAAE,MAAuB,WAC1B,OAAQ,MAAuB,YAAY;AAE/C;AAEA,SAAS,sBAAsB,OAA8B;CAC3D,OAAO;EACL,UAAU,MAAM;EAChB,SAAS,MAAM;EACf,UAAU,MAAM;EAChB,MAAM,MAAM;EACZ,QAAQ,MAAM;CAChB;AACF;AAEA,SAAS,aAAa,aAAqB,UAA0B;CAInE,MAAM,eAAe,SAAS,aAHb,WAAW,QAAQ,IAChC,WACA,QAAQ,aAAa,QAAQ,CACkB;CACnD,IAAI,CAAC,aAAa,WAAW,IAAI,GAC/B,OAAO,gBAAgB;CAEzB,OAAO;AACT;AAEA,SAAS,iBAAiB,OAAoC;CAC5D,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAS,gBAAgB,OAAsC;CAC7D,OAAO,MAAM,QAAQ,KAAK,IACtB,MAAM,QAAQ,SAAyB,OAAO,SAAS,QAAQ,IAC/D,KAAA;AACN;AAEA,SAAS,cAAiD,OAAa;CACrE,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CACjE;AACF;AAEA,SAAS,iBAAiB,OAAwB;CAChD,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D"}
@@ -1,4 +1,4 @@
1
- import { _ as projectRuntimeRoot, d as withRuntimeSetup, g as loadProjectCore, h as importProjectRuntimeModule, m as ProjectRuntimeResolutionError, u as withRuntimeConnection, y as __exportAll } from "./tools-B-CDyCXi.js";
1
+ import { _ as projectRuntimeRoot, d as withRuntimeSetup, g as loadProjectCore, h as importProjectRuntimeModule, m as ProjectRuntimeResolutionError, u as withRuntimeConnection, y as __exportAll } from "./tools-B0uaPTz0.js";
2
2
  import { existsSync, readFileSync, statSync } from "node:fs";
3
3
  import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
4
4
  //#region src/tools/runtime/boot.ts
@@ -383,4 +383,4 @@ var runtimeSchemaDiff = (args = {}) => withRuntimeSetup(() => runtimeSchemaDiffF
383
383
  //#endregion
384
384
  export { bootRuntime as a, runtimeSchemaDiff as i, runtimeObject as n, runtimeRegistry as r, observation_exports as t };
385
385
 
386
- //# sourceMappingURL=observation-BA287SLA.js.map
386
+ //# sourceMappingURL=observation-CmDrAP9J.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"observation-BA287SLA.js","names":[],"sources":["../src/tools/runtime/boot.ts","../src/tools/runtime/observation.ts"],"sourcesContent":["/**\n * Confined runtime bootstrap for the Level 2 observation plane (#1831).\n *\n * \"Booting\" here means registering *manifests* into the in-process\n * `ObjectRegistry` — the project's own `.smrt/manifest.json` (or built\n * `dist/manifest.json`) plus every installed SMRT package manifest that the\n * project's dependency tree resolves to. No project source is imported, no\n * module is executed, no database is touched. That confinement is the safety\n * boundary: an observing agent sees what the runtime *would* register, never\n * what arbitrary project code does on import.\n *\n * The registry is a process-global singleton, so a process boots once. There\n * is deliberately no re-boot tool; restart the process to observe a rebuilt\n * manifest.\n */\n\nimport { existsSync, readFileSync, statSync } from 'node:fs';\nimport { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';\nimport {\n importProjectRuntimeModule,\n loadProjectCore,\n ProjectRuntimeResolutionError,\n projectRuntimeRoot,\n resetProjectRuntimeForTests,\n} from './project-runtime.js';\n\n/** Provenance label for facts read from authored/installed manifests. */\nexport const DECLARED_PROVENANCE = 'declared (manifest)';\n\n/** Project manifest candidates, most authoritative first. */\nconst PROJECT_MANIFEST_CANDIDATES = [\n '.smrt/manifest.json',\n 'dist/manifest.json',\n] as const;\n\nexport interface BootDiagnostic {\n severity: 'info' | 'warning' | 'error';\n code: string;\n message: string;\n}\n\nexport interface BootedManifest {\n /** `project` for the project's own manifest, `dependency` for installed packages. */\n kind: 'project' | 'dependency';\n packageName: string | null;\n /** Path relative to the project root (never absolute). */\n path: string;\n objectCount: number;\n}\n\nexport interface RuntimeBoot {\n provenance: typeof DECLARED_PROVENANCE;\n bootedAt: string;\n /** Basename only; the absolute project root never leaves the process. */\n projectName: string;\n manifests: BootedManifest[];\n objectCount: number;\n diagnostics: BootDiagnostic[];\n}\n\ninterface ManifestLike {\n packageName?: unknown;\n moduleType?: unknown;\n objects?: Record<string, unknown>;\n}\n\nfunction relativePath(projectRoot: string, path: string): string {\n const rel = relative(projectRoot, path);\n // Anything outside the root — the parent itself, a `..` walk, or a path on\n // another drive (Windows keeps those absolute) — is reduced to a basename.\n if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {\n return basename(path);\n }\n return rel.split(sep).join('/');\n}\n\nfunction readManifest(path: string): ManifestLike | null {\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf8')) as unknown;\n return parsed && typeof parsed === 'object'\n ? (parsed as ManifestLike)\n : null;\n } catch {\n return null;\n }\n}\n\nfunction registerManifest(\n ObjectRegistry: typeof import('@happyvertical/smrt-core').ObjectRegistry,\n manifest: ManifestLike,\n fallbackPackageName: string | null,\n): number {\n const manifestPackageName =\n typeof manifest.packageName === 'string' && manifest.packageName\n ? manifest.packageName\n : (fallbackPackageName ?? undefined);\n let count = 0;\n for (const [name, definition] of Object.entries(manifest.objects ?? {})) {\n if (!definition || typeof definition !== 'object') continue;\n // An aggregate project manifest (consumer plugin output) carries\n // dependency objects that keep their own `packageName`; registering them\n // under the app's name would mint false `@app:Object` identities that\n // collide with the dependency's own manifest. Per-object ownership wins.\n const ownPackage = (definition as { packageName?: unknown }).packageName;\n ObjectRegistry.registerFromManifest(\n name,\n definition as Parameters<typeof ObjectRegistry.registerFromManifest>[1],\n typeof ownPackage === 'string' && ownPackage\n ? ownPackage\n : manifestPackageName,\n );\n count += 1;\n }\n return count;\n}\n\nlet booting: Promise<RuntimeBoot> | null = null;\nlet booted: RuntimeBoot | null = null;\nlet bootedProjectRoot: string | null = null;\nlet bootedProjectManifestPath: string | null = null;\nlet bootedProjectManifestMtimeMs: number | null = null;\n\n/**\n * Whether the project manifest on disk is newer than the one this process\n * booted. The registry is process-global and boots once, so this is the only\n * signal an agent has that a rebuild happened underneath it.\n */\nexport function getBootStaleness(): {\n stale: boolean;\n bootedAt: string | null;\n manifestModifiedAt: string | null;\n} {\n if (!booted || !bootedProjectManifestPath) {\n return {\n stale: false,\n bootedAt: booted?.bootedAt ?? null,\n manifestModifiedAt: null,\n };\n }\n try {\n const mtimeMs = statSync(bootedProjectManifestPath).mtimeMs;\n return {\n stale:\n bootedProjectManifestMtimeMs !== null &&\n mtimeMs > bootedProjectManifestMtimeMs,\n bootedAt: booted.bootedAt,\n manifestModifiedAt: new Date(mtimeMs).toISOString(),\n };\n } catch {\n return {\n stale: false,\n bootedAt: booted.bootedAt,\n manifestModifiedAt: null,\n };\n }\n}\n\n/** The boot record for this process, or `null` before {@link bootRuntime}. */\nexport function getRuntimeBoot(): RuntimeBoot | null {\n return booted;\n}\n\n/**\n * The resolved root the process booted from. Consumers must relativize\n * paths against *this* root, never a per-request argument, or a caller could\n * widen the root (e.g. `/`) and read the layout back through \"relative\" paths.\n */\nexport function getBootedProjectRoot(): string | null {\n return bootedProjectRoot;\n}\n\n/** Test seam: forget the boot record (the registry itself is cleared by the caller). */\nexport function resetRuntimeBootForTests(): void {\n resetProjectRuntimeForTests();\n booting = null;\n booted = null;\n bootedProjectRoot = null;\n bootedProjectManifestPath = null;\n bootedProjectManifestMtimeMs = null;\n}\n\nexport interface BootRuntimeOptions {\n projectRoot?: string;\n /** Clock for `bootedAt` (defaults to `new Date()`). */\n now?: Date;\n}\n\n/**\n * Boot the confined runtime once per process. A second call returns the\n * existing record without touching the registry.\n */\nexport async function bootRuntime(\n options: BootRuntimeOptions = {},\n): Promise<RuntimeBoot> {\n const projectRoot = resolve(options.projectRoot ?? process.cwd());\n if (\n bootedProjectRoot &&\n projectRuntimeRoot(bootedProjectRoot) !== projectRuntimeRoot(projectRoot)\n ) {\n throw new ProjectRuntimeResolutionError(\n '@happyvertical/smrt-core',\n 'runtime_project_mismatch',\n );\n }\n if (booted) return booted;\n if (booting) return booting;\n bootedProjectRoot = projectRoot;\n booting = performBoot(projectRoot, options).catch((error) => {\n booting = null;\n bootedProjectRoot = null;\n throw error;\n });\n return booting;\n}\n\nasync function performBoot(\n projectRoot: string,\n options: BootRuntimeOptions,\n): Promise<RuntimeBoot> {\n const { ObjectRegistry } = await loadProjectCore(projectRoot);\n const { discoverSmrtPackages, resolveManifestPath } =\n await importProjectRuntimeModule<\n typeof import('@happyvertical/smrt-core/manifest/discover-smrt-packages')\n >(projectRoot, '@happyvertical/smrt-core/manifest/discover-smrt-packages');\n if (\n typeof ObjectRegistry?.registerFromManifest !== 'function' ||\n typeof discoverSmrtPackages !== 'function' ||\n typeof resolveManifestPath !== 'function'\n ) {\n throw new ProjectRuntimeResolutionError('@happyvertical/smrt-core');\n }\n const diagnostics: BootDiagnostic[] = [];\n const manifests: BootedManifest[] = [];\n\n const projectManifestPath = PROJECT_MANIFEST_CANDIDATES.map((candidate) =>\n join(projectRoot, candidate),\n ).find((path) => existsSync(path));\n const projectPackageName = readProjectPackageName(projectRoot);\n\n if (!projectManifestPath) {\n diagnostics.push({\n severity: 'warning',\n code: 'project_manifest_missing',\n message:\n 'No project manifest found (.smrt/manifest.json or dist/manifest.json); run the project build so the runtime manifest exists.',\n });\n } else {\n const manifest = readManifest(projectManifestPath);\n if (!manifest) {\n diagnostics.push({\n severity: 'error',\n code: 'project_manifest_invalid',\n message: `Project manifest at ${relativePath(projectRoot, projectManifestPath)} is not valid JSON.`,\n });\n } else {\n manifests.push({\n kind: 'project',\n packageName:\n typeof manifest.packageName === 'string'\n ? manifest.packageName\n : projectPackageName,\n path: relativePath(projectRoot, projectManifestPath),\n objectCount: registerManifest(\n ObjectRegistry,\n manifest,\n projectPackageName,\n ),\n });\n }\n }\n\n let dependencyNames: string[] = [];\n try {\n // `noCache` keeps discovery from writing `.smrt/discovery-cache.json`:\n // observing a project must never dirty it.\n dependencyNames = discoverSmrtPackages({\n baseDir: projectRoot,\n noCache: true,\n });\n } catch (error) {\n diagnostics.push({\n severity: 'warning',\n code: 'dependency_discovery_failed',\n message: `Installed SMRT package discovery failed: ${error instanceof Error ? error.message : 'unknown error'}`,\n });\n }\n for (const dependency of dependencyNames.sort()) {\n const manifestPath = resolveManifestPath(dependency, projectRoot);\n if (!manifestPath) {\n diagnostics.push({\n severity: 'info',\n code: 'dependency_manifest_missing',\n message: `Installed SMRT package ${dependency} exposes no runtime manifest.`,\n });\n continue;\n }\n const manifest = readManifest(manifestPath);\n if (!manifest) {\n diagnostics.push({\n severity: 'warning',\n code: 'dependency_manifest_invalid',\n message: `Manifest for ${dependency} is not valid JSON.`,\n });\n continue;\n }\n manifests.push({\n kind: 'dependency',\n packageName: dependency,\n path: relativePath(projectRoot, manifestPath),\n objectCount: registerManifest(ObjectRegistry, manifest, dependency),\n });\n }\n\n bootedProjectRoot = projectRoot;\n if (projectManifestPath) {\n bootedProjectManifestPath = projectManifestPath;\n try {\n bootedProjectManifestMtimeMs = statSync(projectManifestPath).mtimeMs;\n } catch {\n bootedProjectManifestMtimeMs = null;\n }\n }\n booted = {\n provenance: DECLARED_PROVENANCE,\n bootedAt: (options.now ?? new Date()).toISOString(),\n projectName: projectPackageName ?? basename(projectRoot),\n manifests,\n objectCount: manifests.reduce((sum, m) => sum + m.objectCount, 0),\n diagnostics,\n };\n return booted;\n}\n\nfunction readProjectPackageName(projectRoot: string): string | null {\n try {\n const parsed = JSON.parse(\n readFileSync(join(projectRoot, 'package.json'), 'utf8'),\n ) as { name?: unknown };\n return typeof parsed.name === 'string' && parsed.name ? parsed.name : null;\n } catch {\n return null;\n }\n}\n","/**\n * Level 2 read-only observation tools over the booted runtime (#1831).\n *\n * Three facts planes, labelled separately in every envelope:\n * - `declared (manifest)`: what the confined boot registered ({@link bootRuntime});\n * - `booted (registry)`: the in-process `ObjectRegistry` projected through the\n * sanitized {@link snapshotRegistry} DTO;\n * - `runtime (live DB)`: the optional read-only connection, reused from Level 1.\n *\n * Nothing here mutates: no writes, no `do()`, no generated CRUD, no project\n * code execution. `runtime-schema-diff` only *introspects* the live schema.\n */\n\nimport type { RegistrySnapshotObject } from '@happyvertical/smrt-core';\nimport {\n bootRuntime,\n getBootedProjectRoot,\n getBootStaleness,\n type RuntimeBoot,\n} from './boot.js';\nimport type { RuntimeDatabaseArgs } from './connection.js';\nimport {\n importProjectRuntimeModule,\n loadProjectCore,\n ProjectRuntimeResolutionError,\n} from './project-runtime.js';\nimport {\n type RuntimeDiagnostic,\n type RuntimeToolEnvelope,\n withRuntimeConnection,\n withRuntimeSetup,\n} from './tools.js';\n\n/** Row budget for `runtime-schema-diff` change lists. */\nconst SCHEMA_DIFF_CHANGE_LIMIT = 200;\n\nexport interface RuntimeProjectArgs {\n /** Project root to boot from (defaults to the server's cwd). */\n projectPath?: string;\n}\n\nfunction bootDiagnostics(boot: RuntimeBoot): RuntimeDiagnostic[] {\n return boot.diagnostics\n .filter((d) => d.severity !== 'info')\n .map((d) => ({\n severity: d.severity === 'error' ? 'warning' : d.severity,\n code: `boot_${d.code}`,\n message: d.message,\n }));\n}\n\nlet bootPreambleSent = false;\n\n/**\n * The full manifest list is emitted once per process; later envelopes carry\n * only the identifying fields. Every runtime response re-embedding the same\n * multi-manifest block was pure repetition for an agent.\n */\nfunction bootSummary(boot: RuntimeBoot) {\n const compact = {\n provenance: boot.provenance,\n bootedAt: boot.bootedAt,\n projectName: boot.projectName,\n objectCount: boot.objectCount,\n manifestCount: boot.manifests.length,\n };\n if (bootPreambleSent) return compact;\n bootPreambleSent = true;\n return { ...compact, manifests: boot.manifests };\n}\n\n/** Test seam paired with `resetRuntimeBootForTests()`. */\nexport function resetBootPreambleForTests(): void {\n bootPreambleSent = false;\n}\n\nfunction stalenessDiagnostics(): RuntimeDiagnostic[] {\n const staleness = getBootStaleness();\n if (!staleness.stale) return [];\n return [\n {\n severity: 'warning',\n code: 'manifest_newer_than_boot',\n message: `The project manifest changed at ${staleness.manifestModifiedAt} after this process booted at ${staleness.bootedAt}; restart the server to observe the rebuilt registry.`,\n },\n ];\n}\n\nexport interface RuntimeRegistryArgs extends RuntimeProjectArgs {\n /** Restrict object detail to these simple or qualified names. */\n objects?: string[];\n /** Include field/method detail (default: only when `objects` is given). */\n detail?: boolean;\n /**\n * Resume after this object key: the qualified name when the object has\n * one, otherwise its simple name — exactly the value a previous response\n * returned as `page.nextCursor` (#2779). Objects are sorted by that key.\n */\n cursor?: string;\n /** Objects per page (default {@link REGISTRY_PAGE_LIMIT}, max 500). */\n limit?: number;\n}\n\n/** Default objects per `runtime-registry` page. */\nexport const REGISTRY_PAGE_LIMIT = 50;\n\nfunction objectKey(object: {\n qualifiedName: string | null;\n name: string;\n}): string {\n return object.qualifiedName ?? object.name;\n}\n\n/** `runtime-registry`: sanitized snapshot of the booted registry. */\nasync function runtimeRegistryFromProject(\n args: RuntimeRegistryArgs = {},\n): Promise<RuntimeToolEnvelope> {\n const boot = await bootRuntime({ projectRoot: args.projectPath });\n const { snapshotRegistry } = await loadProjectCore(args.projectPath);\n // Paths are relativized against the root the process actually booted from,\n // never the per-request argument (which is ignored after the first boot).\n if (typeof snapshotRegistry !== 'function')\n throw new ProjectRuntimeResolutionError('@happyvertical/smrt-core');\n const snapshot = snapshotRegistry({\n projectRoot: getBootedProjectRoot() ?? undefined,\n objects: args.objects,\n detail: args.detail ?? Boolean(args.objects?.length),\n });\n // Page the object list (summary stays global). A 76-object app answered in\n // 56 KB before paging; an agent hunting one class needs a cursor, not a cut.\n const limit = Math.min(\n Math.max(Math.floor(args.limit ?? REGISTRY_PAGE_LIMIT), 1),\n 500,\n );\n // The cursor is compared against the same key `page.nextCursor` carries.\n const cursor =\n typeof args.cursor === 'string' && args.cursor.length > 0\n ? args.cursor\n : null;\n const all = snapshot.objects;\n const afterCursor = cursor\n ? all.filter((object) => objectKey(object).localeCompare(cursor) > 0)\n : all;\n const objects = afterCursor.slice(0, limit);\n const nextCursor =\n afterCursor.length > objects.length && objects.length > 0\n ? objectKey(objects[objects.length - 1])\n : null;\n return {\n ok: true,\n coverage: null,\n diagnostics: [...bootDiagnostics(boot), ...stalenessDiagnostics()],\n data: {\n provenance: snapshot.provenance,\n boot: bootSummary(boot),\n page: {\n returned: objects.length,\n matched: all.length,\n limit,\n cursor,\n nextCursor,\n },\n snapshot: { ...snapshot, objects },\n },\n };\n}\n\nexport interface RuntimeObjectArgs extends RuntimeProjectArgs {\n /** Simple or qualified object name. */\n name: string;\n /** Engine for the generated DDL preview (default: the registry's default). */\n engine?: 'sqlite' | 'postgres' | 'duckdb';\n}\n\n/** `runtime-object`: one object's sanitized definition plus its generated DDL. */\nasync function runtimeObjectFromProject(\n args: RuntimeObjectArgs,\n): Promise<RuntimeToolEnvelope> {\n const boot = await bootRuntime({ projectRoot: args.projectPath });\n const { ObjectRegistry, snapshotRegistry } = await loadProjectCore(\n args.projectPath,\n );\n const name = typeof args.name === 'string' ? args.name.trim() : '';\n if (typeof snapshotRegistry !== 'function')\n throw new ProjectRuntimeResolutionError('@happyvertical/smrt-core');\n const snapshot = snapshotRegistry({\n projectRoot: getBootedProjectRoot() ?? undefined,\n objects: name ? [name] : [],\n detail: true,\n });\n const diagnostics = [...bootDiagnostics(boot), ...stalenessDiagnostics()];\n let object: RegistrySnapshotObject | null = snapshot.objects[0] ?? null;\n if (snapshot.objects.length > 1) {\n // Two packages registering the same simple name is legal; picking one\n // silently would misreport identity. Ask for the qualified name instead.\n object = null;\n diagnostics.push({\n severity: 'warning',\n code: 'object_ambiguous',\n message: `${name} is registered by several packages; pass a qualified name: ${snapshot.objects\n .map((candidate) => candidate.qualifiedName ?? candidate.name)\n .join(', ')}`,\n });\n } else if (!object) {\n diagnostics.push({\n severity: 'warning',\n code: 'object_not_found',\n message: name\n ? `No booted object named ${name}; use runtime-registry to list names.`\n : 'name is required.',\n });\n }\n let ddl: string | null = null;\n if (object) {\n try {\n // Resolve by qualified identity so a same-name class in another package\n // can never answer for this one.\n ddl =\n ObjectRegistry.getSchemaDDL(\n object.qualifiedName ?? object.name,\n args.engine,\n ) ?? null;\n } catch (error) {\n diagnostics.push({\n severity: 'warning',\n code: 'ddl_unavailable',\n message: `Generated DDL unavailable: ${error instanceof Error ? error.message : 'unknown error'}`,\n });\n }\n }\n return {\n ok: true,\n coverage: null,\n diagnostics,\n data: {\n provenance: snapshot.provenance,\n boot: bootSummary(boot),\n object,\n ddl,\n },\n };\n}\n\nexport interface RuntimeSchemaDiffArgs\n extends RuntimeDatabaseArgs,\n RuntimeProjectArgs {}\n\n/**\n * `runtime-schema-diff`: booted registry schemas versus the live database,\n * using the same comparer `db:diff`/`db:migrate` use. Introspection only —\n * drop/relax options are pinned off and nothing is executed.\n */\nasync function runtimeSchemaDiffFromProject(\n args: RuntimeSchemaDiffArgs = {},\n): Promise<RuntimeToolEnvelope> {\n const boot = await bootRuntime({ projectRoot: args.projectPath });\n const { ObjectRegistry } = await loadProjectCore(args.projectPath);\n const envelope = await withRuntimeConnection(\n args,\n async (db) => {\n const { SchemaComparer } = await importProjectRuntimeModule<\n typeof import('@happyvertical/smrt-core/migrations')\n >(args.projectPath, '@happyvertical/smrt-core/migrations');\n if (typeof SchemaComparer !== 'function')\n throw new ProjectRuntimeResolutionError(\n '@happyvertical/smrt-core/migrations',\n );\n const comparer = new SchemaComparer(\n db as ConstructorParameters<typeof SchemaComparer>[0],\n {\n includeDroppedTables: false,\n includeDroppedColumns: false,\n includeDroppedIndexes: false,\n relaxColumns: false,\n },\n );\n const diff = await comparer.compare(\n ObjectRegistry.getAllSchemasAsDefinitions(),\n );\n const byType: Record<string, number> = {};\n for (const change of diff.changes) {\n const type = String((change as { type?: unknown }).type ?? 'unknown');\n byType[type] = (byType[type] ?? 0) + 1;\n }\n // `hasChanges` covers table adds/drops too; count them so the two agree.\n const addedTables = diff.added_tables.map((t) => t.tableName);\n if (addedTables.length > 0) byType.add_table = addedTables.length;\n if (diff.dropped_tables.length > 0)\n byType.drop_table = diff.dropped_tables.length;\n const changeCount =\n diff.changes.length + addedTables.length + diff.dropped_tables.length;\n return {\n data: {\n boot: bootSummary(boot),\n hasChanges: diff.has_changes,\n addedTables,\n droppedTables: diff.dropped_tables,\n orphanTables: diff.orphan_tables ?? [],\n changeCount,\n columnChangeCount: diff.changes.length,\n changesByType: byType,\n changes: diff.changes.slice(0, SCHEMA_DIFF_CHANGE_LIMIT),\n truncated: diff.changes.length > SCHEMA_DIFF_CHANGE_LIMIT,\n },\n diagnostics: [],\n };\n },\n 'booted registry schemas only; connect a dev database to diff against live tables',\n );\n envelope.diagnostics = [\n ...bootDiagnostics(boot),\n ...stalenessDiagnostics(),\n ...envelope.diagnostics,\n ];\n return envelope;\n}\n\nexport const runtimeRegistry = (args: RuntimeRegistryArgs = {}) =>\n withRuntimeSetup(() => runtimeRegistryFromProject(args));\nexport const runtimeObject = (args: RuntimeObjectArgs) =>\n withRuntimeSetup(() => runtimeObjectFromProject(args));\nexport const runtimeSchemaDiff = (args: RuntimeSchemaDiffArgs = {}) =>\n withRuntimeSetup(() => runtimeSchemaDiffFromProject(args));\n"],"mappings":"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"}
1
+ {"version":3,"file":"observation-CmDrAP9J.js","names":[],"sources":["../src/tools/runtime/boot.ts","../src/tools/runtime/observation.ts"],"sourcesContent":["/**\n * Confined runtime bootstrap for the Level 2 observation plane (#1831).\n *\n * \"Booting\" here means registering *manifests* into the in-process\n * `ObjectRegistry` — the project's own `.smrt/manifest.json` (or built\n * `dist/manifest.json`) plus every installed SMRT package manifest that the\n * project's dependency tree resolves to. No project source is imported, no\n * module is executed, no database is touched. That confinement is the safety\n * boundary: an observing agent sees what the runtime *would* register, never\n * what arbitrary project code does on import.\n *\n * The registry is a process-global singleton, so a process boots once. There\n * is deliberately no re-boot tool; restart the process to observe a rebuilt\n * manifest.\n */\n\nimport { existsSync, readFileSync, statSync } from 'node:fs';\nimport { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';\nimport {\n importProjectRuntimeModule,\n loadProjectCore,\n ProjectRuntimeResolutionError,\n projectRuntimeRoot,\n resetProjectRuntimeForTests,\n} from './project-runtime.js';\n\n/** Provenance label for facts read from authored/installed manifests. */\nexport const DECLARED_PROVENANCE = 'declared (manifest)';\n\n/** Project manifest candidates, most authoritative first. */\nconst PROJECT_MANIFEST_CANDIDATES = [\n '.smrt/manifest.json',\n 'dist/manifest.json',\n] as const;\n\nexport interface BootDiagnostic {\n severity: 'info' | 'warning' | 'error';\n code: string;\n message: string;\n}\n\nexport interface BootedManifest {\n /** `project` for the project's own manifest, `dependency` for installed packages. */\n kind: 'project' | 'dependency';\n packageName: string | null;\n /** Path relative to the project root (never absolute). */\n path: string;\n objectCount: number;\n}\n\nexport interface RuntimeBoot {\n provenance: typeof DECLARED_PROVENANCE;\n bootedAt: string;\n /** Basename only; the absolute project root never leaves the process. */\n projectName: string;\n manifests: BootedManifest[];\n objectCount: number;\n diagnostics: BootDiagnostic[];\n}\n\ninterface ManifestLike {\n packageName?: unknown;\n moduleType?: unknown;\n objects?: Record<string, unknown>;\n}\n\nfunction relativePath(projectRoot: string, path: string): string {\n const rel = relative(projectRoot, path);\n // Anything outside the root — the parent itself, a `..` walk, or a path on\n // another drive (Windows keeps those absolute) — is reduced to a basename.\n if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {\n return basename(path);\n }\n return rel.split(sep).join('/');\n}\n\nfunction readManifest(path: string): ManifestLike | null {\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf8')) as unknown;\n return parsed && typeof parsed === 'object'\n ? (parsed as ManifestLike)\n : null;\n } catch {\n return null;\n }\n}\n\nfunction registerManifest(\n ObjectRegistry: typeof import('@happyvertical/smrt-core').ObjectRegistry,\n manifest: ManifestLike,\n fallbackPackageName: string | null,\n): number {\n const manifestPackageName =\n typeof manifest.packageName === 'string' && manifest.packageName\n ? manifest.packageName\n : (fallbackPackageName ?? undefined);\n let count = 0;\n for (const [name, definition] of Object.entries(manifest.objects ?? {})) {\n if (!definition || typeof definition !== 'object') continue;\n // An aggregate project manifest (consumer plugin output) carries\n // dependency objects that keep their own `packageName`; registering them\n // under the app's name would mint false `@app:Object` identities that\n // collide with the dependency's own manifest. Per-object ownership wins.\n const ownPackage = (definition as { packageName?: unknown }).packageName;\n ObjectRegistry.registerFromManifest(\n name,\n definition as Parameters<typeof ObjectRegistry.registerFromManifest>[1],\n typeof ownPackage === 'string' && ownPackage\n ? ownPackage\n : manifestPackageName,\n );\n count += 1;\n }\n return count;\n}\n\nlet booting: Promise<RuntimeBoot> | null = null;\nlet booted: RuntimeBoot | null = null;\nlet bootedProjectRoot: string | null = null;\nlet bootedProjectManifestPath: string | null = null;\nlet bootedProjectManifestMtimeMs: number | null = null;\n\n/**\n * Whether the project manifest on disk is newer than the one this process\n * booted. The registry is process-global and boots once, so this is the only\n * signal an agent has that a rebuild happened underneath it.\n */\nexport function getBootStaleness(): {\n stale: boolean;\n bootedAt: string | null;\n manifestModifiedAt: string | null;\n} {\n if (!booted || !bootedProjectManifestPath) {\n return {\n stale: false,\n bootedAt: booted?.bootedAt ?? null,\n manifestModifiedAt: null,\n };\n }\n try {\n const mtimeMs = statSync(bootedProjectManifestPath).mtimeMs;\n return {\n stale:\n bootedProjectManifestMtimeMs !== null &&\n mtimeMs > bootedProjectManifestMtimeMs,\n bootedAt: booted.bootedAt,\n manifestModifiedAt: new Date(mtimeMs).toISOString(),\n };\n } catch {\n return {\n stale: false,\n bootedAt: booted.bootedAt,\n manifestModifiedAt: null,\n };\n }\n}\n\n/** The boot record for this process, or `null` before {@link bootRuntime}. */\nexport function getRuntimeBoot(): RuntimeBoot | null {\n return booted;\n}\n\n/**\n * The resolved root the process booted from. Consumers must relativize\n * paths against *this* root, never a per-request argument, or a caller could\n * widen the root (e.g. `/`) and read the layout back through \"relative\" paths.\n */\nexport function getBootedProjectRoot(): string | null {\n return bootedProjectRoot;\n}\n\n/** Test seam: forget the boot record (the registry itself is cleared by the caller). */\nexport function resetRuntimeBootForTests(): void {\n resetProjectRuntimeForTests();\n booting = null;\n booted = null;\n bootedProjectRoot = null;\n bootedProjectManifestPath = null;\n bootedProjectManifestMtimeMs = null;\n}\n\nexport interface BootRuntimeOptions {\n projectRoot?: string;\n /** Clock for `bootedAt` (defaults to `new Date()`). */\n now?: Date;\n}\n\n/**\n * Boot the confined runtime once per process. A second call returns the\n * existing record without touching the registry.\n */\nexport async function bootRuntime(\n options: BootRuntimeOptions = {},\n): Promise<RuntimeBoot> {\n const projectRoot = resolve(options.projectRoot ?? process.cwd());\n if (\n bootedProjectRoot &&\n projectRuntimeRoot(bootedProjectRoot) !== projectRuntimeRoot(projectRoot)\n ) {\n throw new ProjectRuntimeResolutionError(\n '@happyvertical/smrt-core',\n 'runtime_project_mismatch',\n );\n }\n if (booted) return booted;\n if (booting) return booting;\n bootedProjectRoot = projectRoot;\n booting = performBoot(projectRoot, options).catch((error) => {\n booting = null;\n bootedProjectRoot = null;\n throw error;\n });\n return booting;\n}\n\nasync function performBoot(\n projectRoot: string,\n options: BootRuntimeOptions,\n): Promise<RuntimeBoot> {\n const { ObjectRegistry } = await loadProjectCore(projectRoot);\n const { discoverSmrtPackages, resolveManifestPath } =\n await importProjectRuntimeModule<\n typeof import('@happyvertical/smrt-core/manifest/discover-smrt-packages')\n >(projectRoot, '@happyvertical/smrt-core/manifest/discover-smrt-packages');\n if (\n typeof ObjectRegistry?.registerFromManifest !== 'function' ||\n typeof discoverSmrtPackages !== 'function' ||\n typeof resolveManifestPath !== 'function'\n ) {\n throw new ProjectRuntimeResolutionError('@happyvertical/smrt-core');\n }\n const diagnostics: BootDiagnostic[] = [];\n const manifests: BootedManifest[] = [];\n\n const projectManifestPath = PROJECT_MANIFEST_CANDIDATES.map((candidate) =>\n join(projectRoot, candidate),\n ).find((path) => existsSync(path));\n const projectPackageName = readProjectPackageName(projectRoot);\n\n if (!projectManifestPath) {\n diagnostics.push({\n severity: 'warning',\n code: 'project_manifest_missing',\n message:\n 'No project manifest found (.smrt/manifest.json or dist/manifest.json); run the project build so the runtime manifest exists.',\n });\n } else {\n const manifest = readManifest(projectManifestPath);\n if (!manifest) {\n diagnostics.push({\n severity: 'error',\n code: 'project_manifest_invalid',\n message: `Project manifest at ${relativePath(projectRoot, projectManifestPath)} is not valid JSON.`,\n });\n } else {\n manifests.push({\n kind: 'project',\n packageName:\n typeof manifest.packageName === 'string'\n ? manifest.packageName\n : projectPackageName,\n path: relativePath(projectRoot, projectManifestPath),\n objectCount: registerManifest(\n ObjectRegistry,\n manifest,\n projectPackageName,\n ),\n });\n }\n }\n\n let dependencyNames: string[] = [];\n try {\n // `noCache` keeps discovery from writing `.smrt/discovery-cache.json`:\n // observing a project must never dirty it.\n dependencyNames = discoverSmrtPackages({\n baseDir: projectRoot,\n noCache: true,\n });\n } catch (error) {\n diagnostics.push({\n severity: 'warning',\n code: 'dependency_discovery_failed',\n message: `Installed SMRT package discovery failed: ${error instanceof Error ? error.message : 'unknown error'}`,\n });\n }\n for (const dependency of dependencyNames.sort()) {\n const manifestPath = resolveManifestPath(dependency, projectRoot);\n if (!manifestPath) {\n diagnostics.push({\n severity: 'info',\n code: 'dependency_manifest_missing',\n message: `Installed SMRT package ${dependency} exposes no runtime manifest.`,\n });\n continue;\n }\n const manifest = readManifest(manifestPath);\n if (!manifest) {\n diagnostics.push({\n severity: 'warning',\n code: 'dependency_manifest_invalid',\n message: `Manifest for ${dependency} is not valid JSON.`,\n });\n continue;\n }\n manifests.push({\n kind: 'dependency',\n packageName: dependency,\n path: relativePath(projectRoot, manifestPath),\n objectCount: registerManifest(ObjectRegistry, manifest, dependency),\n });\n }\n\n bootedProjectRoot = projectRoot;\n if (projectManifestPath) {\n bootedProjectManifestPath = projectManifestPath;\n try {\n bootedProjectManifestMtimeMs = statSync(projectManifestPath).mtimeMs;\n } catch {\n bootedProjectManifestMtimeMs = null;\n }\n }\n booted = {\n provenance: DECLARED_PROVENANCE,\n bootedAt: (options.now ?? new Date()).toISOString(),\n projectName: projectPackageName ?? basename(projectRoot),\n manifests,\n objectCount: manifests.reduce((sum, m) => sum + m.objectCount, 0),\n diagnostics,\n };\n return booted;\n}\n\nfunction readProjectPackageName(projectRoot: string): string | null {\n try {\n const parsed = JSON.parse(\n readFileSync(join(projectRoot, 'package.json'), 'utf8'),\n ) as { name?: unknown };\n return typeof parsed.name === 'string' && parsed.name ? parsed.name : null;\n } catch {\n return null;\n }\n}\n","/**\n * Level 2 read-only observation tools over the booted runtime (#1831).\n *\n * Three facts planes, labelled separately in every envelope:\n * - `declared (manifest)`: what the confined boot registered ({@link bootRuntime});\n * - `booted (registry)`: the in-process `ObjectRegistry` projected through the\n * sanitized {@link snapshotRegistry} DTO;\n * - `runtime (live DB)`: the optional read-only connection, reused from Level 1.\n *\n * Nothing here mutates: no writes, no `do()`, no generated CRUD, no project\n * code execution. `runtime-schema-diff` only *introspects* the live schema.\n */\n\nimport type { RegistrySnapshotObject } from '@happyvertical/smrt-core';\nimport {\n bootRuntime,\n getBootedProjectRoot,\n getBootStaleness,\n type RuntimeBoot,\n} from './boot.js';\nimport type { RuntimeDatabaseArgs } from './connection.js';\nimport {\n importProjectRuntimeModule,\n loadProjectCore,\n ProjectRuntimeResolutionError,\n} from './project-runtime.js';\nimport {\n type RuntimeDiagnostic,\n type RuntimeToolEnvelope,\n withRuntimeConnection,\n withRuntimeSetup,\n} from './tools.js';\n\n/** Row budget for `runtime-schema-diff` change lists. */\nconst SCHEMA_DIFF_CHANGE_LIMIT = 200;\n\nexport interface RuntimeProjectArgs {\n /** Project root to boot from (defaults to the server's cwd). */\n projectPath?: string;\n}\n\nfunction bootDiagnostics(boot: RuntimeBoot): RuntimeDiagnostic[] {\n return boot.diagnostics\n .filter((d) => d.severity !== 'info')\n .map((d) => ({\n severity: d.severity === 'error' ? 'warning' : d.severity,\n code: `boot_${d.code}`,\n message: d.message,\n }));\n}\n\nlet bootPreambleSent = false;\n\n/**\n * The full manifest list is emitted once per process; later envelopes carry\n * only the identifying fields. Every runtime response re-embedding the same\n * multi-manifest block was pure repetition for an agent.\n */\nfunction bootSummary(boot: RuntimeBoot) {\n const compact = {\n provenance: boot.provenance,\n bootedAt: boot.bootedAt,\n projectName: boot.projectName,\n objectCount: boot.objectCount,\n manifestCount: boot.manifests.length,\n };\n if (bootPreambleSent) return compact;\n bootPreambleSent = true;\n return { ...compact, manifests: boot.manifests };\n}\n\n/** Test seam paired with `resetRuntimeBootForTests()`. */\nexport function resetBootPreambleForTests(): void {\n bootPreambleSent = false;\n}\n\nfunction stalenessDiagnostics(): RuntimeDiagnostic[] {\n const staleness = getBootStaleness();\n if (!staleness.stale) return [];\n return [\n {\n severity: 'warning',\n code: 'manifest_newer_than_boot',\n message: `The project manifest changed at ${staleness.manifestModifiedAt} after this process booted at ${staleness.bootedAt}; restart the server to observe the rebuilt registry.`,\n },\n ];\n}\n\nexport interface RuntimeRegistryArgs extends RuntimeProjectArgs {\n /** Restrict object detail to these simple or qualified names. */\n objects?: string[];\n /** Include field/method detail (default: only when `objects` is given). */\n detail?: boolean;\n /**\n * Resume after this object key: the qualified name when the object has\n * one, otherwise its simple name — exactly the value a previous response\n * returned as `page.nextCursor` (#2779). Objects are sorted by that key.\n */\n cursor?: string;\n /** Objects per page (default {@link REGISTRY_PAGE_LIMIT}, max 500). */\n limit?: number;\n}\n\n/** Default objects per `runtime-registry` page. */\nexport const REGISTRY_PAGE_LIMIT = 50;\n\nfunction objectKey(object: {\n qualifiedName: string | null;\n name: string;\n}): string {\n return object.qualifiedName ?? object.name;\n}\n\n/** `runtime-registry`: sanitized snapshot of the booted registry. */\nasync function runtimeRegistryFromProject(\n args: RuntimeRegistryArgs = {},\n): Promise<RuntimeToolEnvelope> {\n const boot = await bootRuntime({ projectRoot: args.projectPath });\n const { snapshotRegistry } = await loadProjectCore(args.projectPath);\n // Paths are relativized against the root the process actually booted from,\n // never the per-request argument (which is ignored after the first boot).\n if (typeof snapshotRegistry !== 'function')\n throw new ProjectRuntimeResolutionError('@happyvertical/smrt-core');\n const snapshot = snapshotRegistry({\n projectRoot: getBootedProjectRoot() ?? undefined,\n objects: args.objects,\n detail: args.detail ?? Boolean(args.objects?.length),\n });\n // Page the object list (summary stays global). A 76-object app answered in\n // 56 KB before paging; an agent hunting one class needs a cursor, not a cut.\n const limit = Math.min(\n Math.max(Math.floor(args.limit ?? REGISTRY_PAGE_LIMIT), 1),\n 500,\n );\n // The cursor is compared against the same key `page.nextCursor` carries.\n const cursor =\n typeof args.cursor === 'string' && args.cursor.length > 0\n ? args.cursor\n : null;\n const all = snapshot.objects;\n const afterCursor = cursor\n ? all.filter((object) => objectKey(object).localeCompare(cursor) > 0)\n : all;\n const objects = afterCursor.slice(0, limit);\n const nextCursor =\n afterCursor.length > objects.length && objects.length > 0\n ? objectKey(objects[objects.length - 1])\n : null;\n return {\n ok: true,\n coverage: null,\n diagnostics: [...bootDiagnostics(boot), ...stalenessDiagnostics()],\n data: {\n provenance: snapshot.provenance,\n boot: bootSummary(boot),\n page: {\n returned: objects.length,\n matched: all.length,\n limit,\n cursor,\n nextCursor,\n },\n snapshot: { ...snapshot, objects },\n },\n };\n}\n\nexport interface RuntimeObjectArgs extends RuntimeProjectArgs {\n /** Simple or qualified object name. */\n name: string;\n /** Engine for the generated DDL preview (default: the registry's default). */\n engine?: 'sqlite' | 'postgres' | 'duckdb';\n}\n\n/** `runtime-object`: one object's sanitized definition plus its generated DDL. */\nasync function runtimeObjectFromProject(\n args: RuntimeObjectArgs,\n): Promise<RuntimeToolEnvelope> {\n const boot = await bootRuntime({ projectRoot: args.projectPath });\n const { ObjectRegistry, snapshotRegistry } = await loadProjectCore(\n args.projectPath,\n );\n const name = typeof args.name === 'string' ? args.name.trim() : '';\n if (typeof snapshotRegistry !== 'function')\n throw new ProjectRuntimeResolutionError('@happyvertical/smrt-core');\n const snapshot = snapshotRegistry({\n projectRoot: getBootedProjectRoot() ?? undefined,\n objects: name ? [name] : [],\n detail: true,\n });\n const diagnostics = [...bootDiagnostics(boot), ...stalenessDiagnostics()];\n let object: RegistrySnapshotObject | null = snapshot.objects[0] ?? null;\n if (snapshot.objects.length > 1) {\n // Two packages registering the same simple name is legal; picking one\n // silently would misreport identity. Ask for the qualified name instead.\n object = null;\n diagnostics.push({\n severity: 'warning',\n code: 'object_ambiguous',\n message: `${name} is registered by several packages; pass a qualified name: ${snapshot.objects\n .map((candidate) => candidate.qualifiedName ?? candidate.name)\n .join(', ')}`,\n });\n } else if (!object) {\n diagnostics.push({\n severity: 'warning',\n code: 'object_not_found',\n message: name\n ? `No booted object named ${name}; use runtime-registry to list names.`\n : 'name is required.',\n });\n }\n let ddl: string | null = null;\n if (object) {\n try {\n // Resolve by qualified identity so a same-name class in another package\n // can never answer for this one.\n ddl =\n ObjectRegistry.getSchemaDDL(\n object.qualifiedName ?? object.name,\n args.engine,\n ) ?? null;\n } catch (error) {\n diagnostics.push({\n severity: 'warning',\n code: 'ddl_unavailable',\n message: `Generated DDL unavailable: ${error instanceof Error ? error.message : 'unknown error'}`,\n });\n }\n }\n return {\n ok: true,\n coverage: null,\n diagnostics,\n data: {\n provenance: snapshot.provenance,\n boot: bootSummary(boot),\n object,\n ddl,\n },\n };\n}\n\nexport interface RuntimeSchemaDiffArgs\n extends RuntimeDatabaseArgs,\n RuntimeProjectArgs {}\n\n/**\n * `runtime-schema-diff`: booted registry schemas versus the live database,\n * using the same comparer `db:diff`/`db:migrate` use. Introspection only —\n * drop/relax options are pinned off and nothing is executed.\n */\nasync function runtimeSchemaDiffFromProject(\n args: RuntimeSchemaDiffArgs = {},\n): Promise<RuntimeToolEnvelope> {\n const boot = await bootRuntime({ projectRoot: args.projectPath });\n const { ObjectRegistry } = await loadProjectCore(args.projectPath);\n const envelope = await withRuntimeConnection(\n args,\n async (db) => {\n const { SchemaComparer } = await importProjectRuntimeModule<\n typeof import('@happyvertical/smrt-core/migrations')\n >(args.projectPath, '@happyvertical/smrt-core/migrations');\n if (typeof SchemaComparer !== 'function')\n throw new ProjectRuntimeResolutionError(\n '@happyvertical/smrt-core/migrations',\n );\n const comparer = new SchemaComparer(\n db as ConstructorParameters<typeof SchemaComparer>[0],\n {\n includeDroppedTables: false,\n includeDroppedColumns: false,\n includeDroppedIndexes: false,\n relaxColumns: false,\n },\n );\n const diff = await comparer.compare(\n ObjectRegistry.getAllSchemasAsDefinitions(),\n );\n const byType: Record<string, number> = {};\n for (const change of diff.changes) {\n const type = String((change as { type?: unknown }).type ?? 'unknown');\n byType[type] = (byType[type] ?? 0) + 1;\n }\n // `hasChanges` covers table adds/drops too; count them so the two agree.\n const addedTables = diff.added_tables.map((t) => t.tableName);\n if (addedTables.length > 0) byType.add_table = addedTables.length;\n if (diff.dropped_tables.length > 0)\n byType.drop_table = diff.dropped_tables.length;\n const changeCount =\n diff.changes.length + addedTables.length + diff.dropped_tables.length;\n return {\n data: {\n boot: bootSummary(boot),\n hasChanges: diff.has_changes,\n addedTables,\n droppedTables: diff.dropped_tables,\n orphanTables: diff.orphan_tables ?? [],\n changeCount,\n columnChangeCount: diff.changes.length,\n changesByType: byType,\n changes: diff.changes.slice(0, SCHEMA_DIFF_CHANGE_LIMIT),\n truncated: diff.changes.length > SCHEMA_DIFF_CHANGE_LIMIT,\n },\n diagnostics: [],\n };\n },\n 'booted registry schemas only; connect a dev database to diff against live tables',\n );\n envelope.diagnostics = [\n ...bootDiagnostics(boot),\n ...stalenessDiagnostics(),\n ...envelope.diagnostics,\n ];\n return envelope;\n}\n\nexport const runtimeRegistry = (args: RuntimeRegistryArgs = {}) =>\n withRuntimeSetup(() => runtimeRegistryFromProject(args));\nexport const runtimeObject = (args: RuntimeObjectArgs) =>\n withRuntimeSetup(() => runtimeObjectFromProject(args));\nexport const runtimeSchemaDiff = (args: RuntimeSchemaDiffArgs = {}) =>\n withRuntimeSetup(() => runtimeSchemaDiffFromProject(args));\n"],"mappings":"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"}
package/dist/runtime.js CHANGED
@@ -1,5 +1,5 @@
1
- import { a as runtimeMigrationStatus, c as runtimeScheduleHealth, i as runtimeJobHealth, n as STATIC_PROVENANCE, o as runtimeRecentChanges, r as runtimeDispatchHealth, s as runtimeRegistryDrift, t as RUNTIME_PROVENANCE } from "./tools-B-CDyCXi.js";
2
- import { i as runtimeSchemaDiff, n as runtimeObject, r as runtimeRegistry } from "./observation-BA287SLA.js";
1
+ import { a as runtimeMigrationStatus, c as runtimeScheduleHealth, i as runtimeJobHealth, n as STATIC_PROVENANCE, o as runtimeRecentChanges, r as runtimeDispatchHealth, s as runtimeRegistryDrift, t as RUNTIME_PROVENANCE } from "./tools-B0uaPTz0.js";
2
+ import { i as runtimeSchemaDiff, n as runtimeObject, r as runtimeRegistry } from "./observation-CmDrAP9J.js";
3
3
  //#region src/runtime.ts
4
4
  /** Name → handler for every Level 2 runtime tool. */
5
5
  var RUNTIME_TOOLS = {
@@ -52,8 +52,9 @@ export interface ResolvedRuntimeConnection {
52
52
  }
53
53
  /**
54
54
  * Redact a connection string so it can be shown to an agent without leaking
55
- * credentials. Mirrors the CLI's `redactConnectionString` (which is CLI
56
- * private); patterned identically so dev-mcp never depends on the CLI.
55
+ * credentials. Mirrors smrt-core's `redactDatabaseUrlsInText` (#3527), which
56
+ * dev-mcp cannot import at runtime, and additionally reduces local database
57
+ * paths to their basename.
57
58
  *
58
59
  * Query-parameter masking normalizes each key (lowercase, `_`/`-` stripped),
59
60
  * so camelCase forms such as Turso/libsql's `?authToken=` mask exactly like
@@ -1 +1 @@
1
- {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../../src/tools/runtime/connection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAWH,kEAAkE;AAClE,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAC;AAYnE,6DAA6D;AAC7D,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,aAAa,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE9E,MAAM,WAAW,yBAAyB;IACxC,mEAAmE;IACnE,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,gBAAgB,CAAC;IACzB,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AA+BD;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA2C5D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAIvD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAQxE;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAKpE;AAED;;;;;;;;;;;;GAYG;AACH;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CASR;AAED;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,GAAE,mBAAwB,GAC7B,OAAO,CAAC,yBAAyB,CAAC,CA2DpC;AAyGD;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAWvE"}
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../../src/tools/runtime/connection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAWH,kEAAkE;AAClE,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAC;AAYnE,6DAA6D;AAC7D,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,aAAa,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE9E,MAAM,WAAW,yBAAyB;IACxC,mEAAmE;IACnE,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,gBAAgB,CAAC;IACzB,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AA2BD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA6C5D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAIvD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAQxE;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAKpE;AAED;;;;;;;;;;;;GAYG;AACH;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CASR;AAED;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,GAAE,mBAAwB,GAC7B,OAAO,CAAC,yBAAyB,CAAC,CA2DpC;AAyGD;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAWvE"}
@@ -118,33 +118,25 @@ function isRuntimeDatabaseType(value) {
118
118
  return RUNTIME_DATABASE_TYPES.includes(value);
119
119
  }
120
120
  /**
121
- * Sensitive query-parameter names. Matching normalizes the key (lowercase,
122
- * `_`/`-` stripped), so camelCase (`authToken`, `accessToken`) and hyphen
123
- * variants (`api-key`) are masked exactly like their snake_case forms.
121
+ * Whether a query-parameter or keyword name names a secret. Mirrors
122
+ * `isSensitiveConnectionParam` in `@happyvertical/smrt-core` (#3527), which
123
+ * dev-mcp cannot import at runtime (core is a dev dependency only). Names are
124
+ * normalized (lowercase, `_`/`-` stripped), so camelCase (`authToken`) and
125
+ * hyphen variants (`api-key`) match like their snake_case forms.
124
126
  */
125
- var SENSITIVE_QUERY_PARAMS = [
126
- "access_token",
127
- "apikey",
128
- "api_key",
129
- "auth",
130
- "auth_token",
131
- "connectionstring",
132
- "connection_string",
133
- "password",
134
- "token"
135
- ];
136
- function normalizeQueryParamName(key) {
137
- return key.toLowerCase().replace(/[_-]/g, "");
127
+ function isSensitiveParamName(name) {
128
+ const key = name.toLowerCase().replace(/[_-]/g, "");
129
+ return /password|passwd|passphrase|secret|token|credential|connectionstring/.test(key) || key.endsWith("key") || key === "pwd" || key === "pass" || key === "auth" || key === "dsn";
138
130
  }
139
- var SENSITIVE_QUERY_PARAM_NAMES = new Set(SENSITIVE_QUERY_PARAMS.map(normalizeQueryParamName));
140
131
  var DEFAULT_CLI_DATABASE = { database: {
141
132
  type: "sqlite",
142
133
  url: ":memory:"
143
134
  } };
144
135
  /**
145
136
  * Redact a connection string so it can be shown to an agent without leaking
146
- * credentials. Mirrors the CLI's `redactConnectionString` (which is CLI
147
- * private); patterned identically so dev-mcp never depends on the CLI.
137
+ * credentials. Mirrors smrt-core's `redactDatabaseUrlsInText` (#3527), which
138
+ * dev-mcp cannot import at runtime, and additionally reduces local database
139
+ * paths to their basename.
148
140
  *
149
141
  * Query-parameter masking normalizes each key (lowercase, `_`/`-` stripped),
150
142
  * so camelCase forms such as Turso/libsql's `?authToken=` mask exactly like
@@ -158,13 +150,12 @@ function redactConnectionString(value) {
158
150
  try {
159
151
  const url = new URL(value);
160
152
  if (url.password) url.password = "***";
161
- for (const key of [...url.searchParams.keys()]) if (SENSITIVE_QUERY_PARAM_NAMES.has(normalizeQueryParamName(key))) url.searchParams.set(key, "***");
153
+ for (const key of [...url.searchParams.keys()]) if (isSensitiveParamName(key)) url.searchParams.set(key, "***");
162
154
  redacted = url.toString();
163
- } catch {
164
- redacted = value.replace(/([a-z][a-z0-9+.-]*:\/\/[^:\s/@]+:)(?:[^@\s]|@(?=[^@\s]*@))+(@)/gi, "$1***$2");
165
- }
155
+ } catch {}
156
+ redacted = redacted.replace(/([a-z][a-z0-9+.-]*:\/\/[^:\s/@]*:)[^\n]*@/gi, "$1***@");
166
157
  redacted = redacted.replace(/(?:[A-Za-z]:)?(?:[\\/][^\s\\/'"`]+)+[\\/]([^\s\\/'"`]+\.(?:db|sqlite3?|duckdb))/g, "…/$1");
167
- return redacted.replace(/((?:^|[?&,(\s])([a-z][a-z0-9_-]{0,30})=)([^&,\s)]+)/gi, (match, prefix, key) => SENSITIVE_QUERY_PARAM_NAMES.has(normalizeQueryParamName(key)) ? `${prefix}***` : match);
158
+ return redacted.replace(/((?:^|[?&,(\s])([a-z][a-z0-9_-]{0,30})=)([^&,\s)]+)/gi, (match, prefix, key) => isSensitiveParamName(key) ? `${prefix}***` : match);
168
159
  }
169
160
  /**
170
161
  * Build a safe, redacted error message for a database failure. Connection
@@ -583,4 +574,4 @@ async function withRuntimeSetup(operation) {
583
574
  //#endregion
584
575
  export { projectRuntimeRoot as _, runtimeMigrationStatus as a, runtimeScheduleHealth as c, withRuntimeSetup as d, redactConnectionString as f, loadProjectCore as g, importProjectRuntimeModule as h, runtimeJobHealth as i, tools_exports as l, ProjectRuntimeResolutionError as m, STATIC_PROVENANCE as n, runtimeRecentChanges as o, safeErrorMessage as p, runtimeDispatchHealth as r, runtimeRegistryDrift as s, RUNTIME_PROVENANCE as t, withRuntimeConnection as u, resolveProjectRuntimeEntry as v, __exportAll as y };
585
576
 
586
- //# sourceMappingURL=tools-B-CDyCXi.js.map
577
+ //# sourceMappingURL=tools-B0uaPTz0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools-B0uaPTz0.js","names":[],"sources":["../src/tools/runtime/project-runtime.ts","../src/tools/runtime/connection.ts","../src/tools/runtime/tools.ts"],"sourcesContent":["import { readFileSync, realpathSync } from 'node:fs';\nimport { findPackageJSON } from 'node:module';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\n/** Runtime registries are process globals, even across different core versions. */\nlet selectedProject: string | undefined;\n\nexport class ProjectRuntimeResolutionError extends Error {\n readonly code: string;\n constructor(specifier: string, code = 'runtime_dependency_unavailable') {\n super(\n code === 'runtime_project_mismatch'\n ? 'This server already loaded another project runtime. Start a separate server for this project or restart with its working directory.'\n : `The selected project must install a compatible ${specifier}. Install the SMRT runtime in that project, build it, and retry; the MCP server does not supply a runtime.`,\n );\n this.name = 'ProjectRuntimeResolutionError';\n this.code = code;\n }\n}\n\nexport function projectRuntimeRoot(projectPath?: string): string {\n const root = resolve(projectPath ?? process.cwd());\n try {\n return realpathSync(root);\n } catch {\n return root;\n }\n}\n\n/** Test seam; callers must also clear the process-global runtime registry. */\nexport function resetProjectRuntimeForTests(): void {\n selectedProject = undefined;\n}\n\n// findPackageJSON uses Node's ESM package lookup, including import-only exports.\n// Resolve the documented exact export from that same package root: never search\n// again from the server, and never choose a second core for one of its subpaths.\nfunction packageExport(packageJson: string, subpath: string): string {\n const pkg = JSON.parse(readFileSync(packageJson, 'utf8'));\n let target = pkg.exports?.[subpath];\n if (\n target === undefined &&\n subpath === '.' &&\n typeof pkg.exports === 'string'\n )\n target = pkg.exports;\n while (target && typeof target === 'object')\n target = target.import ?? target.node ?? target.default;\n if (typeof target !== 'string' || !target.startsWith('./'))\n throw new Error('Unsupported runtime export');\n const root = dirname(packageJson);\n const entry = resolve(root, target);\n const rel = relative(root, entry);\n if (rel.startsWith('..') || isAbsolute(rel))\n throw new Error('Invalid runtime export');\n return entry;\n}\n\nexport function resolveProjectRuntimeEntry(\n projectPath: string | undefined,\n specifier: string,\n): string {\n const root = projectRuntimeRoot(projectPath);\n let entry: string;\n try {\n const coreJson = findPackageJSON(\n '@happyvertical/smrt-core',\n pathToFileURL(join(root, 'package.json')),\n );\n if (!coreJson) throw new Error('Missing runtime');\n if (\n specifier === '@happyvertical/smrt-core' ||\n specifier.startsWith('@happyvertical/smrt-core/')\n ) {\n const subpath = specifier.slice('@happyvertical/smrt-core'.length);\n entry = packageExport(coreJson, subpath ? `.${subpath}` : '.');\n } else {\n const dependencyJson = findPackageJSON(\n specifier,\n pathToFileURL(coreJson),\n );\n if (!dependencyJson) throw new Error('Missing runtime dependency');\n entry = packageExport(dependencyJson, '.');\n }\n } catch {\n throw new ProjectRuntimeResolutionError(specifier);\n }\n return entry;\n}\n\nexport async function importProjectRuntimeModule<T = Record<string, unknown>>(\n projectPath: string | undefined,\n specifier: string,\n): Promise<T> {\n const root = projectRuntimeRoot(projectPath);\n if (selectedProject && selectedProject !== root) {\n throw new ProjectRuntimeResolutionError(\n specifier,\n 'runtime_project_mismatch',\n );\n }\n const entry = resolveProjectRuntimeEntry(root, specifier);\n // Pin before the first await: concurrent requests cannot import two runtimes.\n selectedProject = root;\n try {\n return (await import(pathToFileURL(entry).href)) as T;\n } catch {\n // Driver/module errors may contain credentials or absolute host paths.\n throw new ProjectRuntimeResolutionError(specifier);\n }\n}\n\nexport const loadProjectCore = (projectPath?: string) =>\n importProjectRuntimeModule<typeof import('@happyvertical/smrt-core')>(\n projectPath,\n '@happyvertical/smrt-core',\n );\n\nexport async function loadProjectRuntime(projectPath?: string) {\n const core = await loadProjectCore(projectPath);\n const sql = await importProjectRuntimeModule<\n typeof import('@happyvertical/sql')\n >(projectPath, '@happyvertical/sql');\n return { core, sql, projectRoot: projectRuntimeRoot(projectPath) };\n}\n","/**\n * Optional read-only dev-database connection resolution for runtime\n * diagnostics tools (#1824).\n *\n * Resolution order per call:\n * 1. explicit `dbUrl`/`dbType` tool arguments\n * 2. `SMRT_DEV_DB_URL` environment variable\n * 3. the project's cosmiconfig CLI section → `database.{type,url}`, else\n * `DATABASE_URL`/`DATABASE_TYPE` — the `smrt` CLI's own precedence,\n * through `resolveCliDatabaseConfig()` from the project's\n * `@happyvertical/smrt-config` (#3446; an older install without it\n * reads only `getPackageConfig('cli', ...)`)\n *\n * No configured connection → `db: null` with `source: 'none'`; callers return\n * a successful static-only envelope. A connection is always opened lazily per\n * call and closed in the caller's `finally` — nothing is cached across calls\n * and the server never holds a database handle.\n *\n * Sensitive handling: connection strings are never logged or echoed. Every\n * surfaced URL passes through {@link redactConnectionString}; driver errors\n * are surfaced only through {@link safeErrorMessage}, which strips anything\n * that looks like a credential-bearing URL.\n */\n\nimport { resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport {\n importProjectRuntimeModule,\n loadProjectRuntime,\n ProjectRuntimeResolutionError,\n projectRuntimeRoot,\n} from './project-runtime.js';\n\n/** Engine labels `@happyvertical/sql`'s `getDatabase` accepts. */\nexport type RuntimeDatabaseType = 'sqlite' | 'postgres' | 'duckdb';\n\nconst RUNTIME_DATABASE_TYPES: readonly RuntimeDatabaseType[] = [\n 'sqlite',\n 'postgres',\n 'duckdb',\n];\n\nfunction isRuntimeDatabaseType(value: string): value is RuntimeDatabaseType {\n return (RUNTIME_DATABASE_TYPES as readonly string[]).includes(value);\n}\n\n/** Tool arguments every runtime-diagnostics tool accepts. */\nexport interface RuntimeDatabaseArgs {\n projectPath?: string;\n /** Optional database URL/connection string (overrides env and config). */\n dbUrl?: string;\n /**\n * Optional engine hint for `dbUrl` or the environment connection.\n * Must be `'sqlite'` | `'postgres'` | `'duckdb'` (case-insensitive); any\n * other non-empty hint surfaces a safe diagnostic error rather than\n * silently opening the wrong adapter. When absent, the engine is inferred\n * from the URL scheme.\n */\n dbType?: string;\n}\n\nexport type ConnectionSource = 'argument' | 'environment' | 'config' | 'none';\n\nexport interface ResolvedRuntimeConnection {\n /** Open connection, or `null` when no connection is configured. */\n db: unknown | null;\n source: ConnectionSource;\n /** Redacted connection string for display; '' when no connection. */\n displayUrl: string;\n /**\n * Normalized engine label for the resolved connection (`sqlite`,\n * `postgres`, or `duckdb`); `null` when no connection is configured.\n */\n databaseType: string | null;\n}\n\n/**\n * Whether a query-parameter or keyword name names a secret. Mirrors\n * `isSensitiveConnectionParam` in `@happyvertical/smrt-core` (#3527), which\n * dev-mcp cannot import at runtime (core is a dev dependency only). Names are\n * normalized (lowercase, `_`/`-` stripped), so camelCase (`authToken`) and\n * hyphen variants (`api-key`) match like their snake_case forms.\n */\nfunction isSensitiveParamName(name: string): boolean {\n const key = name.toLowerCase().replace(/[_-]/g, '');\n return (\n /password|passwd|passphrase|secret|token|credential|connectionstring/.test(\n key,\n ) ||\n key.endsWith('key') ||\n key === 'pwd' ||\n key === 'pass' ||\n key === 'auth' ||\n key === 'dsn'\n );\n}\n\nconst DEFAULT_CLI_DATABASE = {\n database: { type: 'sqlite', url: ':memory:' },\n};\n\n/**\n * Redact a connection string so it can be shown to an agent without leaking\n * credentials. Mirrors smrt-core's `redactDatabaseUrlsInText` (#3527), which\n * dev-mcp cannot import at runtime, and additionally reduces local database\n * paths to their basename.\n *\n * Query-parameter masking normalizes each key (lowercase, `_`/`-` stripped),\n * so camelCase forms such as Turso/libsql's `?authToken=` mask exactly like\n * their snake_case forms. A final regex pass also masks `key=value` pairs\n * embedded in free text (driver error messages often quote the URL); it treats\n * the start of the string, `?`, `&`, `,`, `(`, and whitespace as the\n * preceding boundary.\n */\nexport function redactConnectionString(value: string): string {\n let redacted = value;\n\n try {\n const url = new URL(value);\n if (url.password) {\n url.password = '***';\n }\n for (const key of [...url.searchParams.keys()]) {\n if (isSensitiveParamName(key)) {\n url.searchParams.set(key, '***');\n }\n }\n redacted = url.toString();\n } catch {\n // Not a standalone URL: free text, handled by the passes below.\n }\n\n // Always scrub embedded userinfo, even after a successful parse: free text\n // such as `connect: postgres://u:pw@h` parses as an opaque `connect:` URL\n // with no password, leaving the embedded credential untouched. Greedy\n // through the LAST `@` on the line, like smrt-core: a malformed password\n // (literal `@`, raw whitespace, scheme-like text) has no reliable end short\n // of that, and over-redaction is the safe failure.\n redacted = redacted.replace(\n /([a-z][a-z0-9+.-]*:\\/\\/[^:\\s/@]*:)[^\\n]*@/gi,\n '$1***@',\n );\n\n // Local database files: a `file:` URL or a driver error quoting the path\n // (POSIX or Windows separators) reveals the filesystem layout. Keep only\n // the basename.\n redacted = redacted.replace(\n /(?:[A-Za-z]:)?(?:[\\\\/][^\\s\\\\/'\"`]+)+[\\\\/]([^\\s\\\\/'\"`]+\\.(?:db|sqlite3?|duckdb))/g,\n '…/$1',\n );\n\n // Free-text pass: mask the value of any sensitive `key=value` pair the URL\n // parse above may have left intact (e.g. a URL quoted inside an error\n // message, where `?`/`&` boundaries do not delimit params for `new URL`).\n return redacted.replace(\n /((?:^|[?&,(\\s])([a-z][a-z0-9_-]{0,30})=)([^&,\\s)]+)/gi,\n (match, prefix: string, key: string) =>\n isSensitiveParamName(key) ? `${prefix}***` : match,\n );\n}\n\n/**\n * Build a safe, redacted error message for a database failure. Connection\n * strings and raw driver error objects are never surfaced verbatim.\n */\nexport function safeErrorMessage(error: unknown): string {\n const message =\n error instanceof Error ? error.message : String(error ?? 'unknown error');\n return redactConnectionString(message);\n}\n\n/**\n * Normalize a type hint into an engine `getDatabase` accepts. Unknown values\n * throw a safe error (no URL is included) so the caller can surface a\n * diagnostic instead of silently opening the wrong adapter.\n */\nexport function toRuntimeDatabaseType(value: string): RuntimeDatabaseType {\n const normalized = value.trim().toLowerCase();\n if (isRuntimeDatabaseType(normalized)) {\n return normalized;\n }\n throw new Error(\n `Unsupported runtime database type \"${normalized}\"; expected sqlite, postgres, or duckdb`,\n );\n}\n\n/** Infer an engine hint from a URL scheme when no explicit type is given. */\nexport function inferDatabaseType(url: string, hint?: string): string {\n if (hint && hint.trim().length > 0) return hint;\n if (/^postgres(ql)?:/i.test(url)) return 'postgres';\n if (/^duckdb:/i.test(url)) return 'duckdb';\n return 'sqlite';\n}\n\n/**\n * Normalize the accepted dev-database URL forms to what `getDatabase` opens.\n *\n * `@happyvertical/sql` understands `file:` URLs and bare paths for SQLite,\n * but not a `sqlite:` scheme; passing `sqlite:///abs/dev.db` through verbatim\n * made the driver treat the whole string as a relative filename and join it\n * to cwd. Accepted forms:\n *\n * - `sqlite:///abs/path.db`, `sqlite://rel/path.db`, `sqlite:path.db` →\n * `file:` URL with the path resolved against cwd only when it is relative\n * - `file:` URLs, `postgres:`/`postgresql:`, `duckdb:` → unchanged\n * - a bare path → unchanged (the driver resolves it)\n */\n/**\n * Whether a configured value means \"in-memory, so not a runtime database\":\n * `:memory:` as well as the `sqlite::memory:` / `sqlite://:memory:` spellings.\n */\nexport function isMemoryDatabaseUrl(url: string): boolean {\n return /^(?:sqlite:(?:\\/\\/)?)?:memory:$/i.test(url.trim());\n}\n\nexport function normalizeDatabaseUrl(\n url: string,\n projectPath?: string,\n): string {\n const match = /^sqlite:(?:\\/\\/)?(.*)$/i.exec(url.trim());\n if (!match) return url.trim();\n const path = match[1];\n if (!path) return url.trim();\n const absolute = path.startsWith('/')\n ? path\n : resolve(projectRuntimeRoot(projectPath), path);\n return pathToFileURL(absolute).href;\n}\n\n/**\n * Resolve the dev-database connection for one tool call.\n *\n * Returns `db: null` (never throws) when no connection is configured; callers\n * must treat that as \"no runtime database\" and return a static-only envelope.\n * A thrown connect error is propagated to the caller, which converts it into\n * a diagnostic envelope — it must never reach the MCP transport.\n */\nexport async function resolveRuntimeConnection(\n args: RuntimeDatabaseArgs = {},\n): Promise<ResolvedRuntimeConnection> {\n const argUrl = args.dbUrl?.trim();\n if (argUrl && !isMemoryDatabaseUrl(argUrl)) {\n const databaseType = toRuntimeDatabaseType(\n inferDatabaseType(argUrl, args.dbType),\n );\n const db = await getDatabaseInstance(args.projectPath, {\n type: databaseType,\n url: normalizeDatabaseUrl(argUrl, args.projectPath),\n });\n return {\n db,\n source: 'argument',\n displayUrl: redactConnectionString(argUrl),\n databaseType,\n };\n }\n\n const envUrl = process.env.SMRT_DEV_DB_URL?.trim();\n if (envUrl && !isMemoryDatabaseUrl(envUrl)) {\n const databaseType = toRuntimeDatabaseType(\n inferDatabaseType(envUrl, args.dbType),\n );\n const db = await getDatabaseInstance(args.projectPath, {\n type: databaseType,\n url: normalizeDatabaseUrl(envUrl, args.projectPath),\n });\n return {\n db,\n source: 'environment',\n displayUrl: redactConnectionString(envUrl),\n databaseType,\n };\n }\n\n const config = await loadCliDatabaseConfig(args.projectPath);\n const configUrl = config?.database?.url?.trim();\n if (configUrl && !isMemoryDatabaseUrl(configUrl)) {\n // A configured database keeps its engine. For DATABASE_URL, an explicit\n // `dbType` overrides the environment engine like any env connection; the\n // CLI-resolved type (DATABASE_TYPE or the URL scheme) is the fallback.\n const databaseType = toRuntimeDatabaseType(\n config.source === 'environment' && args.dbType?.trim()\n ? args.dbType\n : config.database?.type || inferDatabaseType(configUrl, args.dbType),\n );\n const db = await getDatabaseInstance(args.projectPath, {\n type: databaseType,\n url: normalizeDatabaseUrl(configUrl, args.projectPath),\n });\n return {\n db,\n source: config.source ?? 'config',\n displayUrl: redactConnectionString(configUrl),\n databaseType,\n };\n }\n\n return { db: null, source: 'none', displayUrl: '', databaseType: null };\n}\n\ninterface DatabaseConfigLike {\n database?: { type?: string; url?: string };\n /** `environment` when `DATABASE_URL` supplied the database. */\n source?: 'config' | 'environment';\n}\n\n/** The project's smrt-config `resolveCliDatabaseConfig()` result shape. */\ninterface CliDatabaseResolution {\n source?: unknown;\n database?: { type?: unknown; url?: unknown } | null;\n}\n\nasync function loadCliDatabaseConfig(\n projectPath?: string,\n): Promise<DatabaseConfigLike> {\n try {\n // `getPackageConfig` only reads the synchronous cache and answers with\n // defaults until `loadConfig()` has run; nothing else in this server loads\n // the project config, so load it here. Cosmiconfig is resolved from the\n // server's cwd (the project the agent is working in); a missing or invalid\n // config falls back to the defaults.\n const configModule = await importProjectRuntimeModule<{\n loadConfig: (options: {\n searchFrom: string;\n cache: boolean;\n }) => Promise<unknown>;\n getPackageConfig: (\n name: string,\n defaults: Record<string, unknown>,\n ) => unknown;\n resolveCliDatabaseConfig?: (\n env: Record<string, string | undefined>,\n ) => CliDatabaseResolution;\n }>(projectPath, '@happyvertical/smrt-config');\n if (\n typeof configModule.loadConfig !== 'function' ||\n typeof configModule.getPackageConfig !== 'function'\n )\n throw new ProjectRuntimeResolutionError('@happyvertical/smrt-config');\n await configModule.loadConfig({\n searchFrom: projectRuntimeRoot(projectPath),\n cache: false,\n });\n if (typeof configModule.resolveCliDatabaseConfig === 'function') {\n // Same precedence as the `smrt` CLI: a configured URL wins and the\n // environment is not read; otherwise DATABASE_URL/DATABASE_TYPE.\n const resolved = configModule.resolveCliDatabaseConfig(process.env);\n const url = resolved?.database?.url;\n if (\n (resolved.source === 'config' || resolved.source === 'environment') &&\n typeof url === 'string'\n ) {\n const type = resolved.database?.type;\n return {\n source: resolved.source,\n database: {\n // A typeless configured URL keeps the CLI's sqlite default.\n type:\n typeof type === 'string'\n ? type\n : DEFAULT_CLI_DATABASE.database.type,\n url,\n },\n };\n }\n return {};\n }\n const config = configModule.getPackageConfig(\n 'cli',\n DEFAULT_CLI_DATABASE as unknown as Record<string, unknown>,\n );\n const database = (config as DatabaseConfigLike).database;\n if (database && typeof database.url === 'string') {\n return { database };\n }\n return {};\n } catch (error) {\n if (error instanceof ProjectRuntimeResolutionError) throw error;\n return {};\n }\n}\n\nasync function getDatabaseInstance(\n projectPath: string | undefined,\n options: {\n type: RuntimeDatabaseType;\n url: string;\n },\n): Promise<unknown> {\n const { sql } = await loadProjectRuntime(projectPath);\n if (typeof sql.getDatabase !== 'function')\n throw new ProjectRuntimeResolutionError('@happyvertical/sql');\n return sql.getDatabase(options);\n}\n\ntype MaybeCloseableDatabase = {\n close?: () => unknown | Promise<unknown>;\n client?: {\n close?: () => unknown | Promise<unknown>;\n end?: () => unknown | Promise<unknown>;\n };\n};\n\n/**\n * Best-effort close of a resolved connection. Never throws; diagnostics must\n * not fail because cleanup hiccuped.\n */\nexport async function closeRuntimeConnection(db: unknown): Promise<void> {\n if (!db || typeof db !== 'object') return;\n const closeable = db as MaybeCloseableDatabase;\n const close =\n closeable.close ?? closeable.client?.end ?? closeable.client?.close;\n if (typeof close !== 'function') return;\n try {\n await close.call(closeable.close ? closeable : closeable.client);\n } catch {\n // Cleanup failures are not diagnostic failures.\n }\n}\n","/**\n * Runtime diagnostics tools (#1824): read-only views over a project's dev\n * database `_smrt_*` system tables, powered by the shared SELECT-only\n * system-diagnostics reader in `@happyvertical/smrt-core`.\n *\n * Contract:\n * - **Optional connection.** No configured connection returns a successful\n * static-only envelope — the server always starts and static tools are\n * unaffected. A live connection is opened lazily per call and closed in\n * `finally`; nothing is cached across calls.\n * - **Read-only.** Every underlying statement is a bounded SELECT; the reader\n * never selects sensitive columns (job payloads/results, schedule\n * `agentConfig`/`methodArgs`, dispatch `payload`/`metadata`).\n * - **Provenance-labeled.** Live results carry `provenance: 'runtime (live DB)'`;\n * static-only results carry `provenance: 'static'` — agents must never\n * conflate runtime facts with declared/manifest facts.\n * - **Fail-safe.** A connect/read error becomes a diagnostic envelope; it must\n * never reach the MCP transport and never includes raw driver text or URLs.\n */\n\nimport { createHash } from 'node:crypto';\nimport {\n closeRuntimeConnection,\n type RuntimeDatabaseArgs,\n redactConnectionString,\n resolveRuntimeConnection,\n safeErrorMessage,\n} from './connection.js';\nimport {\n importProjectRuntimeModule,\n ProjectRuntimeResolutionError,\n} from './project-runtime.js';\n\n/** Provenance labels separating runtime facts from static/declared facts. */\nexport const RUNTIME_PROVENANCE = 'runtime (live DB)';\nexport const STATIC_PROVENANCE = 'static';\n\nexport interface RuntimeDiagnostic {\n severity: 'info' | 'warning';\n code: string;\n message: string;\n}\n\nexport interface RuntimeToolEnvelope {\n ok: true;\n coverage: null;\n diagnostics: RuntimeDiagnostic[];\n data: Record<string, unknown>;\n}\n\ntype RuntimeReadParts = {\n data: Record<string, unknown>;\n diagnostics: RuntimeDiagnostic[];\n};\n\ntype RuntimeRead = (db: unknown) => Promise<RuntimeReadParts>;\n\nasync function projectRead(\n args: RuntimeDatabaseArgs,\n name: string,\n db: unknown,\n options?: unknown,\n) {\n const core = await importProjectRuntimeModule<\n Record<string, (db: unknown, options?: unknown) => Promise<unknown>>\n >(args.projectPath, '@happyvertical/smrt-core');\n if (typeof core[name] !== 'function')\n throw new ProjectRuntimeResolutionError('@happyvertical/smrt-core');\n return core[name](db, options);\n}\n\n/**\n * Serialize resolve → read → close per connection target so overlapping tool\n * calls never close a shared cached handle out from under each other.\n *\n * `@happyvertical/sql`'s `getDatabase` returns a cached handle per URL (no\n * opt-out in its public API). `closeRuntimeConnection` only calls the handle's\n * own `close`/`end`, but the SDK wraps those so a close also evicts the handle\n * from its connection cache. Two concurrent diagnostics calls resolving the\n * same URL would therefore share one handle, with the first finisher closing\n * it mid-read for the second. A per-key promise chain keeps each call's\n * lifecycle private: every call resolves its own view of the connection,\n * performs its read, and only then closes — the next queued call re-resolves\n * a fresh handle.\n *\n * The key is a digest of the resolved target, never the raw URL, so a\n * credential-bearing connection string is not retained in this map.\n */\nconst runtimeReadQueues = new Map<string, Promise<unknown>>();\n\nfunction connectionQueueKey(args: RuntimeDatabaseArgs): string {\n const target =\n args.dbUrl?.trim() || process.env.SMRT_DEV_DB_URL?.trim() || 'cli.config';\n return createHash('sha256').update(target).digest('hex');\n}\n\nasync function enqueueRuntimeRead<T>(\n key: string,\n operation: () => Promise<T>,\n): Promise<T> {\n const previous = runtimeReadQueues.get(key) ?? Promise.resolve();\n const run = previous.then(operation, operation);\n // Track a never-rejecting tail so later calls chain regardless of outcome,\n // then drop the entry once this is the settled tail to bound map size.\n const tail = run.catch(() => undefined);\n runtimeReadQueues.set(key, tail);\n void tail.then(() => {\n if (runtimeReadQueues.get(key) === tail) {\n runtimeReadQueues.delete(key);\n }\n });\n return run;\n}\n\n/**\n * Run one read against the optional runtime connection, mapping every outcome\n * to a successful MCP envelope:\n *\n * - no connection configured → static-only envelope (`connected: false`)\n * - connect failure → static envelope with a safe diagnostic\n * - read failure → connected envelope with a safe diagnostic\n * - success → live result under `provenance: 'runtime (live DB)'`; a\n * category-unavailable reader result keeps its `available: false` data and\n * surfaces its message as a diagnostic\n */\nexport async function withRuntimeConnection(\n args: RuntimeDatabaseArgs,\n read: RuntimeRead,\n staticHint: string,\n): Promise<RuntimeToolEnvelope> {\n // Serialize the full resolve → read → close lifecycle per connection target\n // (see `runtimeReadQueues`) so concurrent calls sharing one cached handle\n // cannot close it mid-read for each other.\n return enqueueRuntimeRead(connectionQueueKey(args), () =>\n runWithRuntimeConnection(args, read, staticHint),\n );\n}\n\nasync function runWithRuntimeConnection(\n args: RuntimeDatabaseArgs,\n read: RuntimeRead,\n staticHint: string,\n): Promise<RuntimeToolEnvelope> {\n let resolved: Awaited<ReturnType<typeof resolveRuntimeConnection>>;\n try {\n resolved = await resolveRuntimeConnection(args);\n } catch (error) {\n return {\n ok: true,\n coverage: null,\n diagnostics: [\n {\n severity: 'warning',\n code:\n error instanceof ProjectRuntimeResolutionError\n ? error.code\n : 'runtime_connection_error',\n message: safeErrorMessage(error),\n },\n ],\n data: {\n provenance: STATIC_PROVENANCE,\n connected: false,\n },\n };\n }\n\n if (!resolved.db) {\n return {\n ok: true,\n coverage: null,\n diagnostics: [\n {\n severity: 'info',\n code: 'runtime_connection_unavailable',\n message: `No runtime dev database configured (set SMRT_DEV_DB_URL, cli.database, or DATABASE_URL); returning static-only result: ${staticHint}. Static tools are unaffected.`,\n },\n ],\n data: {\n provenance: STATIC_PROVENANCE,\n connected: false,\n },\n };\n }\n\n const { db, source, displayUrl, databaseType } = resolved;\n try {\n const { data, diagnostics } = await read(db);\n return {\n ok: true,\n coverage: null,\n diagnostics,\n data: {\n provenance: RUNTIME_PROVENANCE,\n connected: true,\n connectionSource: source,\n databaseType,\n displayUrl,\n ...data,\n },\n };\n } catch (error) {\n return {\n ok: true,\n coverage: null,\n diagnostics: [\n {\n severity: 'warning',\n code:\n error instanceof ProjectRuntimeResolutionError\n ? error.code\n : 'runtime_read_error',\n message: safeErrorMessage(error),\n },\n ],\n data:\n error instanceof ProjectRuntimeResolutionError\n ? {\n provenance: STATIC_PROVENANCE,\n connected: false,\n }\n : {\n provenance: RUNTIME_PROVENANCE,\n connected: true,\n connectionSource: source,\n databaseType,\n displayUrl,\n },\n };\n } finally {\n await closeRuntimeConnection(db);\n }\n}\n\n/**\n * Stored error columns (`error_message`, `last_error`) are free text written\n * at failure time and routinely quote connection URLs or credentials. Every\n * string in a live result passes through {@link redactConnectionString}\n * before it reaches an MCP client; structure and non-string values are kept.\n */\nfunction redactStrings<T>(value: T): T {\n if (typeof value === 'string') {\n return redactConnectionString(value) as T;\n }\n if (Array.isArray(value)) {\n return value.map((item) => redactStrings(item)) as T;\n }\n if (value !== null && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(\n value as Record<string, unknown>,\n )) {\n out[key] = redactStrings(item);\n }\n return out as T;\n }\n return value;\n}\n\n/** Convert a reader result into envelope data + diagnostics. */\nfunction toEnvelopeParts(rawResult: unknown): {\n data: Record<string, unknown>;\n diagnostics: RuntimeDiagnostic[];\n} {\n const result = redactStrings(rawResult);\n if (\n result !== null &&\n typeof result === 'object' &&\n 'available' in result &&\n (result as { available: unknown }).available === false\n ) {\n const unavailable = result as unknown as {\n reason: string;\n message: string;\n detail?: string;\n tableName?: string;\n [key: string]: unknown;\n };\n const { message, detail, ...rest } = unavailable;\n const severity: RuntimeDiagnostic['severity'] =\n unavailable.reason === 'retired' ? 'info' : 'warning';\n return {\n data: rest,\n diagnostics: [\n {\n severity,\n code: `category_unavailable_${String(unavailable.reason).replace(/-/g, '_')}`,\n // `detail` is the driver's own error text, already passed through\n // redactStrings above; surfacing it turns \"read failed\" into a cause.\n message: detail\n ? `${String(message)} Cause: ${detail}`\n : String(message),\n },\n ],\n };\n }\n const data = result as Record<string, unknown>;\n const diagnostics: RuntimeDiagnostic[] = [];\n // An older system schema answers with what it has and names the rest.\n const behind = Array.isArray(data.schemaBehind) ? data.schemaBehind : [];\n for (const entry of behind as Array<{\n tableName?: unknown;\n missingColumns?: unknown;\n }>) {\n const columns = Array.isArray(entry.missingColumns)\n ? entry.missingColumns.map(String)\n : [];\n if (columns.length === 0) continue;\n diagnostics.push({\n severity: 'info',\n code: 'schema_behind',\n message: `${String(entry.tableName ?? 'table')} predates ${columns.join(', ')}; those fields are reported as null. Run db:migrate to bring the system tables current.`,\n });\n }\n return { data, diagnostics };\n}\n\nfunction readToParts(read: unknown): Promise<RuntimeReadParts> {\n return Promise.resolve(read).then((result) => toEnvelopeParts(result));\n}\n\nexport interface MigrationStatusArgs extends RuntimeDatabaseArgs {\n /** Row budget for latest/failed lists (default 50, capped at 500). */\n limit?: number;\n}\n\nexport async function runtimeMigrationStatus(\n args: MigrationStatusArgs = {},\n): Promise<RuntimeToolEnvelope> {\n const { limit, ...connectionArgs } = args;\n return withRuntimeConnection(\n connectionArgs,\n (db) =>\n projectRead(connectionArgs, 'readMigrationStatus', db, { limit }).then(\n readToParts,\n ),\n 'no migration status — the manifest still reports the declared schema',\n );\n}\n\nexport interface JobHealthArgs extends RuntimeDatabaseArgs {\n /** Row budget for the jobs list (default 50, capped at 500). */\n limit?: number;\n}\n\nexport async function runtimeJobHealth(\n args: JobHealthArgs = {},\n): Promise<RuntimeToolEnvelope> {\n const { limit, ...connectionArgs } = args;\n return withRuntimeConnection(\n connectionArgs,\n (db) =>\n projectRead(connectionArgs, 'readJobHealth', db, { limit }).then(\n readToParts,\n ),\n 'no job health snapshot — the manifest still reports declared job queues',\n );\n}\n\nexport interface ScheduleHealthArgs extends RuntimeDatabaseArgs {\n /** Row budget for the schedules list (default 50, capped at 500). */\n limit?: number;\n}\n\nexport async function runtimeScheduleHealth(\n args: ScheduleHealthArgs = {},\n): Promise<RuntimeToolEnvelope> {\n const { limit, ...connectionArgs } = args;\n return withRuntimeConnection(\n connectionArgs,\n (db) =>\n projectRead(connectionArgs, 'readScheduleHealth', db, { limit }).then(\n readToParts,\n ),\n 'no schedule health snapshot — the manifest still reports declared schedules',\n );\n}\n\nexport interface DispatchHealthArgs extends RuntimeDatabaseArgs {\n /** Row budget for messages/subscriptions lists (default 50, capped at 500). */\n limit?: number;\n}\n\nexport async function runtimeDispatchHealth(\n args: DispatchHealthArgs = {},\n): Promise<RuntimeToolEnvelope> {\n const { limit, ...connectionArgs } = args;\n return withRuntimeConnection(\n connectionArgs,\n (db) =>\n projectRead(connectionArgs, 'readDispatchHealth', db, { limit }).then(\n readToParts,\n ),\n 'no dispatch health snapshot — the manifest still reports declared dispatch topology',\n );\n}\n\nexport interface RecentChangesArgs extends RuntimeDatabaseArgs {\n /** Cursor to read after (default 0). */\n since?: number;\n /** Restrict to these physical table names. */\n tables?: string[];\n /** Tenant narrowing filter. */\n tenantId?: string;\n /** Page size (default 200, capped at 500). */\n limit?: number;\n}\n\nexport async function runtimeRecentChanges(\n args: RecentChangesArgs = {},\n): Promise<RuntimeToolEnvelope> {\n const { since, tables, tenantId, limit, ...connectionArgs } = args;\n return withRuntimeConnection(\n connectionArgs,\n (db) =>\n projectRead(connectionArgs, 'readRecentChanges', db, {\n since,\n tables,\n tenantId,\n limit,\n }).then(readToParts),\n 'no recent changes — static knowledge artifacts are unchanged',\n );\n}\n\nexport async function runtimeRegistryDrift(\n args: RuntimeDatabaseArgs = {},\n): Promise<RuntimeToolEnvelope> {\n return withRuntimeConnection(\n args,\n (db) => projectRead(args, 'readRegistryDrift', db).then(readToParts),\n 'no registry drift report — _smrt_registry is retired; declared objects come from the manifest',\n );\n}\n\n/** Setup failures are safe static envelopes, never empty live/booted snapshots. */\nexport async function withRuntimeSetup(\n operation: () => Promise<RuntimeToolEnvelope>,\n): Promise<RuntimeToolEnvelope> {\n try {\n return await operation();\n } catch (error) {\n if (!(error instanceof ProjectRuntimeResolutionError)) throw error;\n return {\n ok: true,\n coverage: null,\n diagnostics: [\n { severity: 'warning', code: error.code, message: error.message },\n ],\n data: { provenance: STATIC_PROVENANCE, connected: false },\n };\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-dev-mcp",
3
- "version": "0.54.4",
3
+ "version": "0.55.2",
4
4
  "description": "Development MCP server for SMRT framework knowledge, review context, architecture context, and code generation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -53,15 +53,15 @@
53
53
  "homepage": "https://github.com/happyvertical/smrt/tree/main/packages/smrt-dev-mcp#readme",
54
54
  "license": "MIT",
55
55
  "dependencies": {
56
- "@happyvertical/smrt-scanner": "0.54.4",
56
+ "@happyvertical/smrt-scanner": "0.55.2",
57
57
  "@modelcontextprotocol/node": "2.0.0",
58
58
  "@modelcontextprotocol/server": "2.0.0",
59
- "@happyvertical/smrt-types": "0.54.4"
59
+ "@happyvertical/smrt-types": "0.55.2"
60
60
  },
61
61
  "devDependencies": {
62
- "@happyvertical/smrt-config": "0.54.4",
63
- "@happyvertical/smrt-core": "0.54.4",
64
- "@happyvertical/sql": "^0.102.1",
62
+ "@happyvertical/smrt-config": "0.55.2",
63
+ "@happyvertical/smrt-core": "0.55.2",
64
+ "@happyvertical/sql": "^0.102.2",
65
65
  "@modelcontextprotocol/client": "2.0.0",
66
66
  "@modelcontextprotocol/conformance": "0.2.0-alpha.11",
67
67
  "@types/node": "24.13.6",
@@ -1 +0,0 @@
1
- {"version":3,"file":"tools-B-CDyCXi.js","names":[],"sources":["../src/tools/runtime/project-runtime.ts","../src/tools/runtime/connection.ts","../src/tools/runtime/tools.ts"],"sourcesContent":["import { readFileSync, realpathSync } from 'node:fs';\nimport { findPackageJSON } from 'node:module';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\n/** Runtime registries are process globals, even across different core versions. */\nlet selectedProject: string | undefined;\n\nexport class ProjectRuntimeResolutionError extends Error {\n readonly code: string;\n constructor(specifier: string, code = 'runtime_dependency_unavailable') {\n super(\n code === 'runtime_project_mismatch'\n ? 'This server already loaded another project runtime. Start a separate server for this project or restart with its working directory.'\n : `The selected project must install a compatible ${specifier}. Install the SMRT runtime in that project, build it, and retry; the MCP server does not supply a runtime.`,\n );\n this.name = 'ProjectRuntimeResolutionError';\n this.code = code;\n }\n}\n\nexport function projectRuntimeRoot(projectPath?: string): string {\n const root = resolve(projectPath ?? process.cwd());\n try {\n return realpathSync(root);\n } catch {\n return root;\n }\n}\n\n/** Test seam; callers must also clear the process-global runtime registry. */\nexport function resetProjectRuntimeForTests(): void {\n selectedProject = undefined;\n}\n\n// findPackageJSON uses Node's ESM package lookup, including import-only exports.\n// Resolve the documented exact export from that same package root: never search\n// again from the server, and never choose a second core for one of its subpaths.\nfunction packageExport(packageJson: string, subpath: string): string {\n const pkg = JSON.parse(readFileSync(packageJson, 'utf8'));\n let target = pkg.exports?.[subpath];\n if (\n target === undefined &&\n subpath === '.' &&\n typeof pkg.exports === 'string'\n )\n target = pkg.exports;\n while (target && typeof target === 'object')\n target = target.import ?? target.node ?? target.default;\n if (typeof target !== 'string' || !target.startsWith('./'))\n throw new Error('Unsupported runtime export');\n const root = dirname(packageJson);\n const entry = resolve(root, target);\n const rel = relative(root, entry);\n if (rel.startsWith('..') || isAbsolute(rel))\n throw new Error('Invalid runtime export');\n return entry;\n}\n\nexport function resolveProjectRuntimeEntry(\n projectPath: string | undefined,\n specifier: string,\n): string {\n const root = projectRuntimeRoot(projectPath);\n let entry: string;\n try {\n const coreJson = findPackageJSON(\n '@happyvertical/smrt-core',\n pathToFileURL(join(root, 'package.json')),\n );\n if (!coreJson) throw new Error('Missing runtime');\n if (\n specifier === '@happyvertical/smrt-core' ||\n specifier.startsWith('@happyvertical/smrt-core/')\n ) {\n const subpath = specifier.slice('@happyvertical/smrt-core'.length);\n entry = packageExport(coreJson, subpath ? `.${subpath}` : '.');\n } else {\n const dependencyJson = findPackageJSON(\n specifier,\n pathToFileURL(coreJson),\n );\n if (!dependencyJson) throw new Error('Missing runtime dependency');\n entry = packageExport(dependencyJson, '.');\n }\n } catch {\n throw new ProjectRuntimeResolutionError(specifier);\n }\n return entry;\n}\n\nexport async function importProjectRuntimeModule<T = Record<string, unknown>>(\n projectPath: string | undefined,\n specifier: string,\n): Promise<T> {\n const root = projectRuntimeRoot(projectPath);\n if (selectedProject && selectedProject !== root) {\n throw new ProjectRuntimeResolutionError(\n specifier,\n 'runtime_project_mismatch',\n );\n }\n const entry = resolveProjectRuntimeEntry(root, specifier);\n // Pin before the first await: concurrent requests cannot import two runtimes.\n selectedProject = root;\n try {\n return (await import(pathToFileURL(entry).href)) as T;\n } catch {\n // Driver/module errors may contain credentials or absolute host paths.\n throw new ProjectRuntimeResolutionError(specifier);\n }\n}\n\nexport const loadProjectCore = (projectPath?: string) =>\n importProjectRuntimeModule<typeof import('@happyvertical/smrt-core')>(\n projectPath,\n '@happyvertical/smrt-core',\n );\n\nexport async function loadProjectRuntime(projectPath?: string) {\n const core = await loadProjectCore(projectPath);\n const sql = await importProjectRuntimeModule<\n typeof import('@happyvertical/sql')\n >(projectPath, '@happyvertical/sql');\n return { core, sql, projectRoot: projectRuntimeRoot(projectPath) };\n}\n","/**\n * Optional read-only dev-database connection resolution for runtime\n * diagnostics tools (#1824).\n *\n * Resolution order per call:\n * 1. explicit `dbUrl`/`dbType` tool arguments\n * 2. `SMRT_DEV_DB_URL` environment variable\n * 3. the project's cosmiconfig CLI section → `database.{type,url}`, else\n * `DATABASE_URL`/`DATABASE_TYPE` — the `smrt` CLI's own precedence,\n * through `resolveCliDatabaseConfig()` from the project's\n * `@happyvertical/smrt-config` (#3446; an older install without it\n * reads only `getPackageConfig('cli', ...)`)\n *\n * No configured connection → `db: null` with `source: 'none'`; callers return\n * a successful static-only envelope. A connection is always opened lazily per\n * call and closed in the caller's `finally` — nothing is cached across calls\n * and the server never holds a database handle.\n *\n * Sensitive handling: connection strings are never logged or echoed. Every\n * surfaced URL passes through {@link redactConnectionString}; driver errors\n * are surfaced only through {@link safeErrorMessage}, which strips anything\n * that looks like a credential-bearing URL.\n */\n\nimport { resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport {\n importProjectRuntimeModule,\n loadProjectRuntime,\n ProjectRuntimeResolutionError,\n projectRuntimeRoot,\n} from './project-runtime.js';\n\n/** Engine labels `@happyvertical/sql`'s `getDatabase` accepts. */\nexport type RuntimeDatabaseType = 'sqlite' | 'postgres' | 'duckdb';\n\nconst RUNTIME_DATABASE_TYPES: readonly RuntimeDatabaseType[] = [\n 'sqlite',\n 'postgres',\n 'duckdb',\n];\n\nfunction isRuntimeDatabaseType(value: string): value is RuntimeDatabaseType {\n return (RUNTIME_DATABASE_TYPES as readonly string[]).includes(value);\n}\n\n/** Tool arguments every runtime-diagnostics tool accepts. */\nexport interface RuntimeDatabaseArgs {\n projectPath?: string;\n /** Optional database URL/connection string (overrides env and config). */\n dbUrl?: string;\n /**\n * Optional engine hint for `dbUrl` or the environment connection.\n * Must be `'sqlite'` | `'postgres'` | `'duckdb'` (case-insensitive); any\n * other non-empty hint surfaces a safe diagnostic error rather than\n * silently opening the wrong adapter. When absent, the engine is inferred\n * from the URL scheme.\n */\n dbType?: string;\n}\n\nexport type ConnectionSource = 'argument' | 'environment' | 'config' | 'none';\n\nexport interface ResolvedRuntimeConnection {\n /** Open connection, or `null` when no connection is configured. */\n db: unknown | null;\n source: ConnectionSource;\n /** Redacted connection string for display; '' when no connection. */\n displayUrl: string;\n /**\n * Normalized engine label for the resolved connection (`sqlite`,\n * `postgres`, or `duckdb`); `null` when no connection is configured.\n */\n databaseType: string | null;\n}\n\n/**\n * Sensitive query-parameter names. Matching normalizes the key (lowercase,\n * `_`/`-` stripped), so camelCase (`authToken`, `accessToken`) and hyphen\n * variants (`api-key`) are masked exactly like their snake_case forms.\n */\nconst SENSITIVE_QUERY_PARAMS = [\n 'access_token',\n 'apikey',\n 'api_key',\n 'auth',\n 'auth_token',\n 'connectionstring',\n 'connection_string',\n 'password',\n 'token',\n];\n\nfunction normalizeQueryParamName(key: string): string {\n return key.toLowerCase().replace(/[_-]/g, '');\n}\n\nconst SENSITIVE_QUERY_PARAM_NAMES = new Set(\n SENSITIVE_QUERY_PARAMS.map(normalizeQueryParamName),\n);\n\nconst DEFAULT_CLI_DATABASE = {\n database: { type: 'sqlite', url: ':memory:' },\n};\n\n/**\n * Redact a connection string so it can be shown to an agent without leaking\n * credentials. Mirrors the CLI's `redactConnectionString` (which is CLI\n * private); patterned identically so dev-mcp never depends on the CLI.\n *\n * Query-parameter masking normalizes each key (lowercase, `_`/`-` stripped),\n * so camelCase forms such as Turso/libsql's `?authToken=` mask exactly like\n * their snake_case forms. A final regex pass also masks `key=value` pairs\n * embedded in free text (driver error messages often quote the URL); it treats\n * the start of the string, `?`, `&`, `,`, `(`, and whitespace as the\n * preceding boundary.\n */\nexport function redactConnectionString(value: string): string {\n let redacted = value;\n\n try {\n const url = new URL(value);\n if (url.password) {\n url.password = '***';\n }\n for (const key of [...url.searchParams.keys()]) {\n if (SENSITIVE_QUERY_PARAM_NAMES.has(normalizeQueryParamName(key))) {\n url.searchParams.set(key, '***');\n }\n }\n redacted = url.toString();\n } catch {\n redacted = value.replace(\n // The password run may itself contain unencoded `@` (invalid but real\n // in driver errors); each interior `@` is consumed only when another\n // `@` follows before whitespace, so the mask always reaches the final\n // userinfo terminator instead of stopping at the first `@`.\n /([a-z][a-z0-9+.-]*:\\/\\/[^:\\s/@]+:)(?:[^@\\s]|@(?=[^@\\s]*@))+(@)/gi,\n '$1***$2',\n );\n }\n\n // Local database files: a `file:` URL or a driver error quoting the path\n // (POSIX or Windows separators) reveals the filesystem layout. Keep only\n // the basename.\n redacted = redacted.replace(\n /(?:[A-Za-z]:)?(?:[\\\\/][^\\s\\\\/'\"`]+)+[\\\\/]([^\\s\\\\/'\"`]+\\.(?:db|sqlite3?|duckdb))/g,\n '…/$1',\n );\n\n // Free-text pass: mask the value of any sensitive `key=value` pair the URL\n // parse above may have left intact (e.g. a URL quoted inside an error\n // message, where `?`/`&` boundaries do not delimit params for `new URL`).\n return redacted.replace(\n /((?:^|[?&,(\\s])([a-z][a-z0-9_-]{0,30})=)([^&,\\s)]+)/gi,\n (match, prefix: string, key: string) =>\n SENSITIVE_QUERY_PARAM_NAMES.has(normalizeQueryParamName(key))\n ? `${prefix}***`\n : match,\n );\n}\n\n/**\n * Build a safe, redacted error message for a database failure. Connection\n * strings and raw driver error objects are never surfaced verbatim.\n */\nexport function safeErrorMessage(error: unknown): string {\n const message =\n error instanceof Error ? error.message : String(error ?? 'unknown error');\n return redactConnectionString(message);\n}\n\n/**\n * Normalize a type hint into an engine `getDatabase` accepts. Unknown values\n * throw a safe error (no URL is included) so the caller can surface a\n * diagnostic instead of silently opening the wrong adapter.\n */\nexport function toRuntimeDatabaseType(value: string): RuntimeDatabaseType {\n const normalized = value.trim().toLowerCase();\n if (isRuntimeDatabaseType(normalized)) {\n return normalized;\n }\n throw new Error(\n `Unsupported runtime database type \"${normalized}\"; expected sqlite, postgres, or duckdb`,\n );\n}\n\n/** Infer an engine hint from a URL scheme when no explicit type is given. */\nexport function inferDatabaseType(url: string, hint?: string): string {\n if (hint && hint.trim().length > 0) return hint;\n if (/^postgres(ql)?:/i.test(url)) return 'postgres';\n if (/^duckdb:/i.test(url)) return 'duckdb';\n return 'sqlite';\n}\n\n/**\n * Normalize the accepted dev-database URL forms to what `getDatabase` opens.\n *\n * `@happyvertical/sql` understands `file:` URLs and bare paths for SQLite,\n * but not a `sqlite:` scheme; passing `sqlite:///abs/dev.db` through verbatim\n * made the driver treat the whole string as a relative filename and join it\n * to cwd. Accepted forms:\n *\n * - `sqlite:///abs/path.db`, `sqlite://rel/path.db`, `sqlite:path.db` →\n * `file:` URL with the path resolved against cwd only when it is relative\n * - `file:` URLs, `postgres:`/`postgresql:`, `duckdb:` → unchanged\n * - a bare path → unchanged (the driver resolves it)\n */\n/**\n * Whether a configured value means \"in-memory, so not a runtime database\":\n * `:memory:` as well as the `sqlite::memory:` / `sqlite://:memory:` spellings.\n */\nexport function isMemoryDatabaseUrl(url: string): boolean {\n return /^(?:sqlite:(?:\\/\\/)?)?:memory:$/i.test(url.trim());\n}\n\nexport function normalizeDatabaseUrl(\n url: string,\n projectPath?: string,\n): string {\n const match = /^sqlite:(?:\\/\\/)?(.*)$/i.exec(url.trim());\n if (!match) return url.trim();\n const path = match[1];\n if (!path) return url.trim();\n const absolute = path.startsWith('/')\n ? path\n : resolve(projectRuntimeRoot(projectPath), path);\n return pathToFileURL(absolute).href;\n}\n\n/**\n * Resolve the dev-database connection for one tool call.\n *\n * Returns `db: null` (never throws) when no connection is configured; callers\n * must treat that as \"no runtime database\" and return a static-only envelope.\n * A thrown connect error is propagated to the caller, which converts it into\n * a diagnostic envelope — it must never reach the MCP transport.\n */\nexport async function resolveRuntimeConnection(\n args: RuntimeDatabaseArgs = {},\n): Promise<ResolvedRuntimeConnection> {\n const argUrl = args.dbUrl?.trim();\n if (argUrl && !isMemoryDatabaseUrl(argUrl)) {\n const databaseType = toRuntimeDatabaseType(\n inferDatabaseType(argUrl, args.dbType),\n );\n const db = await getDatabaseInstance(args.projectPath, {\n type: databaseType,\n url: normalizeDatabaseUrl(argUrl, args.projectPath),\n });\n return {\n db,\n source: 'argument',\n displayUrl: redactConnectionString(argUrl),\n databaseType,\n };\n }\n\n const envUrl = process.env.SMRT_DEV_DB_URL?.trim();\n if (envUrl && !isMemoryDatabaseUrl(envUrl)) {\n const databaseType = toRuntimeDatabaseType(\n inferDatabaseType(envUrl, args.dbType),\n );\n const db = await getDatabaseInstance(args.projectPath, {\n type: databaseType,\n url: normalizeDatabaseUrl(envUrl, args.projectPath),\n });\n return {\n db,\n source: 'environment',\n displayUrl: redactConnectionString(envUrl),\n databaseType,\n };\n }\n\n const config = await loadCliDatabaseConfig(args.projectPath);\n const configUrl = config?.database?.url?.trim();\n if (configUrl && !isMemoryDatabaseUrl(configUrl)) {\n // A configured database keeps its engine. For DATABASE_URL, an explicit\n // `dbType` overrides the environment engine like any env connection; the\n // CLI-resolved type (DATABASE_TYPE or the URL scheme) is the fallback.\n const databaseType = toRuntimeDatabaseType(\n config.source === 'environment' && args.dbType?.trim()\n ? args.dbType\n : config.database?.type || inferDatabaseType(configUrl, args.dbType),\n );\n const db = await getDatabaseInstance(args.projectPath, {\n type: databaseType,\n url: normalizeDatabaseUrl(configUrl, args.projectPath),\n });\n return {\n db,\n source: config.source ?? 'config',\n displayUrl: redactConnectionString(configUrl),\n databaseType,\n };\n }\n\n return { db: null, source: 'none', displayUrl: '', databaseType: null };\n}\n\ninterface DatabaseConfigLike {\n database?: { type?: string; url?: string };\n /** `environment` when `DATABASE_URL` supplied the database. */\n source?: 'config' | 'environment';\n}\n\n/** The project's smrt-config `resolveCliDatabaseConfig()` result shape. */\ninterface CliDatabaseResolution {\n source?: unknown;\n database?: { type?: unknown; url?: unknown } | null;\n}\n\nasync function loadCliDatabaseConfig(\n projectPath?: string,\n): Promise<DatabaseConfigLike> {\n try {\n // `getPackageConfig` only reads the synchronous cache and answers with\n // defaults until `loadConfig()` has run; nothing else in this server loads\n // the project config, so load it here. Cosmiconfig is resolved from the\n // server's cwd (the project the agent is working in); a missing or invalid\n // config falls back to the defaults.\n const configModule = await importProjectRuntimeModule<{\n loadConfig: (options: {\n searchFrom: string;\n cache: boolean;\n }) => Promise<unknown>;\n getPackageConfig: (\n name: string,\n defaults: Record<string, unknown>,\n ) => unknown;\n resolveCliDatabaseConfig?: (\n env: Record<string, string | undefined>,\n ) => CliDatabaseResolution;\n }>(projectPath, '@happyvertical/smrt-config');\n if (\n typeof configModule.loadConfig !== 'function' ||\n typeof configModule.getPackageConfig !== 'function'\n )\n throw new ProjectRuntimeResolutionError('@happyvertical/smrt-config');\n await configModule.loadConfig({\n searchFrom: projectRuntimeRoot(projectPath),\n cache: false,\n });\n if (typeof configModule.resolveCliDatabaseConfig === 'function') {\n // Same precedence as the `smrt` CLI: a configured URL wins and the\n // environment is not read; otherwise DATABASE_URL/DATABASE_TYPE.\n const resolved = configModule.resolveCliDatabaseConfig(process.env);\n const url = resolved?.database?.url;\n if (\n (resolved.source === 'config' || resolved.source === 'environment') &&\n typeof url === 'string'\n ) {\n const type = resolved.database?.type;\n return {\n source: resolved.source,\n database: {\n // A typeless configured URL keeps the CLI's sqlite default.\n type:\n typeof type === 'string'\n ? type\n : DEFAULT_CLI_DATABASE.database.type,\n url,\n },\n };\n }\n return {};\n }\n const config = configModule.getPackageConfig(\n 'cli',\n DEFAULT_CLI_DATABASE as unknown as Record<string, unknown>,\n );\n const database = (config as DatabaseConfigLike).database;\n if (database && typeof database.url === 'string') {\n return { database };\n }\n return {};\n } catch (error) {\n if (error instanceof ProjectRuntimeResolutionError) throw error;\n return {};\n }\n}\n\nasync function getDatabaseInstance(\n projectPath: string | undefined,\n options: {\n type: RuntimeDatabaseType;\n url: string;\n },\n): Promise<unknown> {\n const { sql } = await loadProjectRuntime(projectPath);\n if (typeof sql.getDatabase !== 'function')\n throw new ProjectRuntimeResolutionError('@happyvertical/sql');\n return sql.getDatabase(options);\n}\n\ntype MaybeCloseableDatabase = {\n close?: () => unknown | Promise<unknown>;\n client?: {\n close?: () => unknown | Promise<unknown>;\n end?: () => unknown | Promise<unknown>;\n };\n};\n\n/**\n * Best-effort close of a resolved connection. Never throws; diagnostics must\n * not fail because cleanup hiccuped.\n */\nexport async function closeRuntimeConnection(db: unknown): Promise<void> {\n if (!db || typeof db !== 'object') return;\n const closeable = db as MaybeCloseableDatabase;\n const close =\n closeable.close ?? closeable.client?.end ?? closeable.client?.close;\n if (typeof close !== 'function') return;\n try {\n await close.call(closeable.close ? closeable : closeable.client);\n } catch {\n // Cleanup failures are not diagnostic failures.\n }\n}\n","/**\n * Runtime diagnostics tools (#1824): read-only views over a project's dev\n * database `_smrt_*` system tables, powered by the shared SELECT-only\n * system-diagnostics reader in `@happyvertical/smrt-core`.\n *\n * Contract:\n * - **Optional connection.** No configured connection returns a successful\n * static-only envelope — the server always starts and static tools are\n * unaffected. A live connection is opened lazily per call and closed in\n * `finally`; nothing is cached across calls.\n * - **Read-only.** Every underlying statement is a bounded SELECT; the reader\n * never selects sensitive columns (job payloads/results, schedule\n * `agentConfig`/`methodArgs`, dispatch `payload`/`metadata`).\n * - **Provenance-labeled.** Live results carry `provenance: 'runtime (live DB)'`;\n * static-only results carry `provenance: 'static'` — agents must never\n * conflate runtime facts with declared/manifest facts.\n * - **Fail-safe.** A connect/read error becomes a diagnostic envelope; it must\n * never reach the MCP transport and never includes raw driver text or URLs.\n */\n\nimport { createHash } from 'node:crypto';\nimport {\n closeRuntimeConnection,\n type RuntimeDatabaseArgs,\n redactConnectionString,\n resolveRuntimeConnection,\n safeErrorMessage,\n} from './connection.js';\nimport {\n importProjectRuntimeModule,\n ProjectRuntimeResolutionError,\n} from './project-runtime.js';\n\n/** Provenance labels separating runtime facts from static/declared facts. */\nexport const RUNTIME_PROVENANCE = 'runtime (live DB)';\nexport const STATIC_PROVENANCE = 'static';\n\nexport interface RuntimeDiagnostic {\n severity: 'info' | 'warning';\n code: string;\n message: string;\n}\n\nexport interface RuntimeToolEnvelope {\n ok: true;\n coverage: null;\n diagnostics: RuntimeDiagnostic[];\n data: Record<string, unknown>;\n}\n\ntype RuntimeReadParts = {\n data: Record<string, unknown>;\n diagnostics: RuntimeDiagnostic[];\n};\n\ntype RuntimeRead = (db: unknown) => Promise<RuntimeReadParts>;\n\nasync function projectRead(\n args: RuntimeDatabaseArgs,\n name: string,\n db: unknown,\n options?: unknown,\n) {\n const core = await importProjectRuntimeModule<\n Record<string, (db: unknown, options?: unknown) => Promise<unknown>>\n >(args.projectPath, '@happyvertical/smrt-core');\n if (typeof core[name] !== 'function')\n throw new ProjectRuntimeResolutionError('@happyvertical/smrt-core');\n return core[name](db, options);\n}\n\n/**\n * Serialize resolve → read → close per connection target so overlapping tool\n * calls never close a shared cached handle out from under each other.\n *\n * `@happyvertical/sql`'s `getDatabase` returns a cached handle per URL (no\n * opt-out in its public API). `closeRuntimeConnection` only calls the handle's\n * own `close`/`end`, but the SDK wraps those so a close also evicts the handle\n * from its connection cache. Two concurrent diagnostics calls resolving the\n * same URL would therefore share one handle, with the first finisher closing\n * it mid-read for the second. A per-key promise chain keeps each call's\n * lifecycle private: every call resolves its own view of the connection,\n * performs its read, and only then closes — the next queued call re-resolves\n * a fresh handle.\n *\n * The key is a digest of the resolved target, never the raw URL, so a\n * credential-bearing connection string is not retained in this map.\n */\nconst runtimeReadQueues = new Map<string, Promise<unknown>>();\n\nfunction connectionQueueKey(args: RuntimeDatabaseArgs): string {\n const target =\n args.dbUrl?.trim() || process.env.SMRT_DEV_DB_URL?.trim() || 'cli.config';\n return createHash('sha256').update(target).digest('hex');\n}\n\nasync function enqueueRuntimeRead<T>(\n key: string,\n operation: () => Promise<T>,\n): Promise<T> {\n const previous = runtimeReadQueues.get(key) ?? Promise.resolve();\n const run = previous.then(operation, operation);\n // Track a never-rejecting tail so later calls chain regardless of outcome,\n // then drop the entry once this is the settled tail to bound map size.\n const tail = run.catch(() => undefined);\n runtimeReadQueues.set(key, tail);\n void tail.then(() => {\n if (runtimeReadQueues.get(key) === tail) {\n runtimeReadQueues.delete(key);\n }\n });\n return run;\n}\n\n/**\n * Run one read against the optional runtime connection, mapping every outcome\n * to a successful MCP envelope:\n *\n * - no connection configured → static-only envelope (`connected: false`)\n * - connect failure → static envelope with a safe diagnostic\n * - read failure → connected envelope with a safe diagnostic\n * - success → live result under `provenance: 'runtime (live DB)'`; a\n * category-unavailable reader result keeps its `available: false` data and\n * surfaces its message as a diagnostic\n */\nexport async function withRuntimeConnection(\n args: RuntimeDatabaseArgs,\n read: RuntimeRead,\n staticHint: string,\n): Promise<RuntimeToolEnvelope> {\n // Serialize the full resolve → read → close lifecycle per connection target\n // (see `runtimeReadQueues`) so concurrent calls sharing one cached handle\n // cannot close it mid-read for each other.\n return enqueueRuntimeRead(connectionQueueKey(args), () =>\n runWithRuntimeConnection(args, read, staticHint),\n );\n}\n\nasync function runWithRuntimeConnection(\n args: RuntimeDatabaseArgs,\n read: RuntimeRead,\n staticHint: string,\n): Promise<RuntimeToolEnvelope> {\n let resolved: Awaited<ReturnType<typeof resolveRuntimeConnection>>;\n try {\n resolved = await resolveRuntimeConnection(args);\n } catch (error) {\n return {\n ok: true,\n coverage: null,\n diagnostics: [\n {\n severity: 'warning',\n code:\n error instanceof ProjectRuntimeResolutionError\n ? error.code\n : 'runtime_connection_error',\n message: safeErrorMessage(error),\n },\n ],\n data: {\n provenance: STATIC_PROVENANCE,\n connected: false,\n },\n };\n }\n\n if (!resolved.db) {\n return {\n ok: true,\n coverage: null,\n diagnostics: [\n {\n severity: 'info',\n code: 'runtime_connection_unavailable',\n message: `No runtime dev database configured (set SMRT_DEV_DB_URL, cli.database, or DATABASE_URL); returning static-only result: ${staticHint}. Static tools are unaffected.`,\n },\n ],\n data: {\n provenance: STATIC_PROVENANCE,\n connected: false,\n },\n };\n }\n\n const { db, source, displayUrl, databaseType } = resolved;\n try {\n const { data, diagnostics } = await read(db);\n return {\n ok: true,\n coverage: null,\n diagnostics,\n data: {\n provenance: RUNTIME_PROVENANCE,\n connected: true,\n connectionSource: source,\n databaseType,\n displayUrl,\n ...data,\n },\n };\n } catch (error) {\n return {\n ok: true,\n coverage: null,\n diagnostics: [\n {\n severity: 'warning',\n code:\n error instanceof ProjectRuntimeResolutionError\n ? error.code\n : 'runtime_read_error',\n message: safeErrorMessage(error),\n },\n ],\n data:\n error instanceof ProjectRuntimeResolutionError\n ? {\n provenance: STATIC_PROVENANCE,\n connected: false,\n }\n : {\n provenance: RUNTIME_PROVENANCE,\n connected: true,\n connectionSource: source,\n databaseType,\n displayUrl,\n },\n };\n } finally {\n await closeRuntimeConnection(db);\n }\n}\n\n/**\n * Stored error columns (`error_message`, `last_error`) are free text written\n * at failure time and routinely quote connection URLs or credentials. Every\n * string in a live result passes through {@link redactConnectionString}\n * before it reaches an MCP client; structure and non-string values are kept.\n */\nfunction redactStrings<T>(value: T): T {\n if (typeof value === 'string') {\n return redactConnectionString(value) as T;\n }\n if (Array.isArray(value)) {\n return value.map((item) => redactStrings(item)) as T;\n }\n if (value !== null && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(\n value as Record<string, unknown>,\n )) {\n out[key] = redactStrings(item);\n }\n return out as T;\n }\n return value;\n}\n\n/** Convert a reader result into envelope data + diagnostics. */\nfunction toEnvelopeParts(rawResult: unknown): {\n data: Record<string, unknown>;\n diagnostics: RuntimeDiagnostic[];\n} {\n const result = redactStrings(rawResult);\n if (\n result !== null &&\n typeof result === 'object' &&\n 'available' in result &&\n (result as { available: unknown }).available === false\n ) {\n const unavailable = result as unknown as {\n reason: string;\n message: string;\n detail?: string;\n tableName?: string;\n [key: string]: unknown;\n };\n const { message, detail, ...rest } = unavailable;\n const severity: RuntimeDiagnostic['severity'] =\n unavailable.reason === 'retired' ? 'info' : 'warning';\n return {\n data: rest,\n diagnostics: [\n {\n severity,\n code: `category_unavailable_${String(unavailable.reason).replace(/-/g, '_')}`,\n // `detail` is the driver's own error text, already passed through\n // redactStrings above; surfacing it turns \"read failed\" into a cause.\n message: detail\n ? `${String(message)} Cause: ${detail}`\n : String(message),\n },\n ],\n };\n }\n const data = result as Record<string, unknown>;\n const diagnostics: RuntimeDiagnostic[] = [];\n // An older system schema answers with what it has and names the rest.\n const behind = Array.isArray(data.schemaBehind) ? data.schemaBehind : [];\n for (const entry of behind as Array<{\n tableName?: unknown;\n missingColumns?: unknown;\n }>) {\n const columns = Array.isArray(entry.missingColumns)\n ? entry.missingColumns.map(String)\n : [];\n if (columns.length === 0) continue;\n diagnostics.push({\n severity: 'info',\n code: 'schema_behind',\n message: `${String(entry.tableName ?? 'table')} predates ${columns.join(', ')}; those fields are reported as null. Run db:migrate to bring the system tables current.`,\n });\n }\n return { data, diagnostics };\n}\n\nfunction readToParts(read: unknown): Promise<RuntimeReadParts> {\n return Promise.resolve(read).then((result) => toEnvelopeParts(result));\n}\n\nexport interface MigrationStatusArgs extends RuntimeDatabaseArgs {\n /** Row budget for latest/failed lists (default 50, capped at 500). */\n limit?: number;\n}\n\nexport async function runtimeMigrationStatus(\n args: MigrationStatusArgs = {},\n): Promise<RuntimeToolEnvelope> {\n const { limit, ...connectionArgs } = args;\n return withRuntimeConnection(\n connectionArgs,\n (db) =>\n projectRead(connectionArgs, 'readMigrationStatus', db, { limit }).then(\n readToParts,\n ),\n 'no migration status — the manifest still reports the declared schema',\n );\n}\n\nexport interface JobHealthArgs extends RuntimeDatabaseArgs {\n /** Row budget for the jobs list (default 50, capped at 500). */\n limit?: number;\n}\n\nexport async function runtimeJobHealth(\n args: JobHealthArgs = {},\n): Promise<RuntimeToolEnvelope> {\n const { limit, ...connectionArgs } = args;\n return withRuntimeConnection(\n connectionArgs,\n (db) =>\n projectRead(connectionArgs, 'readJobHealth', db, { limit }).then(\n readToParts,\n ),\n 'no job health snapshot — the manifest still reports declared job queues',\n );\n}\n\nexport interface ScheduleHealthArgs extends RuntimeDatabaseArgs {\n /** Row budget for the schedules list (default 50, capped at 500). */\n limit?: number;\n}\n\nexport async function runtimeScheduleHealth(\n args: ScheduleHealthArgs = {},\n): Promise<RuntimeToolEnvelope> {\n const { limit, ...connectionArgs } = args;\n return withRuntimeConnection(\n connectionArgs,\n (db) =>\n projectRead(connectionArgs, 'readScheduleHealth', db, { limit }).then(\n readToParts,\n ),\n 'no schedule health snapshot — the manifest still reports declared schedules',\n );\n}\n\nexport interface DispatchHealthArgs extends RuntimeDatabaseArgs {\n /** Row budget for messages/subscriptions lists (default 50, capped at 500). */\n limit?: number;\n}\n\nexport async function runtimeDispatchHealth(\n args: DispatchHealthArgs = {},\n): Promise<RuntimeToolEnvelope> {\n const { limit, ...connectionArgs } = args;\n return withRuntimeConnection(\n connectionArgs,\n (db) =>\n projectRead(connectionArgs, 'readDispatchHealth', db, { limit }).then(\n readToParts,\n ),\n 'no dispatch health snapshot — the manifest still reports declared dispatch topology',\n );\n}\n\nexport interface RecentChangesArgs extends RuntimeDatabaseArgs {\n /** Cursor to read after (default 0). */\n since?: number;\n /** Restrict to these physical table names. */\n tables?: string[];\n /** Tenant narrowing filter. */\n tenantId?: string;\n /** Page size (default 200, capped at 500). */\n limit?: number;\n}\n\nexport async function runtimeRecentChanges(\n args: RecentChangesArgs = {},\n): Promise<RuntimeToolEnvelope> {\n const { since, tables, tenantId, limit, ...connectionArgs } = args;\n return withRuntimeConnection(\n connectionArgs,\n (db) =>\n projectRead(connectionArgs, 'readRecentChanges', db, {\n since,\n tables,\n tenantId,\n limit,\n }).then(readToParts),\n 'no recent changes — static knowledge artifacts are unchanged',\n );\n}\n\nexport async function runtimeRegistryDrift(\n args: RuntimeDatabaseArgs = {},\n): Promise<RuntimeToolEnvelope> {\n return withRuntimeConnection(\n args,\n (db) => projectRead(args, 'readRegistryDrift', db).then(readToParts),\n 'no registry drift report — _smrt_registry is retired; declared objects come from the manifest',\n );\n}\n\n/** Setup failures are safe static envelopes, never empty live/booted snapshots. */\nexport async function withRuntimeSetup(\n operation: () => Promise<RuntimeToolEnvelope>,\n): Promise<RuntimeToolEnvelope> {\n try {\n return await operation();\n } catch (error) {\n if (!(error instanceof ProjectRuntimeResolutionError)) throw error;\n return {\n ok: true,\n coverage: null,\n diagnostics: [\n { severity: 'warning', code: error.code, message: error.message },\n ],\n data: { provenance: STATIC_PROVENANCE, connected: false },\n };\n }\n}\n"],"mappings":"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"}