@introspection-ai/recipes 0.13.0 → 0.14.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 (222) hide show
  1. package/README.md +46 -57
  2. package/dist/{agent-tool.d.ts → agents.d.ts} +4 -1
  3. package/dist/agents.d.ts.map +1 -0
  4. package/dist/{agent-tool.js → agents.js} +1 -1
  5. package/dist/agents.js.map +1 -0
  6. package/dist/api/extensions.d.ts +3 -0
  7. package/dist/api/extensions.d.ts.map +1 -0
  8. package/dist/api/extensions.js +2 -0
  9. package/dist/api/extensions.js.map +1 -0
  10. package/dist/api/mcp.d.ts +4 -0
  11. package/dist/api/mcp.d.ts.map +1 -0
  12. package/dist/api/mcp.js +2 -0
  13. package/dist/api/mcp.js.map +1 -0
  14. package/dist/api/session.d.ts +3 -0
  15. package/dist/api/session.d.ts.map +1 -0
  16. package/dist/api/session.js +2 -0
  17. package/dist/api/session.js.map +1 -0
  18. package/dist/child-agent.d.ts +5 -4
  19. package/dist/child-agent.d.ts.map +1 -1
  20. package/dist/child-agent.js +26 -117
  21. package/dist/child-agent.js.map +1 -1
  22. package/dist/child-session.d.ts +23 -0
  23. package/dist/child-session.d.ts.map +1 -0
  24. package/dist/child-session.js +48 -0
  25. package/dist/child-session.js.map +1 -0
  26. package/dist/extensions.d.ts +29 -0
  27. package/dist/extensions.d.ts.map +1 -0
  28. package/dist/extensions.js +119 -0
  29. package/dist/extensions.js.map +1 -0
  30. package/dist/index.d.ts +4 -13
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +2 -13
  33. package/dist/index.js.map +1 -1
  34. package/dist/inspect.d.ts +38 -5
  35. package/dist/inspect.d.ts.map +1 -1
  36. package/dist/inspect.js +92 -41
  37. package/dist/inspect.js.map +1 -1
  38. package/dist/interactions.d.ts +18 -2
  39. package/dist/interactions.d.ts.map +1 -1
  40. package/dist/interactions.js +41 -10
  41. package/dist/interactions.js.map +1 -1
  42. package/dist/mcp-catalog.d.ts +1 -0
  43. package/dist/mcp-catalog.d.ts.map +1 -1
  44. package/dist/mcp-catalog.js +14 -6
  45. package/dist/mcp-catalog.js.map +1 -1
  46. package/dist/mcp-chunks/auth-command-ZLGYIEUY.js +273 -0
  47. package/dist/mcp-chunks/call-arguments-LU3W6D3K.js +13 -0
  48. package/dist/mcp-chunks/call-command-LKTKSYTH.js +612 -0
  49. package/dist/mcp-chunks/chunk-2U4BSDC4.js +3951 -0
  50. package/dist/mcp-chunks/chunk-2XL7MG7R.js +272 -0
  51. package/dist/mcp-chunks/chunk-3BJA27PY.js +2321 -0
  52. package/dist/mcp-chunks/chunk-4SQKWYFF.js +15 -0
  53. package/dist/mcp-chunks/chunk-5QOSCSQB.js +290 -0
  54. package/dist/mcp-chunks/chunk-6W5QFASN.js +77 -0
  55. package/dist/mcp-chunks/chunk-6XCZGU3G.js +1004 -0
  56. package/dist/mcp-chunks/chunk-7FHEBT5G.js +149 -0
  57. package/dist/mcp-chunks/chunk-7KA2VJVZ.js +114 -0
  58. package/dist/mcp-chunks/chunk-7UDVSOOF.js +138 -0
  59. package/dist/mcp-chunks/chunk-BNV3TPE4.js +66 -0
  60. package/dist/mcp-chunks/chunk-BTLAWHTA.js +129 -0
  61. package/dist/mcp-chunks/chunk-BV56DXPW.js +13 -0
  62. package/dist/mcp-chunks/chunk-BWHIA4PC.js +90 -0
  63. package/dist/mcp-chunks/chunk-BZIQOWE6.js +267 -0
  64. package/dist/mcp-chunks/chunk-D3MRAZ6H.js +16140 -0
  65. package/dist/mcp-chunks/chunk-DHBE7UBH.js +750 -0
  66. package/dist/mcp-chunks/chunk-E22DYOW5.js +251 -0
  67. package/dist/mcp-chunks/chunk-ECIVB7P4.js +139 -0
  68. package/dist/mcp-chunks/chunk-HKRJ6O36.js +1076 -0
  69. package/dist/mcp-chunks/chunk-IABRYROW.js +44 -0
  70. package/dist/mcp-chunks/chunk-J6LYIZXN.js +45 -0
  71. package/dist/mcp-chunks/chunk-L7NTQVYN.js +161 -0
  72. package/dist/mcp-chunks/chunk-LEZSACDE.js +141 -0
  73. package/dist/mcp-chunks/chunk-LKIBAYKU.js +208 -0
  74. package/dist/mcp-chunks/chunk-LNH5LAJD.js +152 -0
  75. package/dist/mcp-chunks/chunk-LQQEYD76.js +124 -0
  76. package/dist/mcp-chunks/chunk-LRKZP7DJ.js +47 -0
  77. package/dist/mcp-chunks/chunk-LS7PMWSR.js +109 -0
  78. package/dist/mcp-chunks/chunk-M3TPODXI.js +95 -0
  79. package/dist/mcp-chunks/chunk-MZ2NARG7.js +400 -0
  80. package/dist/mcp-chunks/chunk-N46NEFAF.js +45 -0
  81. package/dist/mcp-chunks/chunk-NNOEGEUZ.js +17273 -0
  82. package/dist/mcp-chunks/chunk-NRAJX5E4.js +69 -0
  83. package/dist/mcp-chunks/chunk-P3PSC54P.js +869 -0
  84. package/dist/mcp-chunks/chunk-PIIYJR44.js +6341 -0
  85. package/dist/mcp-chunks/chunk-PWYFKGKG.js +385 -0
  86. package/dist/mcp-chunks/chunk-SJCCBNZ4.js +45 -0
  87. package/dist/mcp-chunks/chunk-TATVT5IT.js +194 -0
  88. package/dist/mcp-chunks/chunk-TEUKFFQL.js +29 -0
  89. package/dist/mcp-chunks/chunk-UKYNQGNE.js +150 -0
  90. package/dist/mcp-chunks/chunk-UNKB4SBV.js +37 -0
  91. package/dist/mcp-chunks/chunk-WD6BOD24.js +138 -0
  92. package/dist/mcp-chunks/chunk-WQJZMCJN.js +73 -0
  93. package/dist/mcp-chunks/chunk-XANPGTLI.js +364 -0
  94. package/dist/mcp-chunks/chunk-XQK2JSW4.js +35 -0
  95. package/dist/mcp-chunks/chunk-YHQUTU6L.js +571 -0
  96. package/dist/mcp-chunks/chunk-YVZKMV5H.js +57 -0
  97. package/dist/mcp-chunks/cli-UV4QJZZM.js +929 -0
  98. package/dist/mcp-chunks/client-S7RQUG5U.js +13 -0
  99. package/dist/mcp-chunks/config-command-NLAGKID5.js +1101 -0
  100. package/dist/mcp-chunks/daemon-command-U242Z6VY.js +26 -0
  101. package/dist/mcp-chunks/dist-LK3SHAMX.js +6478 -0
  102. package/dist/mcp-chunks/emit-ts-command-VBXVXO6V.js +386 -0
  103. package/dist/mcp-chunks/generate-cli-runner-6CAZZA3P.js +1426 -0
  104. package/dist/mcp-chunks/inspect-cli-command-3ZORTOR7.js +111 -0
  105. package/dist/mcp-chunks/launch-YKRXHXJI.js +10 -0
  106. package/dist/mcp-chunks/lifecycle-XVKRHPH3.js +16 -0
  107. package/dist/mcp-chunks/list-command-2MEGGNFQ.js +4190 -0
  108. package/dist/mcp-chunks/output-utils-NKESLAJ6.js +12 -0
  109. package/dist/mcp-chunks/prompt-B1Yc1NPt-VGOTWJMN.js +918 -0
  110. package/dist/mcp-chunks/record-command-YXHTTTT5.js +17 -0
  111. package/dist/mcp-chunks/replay-command-25HJ6AQU.js +85 -0
  112. package/dist/mcp-chunks/resource-command-OO4NYF5C.js +97 -0
  113. package/dist/mcp-chunks/result-utils-MOHHLNM7.js +15 -0
  114. package/dist/mcp-chunks/runtime-5WJ3G25Y.js +33 -0
  115. package/dist/mcp-chunks/runtime-wrapper-RPWDIDBF.js +10 -0
  116. package/dist/mcp-chunks/serve-command-DHXJZMMN.js +3302 -0
  117. package/dist/mcp-chunks/timeouts-W2XWCCXR.js +18 -0
  118. package/dist/mcp-chunks/vault-command-PIYIUEZL.js +154 -0
  119. package/dist/mcp-daemon-client.d.ts +6 -1
  120. package/dist/mcp-daemon-client.d.ts.map +1 -1
  121. package/dist/mcp-daemon-client.js +119 -2
  122. package/dist/mcp-daemon-client.js.map +1 -1
  123. package/dist/mcp-daemon-protocol.d.ts +18 -1
  124. package/dist/mcp-daemon-protocol.d.ts.map +1 -1
  125. package/dist/mcp-daemon-protocol.js +31 -0
  126. package/dist/mcp-daemon-protocol.js.map +1 -1
  127. package/dist/mcp-daemon.js +169 -76602
  128. package/dist/mcp-daemon.js.map +1 -1
  129. package/dist/mcp-policy.d.ts +11 -0
  130. package/dist/mcp-policy.d.ts.map +1 -0
  131. package/dist/mcp-policy.js +27 -0
  132. package/dist/mcp-policy.js.map +1 -0
  133. package/dist/mcp-run-worker.js +38 -76540
  134. package/dist/mcp-tools.d.ts +39 -0
  135. package/dist/mcp-tools.d.ts.map +1 -0
  136. package/dist/mcp-tools.js +493 -0
  137. package/dist/mcp-tools.js.map +1 -0
  138. package/dist/mcp.d.ts +21 -9
  139. package/dist/mcp.d.ts.map +1 -1
  140. package/dist/mcp.js +101 -31
  141. package/dist/mcp.js.map +1 -1
  142. package/dist/model-binding.d.ts +39 -0
  143. package/dist/model-binding.d.ts.map +1 -0
  144. package/dist/model-binding.js +103 -0
  145. package/dist/model-binding.js.map +1 -0
  146. package/dist/pi-extension.d.ts +2 -22
  147. package/dist/pi-extension.d.ts.map +1 -1
  148. package/dist/pi-extension.js +296 -71
  149. package/dist/pi-extension.js.map +1 -1
  150. package/dist/recipe/resolve.d.ts +43 -20
  151. package/dist/recipe/resolve.d.ts.map +1 -1
  152. package/dist/recipe/resolve.js +215 -70
  153. package/dist/recipe/resolve.js.map +1 -1
  154. package/dist/recipe-agent.d.ts +24 -27
  155. package/dist/recipe-agent.d.ts.map +1 -1
  156. package/dist/recipe-agent.js +278 -230
  157. package/dist/recipe-agent.js.map +1 -1
  158. package/dist/recipe-check.d.ts +18 -0
  159. package/dist/recipe-check.d.ts.map +1 -0
  160. package/dist/recipe-check.js +261 -0
  161. package/dist/recipe-check.js.map +1 -0
  162. package/dist/recipe-extensions.d.ts.map +1 -1
  163. package/dist/recipe-extensions.js +0 -3
  164. package/dist/recipe-extensions.js.map +1 -1
  165. package/dist/recipe-model.d.ts +3 -1
  166. package/dist/recipe-model.d.ts.map +1 -1
  167. package/dist/recipe-model.js +15 -12
  168. package/dist/recipe-model.js.map +1 -1
  169. package/dist/recipe-package.d.ts +6 -28
  170. package/dist/recipe-package.d.ts.map +1 -1
  171. package/dist/recipe-package.js +248 -191
  172. package/dist/recipe-package.js.map +1 -1
  173. package/dist/recipe-skills.d.ts.map +1 -1
  174. package/dist/recipe-skills.js +42 -29
  175. package/dist/recipe-skills.js.map +1 -1
  176. package/dist/run-controller.d.ts +10 -9
  177. package/dist/run-controller.d.ts.map +1 -1
  178. package/dist/run-controller.js +28 -21
  179. package/dist/run-controller.js.map +1 -1
  180. package/dist/session.d.ts +40 -53
  181. package/dist/session.d.ts.map +1 -1
  182. package/dist/session.js +312 -173
  183. package/dist/session.js.map +1 -1
  184. package/dist/test-utils.d.ts +7 -7
  185. package/dist/test-utils.d.ts.map +1 -1
  186. package/dist/test-utils.js +32 -15
  187. package/dist/test-utils.js.map +1 -1
  188. package/docs/agent-composition.md +41 -35
  189. package/docs/host-api.md +201 -0
  190. package/docs/index.md +9 -14
  191. package/docs/interactions.md +12 -11
  192. package/docs/mcp-auth.md +6 -6
  193. package/docs/mcp-configuration.md +73 -25
  194. package/docs/pi-extension.md +39 -11
  195. package/docs/recipe-flow.md +9 -9
  196. package/docs/recipe-format.md +106 -41
  197. package/docs/recipe-judges.md +24 -36
  198. package/package.json +23 -17
  199. package/vendor/mcp-client/darwin-arm64/mcp-client +0 -0
  200. package/vendor/mcp-client/darwin-x64/mcp-client +0 -0
  201. package/vendor/mcp-client/linux-arm64/mcp-client +0 -0
  202. package/vendor/recipe-check/darwin-arm64/recipe-check +0 -0
  203. package/vendor/recipe-check/darwin-x64/recipe-check +0 -0
  204. package/vendor/recipe-check/linux-arm64/recipe-check +0 -0
  205. package/vendor/recipe-check/linux-x64/recipe-check +0 -0
  206. package/vendor/recipe-check/linux-x64-musl/recipe-check +0 -0
  207. package/vendor/recipe-check/win32-x64/recipe-check.exe +0 -0
  208. package/dist/agent-tool.d.ts.map +0 -1
  209. package/dist/agent-tool.js.map +0 -1
  210. package/dist/run.d.ts +0 -28
  211. package/dist/run.d.ts.map +0 -1
  212. package/dist/run.js +0 -84
  213. package/dist/run.js.map +0 -1
  214. package/dist/testing.d.ts +0 -25
  215. package/dist/testing.d.ts.map +0 -1
  216. package/dist/testing.js +0 -96
  217. package/dist/testing.js.map +0 -1
  218. package/docs/deployment-configuration.md +0 -65
  219. package/docs/migration.md +0 -70
  220. package/docs/python-bindings-release.md +0 -47
  221. package/docs/recipe-evals.md +0 -70
  222. package/docs/runtime-library.md +0 -171
@@ -0,0 +1,1004 @@
1
+ import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
+ import {
3
+ createRuntime
4
+ } from "./chunk-NNOEGEUZ.js";
5
+ import {
6
+ listConfigLayerPaths,
7
+ loadDaemonConfig
8
+ } from "./chunk-2U4BSDC4.js";
9
+ import {
10
+ launchDaemonDetached
11
+ } from "./chunk-NRAJX5E4.js";
12
+ import {
13
+ DaemonClient,
14
+ getDaemonLogPath,
15
+ resolveDaemonPaths
16
+ } from "./chunk-PWYFKGKG.js";
17
+ import {
18
+ readJsonFile,
19
+ withFileLock,
20
+ writeJsonFile
21
+ } from "./chunk-2XL7MG7R.js";
22
+ import {
23
+ expandHome
24
+ } from "./chunk-ECIVB7P4.js";
25
+ import {
26
+ isKeepAliveServer,
27
+ keepAliveIdleTimeout
28
+ } from "./chunk-LS7PMWSR.js";
29
+
30
+ // node_modules/.pnpm/mcporter@0.12.3/node_modules/mcporter/dist/cli/daemon-command.js
31
+ import fsPromises from "node:fs/promises";
32
+ import path4 from "node:path";
33
+
34
+ // node_modules/.pnpm/mcporter@0.12.3/node_modules/mcporter/dist/daemon/host.js
35
+ import { randomUUID } from "node:crypto";
36
+ import fs2 from "node:fs/promises";
37
+ import net from "node:net";
38
+ import path3 from "node:path";
39
+
40
+ // node_modules/.pnpm/mcporter@0.12.3/node_modules/mcporter/dist/daemon/config-layers.js
41
+ import fs from "node:fs/promises";
42
+ import path from "node:path";
43
+ async function statConfigMtime(configPath) {
44
+ try {
45
+ const stats = await fs.stat(configPath);
46
+ return stats.mtimeMs;
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+ async function collectConfigLayers(options) {
52
+ const layerPaths = await listConfigLayerPaths(options, options.rootDir ?? process.cwd());
53
+ const entries = [];
54
+ for (const layerPath of layerPaths) {
55
+ entries.push({ path: layerPath, mtimeMs: await statConfigMtime(layerPath) });
56
+ }
57
+ if (entries.length === 0 && options.configPath) {
58
+ entries.push({ path: path.resolve(options.configPath), mtimeMs: await statConfigMtime(options.configPath) });
59
+ }
60
+ return entries;
61
+ }
62
+
63
+ // node_modules/.pnpm/mcporter@0.12.3/node_modules/mcporter/dist/daemon/definition-hash.js
64
+ import { createHash } from "node:crypto";
65
+ function hashDaemonDefinitions(definitions) {
66
+ const sorted = definitions.toSorted((a, b) => a.name.localeCompare(b.name));
67
+ return createHash("sha256").update(stableJsonStringify(sorted)).digest("hex").slice(0, 16);
68
+ }
69
+ function stableJsonStringify(value) {
70
+ const json = JSON.stringify(sortJsonValue(value));
71
+ if (json === void 0) {
72
+ throw new TypeError("Cannot serialize unsupported JSON root value.");
73
+ }
74
+ return json;
75
+ }
76
+ function sortJsonValue(value) {
77
+ if (Array.isArray(value)) {
78
+ return value.map((entry) => sortJsonValue(entry));
79
+ }
80
+ if (!isPlainObject(value)) {
81
+ return value;
82
+ }
83
+ const result = {};
84
+ for (const key of Object.keys(value).toSorted()) {
85
+ const entry = value[key];
86
+ if (entry !== void 0) {
87
+ result[key] = sortJsonValue(entry);
88
+ }
89
+ }
90
+ return result;
91
+ }
92
+ function isPlainObject(value) {
93
+ if (!value || typeof value !== "object") {
94
+ return false;
95
+ }
96
+ const prototype = Object.getPrototypeOf(value);
97
+ return prototype === Object.prototype || prototype === null;
98
+ }
99
+
100
+ // node_modules/.pnpm/mcporter@0.12.3/node_modules/mcporter/dist/daemon/log-context.js
101
+ import fsSync from "node:fs";
102
+ import path2 from "node:path";
103
+ function createLogContext(options) {
104
+ const derivedEnabled = options.enabled || options.logAllServers || options.servers.size > 0;
105
+ const context = {
106
+ enabled: derivedEnabled,
107
+ logAllServers: options.logAllServers,
108
+ servers: options.servers
109
+ };
110
+ if (derivedEnabled && options.logPath) {
111
+ try {
112
+ fsSync.mkdirSync(path2.dirname(options.logPath), { recursive: true });
113
+ context.writer = fsSync.createWriteStream(options.logPath, {
114
+ flags: "a"
115
+ });
116
+ } catch (error) {
117
+ console.warn(`[daemon] Failed to open log file ${options.logPath}: ${error.message}`);
118
+ }
119
+ }
120
+ return context;
121
+ }
122
+ function logEvent(context, message) {
123
+ if (!context.enabled) {
124
+ return;
125
+ }
126
+ const line = `[daemon] ${(/* @__PURE__ */ new Date()).toISOString()} ${message}`;
127
+ console.log(line);
128
+ try {
129
+ context.writer?.write(`${line}
130
+ `);
131
+ } catch {
132
+ }
133
+ }
134
+ async function disposeLogContext(context) {
135
+ const writer = context.writer;
136
+ if (!writer) {
137
+ return;
138
+ }
139
+ await new Promise((resolve) => {
140
+ writer.end(() => resolve());
141
+ writer.on("error", () => resolve());
142
+ });
143
+ }
144
+ function shouldLogServer(context, server) {
145
+ if (!context.enabled) {
146
+ return false;
147
+ }
148
+ if (context.logAllServers) {
149
+ return true;
150
+ }
151
+ return context.servers.has(server);
152
+ }
153
+ function formatError(error) {
154
+ if (error instanceof Error) {
155
+ return error.message;
156
+ }
157
+ if (typeof error === "string") {
158
+ return error;
159
+ }
160
+ return "unknown";
161
+ }
162
+
163
+ // node_modules/.pnpm/mcporter@0.12.3/node_modules/mcporter/dist/daemon/request-utils.js
164
+ function ensureManaged(server, managedServers) {
165
+ if (!managedServers.has(server)) {
166
+ throw new Error(`Server '${server}' is not managed by the daemon.`);
167
+ }
168
+ }
169
+ function markActivity(server, activity) {
170
+ const entry = activity.get(server);
171
+ if (entry) {
172
+ entry.connected = true;
173
+ entry.lastUsedAt = Date.now();
174
+ } else {
175
+ activity.set(server, { connected: true, lastUsedAt: Date.now() });
176
+ }
177
+ }
178
+ async function evictIdleServers(runtime, managedServers, activity) {
179
+ const now = Date.now();
180
+ await Promise.all(Array.from(managedServers.entries()).map(async ([name, definition]) => {
181
+ const timeout = keepAliveIdleTimeout(definition);
182
+ if (!timeout) {
183
+ return;
184
+ }
185
+ const entry = activity.get(name);
186
+ if (!entry?.lastUsedAt) {
187
+ return;
188
+ }
189
+ if (now - entry.lastUsedAt < timeout) {
190
+ return;
191
+ }
192
+ await runtime.close(name).catch(() => {
193
+ });
194
+ activity.set(name, { connected: false });
195
+ }));
196
+ }
197
+ function shouldShutdownDaemonForIdle(lastActivityAt, now, idleTimeoutMs, activeRequests = 0) {
198
+ return activeRequests <= 0 && typeof idleTimeoutMs === "number" && idleTimeoutMs > 0 && now - lastActivityAt >= idleTimeoutMs;
199
+ }
200
+ function daemonIdleWatcherInterval(idleTimeoutMs) {
201
+ if (!idleTimeoutMs) {
202
+ return 3e4;
203
+ }
204
+ return Math.min(3e4, Math.max(100, Math.floor(idleTimeoutMs / 2)));
205
+ }
206
+ function buildErrorResponse(id, code, error) {
207
+ let message = code;
208
+ if (error instanceof Error) {
209
+ message = error.message;
210
+ } else if (typeof error === "string") {
211
+ message = error;
212
+ }
213
+ return {
214
+ id,
215
+ ok: false,
216
+ error: {
217
+ code,
218
+ message
219
+ }
220
+ };
221
+ }
222
+
223
+ // node_modules/.pnpm/mcporter@0.12.3/node_modules/mcporter/dist/daemon/host.js
224
+ async function runDaemonHost(options) {
225
+ const configLayers = await collectConfigLayers({
226
+ configPath: options.configExplicit ? options.configPath : void 0,
227
+ rootDir: options.rootDir
228
+ });
229
+ const daemonConfig = await loadDaemonConfig({
230
+ configPath: options.configExplicit ? options.configPath : void 0,
231
+ rootDir: options.rootDir
232
+ });
233
+ const runtime = await createRuntime({
234
+ configPath: options.configExplicit ? options.configPath : void 0,
235
+ rootDir: options.rootDir
236
+ });
237
+ const keepAliveDefinitions = runtime.getDefinitions().filter(isKeepAliveServer);
238
+ const definitionHash = hashDaemonDefinitions(keepAliveDefinitions);
239
+ if (keepAliveDefinitions.length === 0) {
240
+ throw new Error("No MCP servers require keep-alive; daemon will not start.");
241
+ }
242
+ const managedServers = /* @__PURE__ */ new Map();
243
+ for (const definition of keepAliveDefinitions) {
244
+ managedServers.set(definition.name, definition);
245
+ }
246
+ const serverLoggingOverrides = /* @__PURE__ */ new Set();
247
+ for (const definition of keepAliveDefinitions) {
248
+ if (definition.logging?.daemon?.enabled) {
249
+ serverLoggingOverrides.add(definition.name);
250
+ }
251
+ }
252
+ const combinedServerLogs = /* @__PURE__ */ new Set([
253
+ ...serverLoggingOverrides,
254
+ ...options.logServers ? Array.from(options.logServers) : []
255
+ ]);
256
+ const logContext = createLogContext({
257
+ enabled: Boolean(options.logPath),
258
+ logAllServers: options.logAllServers ?? false,
259
+ servers: combinedServerLogs,
260
+ logPath: options.logPath
261
+ });
262
+ await fs2.mkdir(path3.dirname(options.metadataPath), { recursive: true });
263
+ const configMtimeMs = await statConfigMtime(options.configPath);
264
+ const activity = /* @__PURE__ */ new Map();
265
+ for (const definition of keepAliveDefinitions) {
266
+ activity.set(definition.name, { connected: false });
267
+ }
268
+ let shuttingDown = false;
269
+ let idleWatcher;
270
+ const shutdown = async () => {
271
+ if (shuttingDown) {
272
+ return;
273
+ }
274
+ shuttingDown = true;
275
+ logEvent(logContext, "Shutting down daemon host.");
276
+ if (idleWatcher) {
277
+ clearInterval(idleWatcher);
278
+ }
279
+ server.close();
280
+ await runtime.close().catch(() => {
281
+ });
282
+ await disposeLogContext(logContext).catch(() => {
283
+ });
284
+ await cleanupArtifacts(options);
285
+ process.exit(0);
286
+ };
287
+ let lastDaemonActivityAt = Date.now();
288
+ let activeDaemonRequests = 0;
289
+ idleWatcher = setInterval(() => {
290
+ void (async () => {
291
+ await evictIdleServers(runtime, managedServers, activity);
292
+ if (shouldShutdownDaemonForIdle(lastDaemonActivityAt, Date.now(), daemonConfig.idleTimeoutMs, activeDaemonRequests)) {
293
+ logEvent(logContext, "Daemon idle timeout reached.");
294
+ await shutdown();
295
+ }
296
+ })();
297
+ }, daemonIdleWatcherInterval(daemonConfig.idleTimeoutMs));
298
+ idleWatcher.unref();
299
+ logEvent(logContext, "Daemon host started.");
300
+ const startedAt = Date.now();
301
+ const server = net.createServer({ allowHalfOpen: true }, (socket) => {
302
+ socket.setEncoding("utf8");
303
+ let buffer = "";
304
+ let handled = false;
305
+ const tryHandle = () => {
306
+ if (handled) {
307
+ return;
308
+ }
309
+ const trimmed = buffer.trim();
310
+ if (trimmed.length === 0) {
311
+ return;
312
+ }
313
+ let parsedRequest;
314
+ try {
315
+ parsedRequest = JSON.parse(trimmed);
316
+ } catch {
317
+ return;
318
+ }
319
+ handled = true;
320
+ lastDaemonActivityAt = Date.now();
321
+ activeDaemonRequests += 1;
322
+ void handleSocketRequest(trimmed, socket, runtime, managedServers, activity, {
323
+ configPath: options.configPath,
324
+ configLayers,
325
+ socketPath: options.socketPath,
326
+ startedAt,
327
+ logPath: options.logPath ?? null,
328
+ configMtimeMs,
329
+ definitionHash
330
+ }, logContext, shutdown, parsedRequest).finally(() => {
331
+ activeDaemonRequests -= 1;
332
+ lastDaemonActivityAt = Date.now();
333
+ });
334
+ };
335
+ socket.on("data", (chunk) => {
336
+ buffer += chunk;
337
+ tryHandle();
338
+ });
339
+ socket.on("end", () => {
340
+ if (!handled) {
341
+ tryHandle();
342
+ }
343
+ });
344
+ socket.on("error", () => {
345
+ socket.destroy();
346
+ });
347
+ });
348
+ let claimed = false;
349
+ await withFileLock(`${options.metadataPath}.bind`, async () => {
350
+ const live = await probeLiveDaemon(options.socketPath);
351
+ if (live) {
352
+ if (daemonConfigMatches(live, configLayers, options.configPath, configMtimeMs, definitionHash)) {
353
+ if (!await metadataMatches(options.metadataPath, live)) {
354
+ await writeJsonFile(options.metadataPath, metadataFromStatus(live, configLayers));
355
+ }
356
+ return;
357
+ }
358
+ await stopLiveDaemon(options.socketPath, live.pid);
359
+ }
360
+ await prepareSocket(options.socketPath);
361
+ await new Promise((resolve, reject) => {
362
+ server.once("error", reject);
363
+ server.listen(options.socketPath, () => {
364
+ server.off("error", reject);
365
+ resolve();
366
+ });
367
+ });
368
+ await writeJsonFile(options.metadataPath, {
369
+ pid: process.pid,
370
+ socketPath: options.socketPath,
371
+ configPath: options.configPath,
372
+ configLayers,
373
+ startedAt: Date.now(),
374
+ logPath: options.logPath ?? null,
375
+ configMtimeMs,
376
+ definitionHash
377
+ });
378
+ claimed = true;
379
+ });
380
+ if (!claimed) {
381
+ logEvent(logContext, "Daemon already running for this config; exiting without rebinding.");
382
+ server.close();
383
+ await runtime.close().catch(() => {
384
+ });
385
+ await disposeLogContext(logContext).catch(() => {
386
+ });
387
+ process.exit(0);
388
+ }
389
+ process.once("SIGINT", shutdown);
390
+ process.once("SIGTERM", shutdown);
391
+ process.once("SIGQUIT", shutdown);
392
+ }
393
+ var DAEMON_PROBE_TIMEOUT_MS = 2e3;
394
+ async function probeLiveDaemon(socketPath) {
395
+ const status = await probeDaemonStatus(socketPath);
396
+ if (!status || status.socketPath !== socketPath || !isProcessAlive(status.pid)) {
397
+ return null;
398
+ }
399
+ return status;
400
+ }
401
+ async function metadataMatches(metadataPath, live) {
402
+ try {
403
+ const existing = await readJsonFile(metadataPath);
404
+ return existing?.pid === live.pid && existing?.socketPath === live.socketPath;
405
+ } catch {
406
+ return false;
407
+ }
408
+ }
409
+ function metadataFromStatus(status, fallbackConfigLayers) {
410
+ return {
411
+ pid: status.pid,
412
+ socketPath: status.socketPath,
413
+ configPath: status.configPath,
414
+ configLayers: status.configLayers && status.configLayers.length > 0 ? status.configLayers : fallbackConfigLayers,
415
+ startedAt: status.startedAt,
416
+ logPath: status.logPath ?? null,
417
+ configMtimeMs: status.configMtimeMs ?? null,
418
+ definitionHash: status.definitionHash
419
+ };
420
+ }
421
+ function daemonConfigMatches(live, currentLayers, currentConfigPath, currentConfigMtimeMs, currentDefinitionHash) {
422
+ if (live.definitionHash !== currentDefinitionHash) {
423
+ return false;
424
+ }
425
+ const liveLayers = normalizeLayers(live.configLayers && live.configLayers.length > 0 ? live.configLayers : [{ path: live.configPath, mtimeMs: live.configMtimeMs ?? null }]);
426
+ const expectedLayers = normalizeLayers(currentLayers.length > 0 ? currentLayers : [{ path: currentConfigPath, mtimeMs: currentConfigMtimeMs }]);
427
+ if (liveLayers.length !== expectedLayers.length) {
428
+ return false;
429
+ }
430
+ return liveLayers.every((entry, index) => {
431
+ const expected = expectedLayers[index];
432
+ return Boolean(expected && entry.path === expected.path && entry.mtimeMs === expected.mtimeMs);
433
+ });
434
+ }
435
+ function normalizeLayers(layers) {
436
+ const normalized = layers.map((entry) => ({
437
+ path: path3.isAbsolute(entry.path) ? entry.path : path3.resolve(entry.path),
438
+ mtimeMs: entry.mtimeMs ?? null
439
+ }));
440
+ if (normalized.length < 2) {
441
+ return normalized;
442
+ }
443
+ return normalized.toSorted((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
444
+ }
445
+ async function stopLiveDaemon(socketPath, livePid) {
446
+ const stopped = await sendDaemonStop(socketPath);
447
+ if (!stopped) {
448
+ throw new Error("Live daemon did not accept stop before rebinding.");
449
+ }
450
+ const deadline = Date.now() + 5e3;
451
+ while (Date.now() < deadline) {
452
+ if (!isProcessAlive(livePid)) {
453
+ return;
454
+ }
455
+ await delay(100);
456
+ }
457
+ throw new Error("Live daemon did not stop before rebinding.");
458
+ }
459
+ async function sendDaemonStop(socketPath) {
460
+ return await new Promise((resolve) => {
461
+ const request = {
462
+ id: randomUUID(),
463
+ method: "stop",
464
+ params: {}
465
+ };
466
+ const socket = net.createConnection(socketPath);
467
+ let buffer = "";
468
+ let settled = false;
469
+ const finish = (result) => {
470
+ if (settled) {
471
+ return;
472
+ }
473
+ settled = true;
474
+ socket.removeAllListeners();
475
+ socket.destroy();
476
+ resolve(result);
477
+ };
478
+ socket.setTimeout(DAEMON_PROBE_TIMEOUT_MS, () => finish(false));
479
+ socket.once("connect", () => {
480
+ socket.write(JSON.stringify(request));
481
+ });
482
+ socket.on("data", (chunk) => {
483
+ buffer += chunk.toString();
484
+ });
485
+ socket.once("end", () => {
486
+ try {
487
+ const response = JSON.parse(buffer.trim());
488
+ finish(response.ok);
489
+ } catch {
490
+ finish(false);
491
+ }
492
+ });
493
+ socket.once("error", () => finish(false));
494
+ });
495
+ }
496
+ function delay(ms) {
497
+ return new Promise((resolve) => {
498
+ setTimeout(resolve, ms);
499
+ });
500
+ }
501
+ function isProcessAlive(pid) {
502
+ if (!Number.isInteger(pid) || pid <= 0) {
503
+ return false;
504
+ }
505
+ try {
506
+ process.kill(pid, 0);
507
+ return true;
508
+ } catch (error) {
509
+ return error.code === "EPERM";
510
+ }
511
+ }
512
+ async function probeDaemonStatus(socketPath) {
513
+ return await new Promise((resolve) => {
514
+ const probe = net.createConnection(socketPath);
515
+ let buffer = "";
516
+ let settled = false;
517
+ const finish = (status) => {
518
+ if (settled) {
519
+ return;
520
+ }
521
+ settled = true;
522
+ probe.removeAllListeners();
523
+ probe.destroy();
524
+ resolve(status);
525
+ };
526
+ const parse = () => {
527
+ try {
528
+ const response = JSON.parse(buffer.trim());
529
+ return response.ok && response.result ? response.result : null;
530
+ } catch {
531
+ return null;
532
+ }
533
+ };
534
+ probe.setTimeout(DAEMON_PROBE_TIMEOUT_MS, () => finish(null));
535
+ probe.once("connect", () => {
536
+ probe.write(JSON.stringify({ id: randomUUID(), method: "status", params: {} }));
537
+ });
538
+ probe.on("data", (chunk) => {
539
+ buffer += chunk.toString();
540
+ const status = parse();
541
+ if (status) {
542
+ finish(status);
543
+ }
544
+ });
545
+ probe.once("end", () => finish(parse()));
546
+ probe.once("error", () => finish(null));
547
+ });
548
+ }
549
+ async function prepareSocket(socketPath) {
550
+ if (process.platform === "win32") {
551
+ return;
552
+ }
553
+ try {
554
+ await fs2.unlink(socketPath);
555
+ } catch (error) {
556
+ if (error.code !== "ENOENT") {
557
+ throw error;
558
+ }
559
+ }
560
+ await fs2.mkdir(path3.dirname(socketPath), { recursive: true });
561
+ }
562
+ async function cleanupArtifacts(options) {
563
+ await cleanupDaemonArtifactsIfOwned(options, process.pid);
564
+ }
565
+ async function cleanupDaemonArtifactsIfOwned(paths, ownerPid) {
566
+ const metadata = await readJsonFile(paths.metadataPath).catch(() => void 0);
567
+ if (metadata?.pid !== ownerPid || metadata.socketPath !== paths.socketPath) {
568
+ return;
569
+ }
570
+ if (process.platform !== "win32") {
571
+ await fs2.unlink(paths.socketPath).catch(() => {
572
+ });
573
+ }
574
+ await fs2.unlink(paths.metadataPath).catch(() => {
575
+ });
576
+ }
577
+ async function handleSocketRequest(rawPayload, socket, runtime, managedServers, activity, metadata, logContext, shutdown, preParsedRequest) {
578
+ const { response, shouldShutdown } = await processRequest(rawPayload, runtime, managedServers, activity, metadata, logContext, preParsedRequest);
579
+ socket.write(JSON.stringify(response), () => {
580
+ socket.end(() => {
581
+ if (shouldShutdown) {
582
+ void shutdown();
583
+ }
584
+ });
585
+ });
586
+ }
587
+ function normalizeDaemonDisableOAuth(value) {
588
+ return value === true;
589
+ }
590
+ async function processRequest(rawPayload, runtime, managedServers, activity, metadata, logContext, preParsedRequest) {
591
+ const trimmed = rawPayload.trim();
592
+ if (!trimmed && !preParsedRequest) {
593
+ return {
594
+ response: buildErrorResponse("unknown", "empty_request"),
595
+ shouldShutdown: false
596
+ };
597
+ }
598
+ let request;
599
+ if (preParsedRequest) {
600
+ request = preParsedRequest;
601
+ } else {
602
+ try {
603
+ request = JSON.parse(trimmed);
604
+ } catch (error) {
605
+ return {
606
+ response: buildErrorResponse("unknown", "invalid_json", error),
607
+ shouldShutdown: false
608
+ };
609
+ }
610
+ }
611
+ const id = request.id ?? "unknown";
612
+ try {
613
+ switch (request.method) {
614
+ case "callTool": {
615
+ const params = request.params;
616
+ ensureManaged(params.server, managedServers);
617
+ const loggable = shouldLogServer(logContext, params.server);
618
+ if (loggable) {
619
+ logEvent(logContext, `callTool start server=${params.server} tool=${params.tool}`);
620
+ }
621
+ try {
622
+ const result = await runtime.callTool(params.server, params.tool, {
623
+ args: params.args ?? {},
624
+ timeoutMs: params.timeoutMs,
625
+ disableOAuth: normalizeDaemonDisableOAuth(params.disableOAuth)
626
+ });
627
+ markActivity(params.server, activity);
628
+ if (loggable) {
629
+ logEvent(logContext, `callTool success server=${params.server} tool=${params.tool}`);
630
+ }
631
+ return { response: { id, ok: true, result }, shouldShutdown: false };
632
+ } catch (error) {
633
+ if (loggable) {
634
+ const detail = formatError(error);
635
+ logEvent(logContext, `callTool error server=${params.server} tool=${params.tool} err=${detail}`);
636
+ }
637
+ throw error;
638
+ }
639
+ }
640
+ case "listTools": {
641
+ const params = request.params;
642
+ ensureManaged(params.server, managedServers);
643
+ const definition = managedServers.get(params.server);
644
+ const loggable = shouldLogServer(logContext, params.server);
645
+ if (loggable) {
646
+ logEvent(logContext, `listTools start server=${params.server}`);
647
+ }
648
+ try {
649
+ const result = await runtime.listTools(params.server, {
650
+ includeSchema: params.includeSchema,
651
+ autoAuthorize: resolveDaemonListToolsAutoAuthorize(params, definition),
652
+ allowCachedAuth: params.allowCachedAuth ?? true,
653
+ disableOAuth: normalizeDaemonDisableOAuth(params.disableOAuth)
654
+ });
655
+ markActivity(params.server, activity);
656
+ if (loggable) {
657
+ logEvent(logContext, `listTools success server=${params.server}`);
658
+ }
659
+ return { response: { id, ok: true, result }, shouldShutdown: false };
660
+ } catch (error) {
661
+ if (loggable) {
662
+ const detail = formatError(error);
663
+ logEvent(logContext, `listTools error server=${params.server} err=${detail}`);
664
+ }
665
+ throw error;
666
+ }
667
+ }
668
+ case "listResources": {
669
+ const params = request.params;
670
+ ensureManaged(params.server, managedServers);
671
+ const loggable = shouldLogServer(logContext, params.server);
672
+ if (loggable) {
673
+ logEvent(logContext, `listResources start server=${params.server}`);
674
+ }
675
+ try {
676
+ const result = await runtime.listResources(params.server, {
677
+ ...params.params,
678
+ allowCachedAuth: params.allowCachedAuth,
679
+ disableOAuth: normalizeDaemonDisableOAuth(params.disableOAuth)
680
+ });
681
+ markActivity(params.server, activity);
682
+ if (loggable) {
683
+ logEvent(logContext, `listResources success server=${params.server}`);
684
+ }
685
+ return { response: { id, ok: true, result }, shouldShutdown: false };
686
+ } catch (error) {
687
+ if (loggable) {
688
+ const detail = formatError(error);
689
+ logEvent(logContext, `listResources error server=${params.server} err=${detail}`);
690
+ }
691
+ throw error;
692
+ }
693
+ }
694
+ case "readResource": {
695
+ const params = request.params;
696
+ ensureManaged(params.server, managedServers);
697
+ const loggable = shouldLogServer(logContext, params.server);
698
+ if (loggable) {
699
+ logEvent(logContext, `readResource start server=${params.server} uri=${params.uri}`);
700
+ }
701
+ try {
702
+ const result = await runtime.readResource(params.server, params.uri, {
703
+ allowCachedAuth: params.allowCachedAuth,
704
+ disableOAuth: normalizeDaemonDisableOAuth(params.disableOAuth)
705
+ });
706
+ markActivity(params.server, activity);
707
+ if (loggable) {
708
+ logEvent(logContext, `readResource success server=${params.server}`);
709
+ }
710
+ return { response: { id, ok: true, result }, shouldShutdown: false };
711
+ } catch (error) {
712
+ if (loggable) {
713
+ const detail = formatError(error);
714
+ logEvent(logContext, `readResource error server=${params.server} err=${detail}`);
715
+ }
716
+ throw error;
717
+ }
718
+ }
719
+ case "closeServer": {
720
+ const params = request.params;
721
+ ensureManaged(params.server, managedServers);
722
+ const loggable = shouldLogServer(logContext, params.server);
723
+ if (loggable) {
724
+ logEvent(logContext, `closeServer start server=${params.server}`);
725
+ }
726
+ try {
727
+ await runtime.close(params.server);
728
+ activity.set(params.server, { connected: false });
729
+ if (loggable) {
730
+ logEvent(logContext, `closeServer success server=${params.server}`);
731
+ }
732
+ return {
733
+ response: { id, ok: true, result: true },
734
+ shouldShutdown: false
735
+ };
736
+ } catch (error) {
737
+ if (loggable) {
738
+ const detail = formatError(error);
739
+ logEvent(logContext, `closeServer error server=${params.server} err=${detail}`);
740
+ }
741
+ throw error;
742
+ }
743
+ }
744
+ case "status": {
745
+ const result = {
746
+ pid: process.pid,
747
+ startedAt: metadata.startedAt,
748
+ configPath: metadata.configPath,
749
+ configLayers: metadata.configLayers,
750
+ configMtimeMs: metadata.configMtimeMs,
751
+ definitionHash: metadata.definitionHash,
752
+ socketPath: metadata.socketPath,
753
+ logPath: metadata.logPath ?? void 0,
754
+ servers: Array.from(managedServers.values()).map((def) => {
755
+ const entry = activity.get(def.name);
756
+ return {
757
+ name: def.name,
758
+ connected: Boolean(entry?.connected),
759
+ lastUsedAt: entry?.lastUsedAt
760
+ };
761
+ })
762
+ };
763
+ return { response: { id, ok: true, result }, shouldShutdown: false };
764
+ }
765
+ case "stop": {
766
+ logEvent(logContext, "Received stop request.");
767
+ return {
768
+ response: { id, ok: true, result: true },
769
+ shouldShutdown: true
770
+ };
771
+ }
772
+ default:
773
+ return {
774
+ response: buildErrorResponse(id, "unknown_method"),
775
+ shouldShutdown: false
776
+ };
777
+ }
778
+ } catch (error) {
779
+ return {
780
+ response: buildErrorResponse(id, "runtime_error", error),
781
+ shouldShutdown: false
782
+ };
783
+ }
784
+ }
785
+ function resolveDaemonListToolsAutoAuthorize(params, definition) {
786
+ if (params.autoAuthorize === false && definition.command.kind === "stdio") {
787
+ return void 0;
788
+ }
789
+ return params.autoAuthorize;
790
+ }
791
+
792
+ // node_modules/.pnpm/mcporter@0.12.3/node_modules/mcporter/dist/cli/daemon-command.js
793
+ async function handleDaemonCli(args, options) {
794
+ const subcommand = args.shift();
795
+ if (!subcommand || subcommand === "help" || subcommand === "--help") {
796
+ printDaemonHelp();
797
+ return;
798
+ }
799
+ const client = new DaemonClient({
800
+ configPath: options.configPath,
801
+ configExplicit: options.configExplicit,
802
+ rootDir: options.rootDir
803
+ });
804
+ if (subcommand === "start") {
805
+ await handleDaemonStart(args, options, client);
806
+ return;
807
+ }
808
+ if (subcommand === "status") {
809
+ await handleDaemonStatus(client);
810
+ return;
811
+ }
812
+ if (subcommand === "stop") {
813
+ await client.stop();
814
+ console.log("Daemon stopped (if it was running).");
815
+ return;
816
+ }
817
+ if (subcommand === "restart") {
818
+ await handleDaemonRestart(args, options, client);
819
+ return;
820
+ }
821
+ throw new Error(`Unknown daemon subcommand '${subcommand}'.`);
822
+ }
823
+ function printDaemonHelp() {
824
+ console.log(`Usage: mcporter daemon <start|status|stop|restart>
825
+
826
+ Commands:
827
+ start Start the keep-alive daemon (auto-detects keep-alive servers).
828
+ status Show whether the daemon is running and which servers are active.
829
+ stop Shut down the daemon and all managed servers.
830
+ restart Stop the daemon (if running) and start a fresh instance.
831
+
832
+ Flags:
833
+ --foreground Run the daemon in the current process (debug only).
834
+ --log Enable daemon logging (defaults to ~/.mcporter/daemon/daemon-<hash>.log,
835
+ or $XDG_STATE_HOME/mcporter/daemon/... when set).
836
+ --log-file <path> Write daemon stdout/stderr to a specific log file.
837
+ --log-servers <csv> Only log call activity for the listed servers (implies --log).`);
838
+ }
839
+ async function handleDaemonStart(args, options, client) {
840
+ const foregroundFlag = consumeFlag(args, "--foreground");
841
+ const isChildLaunch = process.env.MCPORTER_DAEMON_CHILD === "1";
842
+ const foreground = foregroundFlag || isChildLaunch;
843
+ const paths = resolveDaemonPaths(options.configPath);
844
+ const socketPath = process.env.MCPORTER_DAEMON_SOCKET ?? paths.socketPath;
845
+ const metadataPath = process.env.MCPORTER_DAEMON_METADATA ?? paths.metadataPath;
846
+ const logging = await resolveDaemonLoggingOptions(args, paths.key);
847
+ const runtime = await createRuntime({
848
+ configPath: options.configExplicit ? options.configPath : void 0,
849
+ rootDir: options.rootDir
850
+ });
851
+ const keepAlive = runtime.getDefinitions().filter(isKeepAliveServer);
852
+ await runtime.close().catch(() => {
853
+ });
854
+ if (keepAlive.length === 0) {
855
+ console.log("No MCP servers are configured for keep-alive; daemon not started.");
856
+ return;
857
+ }
858
+ if (foreground) {
859
+ await runDaemonHost({
860
+ socketPath,
861
+ metadataPath,
862
+ configPath: options.configPath,
863
+ configExplicit: options.configExplicit,
864
+ rootDir: options.rootDir,
865
+ logPath: logging.enabled ? logging.logPath : void 0,
866
+ logServers: logging.serverFilter,
867
+ logAllServers: logging.logAllServers
868
+ });
869
+ return;
870
+ }
871
+ const existing = await client.status();
872
+ if (existing) {
873
+ console.log(`Daemon already running (pid ${existing.pid}).`);
874
+ return;
875
+ }
876
+ const forwardedArgs = [];
877
+ if (logging.enabled && logging.logPath) {
878
+ forwardedArgs.push("--log-file", logging.logPath);
879
+ }
880
+ if (logging.serverFilter.size > 0) {
881
+ forwardedArgs.push("--log-servers", Array.from(logging.serverFilter).join(","));
882
+ }
883
+ launchDaemonDetached({
884
+ configPath: options.configPath,
885
+ configExplicit: options.configExplicit,
886
+ rootDir: options.rootDir,
887
+ metadataPath,
888
+ socketPath,
889
+ extraArgs: forwardedArgs
890
+ });
891
+ const ready = await waitFor(() => client.status(), 1e4, 100);
892
+ if (!ready) {
893
+ throw new Error("Failed to start daemon before timeout expired.");
894
+ }
895
+ console.log(`Daemon started for ${keepAlive.length} server(s).`);
896
+ }
897
+ async function handleDaemonRestart(args, options, client) {
898
+ await client.stop();
899
+ console.log("Daemon stopped (if it was running).");
900
+ const stopped = await waitFor(async () => {
901
+ const status = await client.status();
902
+ return status ? null : true;
903
+ }, 5e3, 100);
904
+ if (!stopped) {
905
+ throw new Error("Daemon did not stop before restart could begin.");
906
+ }
907
+ await handleDaemonStart(args, options, client);
908
+ }
909
+ async function handleDaemonStatus(client) {
910
+ const status = await client.status();
911
+ if (!status) {
912
+ console.log("Daemon is not running.");
913
+ return;
914
+ }
915
+ console.log(`Daemon pid ${status.pid} \u2014 socket: ${status.socketPath}`);
916
+ if (status.logPath) {
917
+ console.log(`Log file: ${status.logPath}`);
918
+ }
919
+ if (status.servers.length === 0) {
920
+ console.log("No keep-alive servers registered.");
921
+ return;
922
+ }
923
+ status.servers.forEach((server) => {
924
+ const state = server.connected ? "connected" : "idle";
925
+ const lastUsed = server.lastUsedAt ? ` (last used ${new Date(server.lastUsedAt).toISOString()})` : "";
926
+ console.log(`- ${server.name}: ${state}${lastUsed}`);
927
+ });
928
+ }
929
+ function consumeFlag(args, flag) {
930
+ const index = args.indexOf(flag);
931
+ if (index === -1) {
932
+ return false;
933
+ }
934
+ args.splice(index, 1);
935
+ return true;
936
+ }
937
+ function consumeValueFlag(args, flag) {
938
+ const index = args.indexOf(flag);
939
+ if (index === -1) {
940
+ return void 0;
941
+ }
942
+ if (index + 1 >= args.length) {
943
+ throw new Error(`Flag '${flag}' requires a value.`);
944
+ }
945
+ const value = args[index + 1];
946
+ args.splice(index, 2);
947
+ return value;
948
+ }
949
+ async function waitFor(probe, timeoutMs, intervalMs) {
950
+ const deadline = Date.now() + timeoutMs;
951
+ while (Date.now() < deadline) {
952
+ const result = await probe();
953
+ if (result) {
954
+ return result;
955
+ }
956
+ await delay2(intervalMs);
957
+ }
958
+ return null;
959
+ }
960
+ function delay2(ms) {
961
+ return new Promise((resolve) => {
962
+ setTimeout(resolve, ms);
963
+ });
964
+ }
965
+ async function resolveDaemonLoggingOptions(args, configKey) {
966
+ const logFlag = consumeFlag(args, "--log");
967
+ const logFileValue = consumeValueFlag(args, "--log-file");
968
+ const logServersValue = consumeValueFlag(args, "--log-servers");
969
+ const envLogEnabled = process.env.MCPORTER_DAEMON_LOG === "1";
970
+ const envLogPath = process.env.MCPORTER_DAEMON_LOG_PATH;
971
+ const envLogServers = process.env.MCPORTER_DAEMON_LOG_SERVERS;
972
+ const serverFilter = parseServerList(logServersValue ?? envLogServers);
973
+ const explicitServerLogging = serverFilter.size > 0;
974
+ const resolvedFileFlag = logFileValue ? path4.resolve(expandHome(logFileValue)) : void 0;
975
+ const resolvedEnvFile = envLogPath ? path4.resolve(expandHome(envLogPath)) : void 0;
976
+ const enabled = logFlag || Boolean(resolvedFileFlag) || envLogEnabled || Boolean(resolvedEnvFile) || explicitServerLogging;
977
+ if (!enabled) {
978
+ return {
979
+ enabled: false,
980
+ logPath: void 0,
981
+ logAllServers: false,
982
+ serverFilter
983
+ };
984
+ }
985
+ const logPath = resolvedFileFlag ?? resolvedEnvFile ?? getDaemonLogPath(configKey);
986
+ await fsPromises.mkdir(path4.dirname(logPath), { recursive: true });
987
+ return {
988
+ enabled: true,
989
+ logPath,
990
+ logAllServers: serverFilter.size === 0,
991
+ serverFilter
992
+ };
993
+ }
994
+ function parseServerList(value) {
995
+ if (!value) {
996
+ return /* @__PURE__ */ new Set();
997
+ }
998
+ const entries = value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
999
+ return new Set(entries);
1000
+ }
1001
+
1002
+ export {
1003
+ handleDaemonCli
1004
+ };