mock-mcp 0.3.1 → 0.5.1

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 (40) hide show
  1. package/README.md +212 -124
  2. package/dist/adapter/index.cjs +875 -0
  3. package/dist/adapter/index.d.cts +142 -0
  4. package/dist/adapter/index.d.ts +142 -0
  5. package/dist/adapter/index.js +835 -0
  6. package/dist/client/connect.cjs +991 -0
  7. package/dist/client/connect.d.cts +218 -0
  8. package/dist/client/connect.d.ts +211 -7
  9. package/dist/client/connect.js +941 -20
  10. package/dist/client/index.cjs +992 -0
  11. package/dist/client/index.d.cts +3 -0
  12. package/dist/client/index.d.ts +3 -2
  13. package/dist/client/index.js +951 -2
  14. package/dist/daemon/index.cjs +717 -0
  15. package/dist/daemon/index.d.cts +62 -0
  16. package/dist/daemon/index.d.ts +62 -0
  17. package/dist/daemon/index.js +678 -0
  18. package/dist/index.cjs +2708 -0
  19. package/dist/index.d.cts +602 -0
  20. package/dist/index.d.ts +602 -11
  21. package/dist/index.js +2651 -53
  22. package/dist/shared/index.cjs +506 -0
  23. package/dist/shared/index.d.cts +241 -0
  24. package/dist/shared/index.d.ts +241 -0
  25. package/dist/shared/index.js +423 -0
  26. package/dist/types-bEGXLBF0.d.cts +190 -0
  27. package/dist/types-bEGXLBF0.d.ts +190 -0
  28. package/package.json +45 -4
  29. package/dist/client/batch-mock-collector.d.ts +0 -111
  30. package/dist/client/batch-mock-collector.js +0 -308
  31. package/dist/client/util.d.ts +0 -1
  32. package/dist/client/util.js +0 -3
  33. package/dist/connect.cjs +0 -400
  34. package/dist/connect.d.cts +0 -82
  35. package/dist/server/index.d.ts +0 -1
  36. package/dist/server/index.js +0 -1
  37. package/dist/server/test-mock-mcp-server.d.ts +0 -73
  38. package/dist/server/test-mock-mcp-server.js +0 -419
  39. package/dist/types.d.ts +0 -45
  40. package/dist/types.js +0 -2
@@ -0,0 +1,423 @@
1
+ import fs from 'fs/promises';
2
+ import fssync from 'fs';
3
+ import os from 'os';
4
+ import path from 'path';
5
+ import crypto from 'crypto';
6
+ import { spawn } from 'child_process';
7
+ import http from 'http';
8
+ import { fileURLToPath, pathToFileURL } from 'url';
9
+ import { createRequire } from 'module';
10
+
11
+ // src/shared/discovery.ts
12
+ function debugLog(_msg) {
13
+ }
14
+ var __curDirname = (() => {
15
+ try {
16
+ const metaUrl = import.meta.url;
17
+ if (metaUrl && typeof metaUrl === "string" && metaUrl.startsWith("file://")) {
18
+ return path.dirname(fileURLToPath(metaUrl));
19
+ }
20
+ } catch {
21
+ }
22
+ return process.cwd();
23
+ })();
24
+ function hasValidProjectMarker(dir) {
25
+ try {
26
+ const gitPath = path.join(dir, ".git");
27
+ try {
28
+ const stat = fssync.statSync(gitPath);
29
+ if (stat.isDirectory() || stat.isFile()) {
30
+ return true;
31
+ }
32
+ } catch {
33
+ }
34
+ const pkgPath = path.join(dir, "package.json");
35
+ try {
36
+ fssync.accessSync(pkgPath, fssync.constants.F_OK);
37
+ return true;
38
+ } catch {
39
+ }
40
+ return false;
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
45
+ function resolveProjectRoot(startDir = process.cwd()) {
46
+ let current = path.resolve(startDir);
47
+ const root = path.parse(current).root;
48
+ while (current !== root) {
49
+ const gitPath = path.join(current, ".git");
50
+ try {
51
+ const stat = fssync.statSync(gitPath);
52
+ if (stat.isDirectory() || stat.isFile()) {
53
+ return current;
54
+ }
55
+ } catch {
56
+ }
57
+ const pkgPath = path.join(current, "package.json");
58
+ try {
59
+ fssync.accessSync(pkgPath, fssync.constants.F_OK);
60
+ return current;
61
+ } catch {
62
+ }
63
+ current = path.dirname(current);
64
+ }
65
+ return path.resolve(startDir);
66
+ }
67
+ function computeProjectId(projectRoot) {
68
+ const real = fssync.realpathSync(projectRoot);
69
+ return crypto.createHash("sha256").update(real).digest("hex").slice(0, 16);
70
+ }
71
+ function getCacheDir(override) {
72
+ if (override) {
73
+ return override;
74
+ }
75
+ const envCacheDir = process.env.MOCK_MCP_CACHE_DIR;
76
+ if (envCacheDir) {
77
+ return envCacheDir;
78
+ }
79
+ const xdg = process.env.XDG_CACHE_HOME;
80
+ if (xdg) {
81
+ return xdg;
82
+ }
83
+ if (process.platform === "win32" && process.env.LOCALAPPDATA) {
84
+ return process.env.LOCALAPPDATA;
85
+ }
86
+ const home = os.homedir();
87
+ if (home) {
88
+ return path.join(home, ".cache");
89
+ }
90
+ return os.tmpdir();
91
+ }
92
+ function getPaths(projectId, cacheDir) {
93
+ const base = path.join(getCacheDir(cacheDir), "mock-mcp");
94
+ const registryPath = path.join(base, `${projectId}.json`);
95
+ const lockPath = path.join(base, `${projectId}.lock`);
96
+ const ipcPath = process.platform === "win32" ? `\\\\.\\pipe\\mock-mcp-${projectId}` : path.join(base, `${projectId}.sock`);
97
+ return { base, registryPath, lockPath, ipcPath };
98
+ }
99
+ async function readRegistry(registryPath) {
100
+ try {
101
+ const txt = await fs.readFile(registryPath, "utf-8");
102
+ return JSON.parse(txt);
103
+ } catch (error) {
104
+ debugLog(`readRegistry error for ${registryPath}: ${error instanceof Error ? error.message : String(error)}`);
105
+ return null;
106
+ }
107
+ }
108
+ async function writeRegistry(registryPath, registry) {
109
+ await fs.writeFile(registryPath, JSON.stringify(registry, null, 2), {
110
+ encoding: "utf-8",
111
+ mode: 384
112
+ // Read/write for owner only
113
+ });
114
+ }
115
+ async function healthCheck(ipcPath, timeoutMs = 2e3) {
116
+ return new Promise((resolve) => {
117
+ const req = http.request(
118
+ {
119
+ method: "GET",
120
+ socketPath: ipcPath,
121
+ path: "/health",
122
+ timeout: timeoutMs
123
+ },
124
+ (res) => {
125
+ resolve(res.statusCode === 200);
126
+ }
127
+ );
128
+ req.on("error", () => resolve(false));
129
+ req.on("timeout", () => {
130
+ req.destroy();
131
+ resolve(false);
132
+ });
133
+ req.end();
134
+ });
135
+ }
136
+ async function tryAcquireLock(lockPath) {
137
+ try {
138
+ const fh = await fs.open(lockPath, "wx");
139
+ await fh.write(`${process.pid}
140
+ `);
141
+ return fh;
142
+ } catch {
143
+ return null;
144
+ }
145
+ }
146
+ async function releaseLock(lockPath, fh) {
147
+ await fh.close();
148
+ await fs.rm(lockPath).catch(() => {
149
+ });
150
+ }
151
+ function randomToken() {
152
+ return crypto.randomBytes(24).toString("base64url");
153
+ }
154
+ function getDaemonEntryPath() {
155
+ try {
156
+ const cwdRequire = createRequire(pathToFileURL(path.join(process.cwd(), "index.js")).href);
157
+ const resolved = cwdRequire.resolve("mock-mcp");
158
+ const distDir = path.dirname(resolved);
159
+ const daemonEntry = path.join(distDir, "index.js");
160
+ if (fssync.existsSync(daemonEntry)) {
161
+ return daemonEntry;
162
+ }
163
+ } catch {
164
+ }
165
+ try {
166
+ const packageRoot = resolveProjectRoot(__curDirname);
167
+ const distPath = path.join(packageRoot, "dist", "index.js");
168
+ if (fssync.existsSync(distPath)) {
169
+ return distPath;
170
+ }
171
+ } catch {
172
+ }
173
+ if (process.argv[1]) {
174
+ return process.argv[1];
175
+ }
176
+ return path.join(process.cwd(), "dist", "index.js");
177
+ }
178
+ async function ensureDaemonRunning(opts = {}) {
179
+ let projectRoot = opts.projectRoot ?? resolveProjectRoot();
180
+ if (!hasValidProjectMarker(projectRoot)) {
181
+ const resolved = resolveProjectRoot(projectRoot);
182
+ if (resolved !== projectRoot && hasValidProjectMarker(resolved)) {
183
+ console.error(`[mock-mcp] Warning: projectRoot "${projectRoot}" doesn't look like a project root`);
184
+ console.error(`[mock-mcp] Found .git/package.json at: "${resolved}"`);
185
+ projectRoot = resolved;
186
+ } else {
187
+ console.error(`[mock-mcp] \u26A0\uFE0F WARNING: Could not find a valid project root!`);
188
+ console.error(`[mock-mcp] Current path: "${projectRoot}"`);
189
+ console.error(`[mock-mcp] This path doesn't contain .git or package.json.`);
190
+ console.error(`[mock-mcp] This may cause project mismatch issues.`);
191
+ console.error(`[mock-mcp] `);
192
+ console.error(`[mock-mcp] For MCP adapters, please specify --project-root explicitly:`);
193
+ console.error(`[mock-mcp] mock-mcp adapter --project-root /path/to/your/project`);
194
+ console.error(`[mock-mcp] `);
195
+ console.error(`[mock-mcp] In your MCP client config (Cursor, Claude Desktop, etc.):`);
196
+ console.error(`[mock-mcp] {`);
197
+ console.error(`[mock-mcp] "args": ["-y", "mock-mcp", "adapter", "--project-root", "/path/to/your/project"]`);
198
+ console.error(`[mock-mcp] }`);
199
+ }
200
+ }
201
+ const projectId = computeProjectId(projectRoot);
202
+ const { base, registryPath, lockPath, ipcPath } = getPaths(
203
+ projectId,
204
+ opts.cacheDir
205
+ );
206
+ const timeoutMs = opts.timeoutMs ?? 1e4;
207
+ await fs.mkdir(base, { recursive: true });
208
+ const existing = await readRegistry(registryPath);
209
+ debugLog(`Registry read result: ${existing ? "Found (PID " + existing.pid + ")" : "Null"}`);
210
+ if (existing) {
211
+ let healthy = false;
212
+ for (let i = 0; i < 3; i++) {
213
+ debugLog(`Checking health attempt ${i + 1}/3 on ${existing.ipcPath}`);
214
+ healthy = await healthCheck(existing.ipcPath);
215
+ if (healthy) break;
216
+ await new Promise((r) => setTimeout(r, 200));
217
+ }
218
+ if (healthy) {
219
+ return existing;
220
+ }
221
+ }
222
+ if (process.platform !== "win32") {
223
+ try {
224
+ await fs.rm(ipcPath);
225
+ } catch {
226
+ }
227
+ }
228
+ const lock = await tryAcquireLock(lockPath);
229
+ if (lock) {
230
+ try {
231
+ const recheckReg = await readRegistry(registryPath);
232
+ if (recheckReg && await healthCheck(recheckReg.ipcPath)) {
233
+ return recheckReg;
234
+ }
235
+ const token = randomToken();
236
+ const daemonEntry = getDaemonEntryPath();
237
+ const child = spawn(
238
+ process.execPath,
239
+ [daemonEntry, "daemon", "--project-root", projectRoot, "--token", token],
240
+ {
241
+ detached: true,
242
+ stdio: ["ignore", "pipe", "pipe"],
243
+ env: {
244
+ ...process.env,
245
+ MOCK_MCP_CACHE_DIR: opts.cacheDir ?? ""
246
+ }
247
+ }
248
+ );
249
+ let daemonStderr = "";
250
+ let daemonStdout = "";
251
+ child.stdout?.on("data", (data) => {
252
+ const str = data.toString();
253
+ debugLog(`Daemon stdout: ${str}`);
254
+ });
255
+ child.stderr?.on("data", (data) => {
256
+ daemonStderr += data.toString();
257
+ debugLog(`Daemon stderr: ${data.toString()}`);
258
+ });
259
+ child.on("error", (err) => {
260
+ console.error(`[mock-mcp] Daemon spawn error: ${err.message}`);
261
+ });
262
+ child.on("exit", (code, signal) => {
263
+ if (code !== null && code !== 0) {
264
+ console.error(`[mock-mcp] Daemon exited with code: ${code}`);
265
+ if (daemonStderr) {
266
+ console.error(`[mock-mcp] Daemon stderr: ${daemonStderr.slice(0, 500)}`);
267
+ }
268
+ } else if (signal) {
269
+ console.error(`[mock-mcp] Daemon killed by signal: ${signal}`);
270
+ }
271
+ });
272
+ child.unref();
273
+ const deadline2 = Date.now() + timeoutMs;
274
+ while (Date.now() < deadline2) {
275
+ const reg = await readRegistry(registryPath);
276
+ if (reg && await healthCheck(reg.ipcPath)) {
277
+ return reg;
278
+ }
279
+ await sleep(50);
280
+ }
281
+ console.error("[mock-mcp] Daemon failed to start within timeout");
282
+ if (daemonStderr) {
283
+ console.error(`[mock-mcp] Daemon stderr:
284
+ ${daemonStderr}`);
285
+ }
286
+ throw new Error(
287
+ `Daemon start timeout after ${timeoutMs}ms. Check logs for details.`
288
+ );
289
+ } finally {
290
+ await releaseLock(lockPath, lock);
291
+ }
292
+ }
293
+ const deadline = Date.now() + timeoutMs;
294
+ while (Date.now() < deadline) {
295
+ const reg = await readRegistry(registryPath);
296
+ if (reg && await healthCheck(reg.ipcPath)) {
297
+ return reg;
298
+ }
299
+ await sleep(50);
300
+ }
301
+ throw new Error(
302
+ `Waiting for daemon timed out after ${timeoutMs}ms. Another process may have failed to start it.`
303
+ );
304
+ }
305
+ function sleep(ms) {
306
+ return new Promise((resolve) => setTimeout(resolve, ms));
307
+ }
308
+ function getGlobalIndexPath(cacheDir) {
309
+ const base = path.join(getCacheDir(cacheDir), "mock-mcp");
310
+ return path.join(base, "active-daemons.json");
311
+ }
312
+ async function readGlobalIndex(cacheDir) {
313
+ const indexPath = getGlobalIndexPath(cacheDir);
314
+ try {
315
+ const txt = await fs.readFile(indexPath, "utf-8");
316
+ return JSON.parse(txt);
317
+ } catch {
318
+ return { daemons: [], updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
319
+ }
320
+ }
321
+ async function writeGlobalIndex(index, cacheDir) {
322
+ const indexPath = getGlobalIndexPath(cacheDir);
323
+ const base = path.dirname(indexPath);
324
+ await fs.mkdir(base, { recursive: true });
325
+ await fs.writeFile(indexPath, JSON.stringify(index, null, 2), {
326
+ encoding: "utf-8",
327
+ mode: 384
328
+ });
329
+ }
330
+ async function registerDaemonGlobally(entry, cacheDir) {
331
+ const index = await readGlobalIndex(cacheDir);
332
+ index.daemons = index.daemons.filter((d) => d.projectId !== entry.projectId);
333
+ index.daemons.push(entry);
334
+ index.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
335
+ await writeGlobalIndex(index, cacheDir);
336
+ debugLog(`Registered daemon ${entry.projectId} in global index`);
337
+ }
338
+ async function unregisterDaemonGlobally(projectId, cacheDir) {
339
+ const index = await readGlobalIndex(cacheDir);
340
+ index.daemons = index.daemons.filter((d) => d.projectId !== projectId);
341
+ index.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
342
+ await writeGlobalIndex(index, cacheDir);
343
+ }
344
+ async function cleanupGlobalIndex(cacheDir) {
345
+ const index = await readGlobalIndex(cacheDir);
346
+ const validDaemons = [];
347
+ for (const entry of index.daemons) {
348
+ try {
349
+ process.kill(entry.pid, 0);
350
+ const healthy = await healthCheck(entry.ipcPath, 1e3);
351
+ if (healthy) {
352
+ validDaemons.push(entry);
353
+ } else {
354
+ debugLog(`Removing unhealthy daemon ${entry.projectId} (pid ${entry.pid})`);
355
+ }
356
+ } catch {
357
+ debugLog(`Removing dead daemon ${entry.projectId} (pid ${entry.pid})`);
358
+ }
359
+ }
360
+ if (validDaemons.length !== index.daemons.length) {
361
+ index.daemons = validDaemons;
362
+ index.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
363
+ await writeGlobalIndex(index, cacheDir);
364
+ }
365
+ }
366
+ async function discoverAllDaemons(cacheDir) {
367
+ await cleanupGlobalIndex(cacheDir);
368
+ const index = await readGlobalIndex(cacheDir);
369
+ const results = [];
370
+ for (const entry of index.daemons) {
371
+ const registry = await readRegistry(entry.registryPath);
372
+ if (registry) {
373
+ const healthy = await healthCheck(entry.ipcPath, 2e3);
374
+ results.push({ registry, healthy });
375
+ }
376
+ }
377
+ return results;
378
+ }
379
+
380
+ // src/shared/protocol.ts
381
+ var HELLO_TEST = "HELLO_TEST";
382
+ var HELLO_ACK = "HELLO_ACK";
383
+ var BATCH_MOCK_REQUEST = "BATCH_MOCK_REQUEST";
384
+ var BATCH_MOCK_RESULT = "BATCH_MOCK_RESULT";
385
+ var HEARTBEAT = "HEARTBEAT";
386
+ var HEARTBEAT_ACK = "HEARTBEAT_ACK";
387
+ var RPC_GET_STATUS = "getStatus";
388
+ var RPC_LIST_RUNS = "listRuns";
389
+ var RPC_CLAIM_NEXT_BATCH = "claimNextBatch";
390
+ var RPC_PROVIDE_BATCH = "provideBatch";
391
+ var RPC_RELEASE_BATCH = "releaseBatch";
392
+ var RPC_GET_BATCH = "getBatch";
393
+ var RPC_ERROR_PARSE = -32700;
394
+ var RPC_ERROR_INVALID_REQUEST = -32600;
395
+ var RPC_ERROR_METHOD_NOT_FOUND = -32601;
396
+ var RPC_ERROR_INVALID_PARAMS = -32602;
397
+ var RPC_ERROR_INTERNAL = -32603;
398
+ var RPC_ERROR_NOT_FOUND = -32e3;
399
+ var RPC_ERROR_UNAUTHORIZED = -32001;
400
+ var RPC_ERROR_CONFLICT = -32002;
401
+ var RPC_ERROR_EXPIRED = -32003;
402
+ function isHelloTestMessage(msg) {
403
+ if (!msg || typeof msg !== "object") return false;
404
+ const m = msg;
405
+ return m.type === HELLO_TEST && typeof m.token === "string" && typeof m.runId === "string" && typeof m.pid === "number" && typeof m.cwd === "string";
406
+ }
407
+ function isBatchMockRequestMessage(msg) {
408
+ if (!msg || typeof msg !== "object") return false;
409
+ const m = msg;
410
+ return m.type === BATCH_MOCK_REQUEST && typeof m.runId === "string" && Array.isArray(m.requests);
411
+ }
412
+ function isHeartbeatMessage(msg) {
413
+ if (!msg || typeof msg !== "object") return false;
414
+ const m = msg;
415
+ return m.type === HEARTBEAT && typeof m.runId === "string";
416
+ }
417
+ function isJsonRpcRequest(msg) {
418
+ if (!msg || typeof msg !== "object") return false;
419
+ const m = msg;
420
+ return m.jsonrpc === "2.0" && (typeof m.id === "string" || typeof m.id === "number") && typeof m.method === "string";
421
+ }
422
+
423
+ export { BATCH_MOCK_REQUEST, BATCH_MOCK_RESULT, HEARTBEAT, HEARTBEAT_ACK, HELLO_ACK, HELLO_TEST, RPC_CLAIM_NEXT_BATCH, RPC_ERROR_CONFLICT, RPC_ERROR_EXPIRED, RPC_ERROR_INTERNAL, RPC_ERROR_INVALID_PARAMS, RPC_ERROR_INVALID_REQUEST, RPC_ERROR_METHOD_NOT_FOUND, RPC_ERROR_NOT_FOUND, RPC_ERROR_PARSE, RPC_ERROR_UNAUTHORIZED, RPC_GET_BATCH, RPC_GET_STATUS, RPC_LIST_RUNS, RPC_PROVIDE_BATCH, RPC_RELEASE_BATCH, cleanupGlobalIndex, computeProjectId, discoverAllDaemons, ensureDaemonRunning, getCacheDir, getDaemonEntryPath, getGlobalIndexPath, getPaths, healthCheck, isBatchMockRequestMessage, isHeartbeatMessage, isHelloTestMessage, isJsonRpcRequest, randomToken, readGlobalIndex, readRegistry, registerDaemonGlobally, releaseLock, resolveProjectRoot, sleep, tryAcquireLock, unregisterDaemonGlobally, writeGlobalIndex, writeRegistry };
@@ -0,0 +1,190 @@
1
+ import fssync from 'node:fs';
2
+
3
+ /**
4
+ * Discovery module for mock-mcp Daemon + Adapter architecture.
5
+ *
6
+ * Provides:
7
+ * - Project root resolution (up-search for .git / package.json)
8
+ * - Project ID computation (sha256 of realpath)
9
+ * - Registry/lock file path management
10
+ * - IPC path (Unix Domain Socket / Windows Named Pipe)
11
+ * - ensureDaemonRunning() - atomic daemon startup with lock
12
+ */
13
+
14
+ interface DaemonRegistry {
15
+ projectId: string;
16
+ projectRoot: string;
17
+ ipcPath: string;
18
+ token: string;
19
+ pid: number;
20
+ startedAt: string;
21
+ version: string;
22
+ }
23
+ interface EnsureDaemonOptions {
24
+ projectRoot?: string;
25
+ timeoutMs?: number;
26
+ /** For testing: override cache directory */
27
+ cacheDir?: string;
28
+ }
29
+ /**
30
+ * Resolve the project root by searching upward for .git or package.json.
31
+ * Falls back to the starting directory if nothing is found.
32
+ */
33
+ declare function resolveProjectRoot(startDir?: string): string;
34
+ /**
35
+ * Compute a stable project ID from the project root path.
36
+ * Uses sha256 of the realpath, truncated to 16 characters.
37
+ */
38
+ declare function computeProjectId(projectRoot: string): string;
39
+ /**
40
+ * Get the cache directory for mock-mcp files.
41
+ * Priority: override > MOCK_MCP_CACHE_DIR env > XDG_CACHE_HOME > LOCALAPPDATA (Windows) > ~/.cache
42
+ */
43
+ declare function getCacheDir(override?: string): string;
44
+ interface DaemonPaths {
45
+ base: string;
46
+ registryPath: string;
47
+ lockPath: string;
48
+ ipcPath: string;
49
+ }
50
+ /**
51
+ * Get all relevant paths for a given project ID.
52
+ */
53
+ declare function getPaths(projectId: string, cacheDir?: string): DaemonPaths;
54
+ /**
55
+ * Read the daemon registry file.
56
+ */
57
+ declare function readRegistry(registryPath: string): Promise<DaemonRegistry | null>;
58
+ /**
59
+ * Write the daemon registry file with restricted permissions.
60
+ */
61
+ declare function writeRegistry(registryPath: string, registry: DaemonRegistry): Promise<void>;
62
+ /**
63
+ * Check if the daemon is healthy by making an HTTP request to /health.
64
+ */
65
+ declare function healthCheck(ipcPath: string, timeoutMs?: number): Promise<boolean>;
66
+ /**
67
+ * Try to acquire an exclusive lock file.
68
+ * Returns the file handle if successful, null otherwise.
69
+ */
70
+ declare function tryAcquireLock(lockPath: string): Promise<fssync.promises.FileHandle | null>;
71
+ /**
72
+ * Release the lock file.
73
+ */
74
+ declare function releaseLock(lockPath: string, fh: fssync.promises.FileHandle): Promise<void>;
75
+ /**
76
+ * Generate a random token for daemon authentication.
77
+ */
78
+ declare function randomToken(): string;
79
+ /**
80
+ * Get the path to the daemon entry point.
81
+ *
82
+ * This function handles multiple scenarios:
83
+ * 1. Direct execution from source (development)
84
+ * 2. Execution from dist (production)
85
+ * 3. When re-bundled by another tool (Vitest, esbuild, webpack)
86
+ *
87
+ * We use require.resolve to reliably find the mock-mcp package location,
88
+ * which works even when the code is re-bundled by another tool.
89
+ */
90
+ declare function getDaemonEntryPath(): string;
91
+ /**
92
+ * Ensure the daemon is running for the given project.
93
+ *
94
+ * This function:
95
+ * 1. Checks if a daemon is already running (via registry + health check)
96
+ * 2. If not, acquires a lock and spawns a new daemon
97
+ * 3. Waits for the daemon to become healthy
98
+ * 4. Returns the registry information
99
+ *
100
+ * Thread-safe: multiple processes calling this concurrently will only start one daemon.
101
+ */
102
+ declare function ensureDaemonRunning(opts?: EnsureDaemonOptions): Promise<DaemonRegistry>;
103
+ declare function sleep(ms: number): Promise<void>;
104
+ /**
105
+ * Entry in the global active daemons index.
106
+ */
107
+ interface ActiveDaemonEntry {
108
+ projectId: string;
109
+ projectRoot: string;
110
+ ipcPath: string;
111
+ registryPath: string;
112
+ pid: number;
113
+ startedAt: string;
114
+ version: string;
115
+ }
116
+ /**
117
+ * Global index of all active daemons.
118
+ */
119
+ interface ActiveDaemonsIndex {
120
+ daemons: ActiveDaemonEntry[];
121
+ updatedAt: string;
122
+ }
123
+ /**
124
+ * Get the path to the global active daemons index file.
125
+ */
126
+ declare function getGlobalIndexPath(cacheDir?: string): string;
127
+ /**
128
+ * Read the global active daemons index.
129
+ */
130
+ declare function readGlobalIndex(cacheDir?: string): Promise<ActiveDaemonsIndex>;
131
+ /**
132
+ * Write the global active daemons index.
133
+ */
134
+ declare function writeGlobalIndex(index: ActiveDaemonsIndex, cacheDir?: string): Promise<void>;
135
+ /**
136
+ * Register a daemon in the global index.
137
+ * Called when a daemon starts.
138
+ */
139
+ declare function registerDaemonGlobally(entry: ActiveDaemonEntry, cacheDir?: string): Promise<void>;
140
+ /**
141
+ * Unregister a daemon from the global index.
142
+ * Called when a daemon stops.
143
+ */
144
+ declare function unregisterDaemonGlobally(projectId: string, cacheDir?: string): Promise<void>;
145
+ /**
146
+ * Clean up stale entries from the global index.
147
+ * Removes entries where the daemon is no longer running.
148
+ */
149
+ declare function cleanupGlobalIndex(cacheDir?: string): Promise<void>;
150
+ /**
151
+ * Discover all active daemons.
152
+ * Returns list of daemons with their connection info.
153
+ */
154
+ declare function discoverAllDaemons(cacheDir?: string): Promise<{
155
+ registry: DaemonRegistry;
156
+ healthy: boolean;
157
+ }[]>;
158
+
159
+ /**
160
+ * Core types for mock-mcp.
161
+ */
162
+ /**
163
+ * Shape of a mock request emitted by the test process.
164
+ */
165
+ interface MockRequestDescriptor {
166
+ requestId: string;
167
+ endpoint: string;
168
+ method: string;
169
+ body?: unknown;
170
+ headers?: Record<string, string>;
171
+ metadata?: Record<string, unknown>;
172
+ }
173
+ /**
174
+ * Shape of the mock data that needs to be returned for a request.
175
+ */
176
+ interface MockResponseDescriptor {
177
+ requestId: string;
178
+ data: unknown;
179
+ status?: number;
180
+ headers?: Record<string, string>;
181
+ delayMs?: number;
182
+ }
183
+ /**
184
+ * Resolved mock with typed data.
185
+ */
186
+ interface ResolvedMock<T = unknown> extends Omit<MockResponseDescriptor, "data"> {
187
+ data: T;
188
+ }
189
+
190
+ export { type ActiveDaemonEntry as A, type DaemonRegistry as D, type EnsureDaemonOptions as E, type MockResponseDescriptor as M, type ResolvedMock as R, type MockRequestDescriptor as a, getPaths as b, computeProjectId as c, readRegistry as d, releaseLock as e, randomToken as f, getCacheDir as g, healthCheck as h, getDaemonEntryPath as i, ensureDaemonRunning as j, getGlobalIndexPath as k, readGlobalIndex as l, writeGlobalIndex as m, registerDaemonGlobally as n, cleanupGlobalIndex as o, discoverAllDaemons as p, type DaemonPaths as q, resolveProjectRoot as r, sleep as s, tryAcquireLock as t, unregisterDaemonGlobally as u, type ActiveDaemonsIndex as v, writeRegistry as w };