@lzehrung/codegraph 2.1.2 → 2.2.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 (317) hide show
  1. package/README.md +2 -2
  2. package/codegraph-skill/codegraph/SKILL.md +21 -2
  3. package/dist/agent/explore.d.ts.map +1 -1
  4. package/dist/agent/explore.js +6 -1
  5. package/dist/agent/explore.js.map +1 -1
  6. package/dist/agent/handles.d.ts.map +1 -1
  7. package/dist/agent/handles.js +2 -8
  8. package/dist/agent/handles.js.map +1 -1
  9. package/dist/agent/orient.js +0 -16
  10. package/dist/agent/orient.js.map +1 -1
  11. package/dist/agent/query-index/store.d.ts +2 -0
  12. package/dist/agent/query-index/store.d.ts.map +1 -1
  13. package/dist/agent/query-index/store.js +4 -0
  14. package/dist/agent/query-index/store.js.map +1 -1
  15. package/dist/agent/session.js +3 -3
  16. package/dist/agent/session.js.map +1 -1
  17. package/dist/bin/cli.js +7306 -5661
  18. package/dist/bin/queryIndexWorker.js +357 -54
  19. package/dist/chunking/chunkSplit.js +0 -15
  20. package/dist/chunking/chunkSplit.js.map +1 -1
  21. package/dist/cli/commandCatalog.d.ts.map +1 -1
  22. package/dist/cli/commandCatalog.js +1 -0
  23. package/dist/cli/commandCatalog.js.map +1 -1
  24. package/dist/cli/commandTable.d.ts.map +1 -1
  25. package/dist/cli/commandTable.js +17 -0
  26. package/dist/cli/commandTable.js.map +1 -1
  27. package/dist/cli/compileCache.d.ts +5 -0
  28. package/dist/cli/compileCache.d.ts.map +1 -1
  29. package/dist/cli/compileCache.js +18 -0
  30. package/dist/cli/compileCache.js.map +1 -1
  31. package/dist/cli/context.d.ts.map +1 -1
  32. package/dist/cli/context.js +0 -1
  33. package/dist/cli/context.js.map +1 -1
  34. package/dist/cli/help.d.ts +2 -1
  35. package/dist/cli/help.d.ts.map +1 -1
  36. package/dist/cli/help.js +23 -2
  37. package/dist/cli/help.js.map +1 -1
  38. package/dist/cli/indexPolicy.d.ts.map +1 -1
  39. package/dist/cli/indexPolicy.js +1 -0
  40. package/dist/cli/indexPolicy.js.map +1 -1
  41. package/dist/cli/inspect.d.ts.map +1 -1
  42. package/dist/cli/inspect.js +5 -2
  43. package/dist/cli/inspect.js.map +1 -1
  44. package/dist/cli/options.d.ts +1 -1
  45. package/dist/cli/options.d.ts.map +1 -1
  46. package/dist/cli/options.js +31 -1
  47. package/dist/cli/options.js.map +1 -1
  48. package/dist/cli/server.d.ts +59 -0
  49. package/dist/cli/server.d.ts.map +1 -0
  50. package/dist/cli/server.js +906 -0
  51. package/dist/cli/server.js.map +1 -0
  52. package/dist/cli.d.ts +1 -1
  53. package/dist/cli.d.ts.map +1 -1
  54. package/dist/cli.js +1 -0
  55. package/dist/cli.js.map +1 -1
  56. package/dist/documentLinks/check.d.ts.map +1 -1
  57. package/dist/documentLinks/check.js +4 -3
  58. package/dist/documentLinks/check.js.map +1 -1
  59. package/dist/documentLinks.d.ts.map +1 -1
  60. package/dist/documentLinks.js +0 -1
  61. package/dist/documentLinks.js.map +1 -1
  62. package/dist/drift/snapshot.js +1 -1
  63. package/dist/drift/snapshot.js.map +1 -1
  64. package/dist/duplicates/scoring.d.ts.map +1 -1
  65. package/dist/duplicates/scoring.js.map +1 -1
  66. package/dist/duplicates/unitCache.d.ts.map +1 -1
  67. package/dist/duplicates/unitCache.js +1 -1
  68. package/dist/duplicates/unitCache.js.map +1 -1
  69. package/dist/duplicates/units.d.ts.map +1 -1
  70. package/dist/duplicates/units.js +2 -3
  71. package/dist/duplicates/units.js.map +1 -1
  72. package/dist/duplicates.d.ts.map +1 -1
  73. package/dist/duplicates.js +0 -6
  74. package/dist/duplicates.js.map +1 -1
  75. package/dist/graph-edge-collector.d.ts +0 -2
  76. package/dist/graph-edge-collector.d.ts.map +1 -1
  77. package/dist/graph-edge-collector.js +2 -4
  78. package/dist/graph-edge-collector.js.map +1 -1
  79. package/dist/graphs/edgeResolution.d.ts.map +1 -1
  80. package/dist/graphs/specifiers.d.ts +1 -1
  81. package/dist/graphs/specifiers.d.ts.map +1 -1
  82. package/dist/graphs/specifiers.js +1 -1
  83. package/dist/graphs/specifiers.js.map +1 -1
  84. package/dist/graphs/symbol-graph-detailed.d.ts.map +1 -1
  85. package/dist/graphs/symbol-graph-detailed.js +0 -1
  86. package/dist/graphs/symbol-graph-detailed.js.map +1 -1
  87. package/dist/index.d.ts +1 -1
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js.map +1 -1
  90. package/dist/indexer/build-cache/module-cache.d.ts.map +1 -1
  91. package/dist/indexer/build-cache/module-cache.js +1 -1
  92. package/dist/indexer/build-cache/module-cache.js.map +1 -1
  93. package/dist/indexer/build-cache/options.d.ts.map +1 -1
  94. package/dist/indexer/build-cache/options.js +0 -2
  95. package/dist/indexer/build-cache/options.js.map +1 -1
  96. package/dist/indexer/build-cache/project-snapshot.d.ts +9 -1
  97. package/dist/indexer/build-cache/project-snapshot.d.ts.map +1 -1
  98. package/dist/indexer/build-cache/project-snapshot.js +1 -5
  99. package/dist/indexer/build-cache/project-snapshot.js.map +1 -1
  100. package/dist/indexer/build-index.d.ts.map +1 -1
  101. package/dist/indexer/build-index.js +59 -44
  102. package/dist/indexer/build-index.js.map +1 -1
  103. package/dist/indexer/build-workers.d.ts +29 -2
  104. package/dist/indexer/build-workers.d.ts.map +1 -1
  105. package/dist/indexer/build-workers.js +54 -3
  106. package/dist/indexer/build-workers.js.map +1 -1
  107. package/dist/indexer/call-hierarchy.d.ts.map +1 -1
  108. package/dist/indexer/imports.d.ts +0 -3
  109. package/dist/indexer/imports.d.ts.map +1 -1
  110. package/dist/indexer/imports.js +1 -1
  111. package/dist/indexer/imports.js.map +1 -1
  112. package/dist/indexer/locals-and-exports.d.ts +2 -2
  113. package/dist/indexer/locals-and-exports.d.ts.map +1 -1
  114. package/dist/indexer/locals-and-exports.js +2 -3
  115. package/dist/indexer/locals-and-exports.js.map +1 -1
  116. package/dist/indexer/navigation-local.d.ts +2 -2
  117. package/dist/indexer/navigation-local.d.ts.map +1 -1
  118. package/dist/indexer/navigation-local.js +2 -2
  119. package/dist/indexer/navigation-local.js.map +1 -1
  120. package/dist/indexer/navigation-references.d.ts +1 -2
  121. package/dist/indexer/navigation-references.d.ts.map +1 -1
  122. package/dist/indexer/navigation-references.js +1 -1
  123. package/dist/indexer/navigation-references.js.map +1 -1
  124. package/dist/indexer/navigation.d.ts.map +1 -1
  125. package/dist/indexer/navigation.js +2 -4
  126. package/dist/indexer/navigation.js.map +1 -1
  127. package/dist/indexer/parse-context.d.ts +1 -4
  128. package/dist/indexer/parse-context.d.ts.map +1 -1
  129. package/dist/indexer/parse-context.js +11 -6
  130. package/dist/indexer/parse-context.js.map +1 -1
  131. package/dist/indexer/scope.d.ts +2 -2
  132. package/dist/indexer/scope.d.ts.map +1 -1
  133. package/dist/indexer/scope.js +1 -1
  134. package/dist/indexer/scope.js.map +1 -1
  135. package/dist/indexer.d.ts +3 -6
  136. package/dist/indexer.d.ts.map +1 -1
  137. package/dist/indexer.js +5 -8
  138. package/dist/indexer.js.map +1 -1
  139. package/dist/installer/registry.js +1 -27
  140. package/dist/installer/registry.js.map +1 -1
  141. package/dist/languages/definitions/c.d.ts.map +1 -1
  142. package/dist/languages/definitions/c.js +0 -1
  143. package/dist/languages/definitions/c.js.map +1 -1
  144. package/dist/languages/definitions/cFamily.d.ts +0 -1
  145. package/dist/languages/definitions/cFamily.d.ts.map +1 -1
  146. package/dist/languages/definitions/cFamily.js +0 -2
  147. package/dist/languages/definitions/cFamily.js.map +1 -1
  148. package/dist/languages/definitions/cpp.d.ts.map +1 -1
  149. package/dist/languages/definitions/cpp.js +0 -1
  150. package/dist/languages/definitions/cpp.js.map +1 -1
  151. package/dist/languages/definitions/csharp.d.ts.map +1 -1
  152. package/dist/languages/definitions/csharp.js +0 -2
  153. package/dist/languages/definitions/csharp.js.map +1 -1
  154. package/dist/languages/definitions/css.d.ts.map +1 -1
  155. package/dist/languages/definitions/css.js +0 -2
  156. package/dist/languages/definitions/css.js.map +1 -1
  157. package/dist/languages/definitions/go.d.ts.map +1 -1
  158. package/dist/languages/definitions/go.js +0 -2
  159. package/dist/languages/definitions/go.js.map +1 -1
  160. package/dist/languages/definitions/html.d.ts.map +1 -1
  161. package/dist/languages/definitions/html.js +0 -2
  162. package/dist/languages/definitions/html.js.map +1 -1
  163. package/dist/languages/definitions/htmlStub.d.ts.map +1 -1
  164. package/dist/languages/definitions/htmlStub.js +0 -2
  165. package/dist/languages/definitions/htmlStub.js.map +1 -1
  166. package/dist/languages/definitions/java.d.ts.map +1 -1
  167. package/dist/languages/definitions/java.js +0 -2
  168. package/dist/languages/definitions/java.js.map +1 -1
  169. package/dist/languages/definitions/javascript.d.ts.map +1 -1
  170. package/dist/languages/definitions/javascript.js +0 -2
  171. package/dist/languages/definitions/javascript.js.map +1 -1
  172. package/dist/languages/definitions/kotlin.d.ts.map +1 -1
  173. package/dist/languages/definitions/kotlin.js +0 -2
  174. package/dist/languages/definitions/kotlin.js.map +1 -1
  175. package/dist/languages/definitions/less.d.ts.map +1 -1
  176. package/dist/languages/definitions/less.js +0 -2
  177. package/dist/languages/definitions/less.js.map +1 -1
  178. package/dist/languages/definitions/php.d.ts.map +1 -1
  179. package/dist/languages/definitions/php.js +0 -2
  180. package/dist/languages/definitions/php.js.map +1 -1
  181. package/dist/languages/definitions/python.d.ts.map +1 -1
  182. package/dist/languages/definitions/python.js +0 -2
  183. package/dist/languages/definitions/python.js.map +1 -1
  184. package/dist/languages/definitions/ruby.d.ts.map +1 -1
  185. package/dist/languages/definitions/ruby.js +0 -2
  186. package/dist/languages/definitions/ruby.js.map +1 -1
  187. package/dist/languages/definitions/rust.d.ts.map +1 -1
  188. package/dist/languages/definitions/rust.js +0 -2
  189. package/dist/languages/definitions/rust.js.map +1 -1
  190. package/dist/languages/definitions/scss.d.ts.map +1 -1
  191. package/dist/languages/definitions/scss.js +0 -2
  192. package/dist/languages/definitions/scss.js.map +1 -1
  193. package/dist/languages/definitions/sql.d.ts.map +1 -1
  194. package/dist/languages/definitions/sql.js +0 -2
  195. package/dist/languages/definitions/sql.js.map +1 -1
  196. package/dist/languages/definitions/svelte.d.ts.map +1 -1
  197. package/dist/languages/definitions/svelte.js +0 -2
  198. package/dist/languages/definitions/svelte.js.map +1 -1
  199. package/dist/languages/definitions/swift.d.ts.map +1 -1
  200. package/dist/languages/definitions/swift.js +0 -2
  201. package/dist/languages/definitions/swift.js.map +1 -1
  202. package/dist/languages/definitions/typescript.d.ts.map +1 -1
  203. package/dist/languages/definitions/typescript.js +0 -3
  204. package/dist/languages/definitions/typescript.js.map +1 -1
  205. package/dist/languages/definitions/vue.d.ts.map +1 -1
  206. package/dist/languages/definitions/vue.js +0 -2
  207. package/dist/languages/definitions/vue.js.map +1 -1
  208. package/dist/languages/definitions/zig.d.ts.map +1 -1
  209. package/dist/languages/definitions/zig.js +0 -2
  210. package/dist/languages/definitions/zig.js.map +1 -1
  211. package/dist/languages/filePrep.d.ts +0 -2
  212. package/dist/languages/filePrep.d.ts.map +1 -1
  213. package/dist/languages/filePrep.js +0 -1
  214. package/dist/languages/filePrep.js.map +1 -1
  215. package/dist/languages/types.d.ts +19 -5
  216. package/dist/languages/types.d.ts.map +1 -1
  217. package/dist/languages.d.ts +1 -3
  218. package/dist/languages.d.ts.map +1 -1
  219. package/dist/languages.js +0 -7
  220. package/dist/languages.js.map +1 -1
  221. package/dist/lifecycle/manifest.d.ts.map +1 -1
  222. package/dist/lifecycle/manifest.js +21 -4
  223. package/dist/lifecycle/manifest.js.map +1 -1
  224. package/dist/mcp/handlers.d.ts +275 -0
  225. package/dist/mcp/handlers.d.ts.map +1 -0
  226. package/dist/mcp/handlers.js +657 -0
  227. package/dist/mcp/handlers.js.map +1 -0
  228. package/dist/mcp/httpTransport.d.ts +14 -0
  229. package/dist/mcp/httpTransport.d.ts.map +1 -0
  230. package/dist/mcp/httpTransport.js +185 -0
  231. package/dist/mcp/httpTransport.js.map +1 -0
  232. package/dist/mcp/legacySessions.d.ts +38 -0
  233. package/dist/mcp/legacySessions.d.ts.map +1 -0
  234. package/dist/mcp/legacySessions.js +264 -0
  235. package/dist/mcp/legacySessions.js.map +1 -0
  236. package/dist/mcp/lifecycleHealth.d.ts +12 -0
  237. package/dist/mcp/lifecycleHealth.d.ts.map +1 -0
  238. package/dist/mcp/lifecycleHealth.js +37 -0
  239. package/dist/mcp/lifecycleHealth.js.map +1 -0
  240. package/dist/mcp/protocol.d.ts +35 -0
  241. package/dist/mcp/protocol.d.ts.map +1 -0
  242. package/dist/mcp/protocol.js +577 -0
  243. package/dist/mcp/protocol.js.map +1 -0
  244. package/dist/mcp/server.d.ts +9 -306
  245. package/dist/mcp/server.d.ts.map +1 -1
  246. package/dist/mcp/server.js +13 -1609
  247. package/dist/mcp/server.js.map +1 -1
  248. package/dist/native/bindingLoader.d.ts +27 -0
  249. package/dist/native/bindingLoader.d.ts.map +1 -1
  250. package/dist/native/bindingLoader.js +18 -1
  251. package/dist/native/bindingLoader.js.map +1 -1
  252. package/dist/native/contracts.d.ts +59 -12
  253. package/dist/native/contracts.d.ts.map +1 -1
  254. package/dist/native/execution.d.ts +10 -1
  255. package/dist/native/execution.d.ts.map +1 -1
  256. package/dist/native/execution.js +59 -3
  257. package/dist/native/execution.js.map +1 -1
  258. package/dist/native/projectedTree.d.ts +20 -5
  259. package/dist/native/projectedTree.d.ts.map +1 -1
  260. package/dist/native/projectedTree.js +100 -48
  261. package/dist/native/projectedTree.js.map +1 -1
  262. package/dist/native/runtime.d.ts +47 -1
  263. package/dist/native/runtime.d.ts.map +1 -1
  264. package/dist/native/runtime.js +151 -8
  265. package/dist/native/runtime.js.map +1 -1
  266. package/dist/native/runtimeCache.d.ts +92 -0
  267. package/dist/native/runtimeCache.d.ts.map +1 -1
  268. package/dist/native/runtimeCache.js +443 -13
  269. package/dist/native/runtimeCache.js.map +1 -1
  270. package/dist/native/treeShape.d.ts +21 -0
  271. package/dist/native/treeShape.d.ts.map +1 -0
  272. package/dist/native/treeShape.js +28 -0
  273. package/dist/native/treeShape.js.map +1 -0
  274. package/dist/native/treeSitterNative.d.ts +3 -3
  275. package/dist/native/treeSitterNative.d.ts.map +1 -1
  276. package/dist/native/treeSitterNative.js +2 -2
  277. package/dist/native/treeSitterNative.js.map +1 -1
  278. package/dist/presets.d.ts +7 -1
  279. package/dist/presets.d.ts.map +1 -1
  280. package/dist/presets.js +4 -0
  281. package/dist/presets.js.map +1 -1
  282. package/dist/review/deleted.js +2 -2
  283. package/dist/review/deleted.js.map +1 -1
  284. package/dist/review.js.map +1 -1
  285. package/dist/sql/extractFacts.d.ts.map +1 -1
  286. package/dist/sql/extractFacts.js +1 -1
  287. package/dist/sql/extractFacts.js.map +1 -1
  288. package/dist/util/lazySymbols.js +1 -1
  289. package/dist/util/lazySymbols.js.map +1 -1
  290. package/dist/util/projectFiles.js +1 -1
  291. package/dist/util/projectFiles.js.map +1 -1
  292. package/dist/util/resolution/jvm.js +1 -1
  293. package/dist/util/resolution/jvm.js.map +1 -1
  294. package/dist/util/resolution/php.d.ts.map +1 -1
  295. package/dist/util/resolution/php.js +0 -1
  296. package/dist/util/resolution/php.js.map +1 -1
  297. package/dist/util/resolution.d.ts.map +1 -1
  298. package/dist/util/resolution.js +5 -5
  299. package/dist/util/resolution.js.map +1 -1
  300. package/dist/worker/nativeExtractWorker.d.ts +10 -4
  301. package/dist/worker/nativeExtractWorker.d.ts.map +1 -1
  302. package/dist/worker/nativeExtractWorker.js +55 -3
  303. package/dist/worker/nativeExtractWorker.js.map +1 -1
  304. package/dist/worker/nativeWorkerPool.d.ts +4 -0
  305. package/dist/worker/nativeWorkerPool.d.ts.map +1 -1
  306. package/dist/worker/nativeWorkerPool.js +6 -1
  307. package/dist/worker/nativeWorkerPool.js.map +1 -1
  308. package/docs/mcp.md +16 -2
  309. package/package.json +3 -3
  310. package/dist/languages/definitions/loadLanguage.d.ts +0 -2
  311. package/dist/languages/definitions/loadLanguage.d.ts.map +0 -1
  312. package/dist/languages/definitions/loadLanguage.js +0 -2
  313. package/dist/languages/definitions/loadLanguage.js.map +0 -1
  314. package/dist/parserBackend.d.ts +0 -57
  315. package/dist/parserBackend.d.ts.map +0 -1
  316. package/dist/parserBackend.js +0 -33
  317. package/dist/parserBackend.js.map +0 -1
@@ -0,0 +1,906 @@
1
+ import { spawn } from "node:child_process";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { withInstallerLeaseLock } from "../installer/locks.js";
5
+ import { isPlainRecord } from "../util/guards.js";
6
+ import { isFilePathWithinRoot, normalizePath } from "../util/paths.js";
7
+ import { exitWithError } from "./context.js";
8
+ import { parseOptionalBoundedIntegerOption } from "./options.js";
9
+ import { writeCliOutput } from "./pretty.js";
10
+ const SERVER_REGISTRY_SCHEMA_VERSION = 2;
11
+ const SERVER_HEALTH_SCHEMA_VERSION = 1;
12
+ const DEFAULT_SERVER_HOST = "127.0.0.1";
13
+ const DEFAULT_SERVER_PORT = 7331;
14
+ const DEFAULT_SERVER_START_TIMEOUT_MS = 15_000;
15
+ const MAX_SERVER_START_TIMEOUT_MS = 24 * 60 * 60 * 1000;
16
+ const SERVER_STOP_TIMEOUT_MS = 5_000;
17
+ const SERVER_RETRY_DELAY_MS = 50;
18
+ const SERVER_HEALTH_GRACE_TIMEOUT_MS = 3_000;
19
+ const SERVER_HEALTH_REQUEST_TIMEOUT_MS = 1_000;
20
+ const SERVER_HEALTH_RETRY_DELAY_MS = 250;
21
+ const MAX_SERVER_START_DIAGNOSTICS_BYTES = 8 * 1024;
22
+ const SERVER_LOG_FILE = "server.log";
23
+ const SERVER_COMMAND_VALUE_OPTIONS = {
24
+ start: [
25
+ "--root",
26
+ "--cache",
27
+ "--cache-dir",
28
+ "--host",
29
+ "--ignore-glob",
30
+ "--include-glob",
31
+ "--native",
32
+ "--port",
33
+ "--resolution-hint",
34
+ "--startup-timeout-ms",
35
+ "--threads",
36
+ ],
37
+ status: ["--root"],
38
+ stop: ["--root"],
39
+ };
40
+ const SERVER_COMMAND_FLAGS = {
41
+ start: [
42
+ "--cache-strict",
43
+ "--cache-verify",
44
+ "--no-gitignore",
45
+ "--replace",
46
+ "--warmup",
47
+ "--warmup-symbols",
48
+ "--workers",
49
+ "--json",
50
+ ],
51
+ status: ["--json", "--pretty"],
52
+ stop: [],
53
+ };
54
+ const FORWARDED_VALUE_OPTIONS = [
55
+ "--cache",
56
+ "--cache-dir",
57
+ "--include-glob",
58
+ "--ignore-glob",
59
+ "--native",
60
+ "--resolution-hint",
61
+ "--threads",
62
+ ];
63
+ const FORWARDED_FLAGS = ["--cache-strict", "--cache-verify", "--no-gitignore", "--workers"];
64
+ class ServerUsageError extends Error {
65
+ }
66
+ export async function handleServerCommand(context) {
67
+ const command = context.positionals[0];
68
+ if (!isServerCommand(command)) {
69
+ context.writeStderrLine("Usage: codegraph server <start|status|stop> [--root <path>]");
70
+ context.exit(2);
71
+ }
72
+ try {
73
+ assertServerCommandOptions(command, context);
74
+ if (command === "start") {
75
+ await startServer(context);
76
+ return;
77
+ }
78
+ if (command === "status") {
79
+ await showServerStatus(context);
80
+ return;
81
+ }
82
+ await stopServer(context);
83
+ }
84
+ catch (error) {
85
+ exitWithError(context, error, error instanceof ServerUsageError ? 2 : 1);
86
+ }
87
+ }
88
+ function isServerCommand(command) {
89
+ return command === "start" || command === "status" || command === "stop";
90
+ }
91
+ function assertServerCommandOptions(command, context) {
92
+ const allowedOptions = SERVER_COMMAND_VALUE_OPTIONS[command];
93
+ for (const option of context.parsedOptions.keys()) {
94
+ if (!allowedOptions.includes(option)) {
95
+ throw new ServerUsageError(`${option} is not valid for codegraph server ${command}.`);
96
+ }
97
+ }
98
+ const allowedFlags = SERVER_COMMAND_FLAGS[command];
99
+ for (const flags of Object.values(SERVER_COMMAND_FLAGS)) {
100
+ for (const flag of flags) {
101
+ if (context.hasFlag(flag) && !allowedFlags.includes(flag)) {
102
+ throw new ServerUsageError(`${flag} is not valid for codegraph server ${command}.`);
103
+ }
104
+ }
105
+ }
106
+ }
107
+ async function startServer(context) {
108
+ const root = await resolveServerRoot(context.root);
109
+ const options = parseServerStartOptions(context);
110
+ await withServerLifecycleLock(root, async () => {
111
+ await startServerForRoot(context, root, options);
112
+ });
113
+ }
114
+ function parseServerStartOptions(context) {
115
+ const host = context.getOpt("--host") ?? DEFAULT_SERVER_HOST;
116
+ if (!host.trim())
117
+ throw new ServerUsageError("Invalid --host value. Expected a non-empty host name or address.");
118
+ try {
119
+ const port = parseOptionalBoundedIntegerOption(context.getOpt("--port"), "--port", 1, 65535) ?? DEFAULT_SERVER_PORT;
120
+ const startupTimeoutMs = parseOptionalBoundedIntegerOption(context.getOpt("--startup-timeout-ms"), "--startup-timeout-ms", 1, MAX_SERVER_START_TIMEOUT_MS) ?? DEFAULT_SERVER_START_TIMEOUT_MS;
121
+ return { host, port, startupTimeoutMs };
122
+ }
123
+ catch (error) {
124
+ throw new ServerUsageError(error instanceof Error ? error.message : String(error));
125
+ }
126
+ }
127
+ async function startServerForRoot(context, root, options) {
128
+ if (context.hasFlag("--warmup") && context.hasFlag("--warmup-symbols")) {
129
+ throw new ServerUsageError("Choose either --warmup or --warmup-symbols for server start.");
130
+ }
131
+ const existingStatus = await readServerStatus(root);
132
+ if (existingStatus.status === "live") {
133
+ if (!context.hasFlag("--replace")) {
134
+ throw new Error(`Codegraph server is already live at ${existingStatus.registry.url}. Use --replace to restart it.`);
135
+ }
136
+ await stopLiveServer(existingStatus.registry, existingStatus.health, root);
137
+ await removeRegistry(root, existingStatus.registry.credentialId);
138
+ }
139
+ else if (existingStatus.status === "stale") {
140
+ if (!canSafelyRemoveStaleRegistry(existingStatus)) {
141
+ throw new Error("Refusing to replace a Codegraph server whose identity does not match the requested root.");
142
+ }
143
+ await removeRegistry(root, existingStatus.registry?.credentialId);
144
+ }
145
+ else if (existingStatus.status === "unreachable") {
146
+ throw new Error("Refusing to replace a Codegraph server whose health endpoint is temporarily unavailable.");
147
+ }
148
+ const startupSignals = ["SIGINT", "SIGTERM"];
149
+ const { promise: interrupted, reject: interruptStart } = Promise.withResolvers();
150
+ void interrupted.catch(() => undefined);
151
+ const onStartupSignal = (signal) => {
152
+ interruptStart(new Error(`Codegraph server start was interrupted by ${signal}.`));
153
+ };
154
+ for (const signal of startupSignals) {
155
+ process.once(signal, onStartupSignal);
156
+ }
157
+ let child;
158
+ let startupDiagnostics;
159
+ let lifecycleCredential;
160
+ let registryWrite;
161
+ try {
162
+ const startedAfterMs = Date.now();
163
+ lifecycleCredential = await createServerLifecycleCredential(root);
164
+ const spawned = await spawnServerProcess(root, options.host, options.port, lifecycleCredential.token, context);
165
+ child = spawned.child;
166
+ startupDiagnostics = spawned.startupDiagnostics;
167
+ const childPid = child.pid;
168
+ if (!childPid)
169
+ throw new Error("Codegraph server process did not provide a pid.");
170
+ const url = serverUrl(options.host, options.port);
171
+ const health = await Promise.race([
172
+ waitForLiveServer(url, root, options.startupTimeoutMs, child, childPid, startedAfterMs, lifecycleCredential.token),
173
+ interrupted,
174
+ ]);
175
+ const registry = {
176
+ schemaVersion: SERVER_REGISTRY_SCHEMA_VERSION,
177
+ pid: childPid,
178
+ url,
179
+ root,
180
+ startedAt: health.startedAt,
181
+ version: health.version,
182
+ credentialId: lifecycleCredential.id,
183
+ };
184
+ registryWrite = writeRegistry(root, registry);
185
+ await Promise.race([registryWrite, interrupted]);
186
+ child.unref();
187
+ if (context.hasFlag("--json")) {
188
+ context.writeJSONLine({ status: "started", ...registry, update: health.update });
189
+ return;
190
+ }
191
+ context.writeStdoutLine(`Codegraph server started at ${registry.url}`);
192
+ }
193
+ catch (error) {
194
+ if (registryWrite) {
195
+ await registryWrite.catch(() => undefined);
196
+ await removeRegistry(root, lifecycleCredential?.id).catch(() => undefined);
197
+ }
198
+ if (child)
199
+ await terminateServerProcess(child);
200
+ if (lifecycleCredential)
201
+ await removeServerLifecycleCredential(root, lifecycleCredential.id).catch(() => undefined);
202
+ throw await withServerStartupDiagnostics(error, startupDiagnostics);
203
+ }
204
+ finally {
205
+ await startupDiagnostics?.dispose();
206
+ for (const signal of startupSignals) {
207
+ process.removeListener(signal, onStartupSignal);
208
+ }
209
+ }
210
+ }
211
+ async function showServerStatus(context) {
212
+ const root = await resolveServerRoot(context.root);
213
+ const status = await readServerStatus(root);
214
+ writeCliOutput({
215
+ hasFlag: context.hasFlag,
216
+ writeJSONLine: context.writeJSONLine,
217
+ writeStdoutLine: context.writeStdoutLine,
218
+ }, status, formatServerStatus);
219
+ }
220
+ async function stopServer(context) {
221
+ const root = await resolveServerRoot(context.root);
222
+ await withServerLifecycleLock(root, async () => {
223
+ const status = await readServerStatus(root);
224
+ if (status.status === "not_started") {
225
+ context.writeStdoutLine("No Codegraph server registry exists for this root.");
226
+ return;
227
+ }
228
+ if (status.status === "unreachable") {
229
+ throw new Error("Refusing to stop a Codegraph server whose health endpoint is temporarily unavailable.");
230
+ }
231
+ if (status.status === "stale") {
232
+ if (!canSafelyRemoveStaleRegistry(status)) {
233
+ throw new Error("Refusing to stop a Codegraph server whose identity does not match the requested root.");
234
+ }
235
+ await removeRegistry(root, status.registry?.credentialId);
236
+ context.writeStdoutLine("Removed stale Codegraph server registry.");
237
+ return;
238
+ }
239
+ await stopLiveServer(status.registry, status.health, root);
240
+ await removeRegistry(root, status.registry.credentialId);
241
+ context.writeStdoutLine(`Stopped Codegraph server at ${status.registry.url}`);
242
+ });
243
+ }
244
+ export function formatServerStatus(status) {
245
+ const lines = ["Codegraph server status", "=======================", `Status: ${status.status}`];
246
+ if (status.registry) {
247
+ const registry = status.registry;
248
+ lines.push(`URL: ${registry.url}`, `PID: ${registry.pid}`, `Root: ${registry.root}`, `Started: ${registry.startedAt}`, `Version: ${registry.version}`);
249
+ }
250
+ if (status.reason)
251
+ lines.push(`Reason: ${status.reason}`);
252
+ if (status.health) {
253
+ lines.push(`Running version: ${status.health.update.runningVersion}`);
254
+ if (status.health.update.installedVersion) {
255
+ lines.push(`Installed version: ${status.health.update.installedVersion}`);
256
+ }
257
+ if (status.health.update.restartRequired) {
258
+ let restartMessage = "Restart required: yes";
259
+ if (status.health.update.reason)
260
+ restartMessage += ` (${status.health.update.reason})`;
261
+ lines.push(restartMessage);
262
+ }
263
+ else {
264
+ lines.push("Restart required: no");
265
+ }
266
+ }
267
+ const remedy = serverStatusRemedy(status);
268
+ if (remedy)
269
+ lines.push(`Remedy: ${remedy}`);
270
+ return lines.join("\n");
271
+ }
272
+ function serverStatusRemedy(status) {
273
+ if (status.status === "unreachable") {
274
+ return "verify the server process and /health endpoint before retrying.";
275
+ }
276
+ if (status.status !== "stale")
277
+ return undefined;
278
+ if (!status.safeToRemove) {
279
+ return "verify the registry and live server identity before retrying.";
280
+ }
281
+ const root = JSON.stringify(status.requestedRoot);
282
+ return `run codegraph server stop --root ${root} or codegraph server start --replace --root ${root}`;
283
+ }
284
+ function canSafelyRemoveStaleRegistry(status) {
285
+ return status.status === "stale" && status.safeToRemove;
286
+ }
287
+ async function readServerStatus(root) {
288
+ const registryPath = await resolveRegistryPath(root, false);
289
+ if (!registryPath)
290
+ return { status: "not_started", requestedRoot: root };
291
+ const readResult = await readRegistry(registryPath);
292
+ if (readResult.status === "missing")
293
+ return { status: "not_started", requestedRoot: root };
294
+ if (readResult.status === "invalid") {
295
+ return { status: "stale", requestedRoot: root, safeToRemove: true, reason: "Server registry is invalid." };
296
+ }
297
+ const registry = readResult.registry;
298
+ if (!sameRoot(registry.root, root)) {
299
+ return {
300
+ status: "stale",
301
+ requestedRoot: root,
302
+ safeToRemove: false,
303
+ registry,
304
+ reason: "Registry root does not match the requested root.",
305
+ };
306
+ }
307
+ if (registry.schemaVersion === 1) {
308
+ if (getServerProcessState(registry.pid) === "missing") {
309
+ return {
310
+ status: "stale",
311
+ requestedRoot: root,
312
+ safeToRemove: true,
313
+ registry,
314
+ reason: "Server process is not running.",
315
+ };
316
+ }
317
+ return {
318
+ status: "unreachable",
319
+ requestedRoot: root,
320
+ registry,
321
+ reason: "Server registry predates lifecycle credentials. Stop the server manually, then start it again.",
322
+ };
323
+ }
324
+ const lifecycleCredential = await readServerLifecycleCredential(root, registry.credentialId);
325
+ if (!lifecycleCredential) {
326
+ if (getServerProcessState(registry.pid) === "missing") {
327
+ return {
328
+ status: "stale",
329
+ requestedRoot: root,
330
+ safeToRemove: true,
331
+ registry,
332
+ reason: "Server process is not running.",
333
+ };
334
+ }
335
+ return {
336
+ status: "unreachable",
337
+ requestedRoot: root,
338
+ registry,
339
+ reason: "Server lifecycle credential is unavailable.",
340
+ };
341
+ }
342
+ const health = await waitForServerHealth(registry.url, SERVER_HEALTH_GRACE_TIMEOUT_MS, lifecycleCredential.token);
343
+ if (!health) {
344
+ if (getServerProcessState(registry.pid) === "missing") {
345
+ return {
346
+ status: "stale",
347
+ requestedRoot: root,
348
+ safeToRemove: true,
349
+ registry,
350
+ reason: "Server process is not running.",
351
+ };
352
+ }
353
+ return {
354
+ status: "unreachable",
355
+ requestedRoot: root,
356
+ registry,
357
+ reason: `Server health endpoint did not prove its lifecycle identity within ${SERVER_HEALTH_GRACE_TIMEOUT_MS}ms.`,
358
+ };
359
+ }
360
+ if (!sameRoot(health.root, root)) {
361
+ return {
362
+ status: "stale",
363
+ requestedRoot: root,
364
+ safeToRemove: false,
365
+ registry,
366
+ health,
367
+ reason: "Server health root does not match the requested root.",
368
+ };
369
+ }
370
+ if (health.pid !== registry.pid) {
371
+ return {
372
+ status: "stale",
373
+ requestedRoot: root,
374
+ safeToRemove: false,
375
+ registry,
376
+ health,
377
+ reason: "Server process identifier does not match the registry.",
378
+ };
379
+ }
380
+ if (health.startedAt !== registry.startedAt) {
381
+ return {
382
+ status: "stale",
383
+ requestedRoot: root,
384
+ safeToRemove: false,
385
+ registry,
386
+ health,
387
+ reason: "Server startup time does not match the registry.",
388
+ };
389
+ }
390
+ return { status: "live", requestedRoot: root, registry, health };
391
+ }
392
+ async function stopLiveServer(registry, health, root) {
393
+ if (!sameRoot(registry.root, root) ||
394
+ !sameRoot(health.root, root) ||
395
+ health.pid !== registry.pid ||
396
+ health.startedAt !== registry.startedAt) {
397
+ throw new Error("Refusing to stop a Codegraph server whose identity does not match the requested root.");
398
+ }
399
+ try {
400
+ process.kill(registry.pid, "SIGTERM");
401
+ }
402
+ catch (error) {
403
+ if (!isMissingProcessError(error))
404
+ throw error;
405
+ }
406
+ await waitForServerStop(registry.url, registry.pid, SERVER_STOP_TIMEOUT_MS);
407
+ }
408
+ async function spawnServerProcess(root, host, port, lifecycleCredential, context) {
409
+ const entryPath = process.argv[1];
410
+ if (!entryPath)
411
+ throw new Error("Unable to locate the Codegraph CLI entrypoint.");
412
+ const args = [entryPath, "mcp", "serve", "--root", root, "--host", host, "--port", String(port)];
413
+ if (context.hasFlag("--warmup"))
414
+ args.push("--warmup");
415
+ if (context.hasFlag("--warmup-symbols"))
416
+ args.push("--warmup-symbols");
417
+ for (const flag of FORWARDED_FLAGS) {
418
+ if (context.hasFlag(flag))
419
+ args.push(flag);
420
+ }
421
+ for (const option of FORWARDED_VALUE_OPTIONS) {
422
+ for (const value of context.parsedOptions.get(option) ?? []) {
423
+ args.push(option, value);
424
+ }
425
+ }
426
+ const lifecycleHealth = await import("../mcp/lifecycleHealth.js");
427
+ const startupDiagnostics = await createServerStartupDiagnostics(root);
428
+ try {
429
+ const child = spawn(process.execPath, args, {
430
+ detached: true,
431
+ env: { ...process.env, [lifecycleHealth.MCP_LIFECYCLE_HEALTH_TOKEN_ENV]: lifecycleCredential },
432
+ stdio: ["ignore", "ignore", startupDiagnostics.fileDescriptor],
433
+ windowsHide: true,
434
+ });
435
+ await waitForServerProcessSpawn(child);
436
+ return { child, startupDiagnostics };
437
+ }
438
+ catch (error) {
439
+ await startupDiagnostics.dispose();
440
+ throw error;
441
+ }
442
+ }
443
+ function waitForServerProcessSpawn(child) {
444
+ const { promise, resolve, reject } = Promise.withResolvers();
445
+ const onSpawn = () => {
446
+ cleanup();
447
+ resolve();
448
+ };
449
+ const onError = (error) => {
450
+ cleanup();
451
+ reject(error);
452
+ };
453
+ const cleanup = () => {
454
+ child.removeListener("spawn", onSpawn);
455
+ child.removeListener("error", onError);
456
+ };
457
+ child.once("spawn", onSpawn);
458
+ child.once("error", onError);
459
+ return promise;
460
+ }
461
+ async function createServerStartupDiagnostics(root) {
462
+ const logPath = await resolveServerStatePath(root, SERVER_LOG_FILE, true);
463
+ if (!logPath)
464
+ throw new Error("Codegraph server log path is unavailable.");
465
+ await assertRegularServerStateFile(logPath);
466
+ const logFile = await fs.open(logPath, "w", 0o600);
467
+ try {
468
+ const startOffset = (await logFile.stat()).size;
469
+ await logFile.chmod(0o600);
470
+ return {
471
+ fileDescriptor: logFile.fd,
472
+ read: () => readServerLogTail(logPath, startOffset),
473
+ dispose: () => logFile.close(),
474
+ };
475
+ }
476
+ catch (error) {
477
+ await logFile.close();
478
+ throw error;
479
+ }
480
+ }
481
+ async function readServerLogTail(logPath, startOffset) {
482
+ try {
483
+ const contents = await fs.readFile(logPath);
484
+ const offset = Math.max(startOffset, contents.length - MAX_SERVER_START_DIAGNOSTICS_BYTES);
485
+ return contents.subarray(offset).toString("utf8").trim();
486
+ }
487
+ catch {
488
+ return "";
489
+ }
490
+ }
491
+ async function withServerStartupDiagnostics(error, diagnostics) {
492
+ const output = await diagnostics?.read();
493
+ if (!output) {
494
+ if (error instanceof Error)
495
+ return error;
496
+ return new Error(String(error));
497
+ }
498
+ const message = error instanceof Error ? error.message : String(error);
499
+ return new Error([message, "Server startup diagnostics:", output].join("\n"), { cause: error });
500
+ }
501
+ async function waitForLiveServer(url, root, timeoutMs, child, childPid, startedAfterMs, lifecycleCredential) {
502
+ const { promise: childClosed, resolve: resolveChildClose } = Promise.withResolvers();
503
+ const onChildClose = () => resolveChildClose();
504
+ child.once("close", onChildClose);
505
+ if (child.exitCode !== null || child.signalCode !== null)
506
+ resolveChildClose();
507
+ try {
508
+ const deadline = Date.now() + timeoutMs;
509
+ while (Date.now() < deadline) {
510
+ const remainingMs = deadline - Date.now();
511
+ if (remainingMs <= 0)
512
+ break;
513
+ const result = await Promise.race([
514
+ requestServerHealth(url, Math.min(SERVER_HEALTH_REQUEST_TIMEOUT_MS, remainingMs), lifecycleCredential).then((health) => ({
515
+ kind: "health",
516
+ health,
517
+ })),
518
+ childClosed.then(() => ({ kind: "child-exited" })),
519
+ ]);
520
+ if (result.kind === "child-exited") {
521
+ throw new Error(`Codegraph server process exited before accepting requests at ${url}.`);
522
+ }
523
+ if (result.health &&
524
+ result.health.pid === childPid &&
525
+ sameRoot(result.health.root, root) &&
526
+ serverStartedAfter(result.health, startedAfterMs)) {
527
+ return result.health;
528
+ }
529
+ await wait(SERVER_RETRY_DELAY_MS);
530
+ }
531
+ }
532
+ finally {
533
+ child.removeListener("close", onChildClose);
534
+ }
535
+ throw new Error(`Codegraph server did not become reachable at ${url} within ${timeoutMs}ms.`);
536
+ }
537
+ function serverStartedAfter(health, startedAfterMs) {
538
+ const startedAtMs = Date.parse(health.startedAt);
539
+ return Number.isFinite(startedAtMs) && startedAtMs >= startedAfterMs;
540
+ }
541
+ async function terminateServerProcess(child) {
542
+ if (child.exitCode !== null)
543
+ return;
544
+ try {
545
+ child.kill("SIGTERM");
546
+ }
547
+ catch (error) {
548
+ if (!isMissingProcessError(error))
549
+ throw error;
550
+ return;
551
+ }
552
+ if (!(await waitForChildExit(child, SERVER_STOP_TIMEOUT_MS))) {
553
+ try {
554
+ child.kill("SIGKILL");
555
+ }
556
+ catch (error) {
557
+ if (!isMissingProcessError(error))
558
+ throw error;
559
+ }
560
+ await waitForChildExit(child, SERVER_STOP_TIMEOUT_MS);
561
+ }
562
+ child.unref();
563
+ }
564
+ async function waitForChildExit(child, timeoutMs) {
565
+ if (child.exitCode !== null || child.signalCode !== null)
566
+ return true;
567
+ const { promise, resolve } = Promise.withResolvers();
568
+ const onClose = () => resolve();
569
+ child.once("close", onClose);
570
+ try {
571
+ await Promise.race([promise, wait(timeoutMs)]);
572
+ return child.exitCode !== null || child.signalCode !== null;
573
+ }
574
+ finally {
575
+ child.removeListener("close", onClose);
576
+ }
577
+ }
578
+ async function waitForServerStop(url, pid, timeoutMs) {
579
+ const deadline = Date.now() + timeoutMs;
580
+ while (Date.now() < deadline) {
581
+ const health = await requestServerHealth(url);
582
+ if (!health && getServerProcessState(pid) === "missing")
583
+ return;
584
+ await wait(SERVER_RETRY_DELAY_MS);
585
+ }
586
+ throw new Error(`Codegraph server at ${url} did not stop within ${timeoutMs}ms.`);
587
+ }
588
+ async function waitForServerHealth(serverUrl, timeoutMs, lifecycleCredential) {
589
+ const deadline = Date.now() + timeoutMs;
590
+ while (true) {
591
+ const remainingMs = deadline - Date.now();
592
+ if (remainingMs <= 0)
593
+ return undefined;
594
+ const health = await requestServerHealth(serverUrl, Math.min(SERVER_HEALTH_REQUEST_TIMEOUT_MS, remainingMs), lifecycleCredential);
595
+ if (health)
596
+ return health;
597
+ const retryDelayMs = Math.min(SERVER_HEALTH_RETRY_DELAY_MS, deadline - Date.now());
598
+ if (retryDelayMs <= 0)
599
+ return undefined;
600
+ await wait(retryDelayMs);
601
+ }
602
+ }
603
+ function wait(delayMs) {
604
+ const { promise, resolve } = Promise.withResolvers();
605
+ setTimeout(resolve, delayMs);
606
+ return promise;
607
+ }
608
+ async function requestServerHealth(serverUrl, timeoutMs = SERVER_HEALTH_REQUEST_TIMEOUT_MS, lifecycleCredential) {
609
+ let healthUrl;
610
+ try {
611
+ healthUrl = new URL(serverUrl);
612
+ healthUrl.pathname = "/health";
613
+ healthUrl.search = "";
614
+ }
615
+ catch {
616
+ return undefined;
617
+ }
618
+ let lifecycleHealth;
619
+ let challenge;
620
+ if (lifecycleCredential) {
621
+ lifecycleHealth = await import("../mcp/lifecycleHealth.js");
622
+ challenge = lifecycleHealth.createMcpLifecycleHealthChallenge();
623
+ }
624
+ const requestOptions = { signal: AbortSignal.timeout(timeoutMs) };
625
+ if (challenge)
626
+ requestOptions.headers = { "x-codegraph-health-challenge": challenge };
627
+ try {
628
+ const response = await fetch(healthUrl, requestOptions);
629
+ if (!response.ok)
630
+ return undefined;
631
+ const body = await response.json();
632
+ if (!isServerHealth(body))
633
+ return undefined;
634
+ if (lifecycleCredential &&
635
+ challenge &&
636
+ lifecycleHealth &&
637
+ !lifecycleHealth.matchesMcpLifecycleHealthProof(body.lifecycleProof, lifecycleCredential, challenge, body)) {
638
+ return undefined;
639
+ }
640
+ return body;
641
+ }
642
+ catch {
643
+ return undefined;
644
+ }
645
+ }
646
+ function isServerHealth(value) {
647
+ if (!isPlainRecord(value))
648
+ return false;
649
+ const update = value.update;
650
+ if (!isPlainRecord(update))
651
+ return false;
652
+ return (value.service === "codegraph" &&
653
+ value.schemaVersion === SERVER_HEALTH_SCHEMA_VERSION &&
654
+ typeof value.pid === "number" &&
655
+ Number.isSafeInteger(value.pid) &&
656
+ value.pid > 0 &&
657
+ typeof value.root === "string" &&
658
+ typeof value.version === "string" &&
659
+ typeof value.startedAt === "string" &&
660
+ typeof update.restartRequired === "boolean" &&
661
+ typeof update.runningVersion === "string" &&
662
+ (update.installedVersion === undefined || typeof update.installedVersion === "string") &&
663
+ (update.reason === undefined || typeof update.reason === "string") &&
664
+ (value.lifecycleProof === undefined || typeof value.lifecycleProof === "string"));
665
+ }
666
+ async function readRegistry(registryPath) {
667
+ let raw;
668
+ try {
669
+ const stats = await fs.lstat(registryPath);
670
+ if (!stats.isFile() || stats.isSymbolicLink())
671
+ return { status: "invalid" };
672
+ raw = await fs.readFile(registryPath, "utf8");
673
+ }
674
+ catch (error) {
675
+ return isMissingFileError(error) ? { status: "missing" } : { status: "invalid" };
676
+ }
677
+ try {
678
+ const parsed = JSON.parse(raw);
679
+ return isServerRegistry(parsed) ? { status: "found", registry: parsed } : { status: "invalid" };
680
+ }
681
+ catch {
682
+ return { status: "invalid" };
683
+ }
684
+ }
685
+ function isServerRegistry(value) {
686
+ if (!isPlainRecord(value))
687
+ return false;
688
+ const isCommonRegistry = typeof value.pid === "number" &&
689
+ Number.isSafeInteger(value.pid) &&
690
+ value.pid > 0 &&
691
+ typeof value.url === "string" &&
692
+ typeof value.root === "string" &&
693
+ typeof value.startedAt === "string" &&
694
+ typeof value.version === "string";
695
+ if (!isCommonRegistry)
696
+ return false;
697
+ if (value.schemaVersion === 1)
698
+ return value.credentialId === undefined;
699
+ return (value.schemaVersion === SERVER_REGISTRY_SCHEMA_VERSION &&
700
+ typeof value.credentialId === "string" &&
701
+ /^[a-f0-9-]{36}$/.test(value.credentialId));
702
+ }
703
+ async function createServerLifecycleCredential(root) {
704
+ const crypto = await import("node:crypto");
705
+ const id = crypto.randomUUID();
706
+ const credentialPath = await resolveServerLifecycleCredentialPath(root, id, true);
707
+ await assertRegularServerStateFile(credentialPath);
708
+ const lifecycleHealth = await import("../mcp/lifecycleHealth.js");
709
+ const credential = lifecycleHealth.createMcpLifecycleHealthToken();
710
+ const temporaryPath = credentialPath + "." + process.pid + "." + Date.now() + ".tmp";
711
+ try {
712
+ await fs.writeFile(temporaryPath, credential + "\n", {
713
+ encoding: "utf8",
714
+ flag: "wx",
715
+ mode: 0o600,
716
+ });
717
+ await fs.rename(temporaryPath, credentialPath);
718
+ return { id, token: credential };
719
+ }
720
+ finally {
721
+ await fs.rm(temporaryPath, { force: true });
722
+ }
723
+ }
724
+ async function readServerLifecycleCredential(root, credentialId) {
725
+ const credentialPath = await resolveServerLifecycleCredentialPath(root, credentialId);
726
+ let raw;
727
+ try {
728
+ const stats = await fs.lstat(credentialPath);
729
+ if (!stats.isFile() || stats.isSymbolicLink()) {
730
+ throw new Error(`Codegraph server lifecycle credential is not a regular file: ${normalizePath(credentialPath)}`);
731
+ }
732
+ raw = await fs.readFile(credentialPath, "utf8");
733
+ }
734
+ catch (error) {
735
+ if (isMissingFileError(error))
736
+ return undefined;
737
+ throw error;
738
+ }
739
+ const lifecycleHealth = await import("../mcp/lifecycleHealth.js");
740
+ const env = {};
741
+ env[lifecycleHealth.MCP_LIFECYCLE_HEALTH_TOKEN_ENV] = raw.trim();
742
+ const token = lifecycleHealth.readMcpLifecycleHealthToken(env);
743
+ if (!token)
744
+ return undefined;
745
+ return { id: credentialId, token };
746
+ }
747
+ async function removeServerLifecycleCredential(root, credentialId) {
748
+ const credentialPath = await resolveServerLifecycleCredentialPath(root, credentialId);
749
+ try {
750
+ const stats = await fs.lstat(credentialPath);
751
+ if (!stats.isFile() || stats.isSymbolicLink()) {
752
+ throw new Error(`Refusing to remove non-regular server lifecycle credential: ${normalizePath(credentialPath)}`);
753
+ }
754
+ await fs.rm(credentialPath);
755
+ }
756
+ catch (error) {
757
+ if (isMissingFileError(error))
758
+ return;
759
+ throw error;
760
+ }
761
+ }
762
+ async function resolveServerLifecycleCredentialPath(root, credentialId, createDirectory = false) {
763
+ const compileCache = await import("./compileCache.js");
764
+ const crypto = await import("node:crypto");
765
+ const rootId = crypto.createHash("sha256").update(normalizeServerRoot(root)).digest("hex");
766
+ const credentialDirectory = path.resolve(compileCache.resolveCodegraphUserStateRoot(), "server-lifecycle-v1", rootId);
767
+ if (isFilePathWithinRoot(root, credentialDirectory)) {
768
+ throw new Error(`Codegraph server lifecycle credentials must resolve outside project root: ${normalizePath(credentialDirectory)}`);
769
+ }
770
+ if (createDirectory)
771
+ await fs.mkdir(credentialDirectory, { recursive: true });
772
+ let realDirectory;
773
+ try {
774
+ realDirectory = await fs.realpath(credentialDirectory);
775
+ }
776
+ catch (error) {
777
+ if (isMissingFileError(error))
778
+ return path.join(credentialDirectory, credentialId + ".credential");
779
+ throw error;
780
+ }
781
+ if (isFilePathWithinRoot(root, realDirectory)) {
782
+ throw new Error(`Codegraph server lifecycle credentials must resolve outside project root: ${normalizePath(realDirectory)}`);
783
+ }
784
+ return path.join(realDirectory, credentialId + ".credential");
785
+ }
786
+ async function writeRegistry(root, registry) {
787
+ const registryPath = await resolveRegistryPath(root, true);
788
+ if (!registryPath)
789
+ throw new Error("Codegraph server registry directory is unavailable.");
790
+ const temporaryPath = `${registryPath}.${process.pid}.${Date.now()}.tmp`;
791
+ try {
792
+ await fs.writeFile(temporaryPath, `${JSON.stringify(registry, null, 2)}\n`, "utf8");
793
+ await fs.rename(temporaryPath, registryPath);
794
+ }
795
+ finally {
796
+ await fs.rm(temporaryPath, { force: true });
797
+ }
798
+ }
799
+ async function removeRegistry(root, credentialId) {
800
+ const registryPath = await resolveRegistryPath(root, false);
801
+ if (registryPath)
802
+ await fs.rm(registryPath, { force: true });
803
+ if (credentialId)
804
+ await removeServerLifecycleCredential(root, credentialId);
805
+ }
806
+ async function resolveRegistryPath(root, createDirectory) {
807
+ return resolveServerStatePath(root, "server.json", createDirectory);
808
+ }
809
+ async function resolveServerStatePath(root, fileName, createDirectory) {
810
+ const directory = await resolveServerStateDirectory(root, createDirectory);
811
+ if (!directory)
812
+ return undefined;
813
+ return path.join(directory, fileName);
814
+ }
815
+ async function resolveServerStateDirectory(root, createDirectory) {
816
+ const registryDirectory = path.join(root, ".codegraph");
817
+ let realDirectory;
818
+ try {
819
+ realDirectory = await fs.realpath(registryDirectory);
820
+ }
821
+ catch (error) {
822
+ if (!isMissingFileError(error))
823
+ throw error;
824
+ if (!createDirectory)
825
+ return undefined;
826
+ await fs.mkdir(registryDirectory, { recursive: true });
827
+ realDirectory = await fs.realpath(registryDirectory);
828
+ }
829
+ if (!isFilePathWithinRoot(root, realDirectory) || sameRoot(root, realDirectory)) {
830
+ throw new Error(`Codegraph server registry directory resolves outside project root: ${normalizePath(realDirectory)}`);
831
+ }
832
+ const stats = await fs.stat(realDirectory);
833
+ if (!stats.isDirectory()) {
834
+ throw new Error(`Codegraph server registry path is not a directory: ${normalizePath(realDirectory)}`);
835
+ }
836
+ return realDirectory;
837
+ }
838
+ async function assertRegularServerStateFile(filePath) {
839
+ try {
840
+ const stats = await fs.lstat(filePath);
841
+ if (!stats.isFile() || stats.isSymbolicLink()) {
842
+ throw new Error(`Codegraph server state path is not a regular file: ${normalizePath(filePath)}`);
843
+ }
844
+ }
845
+ catch (error) {
846
+ if (isMissingFileError(error))
847
+ return;
848
+ throw error;
849
+ }
850
+ }
851
+ async function withServerLifecycleLock(root, operation) {
852
+ const lockPath = path.join(root, ".codegraph-server.lock");
853
+ try {
854
+ return await withInstallerLeaseLock(lockPath, `MCP server lifecycle for ${normalizePath(root)}`, operation);
855
+ }
856
+ catch (error) {
857
+ if (error instanceof Error && error.message.startsWith("Another Codegraph installer")) {
858
+ throw new Error(`Another Codegraph server lifecycle command is in progress for ${normalizePath(root)}.`, {
859
+ cause: error,
860
+ });
861
+ }
862
+ if (error instanceof Error && error.message.startsWith("Codegraph installer found an existing lock")) {
863
+ throw new Error(`Could not safely acquire the Codegraph server lifecycle lock for ${normalizePath(root)}.`, {
864
+ cause: error,
865
+ });
866
+ }
867
+ throw error;
868
+ }
869
+ }
870
+ async function resolveServerRoot(root) {
871
+ return normalizePath(await fs.realpath(path.resolve(root)));
872
+ }
873
+ function sameRoot(left, right) {
874
+ return normalizeServerRoot(left) === normalizeServerRoot(right);
875
+ }
876
+ function normalizeServerRoot(root) {
877
+ const resolved = normalizePath(path.resolve(root));
878
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
879
+ }
880
+ function serverUrl(host, port) {
881
+ const formattedHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
882
+ return `http://${formattedHost}:${port}/mcp`;
883
+ }
884
+ function isMissingFileError(error) {
885
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
886
+ }
887
+ function getServerProcessState(pid) {
888
+ try {
889
+ process.kill(pid, 0);
890
+ return "running";
891
+ }
892
+ catch (error) {
893
+ if (isMissingProcessError(error))
894
+ return "missing";
895
+ if (isPermissionProcessError(error))
896
+ return "unknown";
897
+ throw error;
898
+ }
899
+ }
900
+ function isMissingProcessError(error) {
901
+ return error instanceof Error && "code" in error && error.code === "ESRCH";
902
+ }
903
+ function isPermissionProcessError(error) {
904
+ return error instanceof Error && "code" in error && error.code === "EPERM";
905
+ }
906
+ //# sourceMappingURL=server.js.map