nodedex 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 (469) hide show
  1. package/adapters/claude-code-watcher.mjs +336 -0
  2. package/adapters/hermes-statedb-watcher.mjs +234 -0
  3. package/adapters/nodedex-capture-core.mjs +129 -0
  4. package/adapters/nodedex-capture.mjs +169 -0
  5. package/dist/agent-protocol.d.ts +7 -0
  6. package/dist/agent-protocol.d.ts.map +1 -0
  7. package/dist/agent-protocol.js +38 -0
  8. package/dist/agent-protocol.js.map +1 -0
  9. package/dist/api-server.d.ts +5 -0
  10. package/dist/api-server.d.ts.map +1 -0
  11. package/dist/api-server.js +351 -0
  12. package/dist/api-server.js.map +1 -0
  13. package/dist/boot-env.d.ts +2 -0
  14. package/dist/boot-env.d.ts.map +1 -0
  15. package/dist/boot-env.js +12 -0
  16. package/dist/boot-env.js.map +1 -0
  17. package/dist/engine/__tests__/search-core.test.d.ts +2 -0
  18. package/dist/engine/__tests__/search-core.test.d.ts.map +1 -0
  19. package/dist/engine/__tests__/search-core.test.js +139 -0
  20. package/dist/engine/__tests__/search-core.test.js.map +1 -0
  21. package/dist/engine/ai-provider.d.ts +45 -0
  22. package/dist/engine/ai-provider.d.ts.map +1 -0
  23. package/dist/engine/ai-provider.js +5 -0
  24. package/dist/engine/ai-provider.js.map +1 -0
  25. package/dist/engine/embeddings.d.ts +51 -0
  26. package/dist/engine/embeddings.d.ts.map +1 -0
  27. package/dist/engine/embeddings.js +89 -0
  28. package/dist/engine/embeddings.js.map +1 -0
  29. package/dist/engine/providers/__tests__/failure-policy.test.d.ts +2 -0
  30. package/dist/engine/providers/__tests__/failure-policy.test.d.ts.map +1 -0
  31. package/dist/engine/providers/__tests__/failure-policy.test.js +134 -0
  32. package/dist/engine/providers/__tests__/failure-policy.test.js.map +1 -0
  33. package/dist/engine/providers/__tests__/model-caps.test.d.ts +2 -0
  34. package/dist/engine/providers/__tests__/model-caps.test.d.ts.map +1 -0
  35. package/dist/engine/providers/__tests__/model-caps.test.js +38 -0
  36. package/dist/engine/providers/__tests__/model-caps.test.js.map +1 -0
  37. package/dist/engine/providers/__tests__/openai-structured.test.d.ts +2 -0
  38. package/dist/engine/providers/__tests__/openai-structured.test.d.ts.map +1 -0
  39. package/dist/engine/providers/__tests__/openai-structured.test.js +73 -0
  40. package/dist/engine/providers/__tests__/openai-structured.test.js.map +1 -0
  41. package/dist/engine/providers/__tests__/usage-ledger.test.d.ts +2 -0
  42. package/dist/engine/providers/__tests__/usage-ledger.test.d.ts.map +1 -0
  43. package/dist/engine/providers/__tests__/usage-ledger.test.js +108 -0
  44. package/dist/engine/providers/__tests__/usage-ledger.test.js.map +1 -0
  45. package/dist/engine/providers/anthropic.d.ts +17 -0
  46. package/dist/engine/providers/anthropic.d.ts.map +1 -0
  47. package/dist/engine/providers/anthropic.js +125 -0
  48. package/dist/engine/providers/anthropic.js.map +1 -0
  49. package/dist/engine/providers/failure-policy.d.ts +56 -0
  50. package/dist/engine/providers/failure-policy.d.ts.map +1 -0
  51. package/dist/engine/providers/failure-policy.js +120 -0
  52. package/dist/engine/providers/failure-policy.js.map +1 -0
  53. package/dist/engine/providers/gemini.d.ts +22 -0
  54. package/dist/engine/providers/gemini.d.ts.map +1 -0
  55. package/dist/engine/providers/gemini.js +180 -0
  56. package/dist/engine/providers/gemini.js.map +1 -0
  57. package/dist/engine/providers/index.d.ts +8 -0
  58. package/dist/engine/providers/index.d.ts.map +1 -0
  59. package/dist/engine/providers/index.js +67 -0
  60. package/dist/engine/providers/index.js.map +1 -0
  61. package/dist/engine/providers/local.d.ts +12 -0
  62. package/dist/engine/providers/local.d.ts.map +1 -0
  63. package/dist/engine/providers/local.js +46 -0
  64. package/dist/engine/providers/local.js.map +1 -0
  65. package/dist/engine/providers/model-caps.d.ts +6 -0
  66. package/dist/engine/providers/model-caps.d.ts.map +1 -0
  67. package/dist/engine/providers/model-caps.js +49 -0
  68. package/dist/engine/providers/model-caps.js.map +1 -0
  69. package/dist/engine/providers/openai.d.ts +30 -0
  70. package/dist/engine/providers/openai.d.ts.map +1 -0
  71. package/dist/engine/providers/openai.js +309 -0
  72. package/dist/engine/providers/openai.js.map +1 -0
  73. package/dist/engine/providers/usage-ledger.d.ts +69 -0
  74. package/dist/engine/providers/usage-ledger.d.ts.map +1 -0
  75. package/dist/engine/providers/usage-ledger.js +209 -0
  76. package/dist/engine/providers/usage-ledger.js.map +1 -0
  77. package/dist/engine/search-core.d.ts +40 -0
  78. package/dist/engine/search-core.d.ts.map +1 -0
  79. package/dist/engine/search-core.js +109 -0
  80. package/dist/engine/search-core.js.map +1 -0
  81. package/dist/engine/vector-math.d.ts +5 -0
  82. package/dist/engine/vector-math.d.ts.map +1 -0
  83. package/dist/engine/vector-math.js +25 -0
  84. package/dist/engine/vector-math.js.map +1 -0
  85. package/dist/home-env.d.ts +26 -0
  86. package/dist/home-env.d.ts.map +1 -0
  87. package/dist/home-env.js +87 -0
  88. package/dist/home-env.js.map +1 -0
  89. package/dist/mcp-server.d.ts +13 -0
  90. package/dist/mcp-server.d.ts.map +1 -0
  91. package/dist/mcp-server.js +79 -0
  92. package/dist/mcp-server.js.map +1 -0
  93. package/dist/middleware/auth.d.ts +23 -0
  94. package/dist/middleware/auth.d.ts.map +1 -0
  95. package/dist/middleware/auth.js +104 -0
  96. package/dist/middleware/auth.js.map +1 -0
  97. package/dist/middleware/auto-recall.d.ts +7 -0
  98. package/dist/middleware/auto-recall.d.ts.map +1 -0
  99. package/dist/middleware/auto-recall.js +257 -0
  100. package/dist/middleware/auto-recall.js.map +1 -0
  101. package/dist/middleware/auto-reflect.d.ts +4 -0
  102. package/dist/middleware/auto-reflect.d.ts.map +1 -0
  103. package/dist/middleware/auto-reflect.js +5 -0
  104. package/dist/middleware/auto-reflect.js.map +1 -0
  105. package/dist/middleware/reflect/apply-flag-verdict.d.ts +27 -0
  106. package/dist/middleware/reflect/apply-flag-verdict.d.ts.map +1 -0
  107. package/dist/middleware/reflect/apply-flag-verdict.js +57 -0
  108. package/dist/middleware/reflect/apply-flag-verdict.js.map +1 -0
  109. package/dist/middleware/reflect/arc-entity-resolve.d.ts +29 -0
  110. package/dist/middleware/reflect/arc-entity-resolve.d.ts.map +1 -0
  111. package/dist/middleware/reflect/arc-entity-resolve.js +356 -0
  112. package/dist/middleware/reflect/arc-entity-resolve.js.map +1 -0
  113. package/dist/middleware/reflect/arc-inactivity-timer.d.ts +47 -0
  114. package/dist/middleware/reflect/arc-inactivity-timer.d.ts.map +1 -0
  115. package/dist/middleware/reflect/arc-inactivity-timer.js +175 -0
  116. package/dist/middleware/reflect/arc-inactivity-timer.js.map +1 -0
  117. package/dist/middleware/reflect/arc-pipeline.d.ts +33 -0
  118. package/dist/middleware/reflect/arc-pipeline.d.ts.map +1 -0
  119. package/dist/middleware/reflect/arc-pipeline.js +498 -0
  120. package/dist/middleware/reflect/arc-pipeline.js.map +1 -0
  121. package/dist/middleware/reflect/comprehend-pergroup.d.ts +100 -0
  122. package/dist/middleware/reflect/comprehend-pergroup.d.ts.map +1 -0
  123. package/dist/middleware/reflect/comprehend-pergroup.js +610 -0
  124. package/dist/middleware/reflect/comprehend-pergroup.js.map +1 -0
  125. package/dist/middleware/reflect/comprehend.d.ts +237 -0
  126. package/dist/middleware/reflect/comprehend.d.ts.map +1 -0
  127. package/dist/middleware/reflect/comprehend.js +706 -0
  128. package/dist/middleware/reflect/comprehend.js.map +1 -0
  129. package/dist/middleware/reflect/config.d.ts +34 -0
  130. package/dist/middleware/reflect/config.d.ts.map +1 -0
  131. package/dist/middleware/reflect/config.js +131 -0
  132. package/dist/middleware/reflect/config.js.map +1 -0
  133. package/dist/middleware/reflect/context.d.ts +138 -0
  134. package/dist/middleware/reflect/context.d.ts.map +1 -0
  135. package/dist/middleware/reflect/context.js +619 -0
  136. package/dist/middleware/reflect/context.js.map +1 -0
  137. package/dist/middleware/reflect/cost-breakdown.d.ts +69 -0
  138. package/dist/middleware/reflect/cost-breakdown.d.ts.map +1 -0
  139. package/dist/middleware/reflect/cost-breakdown.js +63 -0
  140. package/dist/middleware/reflect/cost-breakdown.js.map +1 -0
  141. package/dist/middleware/reflect/cost-guard.d.ts +102 -0
  142. package/dist/middleware/reflect/cost-guard.d.ts.map +1 -0
  143. package/dist/middleware/reflect/cost-guard.js +243 -0
  144. package/dist/middleware/reflect/cost-guard.js.map +1 -0
  145. package/dist/middleware/reflect/cost-pricing.d.ts +54 -0
  146. package/dist/middleware/reflect/cost-pricing.d.ts.map +1 -0
  147. package/dist/middleware/reflect/cost-pricing.js +148 -0
  148. package/dist/middleware/reflect/cost-pricing.js.map +1 -0
  149. package/dist/middleware/reflect/cross-group-link.d.ts +61 -0
  150. package/dist/middleware/reflect/cross-group-link.d.ts.map +1 -0
  151. package/dist/middleware/reflect/cross-group-link.js +212 -0
  152. package/dist/middleware/reflect/cross-group-link.js.map +1 -0
  153. package/dist/middleware/reflect/dedup-by-source-and-value.d.ts +70 -0
  154. package/dist/middleware/reflect/dedup-by-source-and-value.d.ts.map +1 -0
  155. package/dist/middleware/reflect/dedup-by-source-and-value.js +0 -0
  156. package/dist/middleware/reflect/dedup-by-source-and-value.js.map +1 -0
  157. package/dist/middleware/reflect/describe-roots.d.ts +58 -0
  158. package/dist/middleware/reflect/describe-roots.d.ts.map +1 -0
  159. package/dist/middleware/reflect/describe-roots.js +266 -0
  160. package/dist/middleware/reflect/describe-roots.js.map +1 -0
  161. package/dist/middleware/reflect/flag-reviewer-startup.d.ts +16 -0
  162. package/dist/middleware/reflect/flag-reviewer-startup.d.ts.map +1 -0
  163. package/dist/middleware/reflect/flag-reviewer-startup.js +107 -0
  164. package/dist/middleware/reflect/flag-reviewer-startup.js.map +1 -0
  165. package/dist/middleware/reflect/flag-reviewer.d.ts +69 -0
  166. package/dist/middleware/reflect/flag-reviewer.d.ts.map +1 -0
  167. package/dist/middleware/reflect/flag-reviewer.js +520 -0
  168. package/dist/middleware/reflect/flag-reviewer.js.map +1 -0
  169. package/dist/middleware/reflect/inline-dedup.d.ts +26 -0
  170. package/dist/middleware/reflect/inline-dedup.d.ts.map +1 -0
  171. package/dist/middleware/reflect/inline-dedup.js +131 -0
  172. package/dist/middleware/reflect/inline-dedup.js.map +1 -0
  173. package/dist/middleware/reflect/justify-decisions.d.ts +37 -0
  174. package/dist/middleware/reflect/justify-decisions.d.ts.map +1 -0
  175. package/dist/middleware/reflect/justify-decisions.js +159 -0
  176. package/dist/middleware/reflect/justify-decisions.js.map +1 -0
  177. package/dist/middleware/reflect/nl-accept.d.ts +35 -0
  178. package/dist/middleware/reflect/nl-accept.d.ts.map +1 -0
  179. package/dist/middleware/reflect/nl-accept.js +167 -0
  180. package/dist/middleware/reflect/nl-accept.js.map +1 -0
  181. package/dist/middleware/reflect/pass0.d.ts +20 -0
  182. package/dist/middleware/reflect/pass0.d.ts.map +1 -0
  183. package/dist/middleware/reflect/pass0.js +423 -0
  184. package/dist/middleware/reflect/pass0.js.map +1 -0
  185. package/dist/middleware/reflect/pass1.d.ts +17 -0
  186. package/dist/middleware/reflect/pass1.d.ts.map +1 -0
  187. package/dist/middleware/reflect/pass1.js +241 -0
  188. package/dist/middleware/reflect/pass1.js.map +1 -0
  189. package/dist/middleware/reflect/pass2-quarantine.d.ts +129 -0
  190. package/dist/middleware/reflect/pass2-quarantine.d.ts.map +1 -0
  191. package/dist/middleware/reflect/pass2-quarantine.js +272 -0
  192. package/dist/middleware/reflect/pass2-quarantine.js.map +1 -0
  193. package/dist/middleware/reflect/pass2-seams.d.ts +205 -0
  194. package/dist/middleware/reflect/pass2-seams.d.ts.map +1 -0
  195. package/dist/middleware/reflect/pass2-seams.js +279 -0
  196. package/dist/middleware/reflect/pass2-seams.js.map +1 -0
  197. package/dist/middleware/reflect/pass2-split-orchestrator.d.ts +37 -0
  198. package/dist/middleware/reflect/pass2-split-orchestrator.d.ts.map +1 -0
  199. package/dist/middleware/reflect/pass2-split-orchestrator.js +531 -0
  200. package/dist/middleware/reflect/pass2-split-orchestrator.js.map +1 -0
  201. package/dist/middleware/reflect/pass2.d.ts +17 -0
  202. package/dist/middleware/reflect/pass2.d.ts.map +1 -0
  203. package/dist/middleware/reflect/pass2.js +324 -0
  204. package/dist/middleware/reflect/pass2.js.map +1 -0
  205. package/dist/middleware/reflect/pass2a.d.ts +141 -0
  206. package/dist/middleware/reflect/pass2a.d.ts.map +1 -0
  207. package/dist/middleware/reflect/pass2a.js +404 -0
  208. package/dist/middleware/reflect/pass2a.js.map +1 -0
  209. package/dist/middleware/reflect/pass2b.d.ts +108 -0
  210. package/dist/middleware/reflect/pass2b.d.ts.map +1 -0
  211. package/dist/middleware/reflect/pass2b.js +480 -0
  212. package/dist/middleware/reflect/pass2b.js.map +1 -0
  213. package/dist/middleware/reflect/pass2c.d.ts +113 -0
  214. package/dist/middleware/reflect/pass2c.d.ts.map +1 -0
  215. package/dist/middleware/reflect/pass2c.js +360 -0
  216. package/dist/middleware/reflect/pass2c.js.map +1 -0
  217. package/dist/middleware/reflect/pass3-batch.d.ts +62 -0
  218. package/dist/middleware/reflect/pass3-batch.d.ts.map +1 -0
  219. package/dist/middleware/reflect/pass3-batch.js +139 -0
  220. package/dist/middleware/reflect/pass3-batch.js.map +1 -0
  221. package/dist/middleware/reflect/pass3.d.ts +23 -0
  222. package/dist/middleware/reflect/pass3.d.ts.map +1 -0
  223. package/dist/middleware/reflect/pass3.js +371 -0
  224. package/dist/middleware/reflect/pass3.js.map +1 -0
  225. package/dist/middleware/reflect/pass4-slice.d.ts +25 -0
  226. package/dist/middleware/reflect/pass4-slice.d.ts.map +1 -0
  227. package/dist/middleware/reflect/pass4-slice.js +315 -0
  228. package/dist/middleware/reflect/pass4-slice.js.map +1 -0
  229. package/dist/middleware/reflect/pass4.d.ts +30 -0
  230. package/dist/middleware/reflect/pass4.d.ts.map +1 -0
  231. package/dist/middleware/reflect/pass4.js +193 -0
  232. package/dist/middleware/reflect/pass4.js.map +1 -0
  233. package/dist/middleware/reflect/pass5.d.ts +22 -0
  234. package/dist/middleware/reflect/pass5.d.ts.map +1 -0
  235. package/dist/middleware/reflect/pass5.js +178 -0
  236. package/dist/middleware/reflect/pass5.js.map +1 -0
  237. package/dist/middleware/reflect/pass_judge.d.ts +44 -0
  238. package/dist/middleware/reflect/pass_judge.d.ts.map +1 -0
  239. package/dist/middleware/reflect/pass_judge.js +263 -0
  240. package/dist/middleware/reflect/pass_judge.js.map +1 -0
  241. package/dist/middleware/reflect/pipeline-flags.d.ts +140 -0
  242. package/dist/middleware/reflect/pipeline-flags.d.ts.map +1 -0
  243. package/dist/middleware/reflect/pipeline-flags.js +314 -0
  244. package/dist/middleware/reflect/pipeline-flags.js.map +1 -0
  245. package/dist/middleware/reflect/pipeline.d.ts +237 -0
  246. package/dist/middleware/reflect/pipeline.d.ts.map +1 -0
  247. package/dist/middleware/reflect/pipeline.js +3114 -0
  248. package/dist/middleware/reflect/pipeline.js.map +1 -0
  249. package/dist/middleware/reflect/promptOverride.d.ts +14 -0
  250. package/dist/middleware/reflect/promptOverride.d.ts.map +1 -0
  251. package/dist/middleware/reflect/promptOverride.js +28 -0
  252. package/dist/middleware/reflect/promptOverride.js.map +1 -0
  253. package/dist/middleware/reflect/provenance-check.d.ts +48 -0
  254. package/dist/middleware/reflect/provenance-check.d.ts.map +1 -0
  255. package/dist/middleware/reflect/provenance-check.js +180 -0
  256. package/dist/middleware/reflect/provenance-check.js.map +1 -0
  257. package/dist/middleware/reflect/provenance-reviewer.d.ts +52 -0
  258. package/dist/middleware/reflect/provenance-reviewer.d.ts.map +1 -0
  259. package/dist/middleware/reflect/provenance-reviewer.js +253 -0
  260. package/dist/middleware/reflect/provenance-reviewer.js.map +1 -0
  261. package/dist/middleware/reflect/prune-collapsed-types.d.ts +11 -0
  262. package/dist/middleware/reflect/prune-collapsed-types.d.ts.map +1 -0
  263. package/dist/middleware/reflect/prune-collapsed-types.js +32 -0
  264. package/dist/middleware/reflect/prune-collapsed-types.js.map +1 -0
  265. package/dist/middleware/reflect/recognize-root.d.ts +75 -0
  266. package/dist/middleware/reflect/recognize-root.d.ts.map +1 -0
  267. package/dist/middleware/reflect/recognize-root.js +204 -0
  268. package/dist/middleware/reflect/recognize-root.js.map +1 -0
  269. package/dist/middleware/reflect/render-agent-flag.d.ts +25 -0
  270. package/dist/middleware/reflect/render-agent-flag.d.ts.map +1 -0
  271. package/dist/middleware/reflect/render-agent-flag.js +39 -0
  272. package/dist/middleware/reflect/render-agent-flag.js.map +1 -0
  273. package/dist/middleware/reflect/retrieve-graph-slice.d.ts +54 -0
  274. package/dist/middleware/reflect/retrieve-graph-slice.d.ts.map +1 -0
  275. package/dist/middleware/reflect/retrieve-graph-slice.js +173 -0
  276. package/dist/middleware/reflect/retrieve-graph-slice.js.map +1 -0
  277. package/dist/middleware/reflect/root-relatedness.d.ts +31 -0
  278. package/dist/middleware/reflect/root-relatedness.d.ts.map +1 -0
  279. package/dist/middleware/reflect/root-relatedness.js +92 -0
  280. package/dist/middleware/reflect/root-relatedness.js.map +1 -0
  281. package/dist/middleware/reflect/schema-heal.d.ts +22 -0
  282. package/dist/middleware/reflect/schema-heal.d.ts.map +1 -0
  283. package/dist/middleware/reflect/schema-heal.js +119 -0
  284. package/dist/middleware/reflect/schema-heal.js.map +1 -0
  285. package/dist/middleware/reflect/schema-validator.d.ts +85 -0
  286. package/dist/middleware/reflect/schema-validator.d.ts.map +1 -0
  287. package/dist/middleware/reflect/schema-validator.js +196 -0
  288. package/dist/middleware/reflect/schema-validator.js.map +1 -0
  289. package/dist/middleware/reflect/stage-audit-graph.d.ts +115 -0
  290. package/dist/middleware/reflect/stage-audit-graph.d.ts.map +1 -0
  291. package/dist/middleware/reflect/stage-audit-graph.js +563 -0
  292. package/dist/middleware/reflect/stage-audit-graph.js.map +1 -0
  293. package/dist/middleware/reflect/stage-d-resolve-graph.d.ts +87 -0
  294. package/dist/middleware/reflect/stage-d-resolve-graph.d.ts.map +1 -0
  295. package/dist/middleware/reflect/stage-d-resolve-graph.js +256 -0
  296. package/dist/middleware/reflect/stage-d-resolve-graph.js.map +1 -0
  297. package/dist/middleware/reflect/synthesizeFromSceneCard.d.ts +15 -0
  298. package/dist/middleware/reflect/synthesizeFromSceneCard.d.ts.map +1 -0
  299. package/dist/middleware/reflect/synthesizeFromSceneCard.js +91 -0
  300. package/dist/middleware/reflect/synthesizeFromSceneCard.js.map +1 -0
  301. package/dist/middleware/reflect/types.d.ts +261 -0
  302. package/dist/middleware/reflect/types.d.ts.map +1 -0
  303. package/dist/middleware/reflect/types.js +3 -0
  304. package/dist/middleware/reflect/types.js.map +1 -0
  305. package/dist/middleware/reflect/v2-integrate.d.ts +120 -0
  306. package/dist/middleware/reflect/v2-integrate.d.ts.map +1 -0
  307. package/dist/middleware/reflect/v2-integrate.js +388 -0
  308. package/dist/middleware/reflect/v2-integrate.js.map +1 -0
  309. package/dist/middleware/reflect/v2-judge.d.ts +44 -0
  310. package/dist/middleware/reflect/v2-judge.d.ts.map +1 -0
  311. package/dist/middleware/reflect/v2-judge.js +191 -0
  312. package/dist/middleware/reflect/v2-judge.js.map +1 -0
  313. package/dist/relation-sets.d.ts +2 -0
  314. package/dist/relation-sets.d.ts.map +1 -0
  315. package/dist/relation-sets.js +35 -0
  316. package/dist/relation-sets.js.map +1 -0
  317. package/dist/routes/__tests__/flags.test.d.ts +2 -0
  318. package/dist/routes/__tests__/flags.test.d.ts.map +1 -0
  319. package/dist/routes/__tests__/flags.test.js +257 -0
  320. package/dist/routes/__tests__/flags.test.js.map +1 -0
  321. package/dist/routes/__tests__/models-catalog.test.d.ts +2 -0
  322. package/dist/routes/__tests__/models-catalog.test.d.ts.map +1 -0
  323. package/dist/routes/__tests__/models-catalog.test.js +130 -0
  324. package/dist/routes/__tests__/models-catalog.test.js.map +1 -0
  325. package/dist/routes/__tests__/reflect-pause-drain.test.d.ts +2 -0
  326. package/dist/routes/__tests__/reflect-pause-drain.test.d.ts.map +1 -0
  327. package/dist/routes/__tests__/reflect-pause-drain.test.js +38 -0
  328. package/dist/routes/__tests__/reflect-pause-drain.test.js.map +1 -0
  329. package/dist/routes/__tests__/spend-pause-drain.test.d.ts +2 -0
  330. package/dist/routes/__tests__/spend-pause-drain.test.d.ts.map +1 -0
  331. package/dist/routes/__tests__/spend-pause-drain.test.js +38 -0
  332. package/dist/routes/__tests__/spend-pause-drain.test.js.map +1 -0
  333. package/dist/routes/admin.d.ts +49 -0
  334. package/dist/routes/admin.d.ts.map +1 -0
  335. package/dist/routes/admin.js +471 -0
  336. package/dist/routes/admin.js.map +1 -0
  337. package/dist/routes/blocks.d.ts +4 -0
  338. package/dist/routes/blocks.d.ts.map +1 -0
  339. package/dist/routes/blocks.js +893 -0
  340. package/dist/routes/blocks.js.map +1 -0
  341. package/dist/routes/chat-proxy.d.ts +5 -0
  342. package/dist/routes/chat-proxy.d.ts.map +1 -0
  343. package/dist/routes/chat-proxy.js +225 -0
  344. package/dist/routes/chat-proxy.js.map +1 -0
  345. package/dist/routes/conversations.d.ts +4 -0
  346. package/dist/routes/conversations.d.ts.map +1 -0
  347. package/dist/routes/conversations.js +139 -0
  348. package/dist/routes/conversations.js.map +1 -0
  349. package/dist/routes/flags.d.ts +4 -0
  350. package/dist/routes/flags.d.ts.map +1 -0
  351. package/dist/routes/flags.js +151 -0
  352. package/dist/routes/flags.js.map +1 -0
  353. package/dist/routes/inject.d.ts +4 -0
  354. package/dist/routes/inject.d.ts.map +1 -0
  355. package/dist/routes/inject.js +183 -0
  356. package/dist/routes/inject.js.map +1 -0
  357. package/dist/routes/mcp-http.d.ts +5 -0
  358. package/dist/routes/mcp-http.d.ts.map +1 -0
  359. package/dist/routes/mcp-http.js +94 -0
  360. package/dist/routes/mcp-http.js.map +1 -0
  361. package/dist/routes/quarantine.d.ts +4 -0
  362. package/dist/routes/quarantine.d.ts.map +1 -0
  363. package/dist/routes/quarantine.js +66 -0
  364. package/dist/routes/quarantine.js.map +1 -0
  365. package/dist/routes/recall.d.ts +5 -0
  366. package/dist/routes/recall.d.ts.map +1 -0
  367. package/dist/routes/recall.js +573 -0
  368. package/dist/routes/recall.js.map +1 -0
  369. package/dist/routes/reflect.d.ts +5 -0
  370. package/dist/routes/reflect.d.ts.map +1 -0
  371. package/dist/routes/reflect.js +231 -0
  372. package/dist/routes/reflect.js.map +1 -0
  373. package/dist/routes/session.d.ts +4 -0
  374. package/dist/routes/session.d.ts.map +1 -0
  375. package/dist/routes/session.js +418 -0
  376. package/dist/routes/session.js.map +1 -0
  377. package/dist/routes/state.d.ts +116 -0
  378. package/dist/routes/state.d.ts.map +1 -0
  379. package/dist/routes/state.js +621 -0
  380. package/dist/routes/state.js.map +1 -0
  381. package/dist/routes/usage.d.ts +3 -0
  382. package/dist/routes/usage.d.ts.map +1 -0
  383. package/dist/routes/usage.js +141 -0
  384. package/dist/routes/usage.js.map +1 -0
  385. package/dist/routes/workspace.d.ts +5 -0
  386. package/dist/routes/workspace.d.ts.map +1 -0
  387. package/dist/routes/workspace.js +435 -0
  388. package/dist/routes/workspace.js.map +1 -0
  389. package/dist/server.d.ts +13 -0
  390. package/dist/server.d.ts.map +1 -0
  391. package/dist/server.js +298 -0
  392. package/dist/server.js.map +1 -0
  393. package/dist/store/__tests__/backup.test.d.ts +2 -0
  394. package/dist/store/__tests__/backup.test.d.ts.map +1 -0
  395. package/dist/store/__tests__/backup.test.js +53 -0
  396. package/dist/store/__tests__/backup.test.js.map +1 -0
  397. package/dist/store/__tests__/quality.test.d.ts +2 -0
  398. package/dist/store/__tests__/quality.test.d.ts.map +1 -0
  399. package/dist/store/__tests__/quality.test.js +75 -0
  400. package/dist/store/__tests__/quality.test.js.map +1 -0
  401. package/dist/store/backup.d.ts +14 -0
  402. package/dist/store/backup.d.ts.map +1 -0
  403. package/dist/store/backup.js +95 -0
  404. package/dist/store/backup.js.map +1 -0
  405. package/dist/store/database.d.ts +407 -0
  406. package/dist/store/database.d.ts.map +1 -0
  407. package/dist/store/database.js +2004 -0
  408. package/dist/store/database.js.map +1 -0
  409. package/dist/store/quality.d.ts +25 -0
  410. package/dist/store/quality.d.ts.map +1 -0
  411. package/dist/store/quality.js +48 -0
  412. package/dist/store/quality.js.map +1 -0
  413. package/dist/tools/__tests__/assemble-block-chains.test.d.ts +2 -0
  414. package/dist/tools/__tests__/assemble-block-chains.test.d.ts.map +1 -0
  415. package/dist/tools/__tests__/assemble-block-chains.test.js +118 -0
  416. package/dist/tools/__tests__/assemble-block-chains.test.js.map +1 -0
  417. package/dist/tools/__tests__/filter-roots-by-concepts.test.d.ts +2 -0
  418. package/dist/tools/__tests__/filter-roots-by-concepts.test.d.ts.map +1 -0
  419. package/dist/tools/__tests__/filter-roots-by-concepts.test.js +68 -0
  420. package/dist/tools/__tests__/filter-roots-by-concepts.test.js.map +1 -0
  421. package/dist/tools/__tests__/flag-surface.test.d.ts +2 -0
  422. package/dist/tools/__tests__/flag-surface.test.d.ts.map +1 -0
  423. package/dist/tools/__tests__/flag-surface.test.js +130 -0
  424. package/dist/tools/__tests__/flag-surface.test.js.map +1 -0
  425. package/dist/tools/core.d.ts +5 -0
  426. package/dist/tools/core.d.ts.map +1 -0
  427. package/dist/tools/core.js +962 -0
  428. package/dist/tools/core.js.map +1 -0
  429. package/dist/tools/derive.d.ts +5 -0
  430. package/dist/tools/derive.d.ts.map +1 -0
  431. package/dist/tools/derive.js +182 -0
  432. package/dist/tools/derive.js.map +1 -0
  433. package/dist/tools/flag-surface.d.ts +26 -0
  434. package/dist/tools/flag-surface.d.ts.map +1 -0
  435. package/dist/tools/flag-surface.js +59 -0
  436. package/dist/tools/flag-surface.js.map +1 -0
  437. package/dist/tools/helpers.d.ts +99 -0
  438. package/dist/tools/helpers.d.ts.map +1 -0
  439. package/dist/tools/helpers.js +243 -0
  440. package/dist/tools/helpers.js.map +1 -0
  441. package/dist/tools/projects.d.ts +5 -0
  442. package/dist/tools/projects.d.ts.map +1 -0
  443. package/dist/tools/projects.js +175 -0
  444. package/dist/tools/projects.js.map +1 -0
  445. package/dist/tools/system.d.ts +5 -0
  446. package/dist/tools/system.d.ts.map +1 -0
  447. package/dist/tools/system.js +1361 -0
  448. package/dist/tools/system.js.map +1 -0
  449. package/dist/tools/tasks.d.ts +5 -0
  450. package/dist/tools/tasks.d.ts.map +1 -0
  451. package/dist/tools/tasks.js +289 -0
  452. package/dist/tools/tasks.js.map +1 -0
  453. package/package.json +69 -0
  454. package/scripts/nodedex-entry.mjs +396 -0
  455. package/tui-dist/App.js +185 -0
  456. package/tui-dist/api.js +197 -0
  457. package/tui-dist/cli.js +53 -0
  458. package/tui-dist/components.js +63 -0
  459. package/tui-dist/config.js +242 -0
  460. package/tui-dist/connect-snippets.js +98 -0
  461. package/tui-dist/feed.js +51 -0
  462. package/tui-dist/health.js +465 -0
  463. package/tui-dist/hooks.js +23 -0
  464. package/tui-dist/memory.js +220 -0
  465. package/tui-dist/onboarding.js +498 -0
  466. package/tui-dist/review.js +193 -0
  467. package/tui-dist/servers.js +556 -0
  468. package/tui-dist/smoke.js +15 -0
  469. package/tui-dist/theme.js +106 -0
@@ -0,0 +1,556 @@
1
+ // servers.ts — multi-server registry + process manager for the Servers pane.
2
+ //
3
+ // The TUI is normally a pure read-client. This module is the one deliberate
4
+ // exception (user-approved): it can LAUNCH and STOP NodeDex servers. That
5
+ // crosses into process-management, so the footguns this project already hit are
6
+ // fenced HERE, in one place:
7
+ // - launched servers run the FULL system: the maintenance workers (flag-reviewer,
8
+ // describer, schema-heal, …) default ON — overridable via env / ~/.nodedex/.env.
9
+ // The legacy per-turn NODEDEX_INACTIVITY_REFLECT stays 0 (arc mode supersedes it).
10
+ // - user settings saved to ~/.nodedex/.env (arc auto-turns, etc.) are read here so they
11
+ // survive a relaunch (the launcher's explicit env otherwise shadows the env-file)
12
+ // - env overrides (PORT, WORKSPACE_DB_PATH) WIN over the server's .env because
13
+ // node --env-file does not override already-set vars (the isolation the
14
+ // RUNBOOK relies on)
15
+ // - stdout/stderr are captured to a log file (no silent failures)
16
+ // - the TUI can only STOP servers IT launched (we hold the child handle);
17
+ // pre-existing servers are shown but never killed (containment)
18
+ // - all launched children are killed when the TUI exits
19
+ import { spawn } from "child_process";
20
+ import { createServer } from "net";
21
+ import { fileURLToPath } from "url";
22
+ import { dirname, resolve } from "path";
23
+ import { homedir } from "os";
24
+ import { mkdirSync, createWriteStream, readFileSync, writeFileSync, existsSync, readdirSync, statSync, renameSync, rmSync } from "fs";
25
+ import { probeServer, prefer127, registerToken } from "./api.js";
26
+ import { providerEnv } from "./config.js";
27
+ import { randomBytes } from "node:crypto";
28
+ // Server dir — two layouts:
29
+ // repo/dev: <repo>/tui/src/servers.ts → ../../server (runs src via tsx)
30
+ // packaged: <pkg>/tui-dist/servers.js → .. (the npm package root; runs dist)
31
+ // Probe for the server's entry instead of assuming the repo shape.
32
+ const HERE = dirname(fileURLToPath(import.meta.url));
33
+ const SERVER_DIR = [resolve(HERE, "..", "..", "server"), resolve(HERE, "..")]
34
+ .find((d) => existsSync(resolve(d, "src", "server.ts")) || existsSync(resolve(d, "dist", "server.js")))
35
+ ?? resolve(HERE, "..", "..", "server");
36
+ const NODEDEX_HOME = resolve(homedir(), ".nodedex");
37
+ /** Read the persisted ~/.nodedex/.env (the file POST /api/admin/config writes) so a user's
38
+ * saved settings — e.g. arc auto-turns — survive a TUI relaunch. The launcher sets some env
39
+ * keys explicitly, and those WIN over the spawned server's --env-file load, so without reading
40
+ * the saved file here a user's choice would be shadowed by the hardcoded default every relaunch.
41
+ * Precedence used below: explicit process.env > saved ~/.nodedex/.env > built-in default.
42
+ * Best-effort: a missing/garbled file yields {}. */
43
+ function readHomeEnv() {
44
+ const out = {};
45
+ try {
46
+ const p = resolve(NODEDEX_HOME, ".env");
47
+ if (!existsSync(p))
48
+ return out;
49
+ for (const raw of readFileSync(p, "utf8").split(/\r?\n/)) {
50
+ const line = raw.trim();
51
+ if (!line || line.startsWith("#"))
52
+ continue;
53
+ const eq = line.indexOf("=");
54
+ if (eq < 1)
55
+ continue;
56
+ out[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();
57
+ }
58
+ }
59
+ catch { /* best-effort — fall back to defaults */ }
60
+ return out;
61
+ }
62
+ const PINS_FILE = resolve(NODEDEX_HOME, "tui-servers.json");
63
+ const SESSION_FILE = resolve(NODEDEX_HOME, "tui-session.json");
64
+ const LOG_DIR = resolve(NODEDEX_HOME, "tui-logs");
65
+ // ports probed during discovery (plus any pinned urls). 127.0.0.1, not localhost —
66
+ // the IPv4 host skips Windows's ::1-first penalty (see prefer127 in api.ts).
67
+ const CANDIDATE_PORTS = [3001, 3002, 3003, 3004, 3005, 3099];
68
+ const candidateUrl = (p) => `http://127.0.0.1:${p}`;
69
+ // ─── available DB files (for the launch/swap picker) ────────────────────────
70
+ // A port is just an access point; the DB is the content. Let the user pick the
71
+ // DB to run rather than type a path. Scan the dirs where DBs actually live.
72
+ // DBs live in the config home (~/.nodedex) — the same place the onboarding wizard
73
+ // (config.ts) creates + lists them, so both pickers agree. (Was repo/data + a hardcoded
74
+ // C:/tmp dev-scratch dir — dev leftovers that put user data inside the repo.)
75
+ const DATA_DIR = NODEDEX_HOME;
76
+ const DB_DIRS = [NODEDEX_HOME];
77
+ export function listDbs() {
78
+ const out = [];
79
+ const seen = new Set();
80
+ for (const dir of DB_DIRS) {
81
+ let names = [];
82
+ try {
83
+ names = readdirSync(dir);
84
+ }
85
+ catch {
86
+ continue;
87
+ }
88
+ for (const f of names) {
89
+ if (!f.endsWith(".db"))
90
+ continue; // excludes -wal/-shm and .backup-* sidecars
91
+ const p = resolve(dir, f);
92
+ if (seen.has(p))
93
+ continue;
94
+ seen.add(p);
95
+ try {
96
+ const s = statSync(p);
97
+ out.push({ path: p, name: f, sizeKB: Math.round(s.size / 1024), mtime: s.mtimeMs, empty: s.size <= 8192 });
98
+ }
99
+ catch { /* skip unreadable */ }
100
+ }
101
+ }
102
+ return out.sort((a, b) => b.mtime - a.mtime); // most-recent first
103
+ }
104
+ // ─── create / rename / delete a db file ─────────────────────────────────────
105
+ // New DBs land in ~/.nodedex (a dir the picker already scans). A NodeDex DB is just a
106
+ // SQLite file the server creates + inits on first open, so "create" only resolves a
107
+ // valid path — the file appears when a server launches on it. Rename/delete touch the
108
+ // file + its -wal/-shm sidecars. GUARD: never rename/delete a db a running server holds
109
+ // (the DB-corruption rule) — refuse if a TUI-managed server has it open, and the OS lock
110
+ // is the backstop for external servers.
111
+ const DB_SIDECARS = ["", "-wal", "-shm"];
112
+ function sanitizeDbName(name) {
113
+ return (name || "").trim().replace(/\.db$/i, "")
114
+ .replace(/[^a-z0-9_-]/gi, "-") // any unsafe char → dash
115
+ .replace(/-+/g, "-") // collapse runs of dashes
116
+ .replace(/^-+|-+$/g, ""); // trim leading/trailing dashes
117
+ }
118
+ /** Is this db file currently open by a server THIS TUI launched? */
119
+ export function isDbInUse(dbPath) {
120
+ const norm = dbPath.replace(/\\/g, "/").toLowerCase();
121
+ for (const m of managed.values()) {
122
+ if ((m.dbPath ?? "").replace(/\\/g, "/").toLowerCase() === norm)
123
+ return true;
124
+ }
125
+ return false;
126
+ }
127
+ /** Resolve a new db path from a user name (data/<name>.db). Rejects empties + collisions. */
128
+ export function resolveNewDbPath(name) {
129
+ const safe = sanitizeDbName(name);
130
+ if (!safe)
131
+ return { ok: false, error: "name needs a letter or number" };
132
+ const path = resolve(DATA_DIR, `${safe}.db`);
133
+ if (existsSync(path))
134
+ return { ok: false, error: `"${safe}" already exists — pick it from the list` };
135
+ return { ok: true, path };
136
+ }
137
+ export function renameDb(oldPath, newName) {
138
+ const safe = sanitizeDbName(newName);
139
+ if (!safe)
140
+ return { ok: false, error: "name needs a letter or number" };
141
+ if (isDbInUse(oldPath))
142
+ return { ok: false, error: "db is in use — stop its server first ([x])" };
143
+ const path = resolve(dirname(oldPath), `${safe}.db`);
144
+ if (path === oldPath)
145
+ return { ok: true, path };
146
+ if (existsSync(path))
147
+ return { ok: false, error: `"${safe}" already exists` };
148
+ try {
149
+ for (const s of DB_SIDECARS) {
150
+ if (existsSync(oldPath + s))
151
+ renameSync(oldPath + s, path + s);
152
+ }
153
+ return { ok: true, path };
154
+ }
155
+ catch (e) {
156
+ return { ok: false, error: e?.code === "EBUSY" || e?.code === "EPERM" ? "db is locked (a server has it open)" : String(e?.message ?? e) };
157
+ }
158
+ }
159
+ export function deleteDb(dbPath) {
160
+ if (isDbInUse(dbPath))
161
+ return { ok: false, error: "db is in use — stop its server first ([x])" };
162
+ try {
163
+ for (const s of DB_SIDECARS) {
164
+ if (existsSync(dbPath + s))
165
+ rmSync(dbPath + s, { force: true });
166
+ }
167
+ return { ok: true };
168
+ }
169
+ catch (e) {
170
+ return { ok: false, error: e?.code === "EBUSY" || e?.code === "EPERM" ? "db is locked (a server has it open)" : String(e?.message ?? e) };
171
+ }
172
+ }
173
+ // ─── pinned servers (~/.nodedex/tui-servers.json) ───────────────────────────
174
+ export function loadPins() {
175
+ try {
176
+ const raw = readFileSync(PINS_FILE, "utf8");
177
+ const arr = JSON.parse(raw);
178
+ return Array.isArray(arr) ? arr.filter((p) => p && typeof p.url === "string") : [];
179
+ }
180
+ catch {
181
+ return [];
182
+ }
183
+ }
184
+ function savePins(pins) {
185
+ try {
186
+ mkdirSync(NODEDEX_HOME, { recursive: true });
187
+ writeFileSync(PINS_FILE, JSON.stringify(pins, null, 2));
188
+ }
189
+ catch {
190
+ /* best-effort; a read-only home just means no persistence */
191
+ }
192
+ }
193
+ export function addPin(url, name) {
194
+ const u = url.replace(/\/$/, "");
195
+ const pins = loadPins().filter((p) => p.url.replace(/\/$/, "") !== u);
196
+ pins.push({ url: u, name: name?.trim() || undefined });
197
+ savePins(pins);
198
+ }
199
+ export function namePin(url, name) {
200
+ const u = url.replace(/\/$/, "");
201
+ const pins = loadPins();
202
+ const hit = pins.find((p) => p.url.replace(/\/$/, "") === u);
203
+ if (hit)
204
+ hit.name = name.trim() || undefined;
205
+ else
206
+ pins.push({ url: u, name: name.trim() || undefined });
207
+ savePins(pins);
208
+ }
209
+ function loadSession() {
210
+ try {
211
+ const r = JSON.parse(readFileSync(SESSION_FILE, "utf8"));
212
+ // back-compat with the OLD single-server format ({url, port, dbPath, managed})
213
+ if (r && typeof r.url === "string") {
214
+ return { managed: r.managed && r.port && r.dbPath ? [{ port: r.port, dbPath: r.dbPath }] : [], connected: r };
215
+ }
216
+ return {
217
+ managed: Array.isArray(r?.managed) ? r.managed.filter((m) => m && typeof m.port === "number" && m.dbPath) : [],
218
+ connected: r?.connected,
219
+ };
220
+ }
221
+ catch {
222
+ return { managed: [] };
223
+ }
224
+ }
225
+ function saveSession(s) {
226
+ try {
227
+ mkdirSync(NODEDEX_HOME, { recursive: true });
228
+ writeFileSync(SESSION_FILE, JSON.stringify(s, null, 2));
229
+ }
230
+ catch { /* best-effort; read-only home just means no restore */ }
231
+ }
232
+ /** Snapshot the CURRENTLY-managed servers into the session. Called on launch + stop — NOT
233
+ * on child-exit, so a quit (which kills every child) can't wipe the list it should restore. */
234
+ function persistManagedSnapshot() {
235
+ const list = [...managed.values()].map((m) => ({ port: m.port, dbPath: m.dbPath, bindHost: m.bindHost, token: m.token }));
236
+ saveSession({ ...loadSession(), managed: list });
237
+ }
238
+ /** Record which server the user is focused on (the one to reconnect to first on restart). */
239
+ export function saveLastServer(rec) {
240
+ saveSession({ ...loadSession(), connected: rec });
241
+ }
242
+ /**
243
+ * Restore the session on startup: relaunch EVERY saved managed server that isn't already up,
244
+ * then reconnect to the focused one (or the first managed). Returns the URL to connect to,
245
+ * or null. We never resurrect an external (non-managed) url — only ones we have a db path for.
246
+ */
247
+ export async function restoreSession() {
248
+ const s = loadSession();
249
+ for (const m of s.managed) {
250
+ if (!existsSync(m.dbPath))
251
+ continue; // db moved/deleted → skip
252
+ if (managed.has(candidateUrl(m.port)))
253
+ continue; // we already launched it
254
+ if ((await probeServer(candidateUrl(m.port))).up)
255
+ continue; // something already there
256
+ launchServer({ port: m.port, dbPath: m.dbPath, bindHost: m.bindHost, token: m.token });
257
+ }
258
+ // Prefer the focused server — but ONLY if it's actually reachable. A stale `connected`
259
+ // pointer (e.g. onboarding just launched a server on a NEW port, leaving the old one dead)
260
+ // must NOT strand the TUI on a dead url, so fall back to a live MANAGED server and adopt it
261
+ // as the focus so the next restart is clean.
262
+ const connected = s.connected?.url ?? null;
263
+ if (!connected && s.managed.length === 0)
264
+ return null;
265
+ const deadline = Date.now() + 12000;
266
+ while (Date.now() < deadline) {
267
+ if (connected && (await probeServer(connected)).up)
268
+ return connected;
269
+ for (const m of s.managed) {
270
+ const url = candidateUrl(m.port);
271
+ if ((await probeServer(url)).up) {
272
+ if (url !== connected)
273
+ saveLastServer({ url, port: m.port, dbPath: m.dbPath, managed: true });
274
+ return url;
275
+ }
276
+ }
277
+ await new Promise((res) => setTimeout(res, 300));
278
+ }
279
+ return connected ?? (s.managed[0] ? candidateUrl(s.managed[0].port) : null); // hand back; poll shows state
280
+ }
281
+ const managed = new Map(); // keyed by normalized url
282
+ /** The DB path a managed server was launched with (for last-session restore). */
283
+ export function managedDbPath(url) {
284
+ return managed.get(norm(url))?.dbPath;
285
+ }
286
+ const norm = (url) => prefer127(url.replace(/\/$/, ""));
287
+ const portOf = (url) => {
288
+ const m = url.match(/:(\d+)/);
289
+ return m ? Number(m[1]) : null;
290
+ };
291
+ // ─── discovery ───────────────────────────────────────────────────────────────
292
+ // Probe candidate ports + pinned urls in parallel; merge with managed state.
293
+ export async function discover() {
294
+ const pins = loadPins();
295
+ const urls = new Set([
296
+ ...CANDIDATE_PORTS.map(candidateUrl),
297
+ ...pins.map((p) => norm(p.url)),
298
+ ...managed.keys(),
299
+ ]);
300
+ const nameFor = (u) => pins.find((p) => norm(p.url) === u)?.name;
301
+ const entries = await Promise.all([...urls].map(async (url) => {
302
+ const r = await probeServer(url);
303
+ return {
304
+ url,
305
+ port: portOf(url),
306
+ name: nameFor(url),
307
+ up: r.up,
308
+ db: r.db,
309
+ blocks: r.blocks,
310
+ managed: managed.has(url),
311
+ };
312
+ }));
313
+ // up first, then by port
314
+ return entries.sort((a, b) => Number(b.up) - Number(a.up) || (a.port ?? 0) - (b.port ?? 0));
315
+ }
316
+ // ─── free-port scan ──────────────────────────────────────────────────────────
317
+ // Return ports that are actually BINDABLE (can host a new server) — not merely "no
318
+ // Nodedex responding". A port held by ANY process (the user's ":3001 always used up")
319
+ // fails the bind, so it won't be suggested. Used by onboarding + the launch flow so the
320
+ // user never has to guess. Momentary bind + close; the launch-fails-loudly path covers
321
+ // the small TOCTOU race if something grabs the port in between.
322
+ function portBindable(port, timeoutMs = 600) {
323
+ return new Promise((resolve) => {
324
+ const srv = createServer();
325
+ let settled = false;
326
+ let timer;
327
+ const done = (free) => {
328
+ if (settled)
329
+ return;
330
+ settled = true;
331
+ if (timer)
332
+ clearTimeout(timer); // don't leave a stray timer per scan
333
+ try {
334
+ srv.close();
335
+ }
336
+ catch { /* */ }
337
+ resolve(free);
338
+ };
339
+ srv.once("error", () => done(false)); // EADDRINUSE / EACCES → not free
340
+ srv.once("listening", () => done(true)); // bound ok → free
341
+ try {
342
+ srv.listen(port, "127.0.0.1");
343
+ }
344
+ catch {
345
+ done(false);
346
+ }
347
+ timer = setTimeout(() => done(false), timeoutMs);
348
+ timer.unref?.(); // never keep the event loop alive on this
349
+ });
350
+ }
351
+ export async function scanFreePorts(range = [3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009, 3099], max = 6) {
352
+ const free = [];
353
+ for (const p of range) {
354
+ // Free ⟺ bindable AND no Nodedex answering. The probe catches the Windows case
355
+ // where 127.0.0.1 binds OK even though a server holds the port on 0.0.0.0.
356
+ if (await portBindable(p) && !(await probeServer(candidateUrl(p))).up)
357
+ free.push(p);
358
+ if (free.length >= max)
359
+ break;
360
+ }
361
+ return free;
362
+ }
363
+ /** Random secret for NODEDEX_API_TOKEN when launching a network-reachable (0.0.0.0) server. */
364
+ export function genToken() {
365
+ return randomBytes(18).toString("hex");
366
+ }
367
+ export function launchServer(opts) {
368
+ const url = candidateUrl(opts.port); // 127.0.0.1 — keep managed keys IPv4 like discovery
369
+ if (managed.has(url))
370
+ return { ok: false, url, error: "already launched by this TUI" };
371
+ if (!existsSync(SERVER_DIR))
372
+ return { ok: false, url, error: `server dir not found: ${SERVER_DIR}` };
373
+ try {
374
+ mkdirSync(LOG_DIR, { recursive: true });
375
+ const logPath = resolve(LOG_DIR, `server-${opts.port}.log`);
376
+ const out = createWriteStream(logPath, { flags: "a" });
377
+ out.write(`\n=== launch ${new Date().toISOString()} port=${opts.port} db=${opts.dbPath} ===\n`);
378
+ // --env-file=.env is a DEV convenience (repo-local overrides). A fresh clone has
379
+ // NO repo .env — config comes from ~/.nodedex/.env (loaded by boot-env) + the env
380
+ // passed below. `node --env-file` HARD-FAILS if the file is missing, so only add it
381
+ // when it actually exists; otherwise a clean install can't launch at all.
382
+ // Dev repo (src present) runs source via tsx; the npm package ships dist only.
383
+ const nodeArgs = existsSync(resolve(SERVER_DIR, "src", "server.ts"))
384
+ ? ["--import=tsx/esm", "src/server.ts"]
385
+ : ["dist/server.js"];
386
+ if (existsSync(resolve(SERVER_DIR, ".env")))
387
+ nodeArgs.unshift("--env-file=.env");
388
+ const homeEnv = readHomeEnv(); // persisted user settings (Settings → ~/.nodedex/.env)
389
+ const child = spawn(process.execPath, // the same node running the TUI
390
+ nodeArgs, {
391
+ cwd: SERVER_DIR,
392
+ env: {
393
+ ...process.env,
394
+ // OpenRouter key/provider from ~/.nodedex/config.json (onboarding). Empty
395
+ // when un-configured. WINS over .env (node --env-file won't override set vars).
396
+ ...providerEnv(),
397
+ PORT: String(opts.port),
398
+ WORKSPACE_DB_PATH: opts.dbPath,
399
+ // Docker/remote launch: bind all interfaces + require a token (set by the launcher
400
+ // when the user picks "agent in a container / on another machine"). Omitted →
401
+ // the server's default localhost bind, no token.
402
+ ...(opts.bindHost ? { NODEDEX_BIND_HOST: opts.bindHost } : {}),
403
+ ...(opts.token ? { NODEDEX_API_TOKEN: opts.token } : {}),
404
+ // FULL RUN: the maintenance workers (flag-reviewer, describer, schema-heal, …) are ON
405
+ // by default — that's the complete system the pipeline is designed to run with.
406
+ // flag-reviewer is set explicitly so the intent is legible (the gate treats anything
407
+ // ≠ "off" as on). OVERRIDABLE: an explicit env or the saved ~/.nodedex/.env wins, so a
408
+ // user who wants to cut the idle reviewer can set NODEDEX_FLAG_REVIEWER_ENABLED=off.
409
+ NODEDEX_FLAG_REVIEWER_ENABLED: process.env.NODEDEX_FLAG_REVIEWER_ENABLED ?? homeEnv.NODEDEX_FLAG_REVIEWER_ENABLED ?? "on",
410
+ // Legacy per-turn inactivity-reflect stays OFF — arc mode uses NODEDEX_ARC_INACTIVITY_ENABLED.
411
+ NODEDEX_INACTIVITY_REFLECT: "0",
412
+ // v2 ARC EXTRACTION is the default pipeline (the validated one; per-turn scene-card is
413
+ // legacy). DEFAULTED here but OVERRIDABLE — an explicit env OR a saved ~/.nodedex/.env
414
+ // value wins (`process.env ?? homeEnv ?? default`), so a user's Settings choice SURVIVES
415
+ // a relaunch. Arc captures turns for batched extraction; the triggers that commit them:
416
+ // · ARC_AUTO_TURNS=N → auto-extract every N captured turns (0 = off; user-settable in Settings)
417
+ // · ARC_INACTIVITY → auto-extract after the conversation goes idle (last-resort sweep)
418
+ // (the agent can still fire workspace_extract_arc sooner at its own task boundaries).
419
+ NODEDEX_ARC_EXTRACTION: process.env.NODEDEX_ARC_EXTRACTION ?? homeEnv.NODEDEX_ARC_EXTRACTION ?? "1",
420
+ NODEDEX_ARC_INACTIVITY_ENABLED: process.env.NODEDEX_ARC_INACTIVITY_ENABLED ?? homeEnv.NODEDEX_ARC_INACTIVITY_ENABLED ?? "on",
421
+ NODEDEX_ARC_AUTO_TURNS: process.env.NODEDEX_ARC_AUTO_TURNS ?? homeEnv.NODEDEX_ARC_AUTO_TURNS ?? "8",
422
+ },
423
+ stdio: ["ignore", "pipe", "pipe"],
424
+ windowsHide: true,
425
+ });
426
+ child.stdout?.pipe(out);
427
+ child.stderr?.pipe(out);
428
+ child.on("exit", () => managed.delete(url));
429
+ managed.set(url, { child, logPath, port: opts.port, dbPath: opts.dbPath, bindHost: opts.bindHost, token: opts.token });
430
+ if (opts.token)
431
+ registerToken(url, opts.token); // so the TUI's own reads authenticate
432
+ if (opts.name)
433
+ addPin(url, opts.name);
434
+ // Track it among the servers to restore next start (the WHOLE managed set, not just one).
435
+ persistManagedSnapshot();
436
+ return { ok: true, url, logPath };
437
+ }
438
+ catch (e) {
439
+ return { ok: false, url, error: String(e) };
440
+ }
441
+ }
442
+ // ─── stop (managed only) ──────────────────────────────────────────────────────
443
+ export function stopServer(url) {
444
+ const m = managed.get(norm(url));
445
+ if (!m)
446
+ return { ok: false, error: "not managed by this TUI — won't kill a server it didn't launch" };
447
+ killChild(m);
448
+ managed.delete(norm(url));
449
+ persistManagedSnapshot(); // explicit stop → drop it from the restore set
450
+ return { ok: true };
451
+ }
452
+ export function isManaged(url) {
453
+ return managed.has(norm(url));
454
+ }
455
+ // ─── swap: run a DIFFERENT db on the SAME port ──────────────────────────────
456
+ // "a single port that can run different db at a time" — stop the managed server
457
+ // on this port, wait for the port to actually release (polling, not a fixed
458
+ // guess — the OS frees it in a few hundred ms but timing varies), then relaunch
459
+ // it on the new db. Only valid for a TUI-managed port.
460
+ export async function swapDb(url, port, dbPath) {
461
+ if (!managed.has(norm(url)))
462
+ return { ok: false, url, error: "not managed by this TUI" };
463
+ stopServer(url);
464
+ const deadline = Date.now() + 8000;
465
+ while (Date.now() < deadline) {
466
+ const r = await probeServer(candidateUrl(port));
467
+ if (!r.up)
468
+ break;
469
+ await new Promise((res) => setTimeout(res, 250));
470
+ }
471
+ return launchServer({ port, dbPath });
472
+ }
473
+ function killChild(m) {
474
+ try {
475
+ if (process.platform === "win32") {
476
+ // tree-kill: --import=tsx/esm runs in-process, but be safe against children
477
+ spawn("taskkill", ["/PID", String(m.child.pid), "/T", "/F"], { windowsHide: true });
478
+ }
479
+ else {
480
+ m.child.kill("SIGTERM");
481
+ }
482
+ }
483
+ catch {
484
+ /* already gone */
485
+ }
486
+ }
487
+ // ─── Capture watchers (one per host — thin shims over nodedex-capture-core) ─
488
+ // Each watcher reads its host's own turn store read-only (Hermes: state.db; Claude Code:
489
+ // ~/.claude/projects/*.jsonl) and posts turns to the live server. The TUI owns their
490
+ // lifecycle so the user controls them from Settings (toggle = start/stop). Each watcher's
491
+ // config lives in ~/.nodedex/config.json and is re-read by the watcher each poll — so
492
+ // editing a filter in the TUI applies WITHOUT a restart here. Default host stays "hermes"
493
+ // for backward-compatible call sites.
494
+ const WATCHER_DEFS = {
495
+ hermes: { script: "hermes-statedb-watcher.mjs", log: "hermes-watcher.log" },
496
+ "claude-code": { script: "claude-code-watcher.mjs", log: "claude-code-watcher.log" },
497
+ };
498
+ export const WATCHER_HOSTS = Object.keys(WATCHER_DEFS);
499
+ const watchers = new Map();
500
+ export function isWatcherRunning(host = "hermes") {
501
+ const w = watchers.get(host);
502
+ return !!w && w.exitCode === null && !w.killed;
503
+ }
504
+ export function launchWatcher(host = "hermes") {
505
+ if (isWatcherRunning(host))
506
+ return { ok: true };
507
+ const def = WATCHER_DEFS[host];
508
+ const script = resolve(SERVER_DIR, "adapters", def.script);
509
+ if (!existsSync(script))
510
+ return { ok: false, error: `watcher not found: ${script}` };
511
+ try {
512
+ mkdirSync(LOG_DIR, { recursive: true });
513
+ const out = createWriteStream(resolve(LOG_DIR, def.log), { flags: "a" });
514
+ out.write(`\n=== watcher launch ${new Date().toISOString()} ===\n`);
515
+ // cwd=SERVER_DIR so a watcher's createRequire resolves better-sqlite3 from server/node_modules.
516
+ const child = spawn(process.execPath, [script], {
517
+ cwd: SERVER_DIR,
518
+ env: { ...process.env },
519
+ stdio: ["ignore", "pipe", "pipe"],
520
+ windowsHide: true,
521
+ });
522
+ child.stdout?.pipe(out);
523
+ child.stderr?.pipe(out);
524
+ child.on("exit", () => { if (watchers.get(host) === child)
525
+ watchers.delete(host); });
526
+ watchers.set(host, child);
527
+ return { ok: true };
528
+ }
529
+ catch (e) {
530
+ return { ok: false, error: String(e) };
531
+ }
532
+ }
533
+ export function stopWatcher(host) {
534
+ const hosts = host ? [host] : [...watchers.keys()];
535
+ for (const h of hosts) {
536
+ const w = watchers.get(h);
537
+ if (!w)
538
+ continue;
539
+ try {
540
+ if (process.platform === "win32")
541
+ spawn("taskkill", ["/PID", String(w.pid), "/T", "/F"], { windowsHide: true });
542
+ else
543
+ w.kill("SIGTERM");
544
+ }
545
+ catch { /* already gone */ }
546
+ watchers.delete(h);
547
+ }
548
+ }
549
+ // kill everything the TUI launched (call on app exit)
550
+ export function killAllManaged() {
551
+ for (const m of managed.values())
552
+ killChild(m);
553
+ managed.clear();
554
+ stopWatcher();
555
+ }
556
+ export { LOG_DIR, SERVER_DIR };
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { PassThrough } from "node:stream";
3
+ import { render } from "ink";
4
+ import { App } from "./App.js";
5
+ import { killAllManaged } from "./servers.js";
6
+ const stdin = new PassThrough();
7
+ const { unmount, waitUntilExit } = render(_jsx(App, {}), { stdin: stdin, exitOnCtrlC: false });
8
+ setTimeout(() => unmount(), 3500);
9
+ await waitUntilExit();
10
+ console.log("\n[smoke] rendered + unmounted cleanly");
11
+ // The App spawns watcher children on mount (capture default-on); their piped
12
+ // stdio keeps the event loop alive after unmount → the smoke hangs. Real runs
13
+ // clean up in cli.tsx's exit handler; the harness must do it itself.
14
+ killAllManaged();
15
+ process.exit(0);
@@ -0,0 +1,106 @@
1
+ // theme.ts — colors, type-colors, glyphs, and small formatting helpers.
2
+ // One place to retune the look.
3
+ //
4
+ // DESIGN (TUI v3, 2026-07-02): cool + casual. A Nord-derived palette — slate
5
+ // chrome, one soft frost-blue accent, and MUTED aurora colors reserved for the
6
+ // block types. The chrome stays quiet on purpose: the memory (typed blocks)
7
+ // carries almost all of the color, so the type system itself is the visual
8
+ // language (decision=green, dead_end=red, …) without anything reading neon.
9
+ export const theme = {
10
+ accent: "#88C0D0", // frost blue — selection, keys, wordmark, active view
11
+ title: "#81A1C1", // section headers (calm blue, one step dimmer than accent)
12
+ label: "#7B88A1", // field labels
13
+ value: "#E5E9F0", // field values (soft snow-white)
14
+ border: "#4C566A", // panel borders (idle)
15
+ borderHot: "#88C0D0", // panel borders (attention)
16
+ ok: "#A3BE8C",
17
+ warn: "#EBCB8B",
18
+ danger: "#BF616A",
19
+ dim: "#616E88",
20
+ };
21
+ // Block type → color, keyed to the stance/role the type carries (muted aurora).
22
+ export const typeColor = {
23
+ decision: "#A3BE8C",
24
+ dead_end: "#BF616A",
25
+ constraint: "#EBCB8B",
26
+ insight: "#B48EAD",
27
+ hypothesis: "#88C0D0",
28
+ fact: "#81A1C1",
29
+ blueprint: "#5E81AC",
30
+ preference: "#D08770",
31
+ artifact: "#D8DEE9",
32
+ task: "#8FBCBB",
33
+ project: "#ECEFF4",
34
+ process: "#616E88",
35
+ chain: "#D08770",
36
+ question: "#EBCB8B",
37
+ event: "#7B88A1",
38
+ };
39
+ export const glyph = {
40
+ up: "●",
41
+ paused: "‖",
42
+ down: "●",
43
+ read: "▸",
44
+ chain: "⛓",
45
+ tree: "⌂",
46
+ save: "+",
47
+ block: "▰",
48
+ flag: "⚑",
49
+ warn: "⚠",
50
+ okMark: "✓",
51
+ arrow: "›",
52
+ tick: "⟳",
53
+ };
54
+ export function typeColorOf(type) {
55
+ return (type && typeColor[type]) || theme.value;
56
+ }
57
+ // One symbol per type so block lists scan without reading words.
58
+ export const typeGlyph = {
59
+ constraint: "▣",
60
+ decision: "◆",
61
+ dead_end: "✕",
62
+ preference: "★",
63
+ blueprint: "◇",
64
+ question: "?",
65
+ hypothesis: "≈",
66
+ task: "▸",
67
+ fact: "▰",
68
+ insight: "◐",
69
+ event: "•",
70
+ chain: "⛓",
71
+ project: "⌂",
72
+ };
73
+ export function typeGlyphOf(type) {
74
+ return (type && typeGlyph[type]) || "▪";
75
+ }
76
+ export function relTime(iso) {
77
+ if (!iso)
78
+ return "";
79
+ const ms = Date.now() - new Date(iso).getTime();
80
+ if (!Number.isFinite(ms) || ms < 0)
81
+ return "now";
82
+ const s = Math.floor(ms / 1000);
83
+ if (s < 60)
84
+ return `${s}s`;
85
+ const m = Math.floor(s / 60);
86
+ if (m < 60)
87
+ return `${m}m`;
88
+ const h = Math.floor(m / 60);
89
+ if (h < 24)
90
+ return `${h}h`;
91
+ return `${Math.floor(h / 24)}d`;
92
+ }
93
+ export function fmtNum(n) {
94
+ if (typeof n !== "number" || !Number.isFinite(n))
95
+ return "—";
96
+ return n.toLocaleString("en-US");
97
+ }
98
+ export function fmtMoney(n) {
99
+ if (typeof n !== "number" || !Number.isFinite(n))
100
+ return "—";
101
+ return `$${n.toFixed(2)}`;
102
+ }
103
+ export function trunc(s, n) {
104
+ const str = (s ?? "").replace(/\s+/g, " ").trim();
105
+ return str.length > n ? str.slice(0, n - 1) + "…" : str;
106
+ }