@happyvertical/smrt-dev-mcp 0.60.9 → 0.60.11

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 (60) hide show
  1. package/AGENTS.md +33 -1
  2. package/README.md +17 -1
  3. package/agents/agent-skills.md +16 -0
  4. package/agents/intent-duplicates.md +26 -0
  5. package/dist/agent-skills.d.ts +2 -0
  6. package/dist/agent-skills.d.ts.map +1 -1
  7. package/dist/agent-skills.js +61 -0
  8. package/dist/agent-skills.js.map +1 -0
  9. package/dist/dev-plane.js +2 -2
  10. package/dist/{http-CCiO7qQW.js → http-BqKiyqgN.js} +2 -2
  11. package/dist/{http-CCiO7qQW.js.map → http-BqKiyqgN.js.map} +1 -1
  12. package/dist/index.js +13 -445
  13. package/dist/index.js.map +1 -1
  14. package/dist/knowledge/index.d.ts +1 -1
  15. package/dist/knowledge/index.d.ts.map +1 -1
  16. package/dist/knowledge/intents/audit-trail.md +63 -0
  17. package/dist/knowledge/intents/auth-and-users.md +67 -0
  18. package/dist/knowledge/intents/background-work.md +72 -0
  19. package/dist/knowledge/intents/feature-flags-and-entitlements.md +60 -0
  20. package/dist/knowledge/intents/file-and-asset-storage.md +71 -0
  21. package/dist/knowledge/intents/human-approvals.md +56 -0
  22. package/dist/knowledge/intents/index.md +41 -0
  23. package/dist/knowledge/intents/money-and-rates.md +65 -0
  24. package/dist/knowledge/intents/outbound-messages.md +67 -0
  25. package/dist/knowledge/intents/prompts-and-ai-operations.md +72 -0
  26. package/dist/knowledge/intents/relationships-and-trees.md +56 -0
  27. package/dist/knowledge/intents/scheduled-runs.md +61 -0
  28. package/dist/knowledge/intents/search-facts-knowledge.md +68 -0
  29. package/dist/knowledge/intents/secrets-and-config.md +66 -0
  30. package/dist/knowledge/intents/tenant-scoping.md +63 -0
  31. package/dist/knowledge/intents/webhooks-and-events.md +71 -0
  32. package/dist/knowledge/okf-bundle.d.ts +61 -0
  33. package/dist/knowledge/okf-bundle.d.ts.map +1 -0
  34. package/dist/{knowledge-DqoDX-lZ.js → knowledge-DUiMUjEg.js} +218 -3
  35. package/dist/knowledge-DUiMUjEg.js.map +1 -0
  36. package/dist/knowledge.js +2 -2
  37. package/dist/review-cli-DJIWUlFp.js +75 -0
  38. package/dist/review-cli-DJIWUlFp.js.map +1 -0
  39. package/dist/review-smrt-project-ByEwPcor.js +774 -0
  40. package/dist/review-smrt-project-ByEwPcor.js.map +1 -0
  41. package/dist/smrt-knowledge.okf.md +65 -0
  42. package/dist/{tool-catalog-RHq7nyCm.js → tool-catalog-BhnSCF8g.js} +11 -2
  43. package/dist/tool-catalog-BhnSCF8g.js.map +1 -0
  44. package/dist/tool-catalog.d.ts.map +1 -1
  45. package/dist/tools/intent-detector.d.ts +57 -0
  46. package/dist/tools/intent-detector.d.ts.map +1 -0
  47. package/dist/tools/intent-index.d.ts +40 -0
  48. package/dist/tools/intent-index.d.ts.map +1 -0
  49. package/dist/tools/review-cli.d.ts +40 -0
  50. package/dist/tools/review-cli.d.ts.map +1 -0
  51. package/dist/tools/review-smrt-project.d.ts +25 -0
  52. package/dist/tools/review-smrt-project.d.ts.map +1 -1
  53. package/package.json +10 -6
  54. package/plugin.json +1 -1
  55. package/skills/smrt-catalog/SKILL.md +42 -0
  56. package/skills/smrt-catalog/evals.json +28 -0
  57. package/skills/smrt-contribute/SKILL.md +73 -0
  58. package/skills/smrt-contribute/evals.json +18 -0
  59. package/dist/knowledge-DqoDX-lZ.js.map +0 -1
  60. package/dist/tool-catalog-RHq7nyCm.js.map +0 -1
package/AGENTS.md CHANGED
@@ -9,7 +9,7 @@ deterministic SMRT ecosystem knowledge, and portable agent workflows.
9
9
  |------|---------|
10
10
  | `generate-smrt-class` | Generates `@smrt()` class with fields, decorator config, imports |
11
11
  | `introspect-project` | Scans project directory for SMRT objects; compact summary by default, `detail: 'full'` for field/schema/method detail |
12
- | `review-smrt-project` | Advisory downstream ecosystem alignment review for dependencies, storage, app shell, auth/tenancy, and manifest generation |
12
+ | `review-smrt-project` | Advisory downstream ecosystem alignment review for dependencies, storage, app shell, auth/tenancy, manifest generation, and intent-driven hand-rolled-equivalent detection (#4690) |
13
13
  | `reflect-knowledge` | Reports deterministic SMRT + HappyVertical SDK knowledge coverage and freshness |
14
14
  | `reflect-domain-knowledge` | Reports downstream/domain artifact coverage and freshness |
15
15
  | `check-knowledge-freshness` | Runs deterministic agent-doc and stale-reference checks |
@@ -33,6 +33,14 @@ deterministic SMRT ecosystem knowledge, and portable agent workflows.
33
33
  README parameter tables to the exported `TOOLS` definitions. Add or change a
34
34
  tool schema and its authored documentation in the same commit.
35
35
 
36
+ ## Intent-driven duplicate detection (#4690)
37
+
38
+ `review-smrt-project` / `smrt-dev-mcp review` flag hand-rolled equivalents of
39
+ smrt packages from the intent index. The rules (`regex:` tokens become `RegExp`)
40
+ come from the full index packaged in this release, never from the reviewed tree;
41
+ `intentIndexPath` / `--intent-index` is caller-trusted. Details, token kinds and
42
+ the CLI/workflow contract: [agents/intent-duplicates.md](agents/intent-duplicates.md).
43
+
36
44
  ## Knowledge Discovery (#2143)
37
45
 
38
46
  Discovery reads the workspace globs instead of assuming `<root>/packages`:
@@ -104,6 +112,18 @@ otherwise emitted docs read `/AGENTS.md` and changed-file matching compares
104
112
  against a leading `/` and selects nothing. The root owns any changed path no
105
113
  member package owns, mirroring the member-excluded scan.
106
114
 
115
+ ### OKF component pages (#4686)
116
+
117
+ `pnpm knowledge:okf` (after `pnpm build`) writes `docs/knowledge/components/<unscoped-name>.md` per
118
+ workspace package from this index; never hand-edit them. `source_revision` is a `sha256` of the page's
119
+ inputs (manifest slice, package AGENTS/module-doc hashes, models, MCP tools, `agentSurface`), not a commit.
120
+ `knowledge:check --strict` fails on stale, edited, missing or extra pages and on dangling `/…md` links in
121
+ hand-authored pages. Details: header of `scripts/knowledge-okf.ts`.
122
+
123
+ Consumer bundle (#4689): `okf-bundle.ts` defines the `.agents/smrt-knowledge/` contract `smrt docs:agents` writes;
124
+ `check-knowledge-freshness` reports its age (`okf-bundle-age` info, `okf-bundle-aged` past 90 days) and flags
125
+ `stale-okf-bundle` when a page's `package_version` no longer matches `node_modules` until `docs:agents` reruns.
126
+
107
127
  ## Agent Skills
108
128
 
109
129
  Bundled skills live under `skills/`, the Agent Plugins fixed discovery
@@ -115,7 +135,11 @@ Skill-aware harnesses can parse the frontmatter; other harnesses can ignore it.
115
135
  Agents should fetch it with `get-agent-skill`, then call `build-context` (`task: review`) for
116
136
  deterministic context, inspect the actual diff, and produce a findings-first
117
137
  review.
138
+ - `skills/smrt-catalog/SKILL.md` — catalog router skill (#4708), installed into consumers by `smrt docs:agents`.
139
+ - `skills/smrt-contribute/SKILL.md` — contributor skill (#4709) for forks of this repository; an identical copy is
140
+ checked in at the root `.agents/skills/smrt-contribute/SKILL.md` and a test fails on drift.
118
141
 
142
+ Trigger evals, the registry and install contract, and the drift rule: [agents/agent-skills.md](agents/agent-skills.md).
119
143
 
120
144
  ## Project runtime prerequisite
121
145
 
@@ -157,6 +181,12 @@ server is always launched from a repo that installs the package. Prefer an
157
181
  absolute `node /path/to/node_modules/@happyvertical/smrt-dev-mcp/dist/index.js`
158
182
  launcher or a small wrapper script with an absolute Node path.
159
183
 
184
+ This monorepo's `.mcp.json` runs `node scripts/mcp-servers/smrt-dev-mcp.mjs`:
185
+ stdout is protocol-only, the launcher never installs or uses `pnpm exec`/`npx`
186
+ (their stdout banner breaks stdio, #4685), and it falls back to `tsx` on `src/`
187
+ when `dist` is missing or older, so rebuild `dist` after server edits. Guarded by
188
+ `src/launcher-stdio.test.ts`; see [scripts/mcp-servers/README.md](../../scripts/mcp-servers/README.md).
189
+
160
190
  ## MCP Tier Context
161
191
 
162
192
  - **Tier 1** (Runtime): auto-generated from `@smrt()` objects — live data operations
@@ -170,6 +200,8 @@ launcher or a small wrapper script with an absolute Node path.
170
200
  - `src/agent-skills.ts` — bundled skill registry and file loader
171
201
  - `plugin.json` / `mcp.json` — Agent Plugins 1.0.0 portable root manifests
172
202
  - `skills/smrt-code-review/SKILL.md` — downstream SMRT review procedure
203
+ - `skills/smrt-contribute/SKILL.md` — contributor skill (copy at repo-root `.agents/skills/smrt-contribute/`)
204
+ - `skills/smrt-catalog/SKILL.md` — catalog router skill; `skills/smrt-catalog/evals.json` — its trigger eval set
173
205
  - `src/tools/generate-smrt-class.ts` — class generation logic and package-ready templates
174
206
  - `src/tools/introspect-project.ts` — manifest-first project scanning, falling back to `@happyvertical/smrt-scanner`
175
207
  - `src/dev-plane.ts` — in-app runtime dev-plane (#2782): `createDevPlane({ token, projectRoot, db })` serves JSON + MCP on one mount with loopback/bearer guards and a positive catalog (`registry-live` + the nine runtime tools); mounted by the generated `_dev/[...tool]` SvelteKit route
package/README.md CHANGED
@@ -334,7 +334,8 @@ available it falls back to `@happyvertical/smrt-scanner`.
334
334
  Run an advisory downstream ecosystem review. The tool scans package manifests
335
335
  and source imports for missing HappyVertical dependencies, direct storage
336
336
  bypasses, custom HTTP shells, custom object manifest generation, local
337
- auth/tenancy/audit seams, and UI shell drift. It returns deterministic findings
337
+ auth/tenancy/audit seams, UI shell drift, and hand-rolled equivalents of
338
+ smrt packages. It returns deterministic findings
338
339
  and suggested follow-up issue titles; it does not modify files.
339
340
 
340
341
  | Parameter | Type | Required | Description |
@@ -343,6 +344,21 @@ and suggested follow-up issue titles; it does not modify files.
343
344
  | `rootDir` | `string` | No | Compatibility alias for `directory` |
344
345
  | `includeSourceEvidence` | `boolean` | No | Include file/line evidence (default: true) |
345
346
  | `maxFindings` | `number` | No | Limit findings returned |
347
+ | `intentIndexPath` | `string` | No | Caller-trusted intent index driving duplicate detection (`regex:` tokens become `RegExp`). Default: the full index packaged in this release (`dist/knowledge/intents/index.md`), else the monorepo's `docs/knowledge/intents/index.md` in a source checkout; never read from `directory` |
348
+ | `detectDuplicates` | `boolean` | No | Run the intent-driven hand-rolled-equivalent pass (default: true) |
349
+
350
+ Duplicate detection is table-driven: each row of the intent index names home
351
+ packages and `import:`, `call:`, `regex:` and `decimal-field:` signals. When a
352
+ package shows a signal and imports none of the row's home packages, the tool
353
+ emits an advisory `duplicate-detection` finding anchored at `path:line` that
354
+ names the intent page and component pages. Unknown signal kinds are skipped
355
+ with a stderr note. The rules are never taken from the reviewed project, so a
356
+ pull request cannot delete them or inject a pattern; when no index can be
357
+ found the tool reports `skipped` with a note. The same pass runs from a shell with
358
+ `npx @happyvertical/smrt-dev-mcp review --directory . [--intent-index PATH] [--format summary]`
359
+ (exit 2 and a stderr message when no index is found, never a silent pass) and
360
+ from the reusable `consumer-knowledge-review.yml` workflow, which always uses
361
+ the packaged index.
346
362
 
347
363
  ### `reflect-knowledge`
348
364
 
@@ -0,0 +1,16 @@
1
+ # Agent skills: catalog and contribute
2
+
3
+ - `skills/smrt-catalog/SKILL.md` — router skill (#4708). Fires before infrastructure, utility, or integration
4
+ code in a project that depends on smrt packages; reads `.agents/smrt-knowledge/intents/index.md`, the intent
5
+ page, then the component page, proposes the smrt package and entry point before code, falls back to
6
+ `build-context` when the bundle is missing or stale, and drafts an upstream issue plus a marked temporary shim
7
+ when the home package lacks the behaviour. The description is the design: `skills/smrt-catalog/evals.json`
8
+ holds the positive/negative trigger prompts and `src/agent-skills.test.ts` asserts every positive signal class
9
+ is named. `smrt docs:agents` installs it into consumers (`./agent-skills` export: `getAgentSkill`,
10
+ `getAgentSkillsPackageVersion`).
11
+ - `skills/smrt-contribute/SKILL.md` — contributor skill (#4709): `CONTRIBUTING.md` as a fork-PR procedure with the
12
+ traps that fail from outside (package-name test filter, format then lint, generated knowledge pages, PR title
13
+ ≤92, no changesets, `.github/` guard, fork CI approval). It deliberately omits the maintainer lifecycle.
14
+ The repository root checks in an identical copy at `.agents/skills/smrt-contribute/SKILL.md` so a harness
15
+ opening a checkout finds it without the MCP server; `src/agent-skills.test.ts` fails when they drift, so edit
16
+ both together. Trigger evals: `skills/smrt-contribute/evals.json`.
@@ -0,0 +1,26 @@
1
+ # Intent-driven duplicate detection (#4690)
2
+
3
+ `review-smrt-project` parses the intent index at runtime
4
+ (`intent-index.ts`, pure) and `intent-detector.ts` evaluates each row's signals;
5
+ no intent is named in code, so a new row with signals needs no change here.
6
+ Trust boundary: `regex:` tokens become `RegExp`, so rules are never read from the
7
+ reviewed tree. Index lookup: `intentIndexPath` / `--intent-index` (trusted by the
8
+ caller), else the full unfiltered index shipped in this package's
9
+ `dist/knowledge/intents/` (`scripts/ship-knowledge-pages.mjs`, list
10
+ `OKF_INTENT_HOME_PACKAGES`), else the monorepo `docs/knowledge/intents/index.md`
11
+ when run from the smrt checkout. The exported consumer bundle index is filtered
12
+ to installed packages and is not consulted. Tokens:
13
+ `import:<module>`, `call:<ident>`, `regex:<pattern>`, and
14
+ `decimal-field:<glob>` (a property matching the glob declared with a decimal
15
+ literal or `type: 'decimal'`). Unknown kinds are skipped with a stderr note. A
16
+ finding needs a signal and no import of any row home package in the same
17
+ package; it is advisory (`area: duplicate-detection`, `file: path:line`,
18
+ `intentPage`, `componentPages`). Fixtures live in `test-fixtures/` (outside
19
+ `src/`, so tsc, Biome, and vitest globs never see them).
20
+
21
+ `smrt-dev-mcp review --directory . [--intent-index P] [--format summary]
22
+ [--fail-on-findings]` runs the pass from a shell (exit 2 with a stderr message when
23
+ no index is found; the tool reports `skipped` with a note). The reusable
24
+ `.github/workflows/consumer-knowledge-review.yml` calls it through the pinned
25
+ published package (public on npmjs, so private consumers need no token), has no
26
+ index input, and writes the summary to `$GITHUB_STEP_SUMMARY`.
@@ -11,6 +11,8 @@ export interface AgentSkillBundle extends AgentSkillSummary {
11
11
  content: string;
12
12
  }>;
13
13
  }
14
+ /** Version of the smrt-dev-mcp package that ships these skills. */
15
+ export declare function getAgentSkillsPackageVersion(): string;
14
16
  export declare function listAgentSkills(): AgentSkillSummary[];
15
17
  export declare function getAgentSkill(options: {
16
18
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-skills.d.ts","sourceRoot":"","sources":["../src/agent-skills.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,KAAK,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;CACJ;AAiBD,wBAAgB,eAAe,IAAI,iBAAiB,EAAE,CAErD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,GAAG,gBAAgB,CAoBnB"}
1
+ {"version":3,"file":"agent-skills.d.ts","sourceRoot":"","sources":["../src/agent-skills.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,KAAK,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;CACJ;AAiCD,mEAAmE;AACnE,wBAAgB,4BAA4B,IAAI,MAAM,CAKrD;AAED,wBAAgB,eAAe,IAAI,iBAAiB,EAAE,CAErD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,GAAG,gBAAgB,CAoBnB"}
@@ -0,0 +1,61 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ //#region src/agent-skills.ts
5
+ var AGENT_SKILLS = [
6
+ {
7
+ name: "smrt-code-review",
8
+ description: "Harness-agnostic downstream SMRT code review workflow using smrt-dev-mcp deterministic context and prompt bundles.",
9
+ path: "skills/smrt-code-review/SKILL.md",
10
+ skillFile: "skills/smrt-code-review/SKILL.md",
11
+ references: ["skills/smrt-code-review/references/review-output.md"]
12
+ },
13
+ {
14
+ name: "smrt-catalog",
15
+ description: "Router skill: before writing infrastructure, utility, or integration code in a project that depends on smrt packages, consult the smrt knowledge catalog and reuse the installed package.",
16
+ path: "skills/smrt-catalog/SKILL.md",
17
+ skillFile: "skills/smrt-catalog/SKILL.md",
18
+ references: []
19
+ },
20
+ {
21
+ name: "smrt-contribute",
22
+ description: "Contributor skill: the fork pull-request procedure for happyvertical/smrt (CONTRIBUTING.md as steps) for agents working in a fork or clone that is not a maintainer session.",
23
+ path: "skills/smrt-contribute/SKILL.md",
24
+ skillFile: "skills/smrt-contribute/SKILL.md",
25
+ references: []
26
+ }
27
+ ];
28
+ /** Version of the smrt-dev-mcp package that ships these skills. */
29
+ function getAgentSkillsPackageVersion() {
30
+ return JSON.parse(readPackageFile("package.json")).version ?? "0.0.0";
31
+ }
32
+ function listAgentSkills() {
33
+ return AGENT_SKILLS.map(({ skillFile: _skillFile, ...skill }) => skill);
34
+ }
35
+ function getAgentSkill(options) {
36
+ const skill = AGENT_SKILLS.find((item) => item.name === options.name);
37
+ if (!skill) throw new Error(`Unknown agent skill: ${options.name}`);
38
+ return {
39
+ name: skill.name,
40
+ description: skill.description,
41
+ path: skill.path,
42
+ references: skill.references,
43
+ skillMarkdown: readPackageFile(skill.skillFile),
44
+ referenceFiles: options.includeReferences === false ? [] : skill.references.map((path) => ({
45
+ path,
46
+ content: readPackageFile(path)
47
+ }))
48
+ };
49
+ }
50
+ function readPackageFile(relativePath) {
51
+ return readFileSync(join(resolvePackageRoot(), relativePath), "utf8");
52
+ }
53
+ function resolvePackageRoot() {
54
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
55
+ if (!existsSync(join(packageRoot, "package.json"))) throw new Error(`Unable to resolve smrt-dev-mcp package root from ${import.meta.url}`);
56
+ return packageRoot;
57
+ }
58
+ //#endregion
59
+ export { getAgentSkill, getAgentSkillsPackageVersion, listAgentSkills };
60
+
61
+ //# sourceMappingURL=agent-skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-skills.js","names":[],"sources":["../src/agent-skills.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport interface AgentSkillSummary {\n name: string;\n description: string;\n path: string;\n references: string[];\n}\n\nexport interface AgentSkillBundle extends AgentSkillSummary {\n skillMarkdown: string;\n referenceFiles: Array<{\n path: string;\n content: string;\n }>;\n}\n\ninterface AgentSkillDefinition extends AgentSkillSummary {\n skillFile: string;\n}\n\nconst AGENT_SKILLS: AgentSkillDefinition[] = [\n {\n name: 'smrt-code-review',\n description:\n 'Harness-agnostic downstream SMRT code review workflow using smrt-dev-mcp deterministic context and prompt bundles.',\n path: 'skills/smrt-code-review/SKILL.md',\n skillFile: 'skills/smrt-code-review/SKILL.md',\n references: ['skills/smrt-code-review/references/review-output.md'],\n },\n {\n name: 'smrt-catalog',\n description:\n 'Router skill: before writing infrastructure, utility, or integration code in a project that depends on smrt packages, consult the smrt knowledge catalog and reuse the installed package.',\n path: 'skills/smrt-catalog/SKILL.md',\n skillFile: 'skills/smrt-catalog/SKILL.md',\n references: [],\n },\n {\n name: 'smrt-contribute',\n description:\n 'Contributor skill: the fork pull-request procedure for happyvertical/smrt (CONTRIBUTING.md as steps) for agents working in a fork or clone that is not a maintainer session.',\n path: 'skills/smrt-contribute/SKILL.md',\n skillFile: 'skills/smrt-contribute/SKILL.md',\n references: [],\n },\n];\n\n/** Version of the smrt-dev-mcp package that ships these skills. */\nexport function getAgentSkillsPackageVersion(): string {\n const manifest = JSON.parse(readPackageFile('package.json')) as {\n version?: string;\n };\n return manifest.version ?? '0.0.0';\n}\n\nexport function listAgentSkills(): AgentSkillSummary[] {\n return AGENT_SKILLS.map(({ skillFile: _skillFile, ...skill }) => skill);\n}\n\nexport function getAgentSkill(options: {\n name: string;\n includeReferences?: boolean;\n}): AgentSkillBundle {\n const skill = AGENT_SKILLS.find((item) => item.name === options.name);\n if (!skill) {\n throw new Error(`Unknown agent skill: ${options.name}`);\n }\n\n return {\n name: skill.name,\n description: skill.description,\n path: skill.path,\n references: skill.references,\n skillMarkdown: readPackageFile(skill.skillFile),\n referenceFiles:\n options.includeReferences === false\n ? []\n : skill.references.map((path) => ({\n path,\n content: readPackageFile(path),\n })),\n };\n}\n\nfunction readPackageFile(relativePath: string): string {\n const packageRoot = resolvePackageRoot();\n return readFileSync(join(packageRoot, relativePath), 'utf8');\n}\n\nfunction resolvePackageRoot(): string {\n // Works in both source tests (`src/agent-skills.ts`) and built packages\n // (`dist/agent-skills.js`); guard it so packaging layout drift fails clearly.\n const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));\n if (!existsSync(join(packageRoot, 'package.json'))) {\n throw new Error(\n `Unable to resolve smrt-dev-mcp package root from ${import.meta.url}`,\n );\n }\n return packageRoot;\n}\n"],"mappings":";;;;AAuBA,IAAM,eAAuC;CAC3C;EACE,MAAM;EACN,aACE;EACF,MAAM;EACN,WAAW;EACX,YAAY,CAAC,qDAAqD;CACpE;CACA;EACE,MAAM;EACN,aACE;EACF,MAAM;EACN,WAAW;EACX,YAAY,CAAC;CACf;CACA;EACE,MAAM;EACN,aACE;EACF,MAAM;EACN,WAAW;EACX,YAAY,CAAC;CACf;AACF;;AAGA,SAAgB,+BAAuC;CAIrD,OAHiB,KAAK,MAAM,gBAAgB,cAAc,CAGnD,CAAA,CAAS,WAAW;AAC7B;AAEA,SAAgB,kBAAuC;CACrD,OAAO,aAAa,KAAK,EAAE,WAAW,YAAY,GAAG,YAAY,KAAK;AACxE;AAEA,SAAgB,cAAc,SAGT;CACnB,MAAM,QAAQ,aAAa,MAAM,SAAS,KAAK,SAAS,QAAQ,IAAI;CACpE,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,wBAAwB,QAAQ,MAAM;CAGxD,OAAO;EACL,MAAM,MAAM;EACZ,aAAa,MAAM;EACnB,MAAM,MAAM;EACZ,YAAY,MAAM;EAClB,eAAe,gBAAgB,MAAM,SAAS;EAC9C,gBACE,QAAQ,sBAAsB,QAC1B,CAAC,IACD,MAAM,WAAW,KAAK,UAAU;GAC9B;GACA,SAAS,gBAAgB,IAAI;EAC/B,EAAE;CACV;AACF;AAEA,SAAS,gBAAgB,cAA8B;CAErD,OAAO,aAAa,KADA,mBACK,GAAa,YAAY,GAAG,MAAM;AAC7D;AAEA,SAAS,qBAA6B;CAGpC,MAAM,cAAc,QAAQ,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC,CAAC;CACnE,IAAI,CAAC,WAAW,KAAK,aAAa,cAAc,CAAC,GAC/C,MAAM,IAAI,MACR,oDAAoD,OAAO,KAAK,KAClE;CAEF,OAAO;AACT"}
package/dist/dev-plane.js CHANGED
@@ -1,6 +1,6 @@
1
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-CCiO7qQW.js";
3
- import { n as TOOLS } from "./tool-catalog-RHq7nyCm.js";
2
+ import { i as SERVER_VERSION, r as SERVER_NAME, t as bearerMatches } from "./http-BqKiyqgN.js";
3
+ import { n as TOOLS } from "./tool-catalog-BhnSCF8g.js";
4
4
  import { i as runtimeSchemaDiff, n as runtimeObject, r as runtimeRegistry } from "./observation-DbB4z-9t.js";
5
5
  import { Server, createMcpHandler } from "@modelcontextprotocol/server";
6
6
  //#region src/dev-plane.ts
@@ -1,5 +1,5 @@
1
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
- import { n as TOOLS } from "./tool-catalog-RHq7nyCm.js";
2
+ import { n as TOOLS } from "./tool-catalog-BhnSCF8g.js";
3
3
  import { a as bootRuntime, i as runtimeSchemaDiff, n as runtimeObject, r as runtimeRegistry } from "./observation-DbB4z-9t.js";
4
4
  import { readFileSync } from "node:fs";
5
5
  import { dirname, join } from "node:path";
@@ -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-CCiO7qQW.js.map
169
+ //# sourceMappingURL=http-BqKiyqgN.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"http-CCiO7qQW.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-BqKiyqgN.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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"}