seer-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/LICENSE +176 -0
  3. package/README.md +272 -0
  4. package/README_dev.md +199 -0
  5. package/dist/bundle/ci.d.ts +47 -0
  6. package/dist/bundle/ci.d.ts.map +1 -0
  7. package/dist/bundle/ci.js +113 -0
  8. package/dist/bundle/ci.js.map +1 -0
  9. package/dist/bundle/contract.d.ts +111 -0
  10. package/dist/bundle/contract.d.ts.map +1 -0
  11. package/dist/bundle/contract.js +352 -0
  12. package/dist/bundle/contract.js.map +1 -0
  13. package/dist/bundle/export.d.ts +36 -0
  14. package/dist/bundle/export.d.ts.map +1 -0
  15. package/dist/bundle/export.js +152 -0
  16. package/dist/bundle/export.js.map +1 -0
  17. package/dist/bundle/external.d.ts +66 -0
  18. package/dist/bundle/external.d.ts.map +1 -0
  19. package/dist/bundle/external.js +238 -0
  20. package/dist/bundle/external.js.map +1 -0
  21. package/dist/bundle/format.d.ts +94 -0
  22. package/dist/bundle/format.d.ts.map +1 -0
  23. package/dist/bundle/format.js +42 -0
  24. package/dist/bundle/format.js.map +1 -0
  25. package/dist/bundle/import.d.ts +49 -0
  26. package/dist/bundle/import.d.ts.map +1 -0
  27. package/dist/bundle/import.js +116 -0
  28. package/dist/bundle/import.js.map +1 -0
  29. package/dist/cli/index.d.ts +3 -0
  30. package/dist/cli/index.d.ts.map +1 -0
  31. package/dist/cli/index.js +1402 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/cli/init.d.ts +48 -0
  34. package/dist/cli/init.d.ts.map +1 -0
  35. package/dist/cli/init.js +284 -0
  36. package/dist/cli/init.js.map +1 -0
  37. package/dist/db/schema.d.ts +3 -0
  38. package/dist/db/schema.d.ts.map +1 -0
  39. package/dist/db/schema.js +616 -0
  40. package/dist/db/schema.js.map +1 -0
  41. package/dist/db/store.d.ts +1011 -0
  42. package/dist/db/store.d.ts.map +1 -0
  43. package/dist/db/store.js +3888 -0
  44. package/dist/db/store.js.map +1 -0
  45. package/dist/graph/pagerank.d.ts +9 -0
  46. package/dist/graph/pagerank.d.ts.map +1 -0
  47. package/dist/graph/pagerank.js +47 -0
  48. package/dist/graph/pagerank.js.map +1 -0
  49. package/dist/indexer/architecture.d.ts +72 -0
  50. package/dist/indexer/architecture.d.ts.map +1 -0
  51. package/dist/indexer/architecture.js +112 -0
  52. package/dist/indexer/architecture.js.map +1 -0
  53. package/dist/indexer/behavior.d.ts +75 -0
  54. package/dist/indexer/behavior.d.ts.map +1 -0
  55. package/dist/indexer/behavior.js +395 -0
  56. package/dist/indexer/behavior.js.map +1 -0
  57. package/dist/indexer/boundaries.d.ts +60 -0
  58. package/dist/indexer/boundaries.d.ts.map +1 -0
  59. package/dist/indexer/boundaries.js +366 -0
  60. package/dist/indexer/boundaries.js.map +1 -0
  61. package/dist/indexer/churn.d.ts +15 -0
  62. package/dist/indexer/churn.d.ts.map +1 -0
  63. package/dist/indexer/churn.js +49 -0
  64. package/dist/indexer/churn.js.map +1 -0
  65. package/dist/indexer/classify.d.ts +9 -0
  66. package/dist/indexer/classify.d.ts.map +1 -0
  67. package/dist/indexer/classify.js +90 -0
  68. package/dist/indexer/classify.js.map +1 -0
  69. package/dist/indexer/context.d.ts +176 -0
  70. package/dist/indexer/context.d.ts.map +1 -0
  71. package/dist/indexer/context.js +193 -0
  72. package/dist/indexer/context.js.map +1 -0
  73. package/dist/indexer/continuity.d.ts +67 -0
  74. package/dist/indexer/continuity.d.ts.map +1 -0
  75. package/dist/indexer/continuity.js +288 -0
  76. package/dist/indexer/continuity.js.map +1 -0
  77. package/dist/indexer/detectchanges.d.ts +32 -0
  78. package/dist/indexer/detectchanges.d.ts.map +1 -0
  79. package/dist/indexer/detectchanges.js +74 -0
  80. package/dist/indexer/detectchanges.js.map +1 -0
  81. package/dist/indexer/discovery.d.ts +37 -0
  82. package/dist/indexer/discovery.d.ts.map +1 -0
  83. package/dist/indexer/discovery.js +136 -0
  84. package/dist/indexer/discovery.js.map +1 -0
  85. package/dist/indexer/externaldeps.d.ts +18 -0
  86. package/dist/indexer/externaldeps.d.ts.map +1 -0
  87. package/dist/indexer/externaldeps.js +288 -0
  88. package/dist/indexer/externaldeps.js.map +1 -0
  89. package/dist/indexer/freshness.d.ts +48 -0
  90. package/dist/indexer/freshness.d.ts.map +1 -0
  91. package/dist/indexer/freshness.js +128 -0
  92. package/dist/indexer/freshness.js.map +1 -0
  93. package/dist/indexer/git.d.ts +144 -0
  94. package/dist/indexer/git.d.ts.map +1 -0
  95. package/dist/indexer/git.js +444 -0
  96. package/dist/indexer/git.js.map +1 -0
  97. package/dist/indexer/index.d.ts +145 -0
  98. package/dist/indexer/index.d.ts.map +1 -0
  99. package/dist/indexer/index.js +930 -0
  100. package/dist/indexer/index.js.map +1 -0
  101. package/dist/indexer/modules.d.ts +62 -0
  102. package/dist/indexer/modules.d.ts.map +1 -0
  103. package/dist/indexer/modules.js +293 -0
  104. package/dist/indexer/modules.js.map +1 -0
  105. package/dist/indexer/preflight.d.ts +154 -0
  106. package/dist/indexer/preflight.d.ts.map +1 -0
  107. package/dist/indexer/preflight.js +399 -0
  108. package/dist/indexer/preflight.js.map +1 -0
  109. package/dist/indexer/protoScanner.d.ts +34 -0
  110. package/dist/indexer/protoScanner.d.ts.map +1 -0
  111. package/dist/indexer/protoScanner.js +133 -0
  112. package/dist/indexer/protoScanner.js.map +1 -0
  113. package/dist/indexer/risk.d.ts +115 -0
  114. package/dist/indexer/risk.d.ts.map +1 -0
  115. package/dist/indexer/risk.js +194 -0
  116. package/dist/indexer/risk.js.map +1 -0
  117. package/dist/indexer/serviceHostScanner.d.ts +25 -0
  118. package/dist/indexer/serviceHostScanner.d.ts.map +1 -0
  119. package/dist/indexer/serviceHostScanner.js +95 -0
  120. package/dist/indexer/serviceHostScanner.js.map +1 -0
  121. package/dist/indexer/serviceLinks.d.ts +105 -0
  122. package/dist/indexer/serviceLinks.d.ts.map +1 -0
  123. package/dist/indexer/serviceLinks.js +509 -0
  124. package/dist/indexer/serviceLinks.js.map +1 -0
  125. package/dist/indexer/shapehash.d.ts +98 -0
  126. package/dist/indexer/shapehash.d.ts.map +1 -0
  127. package/dist/indexer/shapehash.js +354 -0
  128. package/dist/indexer/shapehash.js.map +1 -0
  129. package/dist/indexer/skeleton.d.ts +15 -0
  130. package/dist/indexer/skeleton.d.ts.map +1 -0
  131. package/dist/indexer/skeleton.js +136 -0
  132. package/dist/indexer/skeleton.js.map +1 -0
  133. package/dist/indexer/symbolhistory.d.ts +41 -0
  134. package/dist/indexer/symbolhistory.d.ts.map +1 -0
  135. package/dist/indexer/symbolhistory.js +124 -0
  136. package/dist/indexer/symbolhistory.js.map +1 -0
  137. package/dist/indexer/watcher.d.ts +68 -0
  138. package/dist/indexer/watcher.d.ts.map +1 -0
  139. package/dist/indexer/watcher.js +179 -0
  140. package/dist/indexer/watcher.js.map +1 -0
  141. package/dist/mcp/server.d.ts +80 -0
  142. package/dist/mcp/server.d.ts.map +1 -0
  143. package/dist/mcp/server.js +1610 -0
  144. package/dist/mcp/server.js.map +1 -0
  145. package/dist/parser/index.d.ts +8 -0
  146. package/dist/parser/index.d.ts.map +1 -0
  147. package/dist/parser/index.js +33 -0
  148. package/dist/parser/index.js.map +1 -0
  149. package/dist/parser/languages/cpp.d.ts +3 -0
  150. package/dist/parser/languages/cpp.d.ts.map +1 -0
  151. package/dist/parser/languages/cpp.js +350 -0
  152. package/dist/parser/languages/cpp.js.map +1 -0
  153. package/dist/parser/languages/csharp.d.ts +3 -0
  154. package/dist/parser/languages/csharp.d.ts.map +1 -0
  155. package/dist/parser/languages/csharp.js +239 -0
  156. package/dist/parser/languages/csharp.js.map +1 -0
  157. package/dist/parser/languages/go.d.ts +3 -0
  158. package/dist/parser/languages/go.d.ts.map +1 -0
  159. package/dist/parser/languages/go.js +259 -0
  160. package/dist/parser/languages/go.js.map +1 -0
  161. package/dist/parser/languages/java.d.ts +3 -0
  162. package/dist/parser/languages/java.d.ts.map +1 -0
  163. package/dist/parser/languages/java.js +391 -0
  164. package/dist/parser/languages/java.js.map +1 -0
  165. package/dist/parser/languages/python.d.ts +3 -0
  166. package/dist/parser/languages/python.d.ts.map +1 -0
  167. package/dist/parser/languages/python.js +396 -0
  168. package/dist/parser/languages/python.js.map +1 -0
  169. package/dist/parser/languages/rust.d.ts +3 -0
  170. package/dist/parser/languages/rust.d.ts.map +1 -0
  171. package/dist/parser/languages/rust.js +159 -0
  172. package/dist/parser/languages/rust.js.map +1 -0
  173. package/dist/parser/languages/typescript.d.ts +3 -0
  174. package/dist/parser/languages/typescript.d.ts.map +1 -0
  175. package/dist/parser/languages/typescript.js +1442 -0
  176. package/dist/parser/languages/typescript.js.map +1 -0
  177. package/dist/parser/parserContext.d.ts +77 -0
  178. package/dist/parser/parserContext.d.ts.map +1 -0
  179. package/dist/parser/parserContext.js +354 -0
  180. package/dist/parser/parserContext.js.map +1 -0
  181. package/dist/parser/walker.d.ts +81 -0
  182. package/dist/parser/walker.d.ts.map +1 -0
  183. package/dist/parser/walker.js +217 -0
  184. package/dist/parser/walker.js.map +1 -0
  185. package/dist/parser/worker.d.ts +66 -0
  186. package/dist/parser/worker.d.ts.map +1 -0
  187. package/dist/parser/worker.js +129 -0
  188. package/dist/parser/worker.js.map +1 -0
  189. package/dist/parser/workerpool.d.ts +107 -0
  190. package/dist/parser/workerpool.d.ts.map +1 -0
  191. package/dist/parser/workerpool.js +383 -0
  192. package/dist/parser/workerpool.js.map +1 -0
  193. package/dist/scip/format.d.ts +87 -0
  194. package/dist/scip/format.d.ts.map +1 -0
  195. package/dist/scip/format.js +31 -0
  196. package/dist/scip/format.js.map +1 -0
  197. package/dist/scip/import.d.ts +37 -0
  198. package/dist/scip/import.d.ts.map +1 -0
  199. package/dist/scip/import.js +180 -0
  200. package/dist/scip/import.js.map +1 -0
  201. package/dist/types.d.ts +392 -0
  202. package/dist/types.d.ts.map +1 -0
  203. package/dist/types.js +4 -0
  204. package/dist/types.js.map +1 -0
  205. package/docs/architecture.md +105 -0
  206. package/docs/benchmarks/methodology.md +134 -0
  207. package/docs/benchmarks/raw-results.md +71 -0
  208. package/docs/benchmarks.md +74 -0
  209. package/docs/cli.md +148 -0
  210. package/docs/examples/behavior-tests.md +70 -0
  211. package/docs/examples/change-history.md +85 -0
  212. package/docs/examples/pre-edit-context.md +81 -0
  213. package/docs/examples/service-links.md +88 -0
  214. package/docs/examples.md +80 -0
  215. package/docs/faq.md +70 -0
  216. package/docs/internals.md +104 -0
  217. package/docs/languages.md +70 -0
  218. package/docs/limits.md +52 -0
  219. package/docs/mcp.md +199 -0
  220. package/docs/quickstart.md +119 -0
  221. package/docs/testing.md +123 -0
  222. package/docs/tools.md +115 -0
  223. package/package.json +52 -0
  224. package/research-codebase.md +578 -0
  225. package/seer-cli-docs.md +326 -0
  226. package/seer-master-guide.md +246 -0
  227. package/src/bundle/ci.ts +141 -0
  228. package/src/bundle/contract.ts +387 -0
  229. package/src/bundle/export.ts +175 -0
  230. package/src/bundle/external.ts +285 -0
  231. package/src/bundle/format.ts +92 -0
  232. package/src/bundle/import.ts +157 -0
  233. package/src/cli/index.ts +1249 -0
  234. package/src/cli/init.ts +389 -0
  235. package/src/db/schema.ts +614 -0
  236. package/src/db/store.ts +4306 -0
  237. package/src/graph/pagerank.ts +53 -0
  238. package/src/indexer/architecture.ts +148 -0
  239. package/src/indexer/behavior.ts +466 -0
  240. package/src/indexer/boundaries.ts +374 -0
  241. package/src/indexer/churn.ts +58 -0
  242. package/src/indexer/classify.ts +96 -0
  243. package/src/indexer/context.ts +340 -0
  244. package/src/indexer/continuity.ts +322 -0
  245. package/src/indexer/detectchanges.ts +94 -0
  246. package/src/indexer/discovery.ts +176 -0
  247. package/src/indexer/externaldeps.ts +243 -0
  248. package/src/indexer/freshness.ts +166 -0
  249. package/src/indexer/git.ts +453 -0
  250. package/src/indexer/index.ts +1092 -0
  251. package/src/indexer/modules.ts +358 -0
  252. package/src/indexer/preflight.ts +548 -0
  253. package/src/indexer/protoScanner.ts +147 -0
  254. package/src/indexer/risk.ts +304 -0
  255. package/src/indexer/serviceHostScanner.ts +92 -0
  256. package/src/indexer/serviceLinks.ts +543 -0
  257. package/src/indexer/shapehash.ts +370 -0
  258. package/src/indexer/skeleton.ts +169 -0
  259. package/src/indexer/symbolhistory.ts +172 -0
  260. package/src/indexer/watcher.ts +206 -0
  261. package/src/mcp/server.ts +1659 -0
  262. package/src/parser/index.ts +37 -0
  263. package/src/parser/languages/cpp.ts +361 -0
  264. package/src/parser/languages/csharp.ts +235 -0
  265. package/src/parser/languages/go.ts +259 -0
  266. package/src/parser/languages/java.ts +382 -0
  267. package/src/parser/languages/python.ts +370 -0
  268. package/src/parser/languages/rust.ts +164 -0
  269. package/src/parser/languages/typescript.ts +1435 -0
  270. package/src/parser/parserContext.ts +392 -0
  271. package/src/parser/walker.ts +306 -0
  272. package/src/parser/worker.ts +181 -0
  273. package/src/parser/workerpool.ts +448 -0
  274. package/src/scip/format.ts +83 -0
  275. package/src/scip/import.ts +216 -0
  276. package/src/types.ts +457 -0
  277. package/tests/benchmark-service-links.ts +244 -0
  278. package/tests/bug-regressions.ts +626 -0
  279. package/tests/filters.ts +264 -0
  280. package/tests/fixtures/Counter.tsx +38 -0
  281. package/tests/fixtures/caller.ts +7 -0
  282. package/tests/fixtures/collisions.ts +23 -0
  283. package/tests/fixtures/local_helper.ts +5 -0
  284. package/tests/fixtures/overloads.java +17 -0
  285. package/tests/fixtures/remote_helper.ts +4 -0
  286. package/tests/fixtures/sample.c +15 -0
  287. package/tests/fixtures/sample.cpp +47 -0
  288. package/tests/fixtures/sample.cs +62 -0
  289. package/tests/fixtures/sample.go +68 -0
  290. package/tests/fixtures/sample.h +30 -0
  291. package/tests/fixtures/sample.java +85 -0
  292. package/tests/fixtures/sample.py +46 -0
  293. package/tests/fixtures/sample.rs +78 -0
  294. package/tests/fixtures/sample.ts +76 -0
  295. package/tests/fixtures-service/HttpClients.cs +30 -0
  296. package/tests/fixtures-service/HttpClients.java +24 -0
  297. package/tests/fixtures-service/billing.ts +15 -0
  298. package/tests/fixtures-service/docker-compose.yml +15 -0
  299. package/tests/fixtures-service/gateway.ts +10 -0
  300. package/tests/fixtures-service/get_user.ts +11 -0
  301. package/tests/fixtures-service/graphql_client.ts +63 -0
  302. package/tests/fixtures-service/graphql_server.ts +30 -0
  303. package/tests/fixtures-service/grpc_client.go +30 -0
  304. package/tests/fixtures-service/http_clients.go +23 -0
  305. package/tests/fixtures-service/http_clients.py +38 -0
  306. package/tests/fixtures-service/http_clients.ts +49 -0
  307. package/tests/fixtures-service/k8s/payment-service.yaml +22 -0
  308. package/tests/fixtures-service/k8s_calls.ts +20 -0
  309. package/tests/fixtures-service/messaging.ts +87 -0
  310. package/tests/fixtures-service/trpc_client.ts +39 -0
  311. package/tests/fixtures-service/trpc_server.ts +39 -0
  312. package/tests/fixtures-service/user_service.proto +33 -0
  313. package/tests/fixtures-trackcd/Cargo.toml +11 -0
  314. package/tests/fixtures-trackcd/SpringController.java +36 -0
  315. package/tests/fixtures-trackcd/auth_service.ts +19 -0
  316. package/tests/fixtures-trackcd/complex_module.py +50 -0
  317. package/tests/fixtures-trackcd/express_app.js +30 -0
  318. package/tests/fixtures-trackcd/fastapi_app.py +49 -0
  319. package/tests/fixtures-trackcd/fastify_object_routes.js +32 -0
  320. package/tests/fixtures-trackcd/go.mod +8 -0
  321. package/tests/fixtures-trackcd/package.json +15 -0
  322. package/tests/fixtures-trackcd/requirements.txt +4 -0
  323. package/tests/fixtures-trackcd/tests/auth_service.test.ts +13 -0
  324. package/tests/fixtures-tracke/auth/AuthService.ts +23 -0
  325. package/tests/fixtures-tracke/auth/crypto.ts +7 -0
  326. package/tests/fixtures-tracke/billing/Billing.ts +20 -0
  327. package/tests/fixtures-tracke/billing/Invoice.ts +10 -0
  328. package/tests/fixtures-tracke/billing/server.ts +17 -0
  329. package/tests/fixtures-tracke/package.json +7 -0
  330. package/tests/fixtures-tracke/tests/auth.test.ts +23 -0
  331. package/tests/fixtures-tracke/tests/billing.test.ts +14 -0
  332. package/tests/fixtures-trackf/package.json +5 -0
  333. package/tests/fixtures-trackf/src/auth.ts +26 -0
  334. package/tests/fixtures-trackf/src/handlers.ts +35 -0
  335. package/tests/fixtures-tracki/billing/routes.ts +12 -0
  336. package/tests/fixtures-tracki/gateway/client.ts +13 -0
  337. package/tests/git-features.ts +267 -0
  338. package/tests/init.ts +141 -0
  339. package/tests/mcp-jit.ts +130 -0
  340. package/tests/mcp-smoke.ts +191 -0
  341. package/tests/mcp-trackcd.ts +169 -0
  342. package/tests/mcp-tracke.ts +229 -0
  343. package/tests/mcp-trackf.ts +330 -0
  344. package/tests/mcp-trackg.ts +219 -0
  345. package/tests/mcp-tracki.ts +174 -0
  346. package/tests/mcp-watcher.ts +126 -0
  347. package/tests/optspec.ts +194 -0
  348. package/tests/parallel-index.ts +333 -0
  349. package/tests/parallel-read.ts +125 -0
  350. package/tests/parallel-recovery.ts +241 -0
  351. package/tests/perf-callers.ts +145 -0
  352. package/tests/query-parity.ts +184 -0
  353. package/tests/query-perf.ts +55 -0
  354. package/tests/scale-parallel-parity.ts +225 -0
  355. package/tests/scale-test.ts +523 -0
  356. package/tests/smoke.ts +396 -0
  357. package/tests/trackcd.ts +325 -0
  358. package/tests/tracke-collisions.ts +255 -0
  359. package/tests/tracke.ts +314 -0
  360. package/tests/trackf-bugs.ts +406 -0
  361. package/tests/trackf.ts +390 -0
  362. package/tests/trackg.ts +1372 -0
  363. package/tests/tracki-boundaries.ts +202 -0
  364. package/tests/tracki-continuity.ts +253 -0
  365. package/tests/tracki-contract-diff.ts +249 -0
  366. package/tests/tracki-external-bundles.ts +341 -0
  367. package/tests/tracki-preflight.ts +251 -0
  368. package/tests/verify-roles.ts +51 -0
  369. package/tests/worker-parity.ts +286 -0
  370. package/tests/worker-pool.ts +262 -0
  371. package/tsconfig.json +20 -0
@@ -0,0 +1,285 @@
1
+ /**
2
+ * v10 External Bundle Layers — additive import of a peer repo's .seerbundle as
3
+ * read-only external evidence.
4
+ *
5
+ * The destructive bundle import (src/bundle/import.ts) replaces the local
6
+ * graph DB wholesale; this importer leaves local files/symbols untouched and
7
+ * only adds:
8
+ * - one row in external_bundles (source identity + manifest hash)
9
+ * - one phantom file per bundle (`__external_bundle__/<project>/<id>`)
10
+ * - rows in routes (and where present, service_endpoints) marked
11
+ * `external_bundle_id` so they participate in service-link resolution but
12
+ * can be wiped as a single layer on re-import.
13
+ *
14
+ * Design rules:
15
+ * - never touch local symbols / files / edges / pagerank
16
+ * - never write rows whose external_bundle_id is NULL
17
+ * - re-importing the same path with the same hash is a no-op
18
+ * - re-importing with a NEW hash replaces only that layer (FK cascade)
19
+ * - the bundle is read by spinning up a temp Store against the bundle's
20
+ * SQLite payload — same code path that bundle/info uses to enumerate the
21
+ * manifest, but with full row access scoped read-only
22
+ */
23
+
24
+ import fs from 'fs';
25
+ import path from 'path';
26
+ import os from 'os';
27
+ import zlib from 'zlib';
28
+ import crypto from 'crypto';
29
+ import { BUNDLE_MAGIC, BUNDLE_FORMAT_VERSION, BundleManifest } from './format.js';
30
+ import { CURRENT_SCHEMA_VERSION } from '../db/schema.js';
31
+ import { Store } from '../db/store.js';
32
+ import { resolveServiceLinks } from '../indexer/serviceLinks.js';
33
+
34
+ export interface ExternalImportOptions {
35
+ /** Optional alias for the bundle (defaults to the manifest's gitBranch or basename). */
36
+ alias?: string;
37
+ /** When true, force re-import even if the same hash is already present. */
38
+ force?: boolean;
39
+ /**
40
+ * Skip the dbSha256 / dbBytes integrity check. Off by default — the manifest
41
+ * integrity guarantee is what lets us trust a peer-repo bundle. Use only for
42
+ * forensic inspection of a corrupted bundle whose header still parses.
43
+ */
44
+ skipIntegrityCheck?: boolean;
45
+ /**
46
+ * Skip the schema-version compatibility check. Off by default — a bundle
47
+ * whose schemaVersion is newer than this build can't be safely read.
48
+ */
49
+ skipSchemaCheck?: boolean;
50
+ /**
51
+ * Rebuild service_links after import so cross-repo links are visible to the
52
+ * next query without waiting for a re-index. Default true.
53
+ */
54
+ rebuildServiceLinks?: boolean;
55
+ /** Logger; defaults to no-op. */
56
+ log?: (msg: string) => void;
57
+ }
58
+
59
+ export interface ExternalImportResult {
60
+ bundlePath: string;
61
+ bundleId: number;
62
+ externalProject: string | null;
63
+ externalHash: string;
64
+ schemaVersion: number;
65
+ /** True when the same bundle path + hash was already imported and we skipped. */
66
+ alreadyImported: boolean;
67
+ routesImported: number;
68
+ serviceEndpointsImported: number;
69
+ elapsedMs: number;
70
+ }
71
+
72
+ /**
73
+ * Import a .seerbundle as an additive external layer into the given local
74
+ * Store. The local DB is NOT touched outside of the external_bundles row,
75
+ * the phantom file row, and the new external-marked routes/service_calls.
76
+ */
77
+ export async function importExternalBundle(
78
+ bundlePath: string,
79
+ localStore: Store,
80
+ options: ExternalImportOptions = {},
81
+ ): Promise<ExternalImportResult> {
82
+ const start = Date.now();
83
+ const log = options.log ?? (() => { /* */ });
84
+
85
+ if (localStore.isReadOnly()) {
86
+ throw new Error('Cannot import external bundle into a read-only Store');
87
+ }
88
+ if (!localStore.hasV10()) {
89
+ throw new Error('Local DB does not have v10 external_bundles tables — upgrade first');
90
+ }
91
+
92
+ if (!fs.existsSync(bundlePath)) {
93
+ throw new Error(`No bundle at ${bundlePath}`);
94
+ }
95
+
96
+ // Parse the manifest + payload up front. We need both: the manifest gives us
97
+ // identity + schema_version, the payload gives us the read source.
98
+ const fileBuf = fs.readFileSync(bundlePath);
99
+ if (fileBuf.length < 12 || !fileBuf.subarray(0, 4).equals(BUNDLE_MAGIC)) {
100
+ throw new Error(`Not a Seer bundle: ${bundlePath} (bad magic)`);
101
+ }
102
+ const formatVersion = fileBuf.readUInt32BE(4);
103
+ if (formatVersion > BUNDLE_FORMAT_VERSION) {
104
+ throw new Error(`Bundle format v${formatVersion} is newer than this build (v${BUNDLE_FORMAT_VERSION}). Upgrade Seer.`);
105
+ }
106
+ const manifestLen = fileBuf.readUInt32BE(8);
107
+ const manifestEnd = 12 + manifestLen;
108
+ if (manifestLen <= 0 || manifestEnd > fileBuf.length) {
109
+ throw new Error(`Bundle truncated; manifest length ${manifestLen} exceeds file size`);
110
+ }
111
+ const manifest = JSON.parse(fileBuf.slice(12, manifestEnd).toString('utf-8')) as BundleManifest;
112
+
113
+ // Schema compatibility: a bundle built against a NEWER schema may carry
114
+ // tables/columns we don't understand. Reject up front rather than reading
115
+ // partial data. Older bundles are fine — listRoutes tolerates older shapes.
116
+ if (!options.skipSchemaCheck) {
117
+ if (typeof manifest.schemaVersion !== 'number' || manifest.schemaVersion <= 0) {
118
+ throw new Error(`Bundle has invalid schemaVersion=${manifest.schemaVersion}.`);
119
+ }
120
+ if (manifest.schemaVersion > CURRENT_SCHEMA_VERSION) {
121
+ throw new Error(
122
+ `Bundle schemaVersion=${manifest.schemaVersion} is newer than this build's ` +
123
+ `CURRENT_SCHEMA_VERSION=${CURRENT_SCHEMA_VERSION}. Upgrade Seer or pass skipSchemaCheck=true.`,
124
+ );
125
+ }
126
+ }
127
+
128
+ const externalHash = manifest.dbSha256;
129
+ const externalProject = options.alias
130
+ ?? manifest.source.gitBranch
131
+ ?? path.basename(bundlePath, path.extname(bundlePath));
132
+ const externalVersion = manifest.source.gitHead ?? null;
133
+
134
+ const absBundlePath = path.resolve(bundlePath);
135
+
136
+ // Idempotency check: same path + same hash → no-op.
137
+ const existing = localStore.findExternalBundleByPath(absBundlePath);
138
+ if (existing && existing.externalHash === externalHash && !options.force) {
139
+ log(`already imported with the same hash; skipping`);
140
+ return {
141
+ bundlePath: absBundlePath,
142
+ bundleId: existing.id,
143
+ externalProject: existing.externalProject,
144
+ externalHash,
145
+ schemaVersion: manifest.schemaVersion,
146
+ alreadyImported: true,
147
+ routesImported: 0,
148
+ serviceEndpointsImported: 0,
149
+ elapsedMs: Date.now() - start,
150
+ };
151
+ }
152
+
153
+ // Decompress the embedded DB so we can read its tables.
154
+ const compressed = fileBuf.slice(manifestEnd);
155
+ const dbBuf = zlib.gunzipSync(compressed);
156
+
157
+ // Integrity check: the decompressed payload must match the manifest's
158
+ // sha256 + byte length. A tampered or truncated bundle is rejected here.
159
+ if (!options.skipIntegrityCheck) {
160
+ const dbSha = crypto.createHash('sha256').update(dbBuf).digest('hex');
161
+ if (dbSha !== manifest.dbSha256) {
162
+ throw new Error(`Bundle integrity check FAILED. Expected ${manifest.dbSha256}, got ${dbSha}.`);
163
+ }
164
+ if (typeof manifest.dbBytes === 'number' && dbBuf.length !== manifest.dbBytes) {
165
+ throw new Error(`Bundle integrity check FAILED. Expected ${manifest.dbBytes} bytes, got ${dbBuf.length}.`);
166
+ }
167
+ }
168
+
169
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seer-extbundle-'));
170
+ const externalDbPath = path.join(tmpDir, 'external.db');
171
+ fs.writeFileSync(externalDbPath, dbBuf);
172
+
173
+ let bundleId = 0;
174
+ let routesImported = 0;
175
+ let serviceEndpointsImported = 0;
176
+ try {
177
+ const externalStore = Store.openReadOnly(externalDbPath);
178
+ try {
179
+ // If we already had this path but with a different hash, wipe the
180
+ // previous layer first so the new import is a clean replace.
181
+ if (existing) {
182
+ const wiped = localStore.clearExternalBundle(existing.id);
183
+ log(`re-import: wiped previous layer (routes=${wiped.routes}, serviceCalls=${wiped.serviceCalls})`);
184
+ }
185
+
186
+ // Create the bundle row first so we have a stable id for the phantom
187
+ // file + the imported route rows.
188
+ bundleId = localStore.upsertExternalBundle({
189
+ bundlePath: absBundlePath,
190
+ externalProject,
191
+ externalVersion,
192
+ externalHash,
193
+ schemaVersion: manifest.schemaVersion,
194
+ routesImported: 0,
195
+ serviceCallsImported: 0,
196
+ serviceLinksImported: 0,
197
+ });
198
+ const externalFileId = localStore.ensureExternalFile(bundleId, externalProject);
199
+
200
+ // Import routes from the external bundle. We use the public listRoutes
201
+ // API which transparently handles pre-v9 column shapes (older bundles
202
+ // won't carry protocol/operation/etc.).
203
+ const localRoutes = externalStore.listRoutes({ limit: 1_000_000 });
204
+ // Filter: we never want external-bundle rows from a nested bundle. If
205
+ // the source bundle has external_bundle_id, skip those (they're
206
+ // someone else's external layer).
207
+ const importableRoutes = localRoutes.filter(r => {
208
+ // Cannot easily detect external_bundle_id via the public API; raw
209
+ // probe the column. listRoutes doesn't surface it today, so accept
210
+ // everything; the bundle generator should not have exported external
211
+ // rows itself (round-tripping external layers would be misleading).
212
+ return r.method && r.path;
213
+ });
214
+ log(`importing ${importableRoutes.length} routes from external bundle`);
215
+ localStore.begin();
216
+ try {
217
+ for (const r of importableRoutes) {
218
+ localStore.insertExternalRoute({
219
+ bundleId,
220
+ externalFileId,
221
+ method: r.method,
222
+ path: r.path,
223
+ framework: r.framework,
224
+ handlerName: r.handlerName,
225
+ line: r.line,
226
+ protocol: r.protocol ?? 'http',
227
+ operation: r.operation ?? null,
228
+ topic: r.topic ?? null,
229
+ queue: r.queue ?? null,
230
+ exchange: r.exchange ?? null,
231
+ service: r.service ?? null,
232
+ broker: r.broker ?? null,
233
+ metadataJson: r.metadataJson ?? null,
234
+ });
235
+ routesImported++;
236
+ }
237
+ localStore.commit();
238
+ } catch (err) {
239
+ localStore.rollback();
240
+ throw err;
241
+ }
242
+
243
+ // Rebuild service links so the freshly-imported external routes are
244
+ // visible to the next query immediately, without waiting for a re-index.
245
+ let serviceLinksImported = 0;
246
+ if (options.rebuildServiceLinks !== false) {
247
+ try {
248
+ const sr = resolveServiceLinks(localStore);
249
+ serviceLinksImported = sr.linksInserted ?? 0;
250
+ log(`rebuilt service links (linked=${serviceLinksImported})`);
251
+ } catch (err) {
252
+ log(`service-link rebuild skipped: ${(err as Error).message}`);
253
+ }
254
+ }
255
+
256
+ // Update final counts on the bundle row.
257
+ localStore.upsertExternalBundle({
258
+ bundlePath: absBundlePath,
259
+ externalProject,
260
+ externalVersion,
261
+ externalHash,
262
+ schemaVersion: manifest.schemaVersion,
263
+ routesImported,
264
+ serviceCallsImported: 0,
265
+ serviceLinksImported,
266
+ });
267
+ } finally {
268
+ externalStore.close();
269
+ }
270
+ } finally {
271
+ try { fs.unlinkSync(externalDbPath); fs.rmdirSync(tmpDir); } catch { /* */ }
272
+ }
273
+
274
+ return {
275
+ bundlePath: absBundlePath,
276
+ bundleId,
277
+ externalProject,
278
+ externalHash,
279
+ schemaVersion: manifest.schemaVersion,
280
+ alreadyImported: false,
281
+ routesImported,
282
+ serviceEndpointsImported,
283
+ elapsedMs: Date.now() - start,
284
+ };
285
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Track-F portable .seer bundle format.
3
+ *
4
+ * A bundle is a single self-describing file that lets a team ship a fully
5
+ * built index without every developer having to re-run `seer index`. The
6
+ * format is deliberately tiny and dependency-free:
7
+ *
8
+ * [4 bytes: magic 'SEER']
9
+ * [4 bytes: bundle format version (uint32 BE)]
10
+ * [4 bytes: manifest length (uint32 BE)]
11
+ * [N bytes: manifest JSON (utf-8)]
12
+ * [M bytes: gzip-compressed SQLite DB]
13
+ *
14
+ * Why not tar/zip? Both add a dependency or a hand-rolled archive writer for
15
+ * a single payload file. Our payload IS the DB; everything else is a few
16
+ * structured fields. One header + one gzip stream gets us:
17
+ * - O(1) manifest read without decompressing the DB
18
+ * - cross-platform, no native deps
19
+ *
20
+ * On determinism: the gzip stream and the VACUUM INTO'd DB ARE deterministic
21
+ * for fixed input. The manifest, however, defaults `builtAt = Date.now()` so
22
+ * out-of-the-box exports of the same DB at different wall-clock times
23
+ * produce different bytes. If you need bit-identical bundles (build-cache
24
+ * keys, reproducible-builds CI), pass an explicit `builtAt` to `exportBundle`
25
+ * to pin it.
26
+ *
27
+ * The manifest is the source of truth for:
28
+ * - schema_version (so the consumer can refuse incompatible bundles)
29
+ * - source signature (repo root, git head, file count + hash) so the
30
+ * bundle's "shape" is auditable before the agent trusts it
31
+ * - tool versions, build time, SCIP layers contributing precision
32
+ *
33
+ * Bundles are produced by `seer bundle export` and consumed by
34
+ * `seer bundle import`. CI mode (`seer ci bundle`) is a thin wrapper that
35
+ * runs `index` then `bundle export` and surfaces the resulting path.
36
+ */
37
+
38
+ export const BUNDLE_MAGIC = Buffer.from('SEER', 'utf-8');
39
+ export const BUNDLE_FORMAT_VERSION = 1;
40
+
41
+ export interface BundleManifest {
42
+ /**
43
+ * Bundle format version — bumped when the on-disk layout changes
44
+ * incompatibly. Distinct from the DB schema version, which can bump
45
+ * independently when only the SQL evolves.
46
+ */
47
+ bundleFormatVersion: number;
48
+ /** Schema version of the embedded DB. Consumers refuse on mismatch. */
49
+ schemaVersion: number;
50
+ /** Wall-clock millis the bundle was built. */
51
+ builtAt: number;
52
+ /** Tool that produced the bundle. */
53
+ builtBy: string;
54
+ /** Source identity. */
55
+ source: {
56
+ repoRoot: string;
57
+ gitHead: string | null;
58
+ gitBranch: string | null;
59
+ /** Stable hash over the source file roster — `(relPath, fileHash)` pairs. */
60
+ rosterHash: string;
61
+ /** Number of source files indexed. */
62
+ fileCount: number;
63
+ };
64
+ /** Index summary so a consumer can size-check without unpacking. */
65
+ index: {
66
+ symbols: number;
67
+ edges: number;
68
+ resolvedEdges: number;
69
+ modules: number;
70
+ routes: number;
71
+ externalDependencies: number;
72
+ configKeys: number;
73
+ languages: Record<string, number>;
74
+ /** v7: provenance counts. */
75
+ provenance?: {
76
+ symbols: Record<string, number>;
77
+ edges: Record<string, number>;
78
+ };
79
+ };
80
+ /** SCIP layers folded into the DB (if any). */
81
+ scipImports: Array<{
82
+ path: string; sha256: string; tool: string | null;
83
+ symbolCount: number; refCount: number;
84
+ }>;
85
+ /**
86
+ * Sha-256 of the embedded DB (computed BEFORE compression). Lets the
87
+ * consumer verify a transferred bundle without trusting the gzip CRC alone.
88
+ */
89
+ dbSha256: string;
90
+ /** Uncompressed DB size in bytes. */
91
+ dbBytes: number;
92
+ }
@@ -0,0 +1,157 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import crypto from 'crypto';
4
+ import zlib from 'zlib';
5
+ import {
6
+ BUNDLE_MAGIC, BUNDLE_FORMAT_VERSION, BundleManifest,
7
+ } from './format.js';
8
+ import { CURRENT_SCHEMA_VERSION } from '../db/schema.js';
9
+
10
+ export interface ImportOptions {
11
+ /**
12
+ * Where to land the extracted DB. Defaults to `<repoRoot>/.seer/graph.db`.
13
+ * If the path already exists and `overwrite` is false (the default), the
14
+ * import fails — bundles are meant to bootstrap a missing index, not to
15
+ * silently clobber a fresher local one.
16
+ */
17
+ dbOut?: string;
18
+ overwrite?: boolean;
19
+ /** Logger; defaults to no-op. */
20
+ log?: (msg: string) => void;
21
+ /**
22
+ * Skip the dbSha256 check. Off by default — the integrity guarantee is the
23
+ * whole point of the manifest. Use this only for forensic inspection of a
24
+ * corrupted bundle whose header still parses.
25
+ */
26
+ skipIntegrityCheck?: boolean;
27
+ /**
28
+ * Skip the schema-version compatibility check. Off by default — bundles
29
+ * whose schemaVersion is newer than this build's CURRENT_SCHEMA_VERSION
30
+ * are rejected because we can't safely open them. Override only when you
31
+ * KNOW the schema is compatible (e.g. forensics or a controlled upgrade).
32
+ */
33
+ skipSchemaCheck?: boolean;
34
+ /**
35
+ * Required source repo root for the imported index. Used to decide the
36
+ * default dbOut location only — the bundle's manifest.source.repoRoot is
37
+ * stored as metadata but never enforced (bundles are commonly built on
38
+ * a CI runner with a different absolute path).
39
+ */
40
+ repoRoot: string;
41
+ }
42
+
43
+ export interface ImportResult {
44
+ bundlePath: string;
45
+ dbPath: string;
46
+ manifest: BundleManifest;
47
+ bytes: number;
48
+ elapsedMs: number;
49
+ }
50
+
51
+ /** Read just the manifest from a bundle, without unpacking the DB. */
52
+ export function readBundleManifest(bundlePath: string): BundleManifest {
53
+ const fd = fs.openSync(bundlePath, 'r');
54
+ try {
55
+ const header = Buffer.alloc(12);
56
+ const headerRead = fs.readSync(fd, header, 0, 12, 0);
57
+ if (headerRead < 12 || !header.subarray(0, 4).equals(BUNDLE_MAGIC)) {
58
+ throw new Error(`Not a Seer bundle: ${bundlePath} (bad magic)`);
59
+ }
60
+ const formatVersion = header.readUInt32BE(4);
61
+ if (formatVersion > BUNDLE_FORMAT_VERSION) {
62
+ throw new Error(`Bundle format v${formatVersion} is newer than this build (v${BUNDLE_FORMAT_VERSION}). Upgrade Seer.`);
63
+ }
64
+ const manifestLen = header.readUInt32BE(8);
65
+ if (manifestLen <= 0 || manifestLen > 64 * 1024 * 1024) {
66
+ throw new Error(`Bundle manifest length out of range: ${manifestLen}`);
67
+ }
68
+ const manifestBuf = Buffer.alloc(manifestLen);
69
+ const read = fs.readSync(fd, manifestBuf, 0, manifestLen, 12);
70
+ if (read !== manifestLen) {
71
+ throw new Error(`Bundle truncated reading manifest`);
72
+ }
73
+ return JSON.parse(manifestBuf.toString('utf-8')) as BundleManifest;
74
+ } finally {
75
+ fs.closeSync(fd);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Restore a bundle into the workspace. The default destination is
81
+ * `<repoRoot>/.seer/graph.db`. Verifies the manifest, format version, and
82
+ * (unless skipped) the DB sha256.
83
+ */
84
+ export async function importBundle(
85
+ bundlePath: string, options: ImportOptions,
86
+ ): Promise<ImportResult> {
87
+ const start = Date.now();
88
+ const log = options.log ?? (() => { /* */ });
89
+
90
+ if (!fs.existsSync(bundlePath)) {
91
+ throw new Error(`No bundle at ${bundlePath}`);
92
+ }
93
+
94
+ const fileBuf = fs.readFileSync(bundlePath);
95
+ if (fileBuf.length < 12 || !fileBuf.subarray(0, 4).equals(BUNDLE_MAGIC)) {
96
+ throw new Error(`Not a Seer bundle: ${bundlePath} (bad magic)`);
97
+ }
98
+ const formatVersion = fileBuf.readUInt32BE(4);
99
+ if (formatVersion > BUNDLE_FORMAT_VERSION) {
100
+ throw new Error(`Bundle format v${formatVersion} is newer than this build (v${BUNDLE_FORMAT_VERSION}). Upgrade Seer.`);
101
+ }
102
+ const manifestLen = fileBuf.readUInt32BE(8);
103
+ const manifestEnd = 12 + manifestLen;
104
+ if (manifestEnd > fileBuf.length) {
105
+ throw new Error(`Bundle truncated; manifest length ${manifestLen} exceeds file size`);
106
+ }
107
+ const manifest = JSON.parse(fileBuf.slice(12, manifestEnd).toString('utf-8')) as BundleManifest;
108
+ log(`Manifest: schemaVersion=${manifest.schemaVersion} symbols=${manifest.index.symbols} edges=${manifest.index.edges} builtAt=${new Date(manifest.builtAt).toISOString()}`);
109
+
110
+ // Schema compatibility check. The bundle's embedded DB was built against a
111
+ // specific schema version; if it's NEWER than this build of Seer, the DB
112
+ // contains tables/columns we don't understand and opening it would either
113
+ // crash on read or silently lose precision. Reject up front rather than
114
+ // crashing on first query. Older bundles are accepted because the Store's
115
+ // runMigrations() will catch the DB up at open time.
116
+ if (!options.skipSchemaCheck) {
117
+ if (manifest.schemaVersion > CURRENT_SCHEMA_VERSION) {
118
+ throw new Error(
119
+ `Bundle schemaVersion=${manifest.schemaVersion} is newer than this build's ` +
120
+ `CURRENT_SCHEMA_VERSION=${CURRENT_SCHEMA_VERSION}. Upgrade Seer or pass skipSchemaCheck=true.`,
121
+ );
122
+ }
123
+ if (manifest.schemaVersion <= 0) {
124
+ throw new Error(`Bundle has invalid schemaVersion=${manifest.schemaVersion}.`);
125
+ }
126
+ }
127
+
128
+ const compressed = fileBuf.slice(manifestEnd);
129
+ const dbBuf = zlib.gunzipSync(compressed);
130
+
131
+ if (!options.skipIntegrityCheck) {
132
+ const dbSha = crypto.createHash('sha256').update(dbBuf).digest('hex');
133
+ if (dbSha !== manifest.dbSha256) {
134
+ throw new Error(`Bundle integrity check FAILED. Expected ${manifest.dbSha256}, got ${dbSha}.`);
135
+ }
136
+ if (dbBuf.length !== manifest.dbBytes) {
137
+ throw new Error(`Bundle integrity check FAILED. Expected ${manifest.dbBytes} bytes, got ${dbBuf.length}.`);
138
+ }
139
+ }
140
+
141
+ const dbOut = options.dbOut ?? path.join(options.repoRoot, '.seer', 'graph.db');
142
+ if (fs.existsSync(dbOut) && !options.overwrite) {
143
+ throw new Error(`Refusing to overwrite existing index at ${dbOut} — pass overwrite=true to replace it.`);
144
+ }
145
+ fs.mkdirSync(path.dirname(dbOut), { recursive: true });
146
+ fs.writeFileSync(dbOut, dbBuf);
147
+ // Drop the WAL/SHM siblings of the previous DB (if any) so the imported
148
+ // file isn't read against a stale journal.
149
+ try { fs.unlinkSync(dbOut + '-wal'); } catch { /* */ }
150
+ try { fs.unlinkSync(dbOut + '-shm'); } catch { /* */ }
151
+ log(`Wrote ${dbBuf.length} bytes to ${dbOut}`);
152
+
153
+ return {
154
+ bundlePath, dbPath: dbOut, manifest,
155
+ bytes: fileBuf.length, elapsedMs: Date.now() - start,
156
+ };
157
+ }