@happyvertical/smrt-dev-mcp 0.51.23 → 0.51.33

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.
Files changed (47) hide show
  1. package/AGENTS.md +40 -3
  2. package/README.md +35 -3
  3. package/dist/dev-plane.d.ts.map +1 -1
  4. package/dist/dev-plane.js +6 -4
  5. package/dist/dev-plane.js.map +1 -1
  6. package/dist/{http-TeoaK9Xr.js → http-C302xXnO.js} +5 -4
  7. package/dist/{http-TeoaK9Xr.js.map → http-C302xXnO.js.map} +1 -1
  8. package/dist/http.d.ts +18 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +31 -454
  11. package/dist/index.js.map +1 -1
  12. package/dist/introspect-project-Peu2LC2T.js +490 -0
  13. package/dist/introspect-project-Peu2LC2T.js.map +1 -0
  14. package/dist/knowledge/index.d.ts +3 -1
  15. package/dist/knowledge/index.d.ts.map +1 -1
  16. package/dist/knowledge/mcp-docs.d.ts +1 -1
  17. package/dist/{knowledge-DYe5lCfq.js → knowledge-CvNlDTLz.js} +9 -6
  18. package/dist/knowledge-CvNlDTLz.js.map +1 -0
  19. package/dist/knowledge.js +2 -2
  20. package/dist/observation-CqE0tBgS.js +386 -0
  21. package/dist/observation-CqE0tBgS.js.map +1 -0
  22. package/dist/runtime-test-fixture.d.ts +3 -0
  23. package/dist/runtime-test-fixture.d.ts.map +1 -0
  24. package/dist/runtime.d.ts +1 -1
  25. package/dist/runtime.js +2 -1
  26. package/dist/runtime.js.map +1 -1
  27. package/dist/{tool-catalog-Zl-bcimC.js → tool-catalog-RHq7nyCm.js} +26 -2
  28. package/dist/tool-catalog-RHq7nyCm.js.map +1 -0
  29. package/dist/tool-catalog.d.ts +1 -1
  30. package/dist/tool-catalog.d.ts.map +1 -1
  31. package/dist/tools/introspect-project.d.ts.map +1 -1
  32. package/dist/tools/runtime/boot.d.ts.map +1 -1
  33. package/dist/tools/runtime/connection.d.ts +23 -3
  34. package/dist/tools/runtime/connection.d.ts.map +1 -1
  35. package/dist/tools/runtime/observation.d.ts +17 -12
  36. package/dist/tools/runtime/observation.d.ts.map +1 -1
  37. package/dist/tools/runtime/project-runtime.d.ts +16 -0
  38. package/dist/tools/runtime/project-runtime.d.ts.map +1 -0
  39. package/dist/tools/runtime/tools.d.ts +23 -3
  40. package/dist/tools/runtime/tools.d.ts.map +1 -1
  41. package/dist/tools-ma2JFPVY.js +568 -0
  42. package/dist/tools-ma2JFPVY.js.map +1 -0
  43. package/package.json +11 -9
  44. package/dist/knowledge-DYe5lCfq.js.map +0 -1
  45. package/dist/observation-VJgHaPps.js +0 -802
  46. package/dist/observation-VJgHaPps.js.map +0 -1
  47. package/dist/tool-catalog-Zl-bcimC.js.map +0 -1
@@ -0,0 +1,568 @@
1
+ import { readFileSync, realpathSync } from "node:fs";
2
+ import { findPackageJSON } from "node:module";
3
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import { createHash } from "node:crypto";
6
+ //#region \0rolldown/runtime.js
7
+ var __defProp = Object.defineProperty;
8
+ var __exportAll = (all, no_symbols) => {
9
+ let target = {};
10
+ for (var name in all) __defProp(target, name, {
11
+ get: all[name],
12
+ enumerable: true
13
+ });
14
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
15
+ return target;
16
+ };
17
+ //#endregion
18
+ //#region src/tools/runtime/project-runtime.ts
19
+ /** Runtime registries are process globals, even across different core versions. */
20
+ var selectedProject;
21
+ var ProjectRuntimeResolutionError = class extends Error {
22
+ code;
23
+ constructor(specifier, code = "runtime_dependency_unavailable") {
24
+ super(code === "runtime_project_mismatch" ? "This server already loaded another project runtime. Start a separate server for this project or restart with its working directory." : `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.`);
25
+ this.name = "ProjectRuntimeResolutionError";
26
+ this.code = code;
27
+ }
28
+ };
29
+ function projectRuntimeRoot(projectPath) {
30
+ const root = resolve(projectPath ?? process.cwd());
31
+ try {
32
+ return realpathSync(root);
33
+ } catch {
34
+ return root;
35
+ }
36
+ }
37
+ function packageExport(packageJson, subpath) {
38
+ const pkg = JSON.parse(readFileSync(packageJson, "utf8"));
39
+ let target = pkg.exports?.[subpath];
40
+ if (target === void 0 && subpath === "." && typeof pkg.exports === "string") target = pkg.exports;
41
+ while (target && typeof target === "object") target = target.import ?? target.node ?? target.default;
42
+ if (typeof target !== "string" || !target.startsWith("./")) throw new Error("Unsupported runtime export");
43
+ const root = dirname(packageJson);
44
+ const entry = resolve(root, target);
45
+ const rel = relative(root, entry);
46
+ if (rel.startsWith("..") || isAbsolute(rel)) throw new Error("Invalid runtime export");
47
+ return entry;
48
+ }
49
+ function resolveProjectRuntimeEntry(projectPath, specifier) {
50
+ const root = projectRuntimeRoot(projectPath);
51
+ let entry;
52
+ try {
53
+ const coreJson = findPackageJSON("@happyvertical/smrt-core", pathToFileURL(join(root, "package.json")));
54
+ if (!coreJson) throw new Error("Missing runtime");
55
+ if (specifier === "@happyvertical/smrt-core" || specifier.startsWith("@happyvertical/smrt-core/")) {
56
+ const subpath = specifier.slice(24);
57
+ entry = packageExport(coreJson, subpath ? `.${subpath}` : ".");
58
+ } else {
59
+ const dependencyJson = findPackageJSON(specifier, pathToFileURL(coreJson));
60
+ if (!dependencyJson) throw new Error("Missing runtime dependency");
61
+ entry = packageExport(dependencyJson, ".");
62
+ }
63
+ } catch {
64
+ throw new ProjectRuntimeResolutionError(specifier);
65
+ }
66
+ return entry;
67
+ }
68
+ async function importProjectRuntimeModule(projectPath, specifier) {
69
+ const root = projectRuntimeRoot(projectPath);
70
+ if (selectedProject && selectedProject !== root) throw new ProjectRuntimeResolutionError(specifier, "runtime_project_mismatch");
71
+ const entry = resolveProjectRuntimeEntry(root, specifier);
72
+ selectedProject = root;
73
+ try {
74
+ return await import(pathToFileURL(entry).href);
75
+ } catch {
76
+ throw new ProjectRuntimeResolutionError(specifier);
77
+ }
78
+ }
79
+ var loadProjectCore = (projectPath) => importProjectRuntimeModule(projectPath, "@happyvertical/smrt-core");
80
+ async function loadProjectRuntime(projectPath) {
81
+ return {
82
+ core: await loadProjectCore(projectPath),
83
+ sql: await importProjectRuntimeModule(projectPath, "@happyvertical/sql"),
84
+ projectRoot: projectRuntimeRoot(projectPath)
85
+ };
86
+ }
87
+ //#endregion
88
+ //#region src/tools/runtime/connection.ts
89
+ /**
90
+ * Optional read-only dev-database connection resolution for runtime
91
+ * diagnostics tools (#1824).
92
+ *
93
+ * Resolution order per call:
94
+ * 1. explicit `dbUrl`/`dbType` tool arguments
95
+ * 2. `SMRT_DEV_DB_URL` environment variable
96
+ * 3. the project's cosmiconfig CLI section (`getPackageConfig('cli', ...)`
97
+ * from `@happyvertical/smrt-config`) → `database.{type,url}`
98
+ *
99
+ * No configured connection → `db: null` with `source: 'none'`; callers return
100
+ * a successful static-only envelope. A connection is always opened lazily per
101
+ * call and closed in the caller's `finally` — nothing is cached across calls
102
+ * and the server never holds a database handle.
103
+ *
104
+ * Sensitive handling: connection strings are never logged or echoed. Every
105
+ * surfaced URL passes through {@link redactConnectionString}; driver errors
106
+ * are surfaced only through {@link safeErrorMessage}, which strips anything
107
+ * that looks like a credential-bearing URL.
108
+ */
109
+ var RUNTIME_DATABASE_TYPES = [
110
+ "sqlite",
111
+ "postgres",
112
+ "duckdb"
113
+ ];
114
+ function isRuntimeDatabaseType(value) {
115
+ return RUNTIME_DATABASE_TYPES.includes(value);
116
+ }
117
+ /**
118
+ * Sensitive query-parameter names. Matching normalizes the key (lowercase,
119
+ * `_`/`-` stripped), so camelCase (`authToken`, `accessToken`) and hyphen
120
+ * variants (`api-key`) are masked exactly like their snake_case forms.
121
+ */
122
+ var SENSITIVE_QUERY_PARAMS = [
123
+ "access_token",
124
+ "apikey",
125
+ "api_key",
126
+ "auth",
127
+ "auth_token",
128
+ "connectionstring",
129
+ "connection_string",
130
+ "password",
131
+ "token"
132
+ ];
133
+ function normalizeQueryParamName(key) {
134
+ return key.toLowerCase().replace(/[_-]/g, "");
135
+ }
136
+ var SENSITIVE_QUERY_PARAM_NAMES = new Set(SENSITIVE_QUERY_PARAMS.map(normalizeQueryParamName));
137
+ var DEFAULT_CLI_DATABASE = { database: {
138
+ type: "sqlite",
139
+ url: ":memory:"
140
+ } };
141
+ /**
142
+ * Redact a connection string so it can be shown to an agent without leaking
143
+ * credentials. Mirrors the CLI's `redactConnectionString` (which is CLI
144
+ * private); patterned identically so dev-mcp never depends on the CLI.
145
+ *
146
+ * Query-parameter masking normalizes each key (lowercase, `_`/`-` stripped),
147
+ * so camelCase forms such as Turso/libsql's `?authToken=` mask exactly like
148
+ * their snake_case forms. A final regex pass also masks `key=value` pairs
149
+ * embedded in free text (driver error messages often quote the URL); it treats
150
+ * the start of the string, `?`, `&`, `,`, `(`, and whitespace as the
151
+ * preceding boundary.
152
+ */
153
+ function redactConnectionString(value) {
154
+ let redacted = value;
155
+ try {
156
+ const url = new URL(value);
157
+ if (url.password) url.password = "***";
158
+ for (const key of [...url.searchParams.keys()]) if (SENSITIVE_QUERY_PARAM_NAMES.has(normalizeQueryParamName(key))) url.searchParams.set(key, "***");
159
+ redacted = url.toString();
160
+ } catch {
161
+ redacted = value.replace(/([a-z][a-z0-9+.-]*:\/\/[^:\s/@]+:)(?:[^@\s]|@(?=[^@\s]*@))+(@)/gi, "$1***$2");
162
+ }
163
+ redacted = redacted.replace(/(?:[A-Za-z]:)?(?:[\\/][^\s\\/'"`]+)+[\\/]([^\s\\/'"`]+\.(?:db|sqlite3?|duckdb))/g, "…/$1");
164
+ 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);
165
+ }
166
+ /**
167
+ * Build a safe, redacted error message for a database failure. Connection
168
+ * strings and raw driver error objects are never surfaced verbatim.
169
+ */
170
+ function safeErrorMessage(error) {
171
+ return redactConnectionString(error instanceof Error ? error.message : String(error ?? "unknown error"));
172
+ }
173
+ /**
174
+ * Normalize a type hint into an engine `getDatabase` accepts. Unknown values
175
+ * throw a safe error (no URL is included) so the caller can surface a
176
+ * diagnostic instead of silently opening the wrong adapter.
177
+ */
178
+ function toRuntimeDatabaseType(value) {
179
+ const normalized = value.trim().toLowerCase();
180
+ if (isRuntimeDatabaseType(normalized)) return normalized;
181
+ throw new Error(`Unsupported runtime database type "${normalized}"; expected sqlite, postgres, or duckdb`);
182
+ }
183
+ /** Infer an engine hint from a URL scheme when no explicit type is given. */
184
+ function inferDatabaseType(url, hint) {
185
+ if (hint && hint.trim().length > 0) return hint;
186
+ if (/^postgres(ql)?:/i.test(url)) return "postgres";
187
+ if (/^duckdb:/i.test(url)) return "duckdb";
188
+ return "sqlite";
189
+ }
190
+ /**
191
+ * Normalize the accepted dev-database URL forms to what `getDatabase` opens.
192
+ *
193
+ * `@happyvertical/sql` understands `file:` URLs and bare paths for SQLite,
194
+ * but not a `sqlite:` scheme; passing `sqlite:///abs/dev.db` through verbatim
195
+ * made the driver treat the whole string as a relative filename and join it
196
+ * to cwd. Accepted forms:
197
+ *
198
+ * - `sqlite:///abs/path.db`, `sqlite://rel/path.db`, `sqlite:path.db` →
199
+ * `file:` URL with the path resolved against cwd only when it is relative
200
+ * - `file:` URLs, `postgres:`/`postgresql:`, `duckdb:` → unchanged
201
+ * - a bare path → unchanged (the driver resolves it)
202
+ */
203
+ /**
204
+ * Whether a configured value means "in-memory, so not a runtime database":
205
+ * `:memory:` as well as the `sqlite::memory:` / `sqlite://:memory:` spellings.
206
+ */
207
+ function isMemoryDatabaseUrl(url) {
208
+ return /^(?:sqlite:(?:\/\/)?)?:memory:$/i.test(url.trim());
209
+ }
210
+ function normalizeDatabaseUrl(url, projectPath) {
211
+ const match = /^sqlite:(?:\/\/)?(.*)$/i.exec(url.trim());
212
+ if (!match) return url.trim();
213
+ const path = match[1];
214
+ if (!path) return url.trim();
215
+ return pathToFileURL(path.startsWith("/") ? path : resolve(projectRuntimeRoot(projectPath), path)).href;
216
+ }
217
+ /**
218
+ * Resolve the dev-database connection for one tool call.
219
+ *
220
+ * Returns `db: null` (never throws) when no connection is configured; callers
221
+ * must treat that as "no runtime database" and return a static-only envelope.
222
+ * A thrown connect error is propagated to the caller, which converts it into
223
+ * a diagnostic envelope — it must never reach the MCP transport.
224
+ */
225
+ async function resolveRuntimeConnection(args = {}) {
226
+ const argUrl = args.dbUrl?.trim();
227
+ if (argUrl && !isMemoryDatabaseUrl(argUrl)) {
228
+ const databaseType = toRuntimeDatabaseType(inferDatabaseType(argUrl, args.dbType));
229
+ return {
230
+ db: await getDatabaseInstance(args.projectPath, {
231
+ type: databaseType,
232
+ url: normalizeDatabaseUrl(argUrl, args.projectPath)
233
+ }),
234
+ source: "argument",
235
+ displayUrl: redactConnectionString(argUrl),
236
+ databaseType
237
+ };
238
+ }
239
+ const envUrl = process.env.SMRT_DEV_DB_URL?.trim();
240
+ if (envUrl && !isMemoryDatabaseUrl(envUrl)) {
241
+ const databaseType = toRuntimeDatabaseType(inferDatabaseType(envUrl, args.dbType));
242
+ return {
243
+ db: await getDatabaseInstance(args.projectPath, {
244
+ type: databaseType,
245
+ url: normalizeDatabaseUrl(envUrl, args.projectPath)
246
+ }),
247
+ source: "environment",
248
+ displayUrl: redactConnectionString(envUrl),
249
+ databaseType
250
+ };
251
+ }
252
+ const config = await loadCliDatabaseConfig(args.projectPath);
253
+ const configUrl = config?.database?.url?.trim();
254
+ if (configUrl && !isMemoryDatabaseUrl(configUrl)) {
255
+ const databaseType = toRuntimeDatabaseType(config.database?.type || inferDatabaseType(configUrl, args.dbType));
256
+ return {
257
+ db: await getDatabaseInstance(args.projectPath, {
258
+ type: databaseType,
259
+ url: normalizeDatabaseUrl(configUrl, args.projectPath)
260
+ }),
261
+ source: "config",
262
+ displayUrl: redactConnectionString(configUrl),
263
+ databaseType
264
+ };
265
+ }
266
+ return {
267
+ db: null,
268
+ source: "none",
269
+ displayUrl: "",
270
+ databaseType: null
271
+ };
272
+ }
273
+ async function loadCliDatabaseConfig(projectPath) {
274
+ try {
275
+ const configModule = await importProjectRuntimeModule(projectPath, "@happyvertical/smrt-config");
276
+ if (typeof configModule.loadConfig !== "function" || typeof configModule.getPackageConfig !== "function") throw new ProjectRuntimeResolutionError("@happyvertical/smrt-config");
277
+ await configModule.loadConfig({
278
+ searchFrom: projectRuntimeRoot(projectPath),
279
+ cache: false
280
+ });
281
+ const database = configModule.getPackageConfig("cli", DEFAULT_CLI_DATABASE).database;
282
+ if (database && typeof database.url === "string") return { database };
283
+ return {};
284
+ } catch (error) {
285
+ if (error instanceof ProjectRuntimeResolutionError) throw error;
286
+ return {};
287
+ }
288
+ }
289
+ async function getDatabaseInstance(projectPath, options) {
290
+ const { sql } = await loadProjectRuntime(projectPath);
291
+ if (typeof sql.getDatabase !== "function") throw new ProjectRuntimeResolutionError("@happyvertical/sql");
292
+ return sql.getDatabase(options);
293
+ }
294
+ /**
295
+ * Best-effort close of a resolved connection. Never throws; diagnostics must
296
+ * not fail because cleanup hiccuped.
297
+ */
298
+ async function closeRuntimeConnection(db) {
299
+ if (!db || typeof db !== "object") return;
300
+ const closeable = db;
301
+ const close = closeable.close ?? closeable.client?.end ?? closeable.client?.close;
302
+ if (typeof close !== "function") return;
303
+ try {
304
+ await close.call(closeable.close ? closeable : closeable.client);
305
+ } catch {}
306
+ }
307
+ //#endregion
308
+ //#region src/tools/runtime/tools.ts
309
+ /**
310
+ * Runtime diagnostics tools (#1824): read-only views over a project's dev
311
+ * database `_smrt_*` system tables, powered by the shared SELECT-only
312
+ * system-diagnostics reader in `@happyvertical/smrt-core`.
313
+ *
314
+ * Contract:
315
+ * - **Optional connection.** No configured connection returns a successful
316
+ * static-only envelope — the server always starts and static tools are
317
+ * unaffected. A live connection is opened lazily per call and closed in
318
+ * `finally`; nothing is cached across calls.
319
+ * - **Read-only.** Every underlying statement is a bounded SELECT; the reader
320
+ * never selects sensitive columns (job payloads/results, schedule
321
+ * `agentConfig`/`methodArgs`, dispatch `payload`/`metadata`).
322
+ * - **Provenance-labeled.** Live results carry `provenance: 'runtime (live DB)'`;
323
+ * static-only results carry `provenance: 'static'` — agents must never
324
+ * conflate runtime facts with declared/manifest facts.
325
+ * - **Fail-safe.** A connect/read error becomes a diagnostic envelope; it must
326
+ * never reach the MCP transport and never includes raw driver text or URLs.
327
+ */
328
+ var tools_exports = /* @__PURE__ */ __exportAll({
329
+ RUNTIME_PROVENANCE: () => RUNTIME_PROVENANCE,
330
+ STATIC_PROVENANCE: () => STATIC_PROVENANCE,
331
+ runtimeDispatchHealth: () => runtimeDispatchHealth,
332
+ runtimeJobHealth: () => runtimeJobHealth,
333
+ runtimeMigrationStatus: () => runtimeMigrationStatus,
334
+ runtimeRecentChanges: () => runtimeRecentChanges,
335
+ runtimeRegistryDrift: () => runtimeRegistryDrift,
336
+ runtimeScheduleHealth: () => runtimeScheduleHealth,
337
+ withRuntimeConnection: () => withRuntimeConnection,
338
+ withRuntimeSetup: () => withRuntimeSetup
339
+ });
340
+ /** Provenance labels separating runtime facts from static/declared facts. */
341
+ var RUNTIME_PROVENANCE = "runtime (live DB)";
342
+ var STATIC_PROVENANCE = "static";
343
+ async function projectRead(args, name, db, options) {
344
+ const core = await importProjectRuntimeModule(args.projectPath, "@happyvertical/smrt-core");
345
+ if (typeof core[name] !== "function") throw new ProjectRuntimeResolutionError("@happyvertical/smrt-core");
346
+ return core[name](db, options);
347
+ }
348
+ /**
349
+ * Serialize resolve → read → close per connection target so overlapping tool
350
+ * calls never close a shared cached handle out from under each other.
351
+ *
352
+ * `@happyvertical/sql`'s `getDatabase` returns a cached handle per URL (no
353
+ * opt-out in its public API). `closeRuntimeConnection` only calls the handle's
354
+ * own `close`/`end`, but the SDK wraps those so a close also evicts the handle
355
+ * from its connection cache. Two concurrent diagnostics calls resolving the
356
+ * same URL would therefore share one handle, with the first finisher closing
357
+ * it mid-read for the second. A per-key promise chain keeps each call's
358
+ * lifecycle private: every call resolves its own view of the connection,
359
+ * performs its read, and only then closes — the next queued call re-resolves
360
+ * a fresh handle.
361
+ *
362
+ * The key is a digest of the resolved target, never the raw URL, so a
363
+ * credential-bearing connection string is not retained in this map.
364
+ */
365
+ var runtimeReadQueues = /* @__PURE__ */ new Map();
366
+ function connectionQueueKey(args) {
367
+ const target = args.dbUrl?.trim() || process.env.SMRT_DEV_DB_URL?.trim() || "cli.config";
368
+ return createHash("sha256").update(target).digest("hex");
369
+ }
370
+ async function enqueueRuntimeRead(key, operation) {
371
+ const run = (runtimeReadQueues.get(key) ?? Promise.resolve()).then(operation, operation);
372
+ const tail = run.catch(() => void 0);
373
+ runtimeReadQueues.set(key, tail);
374
+ tail.then(() => {
375
+ if (runtimeReadQueues.get(key) === tail) runtimeReadQueues.delete(key);
376
+ });
377
+ return run;
378
+ }
379
+ /**
380
+ * Run one read against the optional runtime connection, mapping every outcome
381
+ * to a successful MCP envelope:
382
+ *
383
+ * - no connection configured → static-only envelope (`connected: false`)
384
+ * - connect failure → static envelope with a safe diagnostic
385
+ * - read failure → connected envelope with a safe diagnostic
386
+ * - success → live result under `provenance: 'runtime (live DB)'`; a
387
+ * category-unavailable reader result keeps its `available: false` data and
388
+ * surfaces its message as a diagnostic
389
+ */
390
+ async function withRuntimeConnection(args, read, staticHint) {
391
+ return enqueueRuntimeRead(connectionQueueKey(args), () => runWithRuntimeConnection(args, read, staticHint));
392
+ }
393
+ async function runWithRuntimeConnection(args, read, staticHint) {
394
+ let resolved;
395
+ try {
396
+ resolved = await resolveRuntimeConnection(args);
397
+ } catch (error) {
398
+ return {
399
+ ok: true,
400
+ coverage: null,
401
+ diagnostics: [{
402
+ severity: "warning",
403
+ code: error instanceof ProjectRuntimeResolutionError ? error.code : "runtime_connection_error",
404
+ message: safeErrorMessage(error)
405
+ }],
406
+ data: {
407
+ provenance: STATIC_PROVENANCE,
408
+ connected: false
409
+ }
410
+ };
411
+ }
412
+ if (!resolved.db) return {
413
+ ok: true,
414
+ coverage: null,
415
+ diagnostics: [{
416
+ severity: "info",
417
+ code: "runtime_connection_unavailable",
418
+ message: `No runtime dev database configured (set SMRT_DEV_DB_URL or cli.database); returning static-only result: ${staticHint}. Static tools are unaffected.`
419
+ }],
420
+ data: {
421
+ provenance: STATIC_PROVENANCE,
422
+ connected: false
423
+ }
424
+ };
425
+ const { db, source, displayUrl, databaseType } = resolved;
426
+ try {
427
+ const { data, diagnostics } = await read(db);
428
+ return {
429
+ ok: true,
430
+ coverage: null,
431
+ diagnostics,
432
+ data: {
433
+ provenance: RUNTIME_PROVENANCE,
434
+ connected: true,
435
+ connectionSource: source,
436
+ databaseType,
437
+ displayUrl,
438
+ ...data
439
+ }
440
+ };
441
+ } catch (error) {
442
+ return {
443
+ ok: true,
444
+ coverage: null,
445
+ diagnostics: [{
446
+ severity: "warning",
447
+ code: error instanceof ProjectRuntimeResolutionError ? error.code : "runtime_read_error",
448
+ message: safeErrorMessage(error)
449
+ }],
450
+ data: error instanceof ProjectRuntimeResolutionError ? {
451
+ provenance: STATIC_PROVENANCE,
452
+ connected: false
453
+ } : {
454
+ provenance: RUNTIME_PROVENANCE,
455
+ connected: true,
456
+ connectionSource: source,
457
+ databaseType,
458
+ displayUrl
459
+ }
460
+ };
461
+ } finally {
462
+ await closeRuntimeConnection(db);
463
+ }
464
+ }
465
+ /**
466
+ * Stored error columns (`error_message`, `last_error`) are free text written
467
+ * at failure time and routinely quote connection URLs or credentials. Every
468
+ * string in a live result passes through {@link redactConnectionString}
469
+ * before it reaches an MCP client; structure and non-string values are kept.
470
+ */
471
+ function redactStrings(value) {
472
+ if (typeof value === "string") return redactConnectionString(value);
473
+ if (Array.isArray(value)) return value.map((item) => redactStrings(item));
474
+ if (value !== null && typeof value === "object") {
475
+ const out = {};
476
+ for (const [key, item] of Object.entries(value)) out[key] = redactStrings(item);
477
+ return out;
478
+ }
479
+ return value;
480
+ }
481
+ /** Convert a reader result into envelope data + diagnostics. */
482
+ function toEnvelopeParts(rawResult) {
483
+ const result = redactStrings(rawResult);
484
+ if (result !== null && typeof result === "object" && "available" in result && result.available === false) {
485
+ const unavailable = result;
486
+ const { message, detail, ...rest } = unavailable;
487
+ return {
488
+ data: rest,
489
+ diagnostics: [{
490
+ severity: unavailable.reason === "retired" ? "info" : "warning",
491
+ code: `category_unavailable_${String(unavailable.reason).replace(/-/g, "_")}`,
492
+ message: detail ? `${String(message)} Cause: ${detail}` : String(message)
493
+ }]
494
+ };
495
+ }
496
+ const data = result;
497
+ const diagnostics = [];
498
+ const behind = Array.isArray(data.schemaBehind) ? data.schemaBehind : [];
499
+ for (const entry of behind) {
500
+ const columns = Array.isArray(entry.missingColumns) ? entry.missingColumns.map(String) : [];
501
+ if (columns.length === 0) continue;
502
+ diagnostics.push({
503
+ severity: "info",
504
+ code: "schema_behind",
505
+ message: `${String(entry.tableName ?? "table")} predates ${columns.join(", ")}; those fields are reported as null. Run db:migrate to bring the system tables current.`
506
+ });
507
+ }
508
+ return {
509
+ data,
510
+ diagnostics
511
+ };
512
+ }
513
+ function readToParts(read) {
514
+ return Promise.resolve(read).then((result) => toEnvelopeParts(result));
515
+ }
516
+ async function runtimeMigrationStatus(args = {}) {
517
+ const { limit, ...connectionArgs } = args;
518
+ return withRuntimeConnection(connectionArgs, (db) => projectRead(connectionArgs, "readMigrationStatus", db, { limit }).then(readToParts), "no migration status — the manifest still reports the declared schema");
519
+ }
520
+ async function runtimeJobHealth(args = {}) {
521
+ const { limit, ...connectionArgs } = args;
522
+ return withRuntimeConnection(connectionArgs, (db) => projectRead(connectionArgs, "readJobHealth", db, { limit }).then(readToParts), "no job health snapshot — the manifest still reports declared job queues");
523
+ }
524
+ async function runtimeScheduleHealth(args = {}) {
525
+ const { limit, ...connectionArgs } = args;
526
+ return withRuntimeConnection(connectionArgs, (db) => projectRead(connectionArgs, "readScheduleHealth", db, { limit }).then(readToParts), "no schedule health snapshot — the manifest still reports declared schedules");
527
+ }
528
+ async function runtimeDispatchHealth(args = {}) {
529
+ const { limit, ...connectionArgs } = args;
530
+ return withRuntimeConnection(connectionArgs, (db) => projectRead(connectionArgs, "readDispatchHealth", db, { limit }).then(readToParts), "no dispatch health snapshot — the manifest still reports declared dispatch topology");
531
+ }
532
+ async function runtimeRecentChanges(args = {}) {
533
+ const { since, tables, tenantId, limit, ...connectionArgs } = args;
534
+ return withRuntimeConnection(connectionArgs, (db) => projectRead(connectionArgs, "readRecentChanges", db, {
535
+ since,
536
+ tables,
537
+ tenantId,
538
+ limit
539
+ }).then(readToParts), "no recent changes — static knowledge artifacts are unchanged");
540
+ }
541
+ async function runtimeRegistryDrift(args = {}) {
542
+ return withRuntimeConnection(args, (db) => projectRead(args, "readRegistryDrift", db).then(readToParts), "no registry drift report — _smrt_registry is retired; declared objects come from the manifest");
543
+ }
544
+ /** Setup failures are safe static envelopes, never empty live/booted snapshots. */
545
+ async function withRuntimeSetup(operation) {
546
+ try {
547
+ return await operation();
548
+ } catch (error) {
549
+ if (!(error instanceof ProjectRuntimeResolutionError)) throw error;
550
+ return {
551
+ ok: true,
552
+ coverage: null,
553
+ diagnostics: [{
554
+ severity: "warning",
555
+ code: error.code,
556
+ message: error.message
557
+ }],
558
+ data: {
559
+ provenance: STATIC_PROVENANCE,
560
+ connected: false
561
+ }
562
+ };
563
+ }
564
+ }
565
+ //#endregion
566
+ 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 };
567
+
568
+ //# sourceMappingURL=tools-ma2JFPVY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools-ma2JFPVY.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 (`getPackageConfig('cli', ...)`\n * from `@happyvertical/smrt-config`) → `database.{type,url}`\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 const databaseType = toRuntimeDatabaseType(\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',\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}\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 }>(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 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 or cli.database); 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":";;;;;;;;;;;;;;;;;;;AAMA,IAAI;AAEJ,IAAa,gCAAb,cAAmD,MAAM;CACvD;CACA,YAAY,WAAmB,OAAO,kCAAkC;EACtE,MACE,SAAS,6BACL,wIACA,kDAAkD,UAAU,2GAClE;EACA,KAAK,OAAO;EACZ,KAAK,OAAO;CACd;AACF;AAEA,SAAgB,mBAAmB,aAA8B;CAC/D,MAAM,OAAO,QAAQ,eAAe,QAAQ,IAAI,CAAC;CACjD,IAAI;EACF,OAAO,aAAa,IAAI;CAC1B,QAAQ;EACN,OAAO;CACT;AACF;AAUA,SAAS,cAAc,aAAqB,SAAyB;CACnE,MAAM,MAAM,KAAK,MAAM,aAAa,aAAa,MAAM,CAAC;CACxD,IAAI,SAAS,IAAI,UAAU;CAC3B,IACE,WAAW,KAAA,KACX,YAAY,OACZ,OAAO,IAAI,YAAY,UAEvB,SAAS,IAAI;CACf,OAAO,UAAU,OAAO,WAAW,UACjC,SAAS,OAAO,UAAU,OAAO,QAAQ,OAAO;CAClD,IAAI,OAAO,WAAW,YAAY,CAAC,OAAO,WAAW,IAAI,GACvD,MAAM,IAAI,MAAM,4BAA4B;CAC9C,MAAM,OAAO,QAAQ,WAAW;CAChC,MAAM,QAAQ,QAAQ,MAAM,MAAM;CAClC,MAAM,MAAM,SAAS,MAAM,KAAK;CAChC,IAAI,IAAI,WAAW,IAAI,KAAK,WAAW,GAAG,GACxC,MAAM,IAAI,MAAM,wBAAwB;CAC1C,OAAO;AACT;AAEA,SAAgB,2BACd,aACA,WACQ;CACR,MAAM,OAAO,mBAAmB,WAAW;CAC3C,IAAI;CACJ,IAAI;EACF,MAAM,WAAW,gBACf,4BACA,cAAc,KAAK,MAAM,cAAc,CAAC,CAC1C;EACA,IAAI,CAAC,UAAU,MAAM,IAAI,MAAM,iBAAiB;EAChD,IACE,cAAc,8BACd,UAAU,WAAW,2BAA2B,GAChD;GACA,MAAM,UAAU,UAAU,MAAM,EAAiC;GACjE,QAAQ,cAAc,UAAU,UAAU,IAAI,YAAY,GAAG;EAC/D,OAAO;GACL,MAAM,iBAAiB,gBACrB,WACA,cAAc,QAAQ,CACxB;GACA,IAAI,CAAC,gBAAgB,MAAM,IAAI,MAAM,4BAA4B;GACjE,QAAQ,cAAc,gBAAgB,GAAG;EAC3C;CACF,QAAQ;EACN,MAAM,IAAI,8BAA8B,SAAS;CACnD;CACA,OAAO;AACT;AAEA,eAAsB,2BACpB,aACA,WACY;CACZ,MAAM,OAAO,mBAAmB,WAAW;CAC3C,IAAI,mBAAmB,oBAAoB,MACzC,MAAM,IAAI,8BACR,WACA,0BACF;CAEF,MAAM,QAAQ,2BAA2B,MAAM,SAAS;CAExD,kBAAkB;CAClB,IAAI;EACF,OAAQ,MAAM,OAAO,cAAc,KAAK,CAAC,CAAC;CAC5C,QAAQ;EAEN,MAAM,IAAI,8BAA8B,SAAS;CACnD;AACF;AAEA,IAAa,mBAAmB,gBAC9B,2BACE,aACA,0BACF;AAEF,eAAsB,mBAAmB,aAAsB;CAK7D,OAAO;EAAE,MAAA,MAJU,gBAAgB,WAAW;EAI/B,KAAA,MAHG,2BAEhB,aAAa,oBAAoB;EACf,aAAa,mBAAmB,WAAW;CAAE;AACnE;;;;;;;;;;;;;;;;;;;;;;;AC5FA,IAAM,yBAAyD;CAC7D;CACA;CACA;AACF;AAEA,SAAS,sBAAsB,OAA6C;CAC1E,OAAQ,uBAA6C,SAAS,KAAK;AACrE;;;;;;AAqCA,IAAM,yBAAyB;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,wBAAwB,KAAqB;CACpD,OAAO,IAAI,YAAY,CAAC,CAAC,QAAQ,SAAS,EAAE;AAC9C;AAEA,IAAM,8BAA8B,IAAI,IACtC,uBAAuB,IAAI,uBAAuB,CACpD;AAEA,IAAM,uBAAuB,EAC3B,UAAU;CAAE,MAAM;CAAU,KAAK;AAAW,EAC9C;;;;;;;;;;;;;AAcA,SAAgB,uBAAuB,OAAuB;CAC5D,IAAI,WAAW;CAEf,IAAI;EACF,MAAM,MAAM,IAAI,IAAI,KAAK;EACzB,IAAI,IAAI,UACN,IAAI,WAAW;EAEjB,KAAK,MAAM,OAAO,CAAC,GAAG,IAAI,aAAa,KAAK,CAAC,GAC3C,IAAI,4BAA4B,IAAI,wBAAwB,GAAG,CAAC,GAC9D,IAAI,aAAa,IAAI,KAAK,KAAK;EAGnC,WAAW,IAAI,SAAS;CAC1B,QAAQ;EACN,WAAW,MAAM,QAKf,oEACA,SACF;CACF;CAKA,WAAW,SAAS,QAClB,oFACA,MACF;CAKA,OAAO,SAAS,QACd,0DACC,OAAO,QAAgB,QACtB,4BAA4B,IAAI,wBAAwB,GAAG,CAAC,IACxD,GAAG,OAAO,OACV,KACR;AACF;;;;;AAMA,SAAgB,iBAAiB,OAAwB;CAGvD,OAAO,uBADL,iBAAiB,QAAQ,MAAM,UAAU,OAAO,SAAS,eAAe,CACrC;AACvC;;;;;;AAOA,SAAgB,sBAAsB,OAAoC;CACxE,MAAM,aAAa,MAAM,KAAK,CAAC,CAAC,YAAY;CAC5C,IAAI,sBAAsB,UAAU,GAClC,OAAO;CAET,MAAM,IAAI,MACR,sCAAsC,WAAW,wCACnD;AACF;;AAGA,SAAgB,kBAAkB,KAAa,MAAuB;CACpE,IAAI,QAAQ,KAAK,KAAK,CAAC,CAAC,SAAS,GAAG,OAAO;CAC3C,IAAI,mBAAmB,KAAK,GAAG,GAAG,OAAO;CACzC,IAAI,YAAY,KAAK,GAAG,GAAG,OAAO;CAClC,OAAO;AACT;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,oBAAoB,KAAsB;CACxD,OAAO,mCAAmC,KAAK,IAAI,KAAK,CAAC;AAC3D;AAEA,SAAgB,qBACd,KACA,aACQ;CACR,MAAM,QAAQ,0BAA0B,KAAK,IAAI,KAAK,CAAC;CACvD,IAAI,CAAC,OAAO,OAAO,IAAI,KAAK;CAC5B,MAAM,OAAO,MAAM;CACnB,IAAI,CAAC,MAAM,OAAO,IAAI,KAAK;CAI3B,OAAO,cAHU,KAAK,WAAW,GAAG,IAChC,OACA,QAAQ,mBAAmB,WAAW,GAAG,IAAI,CACpB,CAAC,CAAC;AACjC;;;;;;;;;AAUA,eAAsB,yBACpB,OAA4B,CAAC,GACO;CACpC,MAAM,SAAS,KAAK,OAAO,KAAK;CAChC,IAAI,UAAU,CAAC,oBAAoB,MAAM,GAAG;EAC1C,MAAM,eAAe,sBACnB,kBAAkB,QAAQ,KAAK,MAAM,CACvC;EAKA,OAAO;GACL,IAAA,MALe,oBAAoB,KAAK,aAAa;IACrD,MAAM;IACN,KAAK,qBAAqB,QAAQ,KAAK,WAAW;GACpD,CAAC;GAGC,QAAQ;GACR,YAAY,uBAAuB,MAAM;GACzC;EACF;CACF;CAEA,MAAM,SAAS,QAAQ,IAAI,iBAAiB,KAAK;CACjD,IAAI,UAAU,CAAC,oBAAoB,MAAM,GAAG;EAC1C,MAAM,eAAe,sBACnB,kBAAkB,QAAQ,KAAK,MAAM,CACvC;EAKA,OAAO;GACL,IAAA,MALe,oBAAoB,KAAK,aAAa;IACrD,MAAM;IACN,KAAK,qBAAqB,QAAQ,KAAK,WAAW;GACpD,CAAC;GAGC,QAAQ;GACR,YAAY,uBAAuB,MAAM;GACzC;EACF;CACF;CAEA,MAAM,SAAS,MAAM,sBAAsB,KAAK,WAAW;CAC3D,MAAM,YAAY,QAAQ,UAAU,KAAK,KAAK;CAC9C,IAAI,aAAa,CAAC,oBAAoB,SAAS,GAAG;EAChD,MAAM,eAAe,sBACnB,OAAO,UAAU,QAAQ,kBAAkB,WAAW,KAAK,MAAM,CACnE;EAKA,OAAO;GACL,IAAA,MALe,oBAAoB,KAAK,aAAa;IACrD,MAAM;IACN,KAAK,qBAAqB,WAAW,KAAK,WAAW;GACvD,CAAC;GAGC,QAAQ;GACR,YAAY,uBAAuB,SAAS;GAC5C;EACF;CACF;CAEA,OAAO;EAAE,IAAI;EAAM,QAAQ;EAAQ,YAAY;EAAI,cAAc;CAAK;AACxE;AAMA,eAAe,sBACb,aAC6B;CAC7B,IAAI;EAMF,MAAM,eAAe,MAAM,2BASxB,aAAa,4BAA4B;EAC5C,IACE,OAAO,aAAa,eAAe,cACnC,OAAO,aAAa,qBAAqB,YAEzC,MAAM,IAAI,8BAA8B,4BAA4B;EACtE,MAAM,aAAa,WAAW;GAC5B,YAAY,mBAAmB,WAAW;GAC1C,OAAO;EACT,CAAC;EAKD,MAAM,WAJS,aAAa,iBAC1B,OACA,oBAEgB,CAAA,CAA8B;EAChD,IAAI,YAAY,OAAO,SAAS,QAAQ,UACtC,OAAO,EAAE,SAAS;EAEpB,OAAO,CAAC;CACV,SAAS,OAAO;EACd,IAAI,iBAAiB,+BAA+B,MAAM;EAC1D,OAAO,CAAC;CACV;AACF;AAEA,eAAe,oBACb,aACA,SAIkB;CAClB,MAAM,EAAE,QAAQ,MAAM,mBAAmB,WAAW;CACpD,IAAI,OAAO,IAAI,gBAAgB,YAC7B,MAAM,IAAI,8BAA8B,oBAAoB;CAC9D,OAAO,IAAI,YAAY,OAAO;AAChC;;;;;AAcA,eAAsB,uBAAuB,IAA4B;CACvE,IAAI,CAAC,MAAM,OAAO,OAAO,UAAU;CACnC,MAAM,YAAY;CAClB,MAAM,QACJ,UAAU,SAAS,UAAU,QAAQ,OAAO,UAAU,QAAQ;CAChE,IAAI,OAAO,UAAU,YAAY;CACjC,IAAI;EACF,MAAM,MAAM,KAAK,UAAU,QAAQ,YAAY,UAAU,MAAM;CACjE,QAAQ,CAER;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtVA,IAAa,qBAAqB;AAClC,IAAa,oBAAoB;AAsBjC,eAAe,YACb,MACA,MACA,IACA,SACA;CACA,MAAM,OAAO,MAAM,2BAEjB,KAAK,aAAa,0BAA0B;CAC9C,IAAI,OAAO,KAAK,UAAU,YACxB,MAAM,IAAI,8BAA8B,0BAA0B;CACpE,OAAO,KAAK,KAAK,CAAC,IAAI,OAAO;AAC/B;;;;;;;;;;;;;;;;;;AAmBA,IAAM,oCAAoB,IAAI,IAA8B;AAE5D,SAAS,mBAAmB,MAAmC;CAC7D,MAAM,SACJ,KAAK,OAAO,KAAK,KAAK,QAAQ,IAAI,iBAAiB,KAAK,KAAK;CAC/D,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO,KAAK;AACzD;AAEA,eAAe,mBACb,KACA,WACY;CAEZ,MAAM,OADW,kBAAkB,IAAI,GAAG,KAAK,QAAQ,QAAQ,EAAA,CAC1C,KAAK,WAAW,SAAS;CAG9C,MAAM,OAAO,IAAI,YAAY,KAAA,CAAS;CACtC,kBAAkB,IAAI,KAAK,IAAI;CAC/B,KAAU,WAAW;EACnB,IAAI,kBAAkB,IAAI,GAAG,MAAM,MACjC,kBAAkB,OAAO,GAAG;CAEhC,CAAC;CACD,OAAO;AACT;;;;;;;;;;;;AAaA,eAAsB,sBACpB,MACA,MACA,YAC8B;CAI9B,OAAO,mBAAmB,mBAAmB,IAAI,SAC/C,yBAAyB,MAAM,MAAM,UAAU,CACjD;AACF;AAEA,eAAe,yBACb,MACA,MACA,YAC8B;CAC9B,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,yBAAyB,IAAI;CAChD,SAAS,OAAO;EACd,OAAO;GACL,IAAI;GACJ,UAAU;GACV,aAAa,CACX;IACE,UAAU;IACV,MACE,iBAAiB,gCACb,MAAM,OACN;IACN,SAAS,iBAAiB,KAAK;GACjC,CACF;GACA,MAAM;IACJ,YAAY;IACZ,WAAW;GACb;EACF;CACF;CAEA,IAAI,CAAC,SAAS,IACZ,OAAO;EACL,IAAI;EACJ,UAAU;EACV,aAAa,CACX;GACE,UAAU;GACV,MAAM;GACN,SAAS,2GAA2G,WAAW;EACjI,CACF;EACA,MAAM;GACJ,YAAY;GACZ,WAAW;EACb;CACF;CAGF,MAAM,EAAE,IAAI,QAAQ,YAAY,iBAAiB;CACjD,IAAI;EACF,MAAM,EAAE,MAAM,gBAAgB,MAAM,KAAK,EAAE;EAC3C,OAAO;GACL,IAAI;GACJ,UAAU;GACV;GACA,MAAM;IACJ,YAAY;IACZ,WAAW;IACX,kBAAkB;IAClB;IACA;IACA,GAAG;GACL;EACF;CACF,SAAS,OAAO;EACd,OAAO;GACL,IAAI;GACJ,UAAU;GACV,aAAa,CACX;IACE,UAAU;IACV,MACE,iBAAiB,gCACb,MAAM,OACN;IACN,SAAS,iBAAiB,KAAK;GACjC,CACF;GACA,MACE,iBAAiB,gCACb;IACE,YAAY;IACZ,WAAW;GACb,IACA;IACE,YAAY;IACZ,WAAW;IACX,kBAAkB;IAClB;IACA;GACF;EACR;CACF,UAAU;EACR,MAAM,uBAAuB,EAAE;CACjC;AACF;;;;;;;AAQA,SAAS,cAAiB,OAAa;CACrC,IAAI,OAAO,UAAU,UACnB,OAAO,uBAAuB,KAAK;CAErC,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,MAAM,KAAK,SAAS,cAAc,IAAI,CAAC;CAEhD,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;EAC/C,MAAM,MAA+B,CAAC;EACtC,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAC/B,KACF,GACE,IAAI,OAAO,cAAc,IAAI;EAE/B,OAAO;CACT;CACA,OAAO;AACT;;AAGA,SAAS,gBAAgB,WAGvB;CACA,MAAM,SAAS,cAAc,SAAS;CACtC,IACE,WAAW,QACX,OAAO,WAAW,YAClB,eAAe,UACd,OAAkC,cAAc,OACjD;EACA,MAAM,cAAc;EAOpB,MAAM,EAAE,SAAS,QAAQ,GAAG,SAAS;EAGrC,OAAO;GACL,MAAM;GACN,aAAa,CACX;IACE,UALJ,YAAY,WAAW,YAAY,SAAS;IAMxC,MAAM,wBAAwB,OAAO,YAAY,MAAM,CAAC,CAAC,QAAQ,MAAM,GAAG;IAG1E,SAAS,SACL,GAAG,OAAO,OAAO,EAAE,UAAU,WAC7B,OAAO,OAAO;GACpB,CACF;EACF;CACF;CACA,MAAM,OAAO;CACb,MAAM,cAAmC,CAAC;CAE1C,MAAM,SAAS,MAAM,QAAQ,KAAK,YAAY,IAAI,KAAK,eAAe,CAAC;CACvE,KAAK,MAAM,SAAS,QAGhB;EACF,MAAM,UAAU,MAAM,QAAQ,MAAM,cAAc,IAC9C,MAAM,eAAe,IAAI,MAAM,IAC/B,CAAC;EACL,IAAI,QAAQ,WAAW,GAAG;EAC1B,YAAY,KAAK;GACf,UAAU;GACV,MAAM;GACN,SAAS,GAAG,OAAO,MAAM,aAAa,OAAO,EAAE,YAAY,QAAQ,KAAK,IAAI,EAAE;EAChF,CAAC;CACH;CACA,OAAO;EAAE;EAAM;CAAY;AAC7B;AAEA,SAAS,YAAY,MAA0C;CAC7D,OAAO,QAAQ,QAAQ,IAAI,CAAC,CAAC,MAAM,WAAW,gBAAgB,MAAM,CAAC;AACvE;AAOA,eAAsB,uBACpB,OAA4B,CAAC,GACC;CAC9B,MAAM,EAAE,OAAO,GAAG,mBAAmB;CACrC,OAAO,sBACL,iBACC,OACC,YAAY,gBAAgB,uBAAuB,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,KAChE,WACF,GACF,sEACF;AACF;AAOA,eAAsB,iBACpB,OAAsB,CAAC,GACO;CAC9B,MAAM,EAAE,OAAO,GAAG,mBAAmB;CACrC,OAAO,sBACL,iBACC,OACC,YAAY,gBAAgB,iBAAiB,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,KAC1D,WACF,GACF,yEACF;AACF;AAOA,eAAsB,sBACpB,OAA2B,CAAC,GACE;CAC9B,MAAM,EAAE,OAAO,GAAG,mBAAmB;CACrC,OAAO,sBACL,iBACC,OACC,YAAY,gBAAgB,sBAAsB,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,KAC/D,WACF,GACF,6EACF;AACF;AAOA,eAAsB,sBACpB,OAA2B,CAAC,GACE;CAC9B,MAAM,EAAE,OAAO,GAAG,mBAAmB;CACrC,OAAO,sBACL,iBACC,OACC,YAAY,gBAAgB,sBAAsB,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,KAC/D,WACF,GACF,qFACF;AACF;AAaA,eAAsB,qBACpB,OAA0B,CAAC,GACG;CAC9B,MAAM,EAAE,OAAO,QAAQ,UAAU,OAAO,GAAG,mBAAmB;CAC9D,OAAO,sBACL,iBACC,OACC,YAAY,gBAAgB,qBAAqB,IAAI;EACnD;EACA;EACA;EACA;CACF,CAAC,CAAC,CAAC,KAAK,WAAW,GACrB,8DACF;AACF;AAEA,eAAsB,qBACpB,OAA4B,CAAC,GACC;CAC9B,OAAO,sBACL,OACC,OAAO,YAAY,MAAM,qBAAqB,EAAE,CAAC,CAAC,KAAK,WAAW,GACnE,+FACF;AACF;;AAGA,eAAsB,iBACpB,WAC8B;CAC9B,IAAI;EACF,OAAO,MAAM,UAAU;CACzB,SAAS,OAAO;EACd,IAAI,EAAE,iBAAiB,gCAAgC,MAAM;EAC7D,OAAO;GACL,IAAI;GACJ,UAAU;GACV,aAAa,CACX;IAAE,UAAU;IAAW,MAAM,MAAM;IAAM,SAAS,MAAM;GAAQ,CAClE;GACA,MAAM;IAAE,YAAY;IAAmB,WAAW;GAAM;EAC1D;CACF;AACF"}