@fenglimg/fabric-server 0.1.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,75 @@
1
+ import { Server } from 'node:http';
1
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { AuditMode } from '@fenglimg/fabric-shared';
4
+
5
+ type DoctorStatus = "ok" | "warn" | "error";
6
+ type DoctorCheck = {
7
+ name: string;
8
+ status: DoctorStatus;
9
+ message: string;
10
+ };
11
+ type DoctorSummary = {
12
+ target: string;
13
+ framework: {
14
+ kind: string;
15
+ version: string;
16
+ subkind: string;
17
+ };
18
+ entryPoints: Array<{
19
+ path: string;
20
+ reason: string;
21
+ }>;
22
+ driftCount: number;
23
+ protectedPathCount: number;
24
+ protectedPathsIntact: boolean;
25
+ lastLedgerEntryTs: number | null;
26
+ lastLedgerEntryAgeMs: number | null;
27
+ metaRevision: string | null;
28
+ audit: {
29
+ enabled: boolean;
30
+ mode: AuditMode;
31
+ checkedPathCount: number;
32
+ violationCount: number;
33
+ windowMs: number;
34
+ } | null;
35
+ };
36
+ type DoctorReport = {
37
+ status: DoctorStatus;
38
+ checks: DoctorCheck[];
39
+ summary: DoctorSummary;
40
+ audit: DoctorAuditReport | null;
41
+ };
42
+ type DoctorAuditViolation = {
43
+ editTs: number;
44
+ entryId: string;
45
+ intent: string;
46
+ lastGetRulesTs: number | null;
47
+ path: string;
48
+ };
49
+ type DoctorAuditReport = {
50
+ mode: AuditMode;
51
+ skipped: boolean;
52
+ windowMs: number;
53
+ checkedPathCount: number;
54
+ violationCount: number;
55
+ violations: DoctorAuditViolation[];
56
+ };
57
+ declare function runDoctorReport(target: string): Promise<DoctorReport>;
58
+ declare function runDoctorAuditReport(target: string, options?: {
59
+ force?: boolean;
60
+ mode?: AuditMode;
61
+ windowMs?: number;
62
+ }): Promise<DoctorAuditReport>;
2
63
 
3
64
  declare function createFabricServer(): McpServer;
4
65
  declare function startStdioServer(): Promise<void>;
66
+ declare function startHttpServer(options: {
67
+ port: number;
68
+ projectRoot: string;
69
+ host?: string;
70
+ authToken?: string;
71
+ dashboardDistPath?: string;
72
+ dev?: boolean;
73
+ }): Promise<Server>;
5
74
 
6
- export { createFabricServer, startStdioServer };
75
+ export { type DoctorAuditReport, type DoctorReport, createFabricServer, runDoctorAuditReport, runDoctorReport, startHttpServer, startStdioServer };
package/dist/index.js CHANGED
@@ -1,3 +1,17 @@
1
+ import {
2
+ FABRIC_DIR,
3
+ appendEditIntentAuditEvents,
4
+ appendGetRulesAuditEvent,
5
+ appendLedgerEntry,
6
+ atomicWriteText,
7
+ readAgentsMeta,
8
+ readHumanLock,
9
+ resolveProjectRoot,
10
+ runDoctorAuditReport,
11
+ runDoctorReport,
12
+ sha256
13
+ } from "./chunk-U3IQH5H6.js";
14
+
1
15
  // src/index.ts
2
16
  import { resolve } from "path";
3
17
  import { fileURLToPath } from "url";
@@ -5,74 +19,38 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
19
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
20
 
7
21
  // src/tools/append-intent.ts
8
- import { appendFile } from "fs/promises";
9
- import { join as join2 } from "path";
10
- import { z as z2 } from "zod";
22
+ import { aiLedgerEntrySchema } from "@fenglimg/fabric-shared";
11
23
 
12
- // src/meta-reader.ts
13
- import { readFileSync } from "fs";
14
- import { join } from "path";
15
- import { z } from "zod";
16
- var agentsMetaNodeSchema = z.object({
17
- file: z.string(),
18
- scope_glob: z.string(),
19
- deps: z.array(z.string()),
20
- priority: z.enum(["high", "medium", "low"]),
21
- hash: z.string()
22
- });
23
- var agentsMetaSchema = z.object({
24
- revision: z.string(),
25
- nodes: z.record(agentsMetaNodeSchema)
26
- });
27
- var AgentsMetaFileMissingError = class extends Error {
28
- constructor(metaPath) {
29
- super(`Fabric agents metadata file is missing: ${metaPath}`);
30
- this.metaPath = metaPath;
31
- this.name = "AgentsMetaFileMissingError";
32
- }
33
- metaPath;
34
- code = "FABRIC_META_MISSING";
35
- };
36
- var AgentsMetaInvalidError = class extends Error {
37
- constructor(metaPath, cause) {
38
- const detail = cause instanceof Error ? cause.message : String(cause);
39
- super(`Fabric agents metadata file is invalid: ${metaPath}. ${detail}`);
40
- this.metaPath = metaPath;
41
- this.name = "AgentsMetaInvalidError";
42
- }
43
- metaPath;
44
- code = "FABRIC_META_INVALID";
45
- };
46
- function getAgentsMetaPath(projectRoot) {
47
- return join(projectRoot, ".fabric", "agents.meta.json");
48
- }
49
- function resolveProjectRoot() {
50
- return process.env.FABRIC_PROJECT_ROOT ?? process.cwd();
51
- }
52
- function readAgentsMeta(projectRoot) {
53
- const metaPath = getAgentsMetaPath(projectRoot);
54
- let raw;
55
- try {
56
- raw = readFileSync(metaPath, "utf8");
57
- } catch (error) {
58
- if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
59
- throw new AgentsMetaFileMissingError(metaPath);
60
- }
61
- throw error;
62
- }
24
+ // src/services/append-intent.ts
25
+ async function appendIntent(projectRoot, input) {
26
+ const ts = Date.now();
27
+ const entry = await appendLedgerEntry(projectRoot, {
28
+ ...input.entry,
29
+ ts,
30
+ source: "ai"
31
+ });
63
32
  try {
64
- return agentsMetaSchema.parse(JSON.parse(raw));
65
- } catch (error) {
66
- throw new AgentsMetaInvalidError(metaPath, error);
33
+ await appendEditIntentAuditEvents(projectRoot, {
34
+ affected_paths: entry.affected_paths,
35
+ intent: entry.intent,
36
+ ledger_entry_id: entry.id,
37
+ ts
38
+ });
39
+ } catch {
67
40
  }
41
+ return {
42
+ success: true,
43
+ timestamp: ts,
44
+ entry
45
+ };
68
46
  }
69
47
 
70
48
  // src/tools/append-intent.ts
71
49
  var inputSchema = {
72
- entry: z2.object({
73
- commit_sha: z2.string().optional(),
74
- intent: z2.string(),
75
- affected_paths: z2.array(z2.string())
50
+ entry: aiLedgerEntrySchema.omit({
51
+ id: true,
52
+ source: true,
53
+ ts: true
76
54
  })
77
55
  };
78
56
  function createTextResponse(payload) {
@@ -92,43 +70,66 @@ function registerAppendIntent(server) {
92
70
  inputSchema,
93
71
  async ({ entry }) => {
94
72
  const projectRoot = resolveProjectRoot();
95
- const ledgerPath = join2(projectRoot, ".intent-ledger.jsonl");
96
- const ts = Date.now();
97
- await appendFile(ledgerPath, `${JSON.stringify({ ts, ...entry })}
98
- `, "utf8");
99
- return createTextResponse({
100
- success: true,
101
- timestamp: ts
102
- });
73
+ const result = await appendIntent(projectRoot, { entry });
74
+ return createTextResponse(result);
103
75
  }
104
76
  );
105
77
  }
106
78
 
107
79
  // src/tools/get-rules.ts
80
+ import { z } from "zod";
81
+
82
+ // src/services/get-rules.ts
108
83
  import { readFile } from "fs/promises";
109
- import { join as join3 } from "path";
84
+ import { join } from "path";
110
85
  import { minimatch } from "minimatch";
111
- import { z as z3 } from "zod";
112
- var inputSchema2 = {
113
- path: z3.string().describe("Target file path to query rules for"),
114
- client_hash: z3.string().optional().describe("Revision hash from prior fab_get_rules response; enables stale detection")
115
- };
116
86
  var PRIORITY_ORDER = {
117
87
  high: 0,
118
88
  medium: 1,
119
89
  low: 2
120
90
  };
121
- function createTextResponse2(payload) {
91
+ async function getRules(projectRoot, input) {
92
+ const context = await loadGetRulesContext(projectRoot);
93
+ const stale = input.client_hash !== void 0 && input.client_hash !== context.meta.revision;
94
+ const rules = await resolveRulesForPath(projectRoot, context, input.path);
95
+ const result = {
96
+ revision_hash: context.meta.revision,
97
+ stale,
98
+ rules
99
+ };
100
+ try {
101
+ await appendGetRulesAuditEvent(projectRoot, {
102
+ path: input.path,
103
+ client_hash: input.client_hash
104
+ });
105
+ } catch {
106
+ }
107
+ return result;
108
+ }
109
+ async function loadGetRulesContext(projectRoot) {
110
+ const meta = readAgentsMeta(projectRoot);
111
+ const l0Content = await readFile(join(projectRoot, "AGENTS.md"), "utf8");
112
+ const humanLockedNearby = (await readHumanLock(projectRoot)).map((entry) => ({
113
+ file: entry.file,
114
+ excerpt: JSON.stringify(entry)
115
+ }));
122
116
  return {
123
- content: [
124
- {
125
- type: "text",
126
- text: JSON.stringify(payload)
127
- }
128
- ]
117
+ meta,
118
+ l0Content,
119
+ humanLockedNearby
120
+ };
121
+ }
122
+ async function resolveRulesForPath(projectRoot, context, path, options = {}) {
123
+ const loadedRules = await loadRulesForPath(projectRoot, context.meta, path);
124
+ const { L1, L2 } = partitionRulesByLevel(loadedRules, options.dedupeByPath ?? false);
125
+ return {
126
+ L0: context.l0Content,
127
+ L1,
128
+ L2,
129
+ human_locked_nearby: context.humanLockedNearby
129
130
  };
130
131
  }
131
- function normalizePath(value) {
132
+ function normalizeRulesPath(value) {
132
133
  return value.replaceAll("\\", "/");
133
134
  }
134
135
  function classifyNode(nodeId) {
@@ -140,32 +141,66 @@ function classifyNode(nodeId) {
140
141
  }
141
142
  return null;
142
143
  }
143
- async function readHumanLockedNearby(projectRoot) {
144
- const humanLockPath = join3(projectRoot, ".fabric", "human-lock.json");
145
- try {
146
- const raw = await readFile(humanLockPath, "utf8");
147
- const parsed = JSON.parse(raw);
148
- const entries = Array.isArray(parsed) ? parsed : parsed && typeof parsed === "object" && "human_locked" in parsed && Array.isArray(parsed.human_locked) ? parsed.human_locked : [];
149
- return entries.map((entry) => {
150
- if (!entry || typeof entry !== "object") {
151
- return {
152
- file: "(unknown)",
153
- excerpt: JSON.stringify(entry)
154
- };
144
+ async function loadRulesForPath(projectRoot, meta, path) {
145
+ const requestedPath = normalizeRulesPath(path);
146
+ const matchedNodes = Object.entries(meta.nodes).filter(([, node]) => minimatch(requestedPath, normalizeRulesPath(node.scope_glob), { dot: true })).sort((left, right) => {
147
+ const [leftId, leftNode] = left;
148
+ const [rightId, rightNode] = right;
149
+ const priorityDelta = PRIORITY_ORDER[leftNode.priority] - PRIORITY_ORDER[rightNode.priority];
150
+ return priorityDelta !== 0 ? priorityDelta : leftId.localeCompare(rightId);
151
+ });
152
+ return await Promise.all(
153
+ matchedNodes.map(async ([nodeId, node]) => ({
154
+ level: classifyNode(nodeId),
155
+ entry: {
156
+ path: node.file,
157
+ content: await readFile(join(projectRoot, node.file), "utf8")
155
158
  }
156
- const file = typeof entry.file === "string" ? entry.file : "(unknown)";
157
- const excerptCandidate = typeof entry.excerpt === "string" ? entry.excerpt : typeof entry.locked_text === "string" ? entry.locked_text : typeof entry.text === "string" ? entry.text : typeof entry.content === "string" ? entry.content : JSON.stringify(entry);
158
- return {
159
- file,
160
- excerpt: excerptCandidate
161
- };
162
- });
163
- } catch (error) {
164
- if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
165
- return [];
159
+ }))
160
+ );
161
+ }
162
+ function partitionRulesByLevel(loadedRules, dedupeByPath) {
163
+ const l1 = [];
164
+ const l2 = [];
165
+ for (const rule of loadedRules) {
166
+ if (rule.level === "L1") {
167
+ l1.push(rule.entry);
168
+ continue;
169
+ }
170
+ if (rule.level === "L2") {
171
+ l2.push(rule.entry);
166
172
  }
167
- throw error;
168
173
  }
174
+ return {
175
+ L1: dedupeByPath ? dedupeEntriesByPath(l1) : l1,
176
+ L2: dedupeByPath ? dedupeEntriesByPath(l2) : l2
177
+ };
178
+ }
179
+ function dedupeEntriesByPath(entries) {
180
+ const seenPaths = /* @__PURE__ */ new Set();
181
+ return entries.filter((entry) => {
182
+ if (seenPaths.has(entry.path)) {
183
+ return false;
184
+ }
185
+ seenPaths.add(entry.path);
186
+ return true;
187
+ });
188
+ }
189
+
190
+ // src/tools/get-rules.ts
191
+ var inputSchema2 = {
192
+ path: z.string().describe("Target file path to query rules for"),
193
+ client_hash: z.string().optional().describe("Revision hash from prior fab_get_rules response; enables stale detection")
194
+ };
195
+ function createTextResponse2(payload) {
196
+ return {
197
+ content: [
198
+ {
199
+ type: "text",
200
+ text: JSON.stringify(payload)
201
+ }
202
+ ]
203
+ };
169
204
  }
170
205
  function registerGetRules(server) {
171
206
  server.tool(
@@ -174,68 +209,49 @@ function registerGetRules(server) {
174
209
  inputSchema2,
175
210
  async ({ path, client_hash }) => {
176
211
  const projectRoot = resolveProjectRoot();
177
- const meta = readAgentsMeta(projectRoot);
178
- const stale = client_hash !== void 0 && client_hash !== meta.revision;
179
- const requestedPath = normalizePath(path);
180
- const l0Content = await readFile(join3(projectRoot, "AGENTS.md"), "utf8");
181
- const matchedNodes = Object.entries(meta.nodes).filter(([, node]) => minimatch(requestedPath, normalizePath(node.scope_glob), { dot: true })).sort((left, right) => {
182
- const [leftId, leftNode] = left;
183
- const [rightId, rightNode] = right;
184
- const priorityDelta = PRIORITY_ORDER[leftNode.priority] - PRIORITY_ORDER[rightNode.priority];
185
- return priorityDelta !== 0 ? priorityDelta : leftId.localeCompare(rightId);
186
- });
187
- const loadedRules = await Promise.all(
188
- matchedNodes.map(async ([nodeId, node]) => ({
189
- level: classifyNode(nodeId),
190
- entry: {
191
- path: node.file,
192
- content: await readFile(join3(projectRoot, node.file), "utf8")
193
- }
194
- }))
195
- );
196
- const l1 = [];
197
- const l2 = [];
198
- for (const rule of loadedRules) {
199
- if (rule.level === "L1") {
200
- l1.push(rule.entry);
201
- continue;
202
- }
203
- if (rule.level === "L2") {
204
- l2.push(rule.entry);
205
- }
206
- }
207
- const humanLockedNearby = await readHumanLockedNearby(projectRoot);
208
- return createTextResponse2({
209
- revision_hash: meta.revision,
210
- stale,
211
- rules: {
212
- L0: l0Content,
213
- L1: l1,
214
- L2: l2,
215
- human_locked_nearby: humanLockedNearby
216
- }
217
- });
212
+ const result = await getRules(projectRoot, { path, client_hash });
213
+ return createTextResponse2(result);
218
214
  }
219
215
  );
220
216
  }
221
217
 
222
- // src/tools/update-registry.ts
223
- import { createHash } from "crypto";
224
- import { writeFile } from "fs/promises";
225
- import { join as join4 } from "path";
226
- import { z as z4 } from "zod";
218
+ // src/tools/plan-context.ts
219
+ import { z as z2 } from "zod";
220
+
221
+ // src/services/plan-context.ts
222
+ async function planContext(projectRoot, input) {
223
+ const context = await loadGetRulesContext(projectRoot);
224
+ const stale = input.client_hash !== void 0 && input.client_hash !== context.meta.revision;
225
+ const uniquePaths = dedupePaths(input.paths);
226
+ const entries = await Promise.all(
227
+ uniquePaths.map(async (path) => ({
228
+ path,
229
+ rules: await resolveRulesForPath(projectRoot, context, path, { dedupeByPath: true })
230
+ }))
231
+ );
232
+ return {
233
+ revision_hash: context.meta.revision,
234
+ stale,
235
+ entries
236
+ };
237
+ }
238
+ function dedupePaths(paths) {
239
+ const seenPaths = /* @__PURE__ */ new Set();
240
+ return paths.flatMap((path) => {
241
+ const normalizedPath = normalizeRulesPath(path);
242
+ if (seenPaths.has(normalizedPath)) {
243
+ return [];
244
+ }
245
+ seenPaths.add(normalizedPath);
246
+ return [normalizedPath];
247
+ });
248
+ }
249
+
250
+ // src/tools/plan-context.ts
227
251
  var inputSchema3 = {
228
- op: z4.enum(["add-node", "remove-node", "update-node"]),
229
- node_id: z4.string(),
230
- data: z4.record(z4.unknown()).optional()
252
+ paths: z2.array(z2.string()).min(1).describe("Candidate file paths to query rules for during planning or architecture review"),
253
+ client_hash: z2.string().optional().describe("Revision hash from a prior fab_plan_context response; enables stale detection")
231
254
  };
232
- var agentsMetaNodeSchema2 = z4.object({
233
- file: z4.string(),
234
- scope_glob: z4.string(),
235
- deps: z4.array(z4.string()),
236
- priority: z4.enum(["high", "medium", "low"]),
237
- hash: z4.string()
238
- });
239
255
  function createTextResponse3(payload) {
240
256
  return {
241
257
  content: [
@@ -246,15 +262,56 @@ function createTextResponse3(payload) {
246
262
  ]
247
263
  };
248
264
  }
265
+ function registerPlanContext(server) {
266
+ server.tool(
267
+ "fab_plan_context",
268
+ "Use during plan or architecture phases to batch-query Fabric rules for multiple candidate paths in one round-trip.",
269
+ inputSchema3,
270
+ async ({ paths, client_hash }) => {
271
+ const projectRoot = resolveProjectRoot();
272
+ const result = await planContext(projectRoot, { paths, client_hash });
273
+ return createTextResponse3(result);
274
+ }
275
+ );
276
+ }
277
+
278
+ // src/tools/update-registry.ts
279
+ import { z as z3 } from "zod";
280
+
281
+ // src/services/update-registry.ts
282
+ import { agentsMetaNodeSchema } from "@fenglimg/fabric-shared";
283
+ import { join as join2 } from "path";
284
+ async function updateRegistry(projectRoot, input) {
285
+ const metaPath = join2(projectRoot, FABRIC_DIR, "agents.meta.json");
286
+ const currentMeta = readAgentsMeta(projectRoot);
287
+ const nextMeta = applyRegistryOperation(currentMeta, input.op, input.node_id, input.data);
288
+ const newRevision = computeRevision(nextMeta);
289
+ await atomicWriteText(
290
+ metaPath,
291
+ `${JSON.stringify(
292
+ {
293
+ ...nextMeta,
294
+ revision: newRevision
295
+ },
296
+ null,
297
+ 2
298
+ )}
299
+ `
300
+ );
301
+ return {
302
+ revision_hash: newRevision,
303
+ success: true
304
+ };
305
+ }
249
306
  function computeRevision(meta) {
250
307
  const joinedHashes = Object.entries(meta.nodes).sort(([leftId], [rightId]) => leftId.localeCompare(rightId)).map(([, node]) => node.hash).join("");
251
- return `sha256:${createHash("sha256").update(joinedHashes).digest("hex")}`;
308
+ return sha256(joinedHashes);
252
309
  }
253
310
  function assertNodeData(data, message) {
254
311
  if (data === void 0) {
255
312
  throw new Error(message);
256
313
  }
257
- return agentsMetaNodeSchema2.parse(data);
314
+ return agentsMetaNodeSchema.parse(data);
258
315
  }
259
316
  function applyRegistryOperation(meta, op, nodeId, data) {
260
317
  const nextNodes = { ...meta.nodes };
@@ -276,7 +333,7 @@ function applyRegistryOperation(meta, op, nodeId, data) {
276
333
  if (currentNode === void 0) {
277
334
  throw new Error(`Cannot update missing Fabric registry node: ${nodeId}`);
278
335
  }
279
- nextNodes[nodeId] = agentsMetaNodeSchema2.parse({
336
+ nextNodes[nodeId] = agentsMetaNodeSchema.parse({
280
337
  ...currentNode,
281
338
  ...data
282
339
  });
@@ -285,34 +342,32 @@ function applyRegistryOperation(meta, op, nodeId, data) {
285
342
  nodes: nextNodes
286
343
  };
287
344
  }
345
+
346
+ // src/tools/update-registry.ts
347
+ var inputSchema4 = {
348
+ op: z3.enum(["add-node", "remove-node", "update-node"]),
349
+ node_id: z3.string(),
350
+ data: z3.record(z3.unknown()).optional()
351
+ };
352
+ function createTextResponse4(payload) {
353
+ return {
354
+ content: [
355
+ {
356
+ type: "text",
357
+ text: JSON.stringify(payload)
358
+ }
359
+ ]
360
+ };
361
+ }
288
362
  function registerUpdateRegistry(server) {
289
363
  server.tool(
290
364
  "fab_update_registry",
291
365
  "MANDATORY: Call to add, remove, or update Fabric registry nodes instead of editing registry files directly.",
292
- inputSchema3,
366
+ inputSchema4,
293
367
  async ({ op, node_id, data }) => {
294
368
  const projectRoot = resolveProjectRoot();
295
- const metaPath = join4(projectRoot, ".fabric", "agents.meta.json");
296
- const currentMeta = readAgentsMeta(projectRoot);
297
- const nextMeta = applyRegistryOperation(currentMeta, op, node_id, data);
298
- const newRevision = computeRevision(nextMeta);
299
- await writeFile(
300
- metaPath,
301
- `${JSON.stringify(
302
- {
303
- ...nextMeta,
304
- revision: newRevision
305
- },
306
- null,
307
- 2
308
- )}
309
- `,
310
- "utf8"
311
- );
312
- return createTextResponse3({
313
- revision_hash: newRevision,
314
- success: true
315
- });
369
+ const result = await updateRegistry(projectRoot, { op, node_id, data });
370
+ return createTextResponse4(result);
316
371
  }
317
372
  );
318
373
  }
@@ -331,9 +386,10 @@ function formatError(error) {
331
386
  function createFabricServer() {
332
387
  const server = new McpServer({
333
388
  name: "fabric-context-server",
334
- version: "0.0.0"
389
+ version: "1.1.0"
335
390
  });
336
391
  registerGetRules(server);
392
+ registerPlanContext(server);
337
393
  registerAppendIntent(server);
338
394
  registerUpdateRegistry(server);
339
395
  return server;
@@ -343,6 +399,20 @@ async function startStdioServer() {
343
399
  const transport = new StdioServerTransport();
344
400
  await server.connect(transport);
345
401
  }
402
+ async function startHttpServer(options) {
403
+ const { createFabricHttpApp } = await import("./http-FL3MB4L2.js");
404
+ const { port, projectRoot, host = "127.0.0.1", authToken, dashboardDistPath, dev } = options;
405
+ const app = createFabricHttpApp({ projectRoot, host, authToken, dashboardDistPath, dev });
406
+ return await new Promise((resolveServer, rejectServer) => {
407
+ const server = app.listen(port, host);
408
+ server.once("listening", () => {
409
+ resolveServer(server);
410
+ });
411
+ server.once("error", (error) => {
412
+ rejectServer(error);
413
+ });
414
+ });
415
+ }
346
416
  var entrypoint = process.argv[1];
347
417
  var currentFilePath = fileURLToPath(import.meta.url);
348
418
  var isMainModule = entrypoint !== void 0 && resolve(entrypoint) === currentFilePath;
@@ -354,5 +424,8 @@ if (isMainModule) {
354
424
  }
355
425
  export {
356
426
  createFabricServer,
427
+ runDoctorAuditReport,
428
+ runDoctorReport,
429
+ startHttpServer,
357
430
  startStdioServer
358
431
  };