stellavault 0.2.0 → 0.3.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 (400) hide show
  1. package/package.json +1 -1
  2. package/packages/core/dist/api/dashboard.d.ts +3 -0
  3. package/packages/core/{src/api/dashboard.ts → dist/api/dashboard.js} +8 -11
  4. package/packages/core/dist/api/graph-data.d.ts +11 -0
  5. package/packages/core/dist/api/graph-data.js +255 -0
  6. package/packages/core/dist/api/pwa.d.ts +3 -0
  7. package/packages/core/{src/api/pwa.ts → dist/api/pwa.js} +27 -32
  8. package/packages/core/dist/api/server.d.ts +16 -0
  9. package/packages/core/dist/api/server.js +718 -0
  10. package/packages/core/dist/capture/voice.d.ts +24 -0
  11. package/packages/core/dist/capture/voice.js +135 -0
  12. package/packages/core/{src/cloud/index.ts → dist/cloud/index.d.ts} +1 -0
  13. package/packages/core/dist/cloud/index.js +2 -0
  14. package/packages/core/dist/cloud/sync.d.ts +29 -0
  15. package/packages/core/dist/cloud/sync.js +137 -0
  16. package/packages/core/dist/config.d.ts +27 -0
  17. package/packages/core/dist/config.js +55 -0
  18. package/packages/core/dist/federation/credits.d.ts +26 -0
  19. package/packages/core/dist/federation/credits.js +56 -0
  20. package/packages/core/dist/federation/identity.d.ts +14 -0
  21. package/packages/core/dist/federation/identity.js +74 -0
  22. package/packages/core/{src/federation/index.ts → dist/federation/index.d.ts} +1 -2
  23. package/packages/core/dist/federation/index.js +5 -0
  24. package/packages/core/dist/federation/node.d.ts +31 -0
  25. package/packages/core/dist/federation/node.js +216 -0
  26. package/packages/core/dist/federation/privacy.d.ts +8 -0
  27. package/packages/core/dist/federation/privacy.js +40 -0
  28. package/packages/core/dist/federation/reputation.d.ts +37 -0
  29. package/packages/core/dist/federation/reputation.js +139 -0
  30. package/packages/core/dist/federation/search.d.ts +19 -0
  31. package/packages/core/dist/federation/search.js +101 -0
  32. package/packages/core/dist/federation/sharing.d.ts +72 -0
  33. package/packages/core/dist/federation/sharing.js +246 -0
  34. package/packages/core/dist/federation/trust.d.ts +15 -0
  35. package/packages/core/dist/federation/trust.js +60 -0
  36. package/packages/core/dist/federation/types.d.ts +40 -0
  37. package/packages/core/dist/federation/types.js +3 -0
  38. package/packages/core/dist/i18n/index.d.ts +6 -0
  39. package/packages/core/dist/i18n/index.js +81 -0
  40. package/packages/core/{src/index.ts → dist/index.d.ts} +46 -65
  41. package/packages/core/dist/index.js +69 -0
  42. package/packages/core/dist/indexer/chunker.d.ts +14 -0
  43. package/packages/core/dist/indexer/chunker.js +148 -0
  44. package/packages/core/dist/indexer/embedder.d.ts +8 -0
  45. package/packages/core/dist/indexer/embedder.js +3 -0
  46. package/packages/core/dist/indexer/index.d.ts +28 -0
  47. package/packages/core/dist/indexer/index.js +74 -0
  48. package/packages/core/dist/indexer/local-embedder.d.ts +3 -0
  49. package/packages/core/dist/indexer/local-embedder.js +29 -0
  50. package/packages/core/dist/indexer/scanner.d.ts +11 -0
  51. package/packages/core/dist/indexer/scanner.js +137 -0
  52. package/packages/core/dist/indexer/watcher.d.ts +19 -0
  53. package/packages/core/dist/indexer/watcher.js +49 -0
  54. package/packages/core/dist/intelligence/code-linker.d.ts +20 -0
  55. package/packages/core/dist/intelligence/code-linker.js +88 -0
  56. package/packages/core/dist/intelligence/contradiction-detector.d.ts +20 -0
  57. package/packages/core/dist/intelligence/contradiction-detector.js +115 -0
  58. package/packages/core/dist/intelligence/decay-engine.d.ts +27 -0
  59. package/packages/core/dist/intelligence/decay-engine.js +190 -0
  60. package/packages/core/dist/intelligence/duplicate-detector.d.ts +20 -0
  61. package/packages/core/dist/intelligence/duplicate-detector.js +55 -0
  62. package/packages/core/dist/intelligence/fsrs.d.ts +43 -0
  63. package/packages/core/dist/intelligence/fsrs.js +70 -0
  64. package/packages/core/dist/intelligence/gap-detector.d.ts +25 -0
  65. package/packages/core/dist/intelligence/gap-detector.js +78 -0
  66. package/packages/core/dist/intelligence/learning-path.d.ts +31 -0
  67. package/packages/core/dist/intelligence/learning-path.js +53 -0
  68. package/packages/core/dist/intelligence/notifications.d.ts +31 -0
  69. package/packages/core/dist/intelligence/notifications.js +65 -0
  70. package/packages/core/dist/intelligence/predictive-gaps.d.ts +14 -0
  71. package/packages/core/dist/intelligence/predictive-gaps.js +74 -0
  72. package/packages/core/dist/intelligence/semantic-versioning.d.ts +37 -0
  73. package/packages/core/dist/intelligence/semantic-versioning.js +68 -0
  74. package/packages/core/dist/intelligence/types.d.ts +28 -0
  75. package/packages/core/dist/intelligence/types.js +3 -0
  76. package/packages/core/dist/mcp/custom-tools.d.ts +29 -0
  77. package/packages/core/dist/mcp/custom-tools.js +70 -0
  78. package/packages/core/{src/mcp/index.ts → dist/mcp/index.d.ts} +1 -0
  79. package/packages/core/dist/mcp/index.js +2 -0
  80. package/packages/core/dist/mcp/server.d.ts +49 -0
  81. package/packages/core/dist/mcp/server.js +151 -0
  82. package/packages/core/dist/mcp/tools/agentic-graph.d.ts +87 -0
  83. package/packages/core/dist/mcp/tools/agentic-graph.js +88 -0
  84. package/packages/core/dist/mcp/tools/brief.d.ts +31 -0
  85. package/packages/core/dist/mcp/tools/brief.js +39 -0
  86. package/packages/core/dist/mcp/tools/decay.d.ts +33 -0
  87. package/packages/core/dist/mcp/tools/decay.js +32 -0
  88. package/packages/core/dist/mcp/tools/decision-journal.d.ts +78 -0
  89. package/packages/core/dist/mcp/tools/decision-journal.js +79 -0
  90. package/packages/core/dist/mcp/tools/detect-gaps.d.ts +24 -0
  91. package/packages/core/dist/mcp/tools/detect-gaps.js +47 -0
  92. package/packages/core/dist/mcp/tools/export.d.ts +29 -0
  93. package/packages/core/dist/mcp/tools/export.js +60 -0
  94. package/packages/core/dist/mcp/tools/federated-search.d.ts +29 -0
  95. package/packages/core/dist/mcp/tools/federated-search.js +36 -0
  96. package/packages/core/dist/mcp/tools/generate-claude-md.d.ts +35 -0
  97. package/packages/core/dist/mcp/tools/generate-claude-md.js +107 -0
  98. package/packages/core/dist/mcp/tools/get-document.d.ts +35 -0
  99. package/packages/core/dist/mcp/tools/get-document.js +25 -0
  100. package/packages/core/dist/mcp/tools/get-evolution.d.ts +28 -0
  101. package/packages/core/dist/mcp/tools/get-evolution.js +70 -0
  102. package/packages/core/dist/mcp/tools/get-related.d.ts +32 -0
  103. package/packages/core/dist/mcp/tools/get-related.js +33 -0
  104. package/packages/core/dist/mcp/tools/learning-path.d.ts +23 -0
  105. package/packages/core/dist/mcp/tools/learning-path.js +45 -0
  106. package/packages/core/dist/mcp/tools/link-code.d.ts +34 -0
  107. package/packages/core/dist/mcp/tools/link-code.js +44 -0
  108. package/packages/core/dist/mcp/tools/list-topics.d.ts +15 -0
  109. package/packages/core/dist/mcp/tools/list-topics.js +18 -0
  110. package/packages/core/dist/mcp/tools/search.d.ts +39 -0
  111. package/packages/core/dist/mcp/tools/search.js +29 -0
  112. package/packages/core/dist/mcp/tools/snapshot.d.ts +47 -0
  113. package/packages/core/dist/mcp/tools/snapshot.js +84 -0
  114. package/packages/core/dist/multi-vault/index.d.ts +26 -0
  115. package/packages/core/dist/multi-vault/index.js +80 -0
  116. package/packages/core/dist/pack/creator.d.ts +21 -0
  117. package/packages/core/dist/pack/creator.js +105 -0
  118. package/packages/core/dist/pack/exporter.d.ts +4 -0
  119. package/packages/core/dist/pack/exporter.js +18 -0
  120. package/packages/core/dist/pack/importer.d.ts +10 -0
  121. package/packages/core/dist/pack/importer.js +55 -0
  122. package/packages/core/{src/pack/index.ts → dist/pack/index.d.ts} +1 -0
  123. package/packages/core/dist/pack/index.js +5 -0
  124. package/packages/core/dist/pack/marketplace.d.ts +14 -0
  125. package/packages/core/dist/pack/marketplace.js +90 -0
  126. package/packages/core/dist/pack/pii-masker.d.ts +7 -0
  127. package/packages/core/dist/pack/pii-masker.js +29 -0
  128. package/packages/core/dist/pack/types.d.ts +36 -0
  129. package/packages/core/dist/pack/types.js +3 -0
  130. package/packages/core/dist/plugins/index.d.ts +35 -0
  131. package/packages/core/dist/plugins/index.js +57 -0
  132. package/packages/core/dist/plugins/webhooks.d.ts +30 -0
  133. package/packages/core/dist/plugins/webhooks.js +79 -0
  134. package/packages/core/dist/search/adaptive.d.ts +16 -0
  135. package/packages/core/dist/search/adaptive.js +67 -0
  136. package/packages/core/dist/search/bm25.d.ts +4 -0
  137. package/packages/core/dist/search/bm25.js +10 -0
  138. package/packages/core/dist/search/index.d.ts +15 -0
  139. package/packages/core/dist/search/index.js +64 -0
  140. package/packages/core/dist/search/rrf.d.ts +7 -0
  141. package/packages/core/dist/search/rrf.js +21 -0
  142. package/packages/core/dist/search/semantic.d.ts +5 -0
  143. package/packages/core/dist/search/semantic.js +6 -0
  144. package/packages/core/{src/store/index.ts → dist/store/index.d.ts} +1 -0
  145. package/packages/core/dist/store/index.js +2 -0
  146. package/packages/core/dist/store/sqlite-vec.d.ts +6 -0
  147. package/packages/core/dist/store/sqlite-vec.js +251 -0
  148. package/packages/core/dist/store/types.d.ts +20 -0
  149. package/packages/core/dist/store/types.js +3 -0
  150. package/packages/core/dist/team/index.d.ts +25 -0
  151. package/packages/core/dist/team/index.js +97 -0
  152. package/packages/core/dist/types/chunk.d.ts +23 -0
  153. package/packages/core/dist/types/chunk.js +3 -0
  154. package/packages/core/dist/types/document.d.ts +23 -0
  155. package/packages/core/dist/types/document.js +3 -0
  156. package/packages/core/dist/types/graph.d.ts +39 -0
  157. package/packages/core/dist/types/graph.js +3 -0
  158. package/packages/core/dist/types/index.d.ts +5 -0
  159. package/packages/core/dist/types/index.js +2 -0
  160. package/packages/core/dist/types/search.d.ts +39 -0
  161. package/packages/core/dist/types/search.js +3 -0
  162. package/packages/core/dist/utils/retry.d.ts +25 -0
  163. package/packages/core/dist/utils/retry.js +59 -0
  164. package/.github/workflows/pages.yml +0 -37
  165. package/memory/MEMORY.md +0 -25
  166. package/packages/cli/dist/commands/brief-cmd.d.ts.map +0 -1
  167. package/packages/cli/dist/commands/brief-cmd.js.map +0 -1
  168. package/packages/cli/dist/commands/capture-cmd.d.ts.map +0 -1
  169. package/packages/cli/dist/commands/capture-cmd.js.map +0 -1
  170. package/packages/cli/dist/commands/card-cmd.d.ts.map +0 -1
  171. package/packages/cli/dist/commands/card-cmd.js.map +0 -1
  172. package/packages/cli/dist/commands/clip-cmd.d.ts.map +0 -1
  173. package/packages/cli/dist/commands/clip-cmd.js.map +0 -1
  174. package/packages/cli/dist/commands/cloud-cmd.d.ts.map +0 -1
  175. package/packages/cli/dist/commands/cloud-cmd.js.map +0 -1
  176. package/packages/cli/dist/commands/contradictions-cmd.d.ts.map +0 -1
  177. package/packages/cli/dist/commands/contradictions-cmd.js.map +0 -1
  178. package/packages/cli/dist/commands/decay-cmd.d.ts.map +0 -1
  179. package/packages/cli/dist/commands/decay-cmd.js.map +0 -1
  180. package/packages/cli/dist/commands/digest-cmd.d.ts.map +0 -1
  181. package/packages/cli/dist/commands/digest-cmd.js.map +0 -1
  182. package/packages/cli/dist/commands/duplicates-cmd.d.ts.map +0 -1
  183. package/packages/cli/dist/commands/duplicates-cmd.js.map +0 -1
  184. package/packages/cli/dist/commands/federate-cmd.d.ts.map +0 -1
  185. package/packages/cli/dist/commands/federate-cmd.js.map +0 -1
  186. package/packages/cli/dist/commands/gaps-cmd.d.ts.map +0 -1
  187. package/packages/cli/dist/commands/gaps-cmd.js.map +0 -1
  188. package/packages/cli/dist/commands/graph-cmd.d.ts.map +0 -1
  189. package/packages/cli/dist/commands/graph-cmd.js.map +0 -1
  190. package/packages/cli/dist/commands/index-cmd.d.ts.map +0 -1
  191. package/packages/cli/dist/commands/index-cmd.js.map +0 -1
  192. package/packages/cli/dist/commands/init-cmd.d.ts.map +0 -1
  193. package/packages/cli/dist/commands/init-cmd.js.map +0 -1
  194. package/packages/cli/dist/commands/learn-cmd.d.ts.map +0 -1
  195. package/packages/cli/dist/commands/learn-cmd.js.map +0 -1
  196. package/packages/cli/dist/commands/pack-cmd.d.ts.map +0 -1
  197. package/packages/cli/dist/commands/pack-cmd.js.map +0 -1
  198. package/packages/cli/dist/commands/review-cmd.d.ts.map +0 -1
  199. package/packages/cli/dist/commands/review-cmd.js.map +0 -1
  200. package/packages/cli/dist/commands/search-cmd.d.ts.map +0 -1
  201. package/packages/cli/dist/commands/search-cmd.js.map +0 -1
  202. package/packages/cli/dist/commands/serve-cmd.d.ts.map +0 -1
  203. package/packages/cli/dist/commands/serve-cmd.js.map +0 -1
  204. package/packages/cli/dist/commands/status-cmd.d.ts.map +0 -1
  205. package/packages/cli/dist/commands/status-cmd.js.map +0 -1
  206. package/packages/cli/dist/commands/sync-cmd.d.ts.map +0 -1
  207. package/packages/cli/dist/commands/sync-cmd.js.map +0 -1
  208. package/packages/cli/dist/commands/vault-cmd.d.ts.map +0 -1
  209. package/packages/cli/dist/commands/vault-cmd.js.map +0 -1
  210. package/packages/cli/dist/index.d.ts.map +0 -1
  211. package/packages/cli/dist/index.js.map +0 -1
  212. package/packages/cli/src/commands/brief-cmd.ts +0 -87
  213. package/packages/cli/src/commands/capture-cmd.ts +0 -34
  214. package/packages/cli/src/commands/card-cmd.ts +0 -29
  215. package/packages/cli/src/commands/clip-cmd.ts +0 -172
  216. package/packages/cli/src/commands/cloud-cmd.ts +0 -75
  217. package/packages/cli/src/commands/contradictions-cmd.ts +0 -41
  218. package/packages/cli/src/commands/decay-cmd.ts +0 -57
  219. package/packages/cli/src/commands/digest-cmd.ts +0 -89
  220. package/packages/cli/src/commands/duplicates-cmd.ts +0 -38
  221. package/packages/cli/src/commands/federate-cmd.ts +0 -256
  222. package/packages/cli/src/commands/gaps-cmd.ts +0 -40
  223. package/packages/cli/src/commands/graph-cmd.ts +0 -88
  224. package/packages/cli/src/commands/index-cmd.ts +0 -65
  225. package/packages/cli/src/commands/init-cmd.ts +0 -145
  226. package/packages/cli/src/commands/learn-cmd.ts +0 -56
  227. package/packages/cli/src/commands/pack-cmd.ts +0 -121
  228. package/packages/cli/src/commands/review-cmd.ts +0 -125
  229. package/packages/cli/src/commands/search-cmd.ts +0 -45
  230. package/packages/cli/src/commands/serve-cmd.ts +0 -17
  231. package/packages/cli/src/commands/status-cmd.ts +0 -37
  232. package/packages/cli/src/commands/sync-cmd.ts +0 -68
  233. package/packages/cli/src/commands/vault-cmd.ts +0 -64
  234. package/packages/cli/src/index.ts +0 -187
  235. package/packages/core/src/api/graph-data.ts +0 -286
  236. package/packages/core/src/api/server.ts +0 -660
  237. package/packages/core/src/capture/voice.ts +0 -168
  238. package/packages/core/src/cloud/sync.ts +0 -167
  239. package/packages/core/src/config.ts +0 -82
  240. package/packages/core/src/federation/credits.ts +0 -80
  241. package/packages/core/src/federation/hyperswarm.d.ts +0 -19
  242. package/packages/core/src/federation/identity.ts +0 -90
  243. package/packages/core/src/federation/node.ts +0 -235
  244. package/packages/core/src/federation/privacy.ts +0 -52
  245. package/packages/core/src/federation/reputation.ts +0 -202
  246. package/packages/core/src/federation/search.ts +0 -129
  247. package/packages/core/src/federation/sharing.ts +0 -315
  248. package/packages/core/src/federation/trust.ts +0 -76
  249. package/packages/core/src/federation/types.ts +0 -25
  250. package/packages/core/src/i18n/index.ts +0 -85
  251. package/packages/core/src/indexer/chunker.ts +0 -180
  252. package/packages/core/src/indexer/embedder.ts +0 -9
  253. package/packages/core/src/indexer/index.ts +0 -113
  254. package/packages/core/src/indexer/local-embedder.ts +0 -35
  255. package/packages/core/src/indexer/scanner.ts +0 -142
  256. package/packages/core/src/indexer/watcher.ts +0 -62
  257. package/packages/core/src/intelligence/contradiction-detector.ts +0 -134
  258. package/packages/core/src/intelligence/decay-engine.ts +0 -229
  259. package/packages/core/src/intelligence/duplicate-detector.ts +0 -71
  260. package/packages/core/src/intelligence/fsrs.ts +0 -79
  261. package/packages/core/src/intelligence/gap-detector.ts +0 -109
  262. package/packages/core/src/intelligence/learning-path.ts +0 -86
  263. package/packages/core/src/intelligence/notifications.ts +0 -106
  264. package/packages/core/src/intelligence/predictive-gaps.ts +0 -94
  265. package/packages/core/src/intelligence/semantic-versioning.ts +0 -97
  266. package/packages/core/src/intelligence/types.ts +0 -28
  267. package/packages/core/src/mcp/custom-tools.ts +0 -97
  268. package/packages/core/src/mcp/server.ts +0 -142
  269. package/packages/core/src/mcp/tools/agentic-graph.ts +0 -96
  270. package/packages/core/src/mcp/tools/brief.ts +0 -49
  271. package/packages/core/src/mcp/tools/decay.ts +0 -40
  272. package/packages/core/src/mcp/tools/decision-journal.ts +0 -95
  273. package/packages/core/src/mcp/tools/export.ts +0 -72
  274. package/packages/core/src/mcp/tools/federated-search.ts +0 -43
  275. package/packages/core/src/mcp/tools/generate-claude-md.ts +0 -130
  276. package/packages/core/src/mcp/tools/get-document.ts +0 -26
  277. package/packages/core/src/mcp/tools/get-related.ts +0 -41
  278. package/packages/core/src/mcp/tools/learning-path.ts +0 -52
  279. package/packages/core/src/mcp/tools/list-topics.ts +0 -20
  280. package/packages/core/src/mcp/tools/search.ts +0 -35
  281. package/packages/core/src/mcp/tools/snapshot.ts +0 -98
  282. package/packages/core/src/multi-vault/index.ts +0 -118
  283. package/packages/core/src/pack/creator.ts +0 -127
  284. package/packages/core/src/pack/exporter.ts +0 -21
  285. package/packages/core/src/pack/importer.ts +0 -82
  286. package/packages/core/src/pack/marketplace.ts +0 -103
  287. package/packages/core/src/pack/pii-masker.ts +0 -38
  288. package/packages/core/src/pack/types.ts +0 -39
  289. package/packages/core/src/plugins/index.ts +0 -100
  290. package/packages/core/src/plugins/webhooks.ts +0 -110
  291. package/packages/core/src/search/bm25.ts +0 -16
  292. package/packages/core/src/search/index.ts +0 -83
  293. package/packages/core/src/search/rrf.ts +0 -31
  294. package/packages/core/src/search/semantic.ts +0 -15
  295. package/packages/core/src/store/sqlite-vec.ts +0 -290
  296. package/packages/core/src/store/types.ts +0 -22
  297. package/packages/core/src/team/index.ts +0 -126
  298. package/packages/core/src/types/chunk.ts +0 -25
  299. package/packages/core/src/types/document.ts +0 -24
  300. package/packages/core/src/types/graph.ts +0 -44
  301. package/packages/core/src/types/index.ts +0 -15
  302. package/packages/core/src/types/search.ts +0 -38
  303. package/packages/core/src/utils/retry.ts +0 -85
  304. package/packages/core/tests/api-card.test.ts +0 -60
  305. package/packages/core/tests/api-routes.test.ts +0 -98
  306. package/packages/core/tests/bm25.test.ts +0 -87
  307. package/packages/core/tests/chunker.test.ts +0 -48
  308. package/packages/core/tests/cluster.test.ts +0 -75
  309. package/packages/core/tests/constellation.test.ts +0 -77
  310. package/packages/core/tests/export-utils.test.ts +0 -97
  311. package/packages/core/tests/fsrs.test.ts +0 -96
  312. package/packages/core/tests/gesture-detector.test.ts +0 -45
  313. package/packages/core/tests/graph-data.test.ts +0 -87
  314. package/packages/core/tests/layout.test.ts +0 -83
  315. package/packages/core/tests/mcp.test.ts +0 -148
  316. package/packages/core/tests/pack.test.ts +0 -127
  317. package/packages/core/tests/pii-masker.test.ts +0 -42
  318. package/packages/core/tests/profile-card.test.ts +0 -62
  319. package/packages/core/tests/rrf.test.ts +0 -29
  320. package/packages/core/tests/search-integration.test.ts +0 -139
  321. package/packages/core/tests/store.test.ts +0 -80
  322. package/packages/graph/click-result.png +0 -0
  323. package/packages/graph/dist/assets/camera_utils-BMxqtvoZ.js +0 -1
  324. package/packages/graph/dist/assets/hands-DXA01_mx.js +0 -18
  325. package/packages/graph/dist/assets/index-DMEe2diW.js +0 -4192
  326. package/packages/graph/dist/assets/layout.worker-DbKCEFTz.js +0 -1
  327. package/packages/graph/dist/index.html +0 -17
  328. package/packages/graph/index.html +0 -17
  329. package/packages/graph/package.json +0 -32
  330. package/packages/graph/src/App.tsx +0 -7
  331. package/packages/graph/src/api/client.ts +0 -39
  332. package/packages/graph/src/components/ClusterFilter.tsx +0 -73
  333. package/packages/graph/src/components/ConstellationView.tsx +0 -232
  334. package/packages/graph/src/components/ExportPanel.tsx +0 -177
  335. package/packages/graph/src/components/Graph3D.tsx +0 -230
  336. package/packages/graph/src/components/GraphEdges.tsx +0 -100
  337. package/packages/graph/src/components/GraphNodes.tsx +0 -386
  338. package/packages/graph/src/components/HealthDashboard.tsx +0 -173
  339. package/packages/graph/src/components/Layout.tsx +0 -214
  340. package/packages/graph/src/components/MotionOverlay.tsx +0 -81
  341. package/packages/graph/src/components/MotionToggle.tsx +0 -33
  342. package/packages/graph/src/components/MultiverseView.tsx +0 -286
  343. package/packages/graph/src/components/NodeDetail.tsx +0 -232
  344. package/packages/graph/src/components/PulseParticle.tsx +0 -232
  345. package/packages/graph/src/components/SearchBar.tsx +0 -107
  346. package/packages/graph/src/components/StarField.tsx +0 -197
  347. package/packages/graph/src/components/StatusBar.tsx +0 -53
  348. package/packages/graph/src/components/Timeline.tsx +0 -148
  349. package/packages/graph/src/components/ToolsPanel.tsx +0 -512
  350. package/packages/graph/src/components/Tooltip.tsx +0 -100
  351. package/packages/graph/src/components/TypeFilter.tsx +0 -131
  352. package/packages/graph/src/embed/EmbedGraph.tsx +0 -144
  353. package/packages/graph/src/hooks/useConstellationLOD.ts +0 -76
  354. package/packages/graph/src/hooks/useDecay.ts +0 -37
  355. package/packages/graph/src/hooks/useExport.ts +0 -165
  356. package/packages/graph/src/hooks/useGraph.ts +0 -69
  357. package/packages/graph/src/hooks/useKeyboardNav.ts +0 -122
  358. package/packages/graph/src/hooks/useLayout.ts +0 -45
  359. package/packages/graph/src/hooks/useMotion.ts +0 -120
  360. package/packages/graph/src/hooks/usePulse.ts +0 -58
  361. package/packages/graph/src/hooks/useSearch.ts +0 -71
  362. package/packages/graph/src/lib/constellation.ts +0 -107
  363. package/packages/graph/src/lib/export-utils.ts +0 -48
  364. package/packages/graph/src/lib/gesture-detector.ts +0 -123
  365. package/packages/graph/src/lib/layout.worker.ts +0 -153
  366. package/packages/graph/src/lib/motion-controller.ts +0 -83
  367. package/packages/graph/src/lib/profile-card.ts +0 -122
  368. package/packages/graph/src/main.tsx +0 -4
  369. package/packages/graph/src/stores/graph-store.ts +0 -155
  370. package/packages/graph/success.png +0 -0
  371. package/packages/graph/test-click.mjs +0 -49
  372. package/packages/graph/test-explore.mjs +0 -102
  373. package/packages/graph/test-final.mjs +0 -61
  374. package/packages/graph/test-graph.mjs +0 -139
  375. package/packages/graph/test-hover.mjs +0 -48
  376. package/packages/graph/test-pulse.mjs +0 -68
  377. package/packages/graph/test-screenshot.mjs +0 -56
  378. package/packages/graph/test-v2.mjs +0 -97
  379. package/packages/graph/tsconfig.tsbuildinfo +0 -1
  380. package/packages/graph/vite.config.ts +0 -15
  381. package/packages/sync/.env.example +0 -11
  382. package/packages/sync/.sync-state.json +0 -317
  383. package/packages/sync/.upload-state.json +0 -1009
  384. package/packages/sync/create-stella-network-notion.mjs +0 -151
  385. package/packages/sync/create-stellavault-project-notion.mjs +0 -322
  386. package/packages/sync/logs/sync-2026-03-28.log +0 -6
  387. package/packages/sync/logs/sync-2026-03-29.log +0 -12
  388. package/packages/sync/logs/sync-2026-03-30.log +0 -6
  389. package/packages/sync/logs/sync-2026-03-31.log +0 -6
  390. package/packages/sync/logs/sync-2026-04-01.log +0 -6
  391. package/packages/sync/logs/sync-2026-04-02.log +0 -6
  392. package/packages/sync/package-lock.json +0 -373
  393. package/packages/sync/package.json +0 -16
  394. package/packages/sync/run-sync.bat +0 -18
  395. package/packages/sync/run-sync.mjs +0 -46
  396. package/packages/sync/setup-scheduler.mjs +0 -119
  397. package/packages/sync/structured-sync.mjs +0 -187
  398. package/packages/sync/sync-to-obsidian.mjs +0 -264
  399. package/packages/sync/upload-pdca-to-notion.mjs +0 -495
  400. package/tsconfig.base.json +0 -18
@@ -0,0 +1,718 @@
1
+ // Design Ref: §4.1 — REST API (core/api/)
2
+ // Design Ref: §7 — Security: localhost only, CORS restricted
3
+ import express from 'express';
4
+ import cors from 'cors';
5
+ import { buildGraphData } from './graph-data.js';
6
+ import { detectDuplicates } from '../intelligence/duplicate-detector.js';
7
+ import { detectKnowledgeGaps } from '../intelligence/gap-detector.js';
8
+ export function createApiServer(options) {
9
+ const { store, searchEngine, port = 3333, vaultName = '', vaultPath = '', decayEngine } = options;
10
+ const app = express();
11
+ app.use(cors({ origin: ['http://localhost:5173', 'http://127.0.0.1:5173'] }));
12
+ app.use(express.json());
13
+ // GET /api/graph?mode=semantic|folder — 전체 그래프 데이터
14
+ const graphCaches = new Map();
15
+ app.get('/api/graph', async (req, res) => {
16
+ try {
17
+ const mode = req.query.mode === 'folder' ? 'folder' : 'semantic';
18
+ if (!graphCaches.has(mode)) {
19
+ const data = await buildGraphData(store, { mode });
20
+ graphCaches.set(mode, { data, generatedAt: new Date().toISOString() });
21
+ }
22
+ const cached = graphCaches.get(mode);
23
+ res.json({ data: cached.data, generatedAt: cached.generatedAt, mode });
24
+ }
25
+ catch (err) {
26
+ console.error(err);
27
+ res.status(500).json({ error: 'Internal server error' });
28
+ }
29
+ });
30
+ // GET /api/graph/refresh?mode= — 캐시 무효화 + 재생성
31
+ app.get('/api/graph/refresh', async (req, res) => {
32
+ try {
33
+ const mode = req.query.mode === 'folder' ? 'folder' : 'semantic';
34
+ const data = await buildGraphData(store, { mode });
35
+ graphCaches.set(mode, { data, generatedAt: new Date().toISOString() });
36
+ const cached = graphCaches.get(mode);
37
+ res.json({ data: cached.data, generatedAt: cached.generatedAt, mode });
38
+ }
39
+ catch (err) {
40
+ console.error(err);
41
+ res.status(500).json({ error: 'Internal server error' });
42
+ }
43
+ });
44
+ // GET /api/search?q=&limit=
45
+ app.get('/api/search', async (req, res) => {
46
+ try {
47
+ const query = String(req.query.q || '');
48
+ const limit = parseInt(String(req.query.limit || '10'), 10);
49
+ if (!query) {
50
+ res.json({ results: [], query: '' });
51
+ return;
52
+ }
53
+ const results = await searchEngine.search({ query, limit });
54
+ // 검색 결과 문서에 대해 접근 이벤트 기록 (감쇠 리셋)
55
+ if (decayEngine) {
56
+ const now = new Date().toISOString();
57
+ for (const r of results) {
58
+ decayEngine.recordAccess({ documentId: r.document.id, type: 'search', timestamp: now }).catch(() => { });
59
+ }
60
+ }
61
+ res.json({
62
+ results: results.map(r => ({
63
+ documentId: r.document.id,
64
+ title: r.document.title,
65
+ score: Math.round(r.score * 1000) / 1000,
66
+ highlights: r.highlights,
67
+ })),
68
+ query,
69
+ });
70
+ }
71
+ catch (err) {
72
+ console.error(err);
73
+ res.status(500).json({ error: 'Internal server error' });
74
+ }
75
+ });
76
+ // GET /api/document/:id
77
+ app.get('/api/document/:id', async (req, res) => {
78
+ try {
79
+ const doc = await store.getDocument(req.params.id);
80
+ if (!doc) {
81
+ res.status(404).json({ error: 'Not found' });
82
+ return;
83
+ }
84
+ // 접근 이벤트 기록 (감쇠 리셋)
85
+ if (decayEngine) {
86
+ decayEngine.recordAccess({ documentId: doc.id, type: 'view', timestamp: new Date().toISOString() }).catch(() => { });
87
+ }
88
+ // 관련 문서 (제목 기반 검색)
89
+ const related = await searchEngine.search({
90
+ query: doc.title,
91
+ limit: 6,
92
+ });
93
+ res.json({
94
+ id: doc.id,
95
+ title: doc.title,
96
+ filePath: doc.filePath,
97
+ content: doc.content,
98
+ tags: doc.tags,
99
+ lastModified: doc.lastModified,
100
+ related: related
101
+ .filter(r => r.document.id !== doc.id)
102
+ .slice(0, 5)
103
+ .map(r => ({ id: r.document.id, title: r.document.title, score: r.score })),
104
+ });
105
+ }
106
+ catch (err) {
107
+ console.error(err);
108
+ res.status(500).json({ error: 'Internal server error' });
109
+ }
110
+ });
111
+ // GET /api/profile-card → SVG
112
+ app.get('/api/profile-card', async (_req, res) => {
113
+ try {
114
+ const mode = _req.query.mode === 'folder' ? 'folder' : 'semantic';
115
+ if (!graphCaches.has(mode)) {
116
+ const data = await buildGraphData(store, { mode });
117
+ graphCaches.set(mode, { data, generatedAt: new Date().toISOString() });
118
+ }
119
+ const graphData = graphCaches.get(mode).data;
120
+ const topics = await store.getTopics();
121
+ // 동적 import (graph 패키지의 profile-card)
122
+ // 여기서는 간단히 SVG를 직접 생성
123
+ const stats = await store.getStats();
124
+ const top6 = graphData.clusters
125
+ .sort((a, b) => b.nodeCount - a.nodeCount)
126
+ .slice(0, 6);
127
+ const maxCount = Math.max(1, ...top6.map((c) => c.nodeCount));
128
+ const W = 800, H = 420;
129
+ const radarCx = 200, radarCy = 220, radarR = 100;
130
+ const radarPoints = top6.map((c, i) => {
131
+ const angle = (Math.PI * 2 * i) / top6.length - Math.PI / 2;
132
+ const r = radarR * (c.nodeCount / maxCount);
133
+ return {
134
+ x: radarCx + r * Math.cos(angle),
135
+ y: radarCy + r * Math.sin(angle),
136
+ lx: radarCx + (radarR + 20) * Math.cos(angle),
137
+ ly: radarCy + (radarR + 20) * Math.sin(angle),
138
+ label: c.label.split(',')[0].trim().slice(0, 12),
139
+ color: c.color,
140
+ };
141
+ });
142
+ const radarPath = radarPoints.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`).join(' ') + 'Z';
143
+ const gridPaths = [0.33, 0.66, 1].map((s) => top6.map((_, i) => {
144
+ const a = (Math.PI * 2 * i) / top6.length - Math.PI / 2;
145
+ return `${i === 0 ? 'M' : 'L'}${radarCx + radarR * s * Math.cos(a)},${radarCy + radarR * s * Math.sin(a)}`;
146
+ }).join(' ') + 'Z');
147
+ const tags20 = topics.slice(0, 20);
148
+ const maxTag = Math.max(1, ...tags20.map((t) => t.count));
149
+ // HIGH-07: SVG injection 방어 — 모든 특수문자 이스케이프
150
+ const esc = (s) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
151
+ const tagEls = tags20.map((t, i) => {
152
+ const sz = 10 + 14 * (t.count / maxTag);
153
+ const x = 480 + (Math.floor(i / 2) % 5) * 60;
154
+ const y = 140 + (i % 2) * 30 + Math.floor(i / 10) * 70;
155
+ const op = 0.5 + 0.5 * (t.count / maxTag);
156
+ return `<text x="${x}" y="${y}" font-size="${sz}" fill="#88aaff" opacity="${op}" font-family="monospace">#${esc(t.topic)}</text>`;
157
+ }).join('\n ');
158
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
159
+ <defs>
160
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
161
+ <stop offset="0%" stop-color="#0d1028"/><stop offset="100%" stop-color="#050510"/>
162
+ </linearGradient>
163
+ <linearGradient id="rf" x1="0%" y1="0%" x2="100%" y2="100%">
164
+ <stop offset="0%" stop-color="#6366f1" stop-opacity="0.3"/><stop offset="100%" stop-color="#06b6d4" stop-opacity="0.15"/>
165
+ </linearGradient>
166
+ </defs>
167
+ <rect width="${W}" height="${H}" rx="16" fill="url(#bg)"/>
168
+ <rect width="${W}" height="${H}" rx="16" fill="none" stroke="#6366f140"/>
169
+ <text x="30" y="40" font-size="20" font-weight="700" fill="#c0c0f0" font-family="system-ui">🧠 Knowledge Universe</text>
170
+ <text x="30" y="65" font-size="13" fill="#556" font-family="monospace">${stats.documentCount} docs · ${graphData.clusters.length} clusters · ${graphData.edges.length} connections</text>
171
+ <line x1="30" y1="80" x2="${W - 30}" y2="80" stroke="#6366f120"/>
172
+ <text x="${radarCx}" y="105" font-size="11" fill="#667" text-anchor="middle" font-family="system-ui">KNOWLEDGE DISTRIBUTION</text>
173
+ ${gridPaths.map((p) => `<path d="${p}" fill="none" stroke="#6366f115" stroke-width="0.5"/>`).join('\n ')}
174
+ ${radarPoints.map((p) => `<line x1="${radarCx}" y1="${radarCy}" x2="${p.lx}" y2="${p.ly}" stroke="#6366f110" stroke-width="0.5"/>`).join('\n ')}
175
+ <path d="${radarPath}" fill="url(#rf)" stroke="#818cf8" stroke-width="1.5"/>
176
+ ${radarPoints.map((p) => `<circle cx="${p.x}" cy="${p.y}" r="3" fill="${p.color}"/><text x="${p.lx}" y="${p.ly + 4}" font-size="9" fill="#889" text-anchor="middle" font-family="monospace">${esc(p.label)}</text>`).join('\n ')}
177
+ <text x="580" y="105" font-size="11" fill="#667" text-anchor="middle" font-family="system-ui">TOP TOPICS</text>
178
+ <rect x="440" y="115" width="320" height="240" rx="8" fill="#6366f108"/>
179
+ ${tagEls}
180
+ <text x="${W / 2}" y="${H - 15}" font-size="10" fill="#334" text-anchor="middle" font-family="monospace">Generated by Stellavault</text>
181
+ </svg>`;
182
+ res.setHeader('Content-Type', 'image/svg+xml');
183
+ res.send(svg);
184
+ }
185
+ catch (err) {
186
+ console.error(err);
187
+ res.status(500).json({ error: 'Internal server error' });
188
+ }
189
+ });
190
+ // GET /api/stats
191
+ app.get('/api/stats', async (_req, res) => {
192
+ try {
193
+ const stats = await store.getStats();
194
+ res.json({ ...stats, vaultName });
195
+ }
196
+ catch (err) {
197
+ console.error(err);
198
+ res.status(500).json({ error: 'Internal server error' });
199
+ }
200
+ });
201
+ // GET /api/decay — 감쇠 상태 리포트
202
+ app.get('/api/decay', async (_req, res) => {
203
+ if (!decayEngine) {
204
+ res.json({ error: 'Decay engine not initialized' });
205
+ return;
206
+ }
207
+ try {
208
+ const report = await decayEngine.computeAll();
209
+ res.json(report);
210
+ }
211
+ catch (err) {
212
+ console.error(err);
213
+ res.status(500).json({ error: 'Internal server error' });
214
+ }
215
+ });
216
+ // GET /api/heatmap — Design Ref: §2.2 — 지식 히트맵 활동 점수
217
+ app.get('/api/heatmap', async (_req, res) => {
218
+ try {
219
+ const docs = await store.getAllDocuments();
220
+ const now = Date.now();
221
+ const scores = {};
222
+ let hotCount = 0;
223
+ let coldCount = 0;
224
+ // Pre-fetch decay data if available
225
+ let decayMap = {};
226
+ if (decayEngine) {
227
+ try {
228
+ const report = await decayEngine.computeAll();
229
+ // topDecaying has R values for worst-performing docs
230
+ for (const item of report.topDecaying) {
231
+ decayMap[item.documentId] = item.retrievability;
232
+ }
233
+ }
234
+ catch { /* ignore */ }
235
+ }
236
+ for (const doc of docs) {
237
+ // 최근 수정 기반 점수 (0~0.4)
238
+ const modified = doc.lastModified ? new Date(doc.lastModified).getTime() : now - 86400000 * 60;
239
+ const daysSinceModified = (now - modified) / 86400000;
240
+ const recencyScore = Math.max(0, 1 - daysSinceModified / 90) * 0.4;
241
+ // 감쇠 R값 기반 (0~0.3)
242
+ const decayScore = (decayMap[doc.id] ?? 0.5) * 0.3;
243
+ // 태그 수 기반 연결도 (0~0.3)
244
+ const tagScore = Math.min((doc.tags?.length ?? 0) / 10, 1) * 0.3;
245
+ const score = Math.min(1, recencyScore + decayScore + tagScore);
246
+ scores[doc.id] = score;
247
+ if (score > 0.6)
248
+ hotCount++;
249
+ if (score < 0.2)
250
+ coldCount++;
251
+ }
252
+ res.json({ scores, stats: { total: docs.length, hotCount, coldCount } });
253
+ }
254
+ catch (err) {
255
+ console.error(err);
256
+ res.status(500).json({ error: 'Internal server error' });
257
+ }
258
+ });
259
+ // GET /api/evolution — Design Ref: F02 — 시맨틱 진화 데이터
260
+ app.get('/api/evolution', async (req, res) => {
261
+ try {
262
+ const topic = req.query.topic;
263
+ const limit = parseInt(String(req.query.limit ?? '20'), 10);
264
+ const docs = await store.getAllDocuments();
265
+ let filtered = docs;
266
+ if (topic) {
267
+ const t = topic.toLowerCase();
268
+ filtered = docs.filter((d) => d.tags.some((tag) => tag.toLowerCase().includes(t)) || d.title.toLowerCase().includes(t));
269
+ }
270
+ const evolved = filtered
271
+ .filter((d) => d.lastModified)
272
+ .sort((a, b) => new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime())
273
+ .slice(0, limit)
274
+ .map((d) => ({
275
+ id: d.id,
276
+ title: d.title,
277
+ lastModified: d.lastModified,
278
+ tags: d.tags.slice(0, 5),
279
+ }));
280
+ res.json({ topic: topic ?? 'all', total: filtered.length, recentlyEvolved: evolved });
281
+ }
282
+ catch (err) {
283
+ console.error(err);
284
+ res.status(500).json({ error: 'Internal server error' });
285
+ }
286
+ });
287
+ // GET /api/duplicates — 중복 노트 탐지
288
+ app.get('/api/duplicates', async (req, res) => {
289
+ try {
290
+ const threshold = parseFloat(String(req.query.threshold ?? '0.88'));
291
+ const pairs = await detectDuplicates(store, threshold, 20);
292
+ res.json({ pairs, count: pairs.length, threshold });
293
+ }
294
+ catch (err) {
295
+ console.error(err);
296
+ res.status(500).json({ error: 'Internal server error' });
297
+ }
298
+ });
299
+ // GET /api/gaps — 지식 갭 탐지
300
+ app.get('/api/gaps', async (_req, res) => {
301
+ try {
302
+ const report = await detectKnowledgeGaps(store);
303
+ res.json(report);
304
+ }
305
+ catch (err) {
306
+ console.error(err);
307
+ res.status(500).json({ error: 'Internal server error' });
308
+ }
309
+ });
310
+ // POST /api/duplicates/merge — 중복 노트 자동 병합
311
+ app.post('/api/duplicates/merge', async (req, res) => {
312
+ try {
313
+ const { docAId, docBId } = req.body;
314
+ if (!docAId || !docBId) {
315
+ res.status(400).json({ error: 'docAId, docBId required' });
316
+ return;
317
+ }
318
+ const docA = await store.getDocument(docAId);
319
+ const docB = await store.getDocument(docBId);
320
+ if (!docA || !docB) {
321
+ res.status(404).json({ error: 'Document not found' });
322
+ return;
323
+ }
324
+ const { readFileSync, writeFileSync, unlinkSync } = await import('node:fs');
325
+ const { join, resolve, relative } = await import('node:path');
326
+ // 긴 노트를 기준으로 유지, 짧은 노트의 고유 내용을 추가
327
+ const [keeper, removed] = docA.content.length >= docB.content.length
328
+ ? [docA, docB] : [docB, docA];
329
+ // HIGH-02: Path Traversal 방어 — vault 외부 접근 차단
330
+ const keeperPath = resolve(join(vaultPath, keeper.filePath));
331
+ const removedPath = resolve(join(vaultPath, removed.filePath));
332
+ const vaultRoot = resolve(vaultPath);
333
+ if (!keeperPath.startsWith(vaultRoot) || !removedPath.startsWith(vaultRoot)) {
334
+ res.status(400).json({ error: 'Invalid file path' });
335
+ return;
336
+ }
337
+ // 병합: keeper 끝에 removed 고유 내용 추가
338
+ const keeperContent = readFileSync(keeperPath, 'utf-8');
339
+ const appendix = `\n\n---\n\n> Merged from: ${removed.title} (${removed.filePath})\n\n${removed.content}`;
340
+ writeFileSync(keeperPath, keeperContent + appendix, 'utf-8');
341
+ // 삭제
342
+ try {
343
+ unlinkSync(removedPath);
344
+ }
345
+ catch { /* 이미 없을 수 있음 */ }
346
+ // DB에서도 삭제
347
+ await store.deleteByDocumentId(removed.id);
348
+ res.json({
349
+ success: true,
350
+ kept: { id: keeper.id, title: keeper.title },
351
+ removed: { id: removed.id, title: removed.title },
352
+ });
353
+ }
354
+ catch (err) {
355
+ console.error(err);
356
+ res.status(500).json({ error: 'Merge failed' });
357
+ }
358
+ });
359
+ // POST /api/gaps/create-bridge — 갭 브릿지 노트 자동 생성
360
+ app.post('/api/gaps/create-bridge', async (req, res) => {
361
+ try {
362
+ const { clusterA, clusterB } = req.body;
363
+ if (!clusterA || !clusterB) {
364
+ res.status(400).json({ error: 'clusterA, clusterB required' });
365
+ return;
366
+ }
367
+ const { writeFileSync, mkdirSync } = await import('node:fs');
368
+ const { join } = await import('node:path');
369
+ const nameA = clusterA.replace(/\s*\(\d+\)$/, '');
370
+ const nameB = clusterB.replace(/\s*\(\d+\)$/, '');
371
+ // 양쪽 클러스터의 대표 노트 검색
372
+ const resultsA = await searchEngine.search({ query: nameA, limit: 3 });
373
+ const resultsB = await searchEngine.search({ query: nameB, limit: 3 });
374
+ const refsA = resultsA.map(r => `- [[${r.document.title}]]: ${r.document.content.slice(0, 100).replace(/\n/g, ' ')}...`).join('\n');
375
+ const refsB = resultsB.map(r => `- [[${r.document.title}]]: ${r.document.content.slice(0, 100).replace(/\n/g, ' ')}...`).join('\n');
376
+ const title = `${nameA} × ${nameB}`;
377
+ const date = new Date().toISOString().slice(0, 10);
378
+ const content = [
379
+ '---',
380
+ `title: "${title}"`,
381
+ `created: ${date}`,
382
+ 'tags: [bridge, auto-generated]',
383
+ '---',
384
+ '',
385
+ `# ${title}`,
386
+ '',
387
+ `> 이 노트는 지식 갭 탐지기에 의해 자동 생성되었습니다.`,
388
+ `> ${nameA}와 ${nameB} 사이의 연결 지식을 정리하세요.`,
389
+ '',
390
+ `## ${nameA} 핵심 노트`,
391
+ '',
392
+ refsA || '- (관련 노트 없음)',
393
+ '',
394
+ `## ${nameB} 핵심 노트`,
395
+ '',
396
+ refsB || '- (관련 노트 없음)',
397
+ '',
398
+ '## 연결 포인트',
399
+ '',
400
+ `${nameA}와 ${nameB}의 관계:`,
401
+ '',
402
+ '- ',
403
+ '',
404
+ '## 메모',
405
+ '',
406
+ '',
407
+ ].join('\n');
408
+ const safeTitle = title.replace(/[<>:"/\\|?*]/g, '').replace(/\s+/g, ' ');
409
+ const { resolve } = await import('node:path');
410
+ const dir = resolve(join(vaultPath, '01_Knowledge'));
411
+ if (!dir.startsWith(resolve(vaultPath))) {
412
+ res.status(400).json({ error: 'Invalid path' });
413
+ return;
414
+ }
415
+ mkdirSync(dir, { recursive: true });
416
+ const filePath = join(dir, `${safeTitle}.md`);
417
+ writeFileSync(filePath, content, 'utf-8');
418
+ res.json({ success: true, title: safeTitle, path: filePath });
419
+ }
420
+ catch (err) {
421
+ console.error(err);
422
+ res.status(500).json({ error: 'Bridge creation failed' });
423
+ }
424
+ });
425
+ // GET /api/health — 종합 건강도 대시보드
426
+ app.get('/api/health', async (_req, res) => {
427
+ try {
428
+ const stats = await store.getStats();
429
+ const docs = await store.getAllDocuments();
430
+ // Decay 요약
431
+ let decaySummary = { totalDocuments: 0, criticalCount: 0, decayingCount: 0, averageR: 1.0, topDecaying: [] };
432
+ if (decayEngine) {
433
+ const report = await decayEngine.computeAll();
434
+ decaySummary = {
435
+ totalDocuments: report.totalDocuments ?? docs.length,
436
+ criticalCount: report.criticalCount ?? 0,
437
+ decayingCount: report.decayingCount ?? 0,
438
+ averageR: report.averageR ?? 1.0,
439
+ topDecaying: (report.topDecaying ?? []).slice(0, 5),
440
+ };
441
+ }
442
+ // Gaps 요약
443
+ let gapSummary = { gapCount: 0, isolatedCount: 0 };
444
+ try {
445
+ const gapReport = await detectKnowledgeGaps(store);
446
+ gapSummary = {
447
+ gapCount: gapReport.gaps?.length ?? 0,
448
+ isolatedCount: gapReport.isolatedNodes?.length ?? 0,
449
+ };
450
+ }
451
+ catch { /* gaps may fail if no embeddings */ }
452
+ // Duplicates 요약
453
+ let dupCount = 0;
454
+ try {
455
+ const pairs = await detectDuplicates(store, 0.88, 50);
456
+ dupCount = pairs.length;
457
+ }
458
+ catch { /* duplicates may fail */ }
459
+ // Source/Type 분포
460
+ const sourceDist = new Map();
461
+ const typeDist = new Map();
462
+ for (const doc of docs) {
463
+ const s = doc.source ?? 'local';
464
+ const t = doc.type ?? 'note';
465
+ sourceDist.set(s, (sourceDist.get(s) ?? 0) + 1);
466
+ typeDist.set(t, (typeDist.get(t) ?? 0) + 1);
467
+ }
468
+ // 시간별 문서 증가 (월별)
469
+ const monthlyGrowth = new Map();
470
+ for (const doc of docs) {
471
+ const month = doc.lastModified?.slice(0, 7) ?? 'unknown';
472
+ monthlyGrowth.set(month, (monthlyGrowth.get(month) ?? 0) + 1);
473
+ }
474
+ res.json({
475
+ stats: { ...stats, vaultName },
476
+ decay: decaySummary,
477
+ gaps: gapSummary,
478
+ duplicates: { count: dupCount },
479
+ distribution: {
480
+ source: Object.fromEntries(sourceDist),
481
+ type: Object.fromEntries(typeDist),
482
+ },
483
+ growth: Object.fromEntries([...monthlyGrowth.entries()].sort((a, b) => a[0].localeCompare(b[0]))),
484
+ });
485
+ }
486
+ catch (err) {
487
+ console.error(err);
488
+ res.status(500).json({ error: 'Internal server error' });
489
+ }
490
+ });
491
+ // GET /api/profile — Knowledge Profile summary (F-A09)
492
+ app.get('/api/profile', async (_req, res) => {
493
+ try {
494
+ const stats = await store.getStats();
495
+ const topics = await store.getTopics();
496
+ const docs = await store.getAllDocuments();
497
+ let decaySummary = { averageR: 1.0, criticalCount: 0, healthScore: 100 };
498
+ if (decayEngine) {
499
+ const report = await decayEngine.computeAll();
500
+ const avgR = report.averageR ?? 1.0;
501
+ decaySummary = {
502
+ averageR: avgR,
503
+ criticalCount: report.criticalCount ?? 0,
504
+ healthScore: Math.round(avgR * 100),
505
+ };
506
+ }
507
+ // Source/Type distribution
508
+ const sourceDist = {};
509
+ const typeDist = {};
510
+ for (const doc of docs) {
511
+ const s = doc.source ?? 'local';
512
+ const t = doc.type ?? 'note';
513
+ sourceDist[s] = (sourceDist[s] ?? 0) + 1;
514
+ typeDist[t] = (typeDist[t] ?? 0) + 1;
515
+ }
516
+ // Activity: docs per month (last 12)
517
+ const monthlyActivity = {};
518
+ for (const doc of docs) {
519
+ const month = doc.lastModified?.slice(0, 7);
520
+ if (month)
521
+ monthlyActivity[month] = (monthlyActivity[month] ?? 0) + 1;
522
+ }
523
+ res.setHeader('Access-Control-Allow-Origin', '*');
524
+ res.json({
525
+ name: vaultName || 'Knowledge Vault',
526
+ stats: {
527
+ documents: stats.documentCount,
528
+ chunks: stats.chunkCount,
529
+ topics: topics.length,
530
+ },
531
+ healthScore: decaySummary.healthScore,
532
+ topTopics: topics.slice(0, 15).map(t => ({ name: t.topic, count: t.count })),
533
+ distribution: { source: sourceDist, type: typeDist },
534
+ activity: Object.fromEntries(Object.entries(monthlyActivity).sort((a, b) => a[0].localeCompare(b[0])).slice(-12)),
535
+ generatedAt: new Date().toISOString(),
536
+ });
537
+ }
538
+ catch (err) {
539
+ console.error(err);
540
+ res.status(500).json({ error: 'Internal server error' });
541
+ }
542
+ });
543
+ // GET /api/embed — 임베드용 경량 그래프 데이터 (F-A08)
544
+ app.get('/api/embed', async (req, res) => {
545
+ try {
546
+ const mode = req.query.mode === 'folder' ? 'folder' : 'semantic';
547
+ const maxNodes = Math.min(parseInt(String(req.query.max ?? '200'), 10), 500);
548
+ if (!graphCaches.has(mode)) {
549
+ const data = await buildGraphData(store, { mode });
550
+ graphCaches.set(mode, { data, generatedAt: new Date().toISOString() });
551
+ }
552
+ const cached = graphCaches.get(mode);
553
+ const { nodes, edges, clusters } = cached.data;
554
+ const connCount = new Map();
555
+ for (const e of edges) {
556
+ connCount.set(e.source, (connCount.get(e.source) ?? 0) + 1);
557
+ connCount.set(e.target, (connCount.get(e.target) ?? 0) + 1);
558
+ }
559
+ const sortedNodes = [...nodes].sort((a, b) => (connCount.get(b.id) ?? 0) - (connCount.get(a.id) ?? 0));
560
+ const selectedNodes = sortedNodes.slice(0, maxNodes);
561
+ const selectedIds = new Set(selectedNodes.map(n => n.id));
562
+ const selectedEdges = edges.filter((e) => selectedIds.has(e.source) && selectedIds.has(e.target));
563
+ const embedNodes = selectedNodes.map((n, i) => {
564
+ const angle = (i / selectedNodes.length) * Math.PI * 2;
565
+ const r = 100 + n.clusterId * 15;
566
+ return {
567
+ id: n.id, label: n.label, clusterId: n.clusterId, size: n.size,
568
+ position: [
569
+ r * Math.cos(angle) + (Math.random() - 0.5) * 60,
570
+ (Math.random() - 0.5) * 200,
571
+ r * Math.sin(angle) + (Math.random() - 0.5) * 60,
572
+ ],
573
+ };
574
+ });
575
+ res.setHeader('Access-Control-Allow-Origin', '*');
576
+ res.json({
577
+ nodes: embedNodes, edges: selectedEdges,
578
+ stats: { nodeCount: embedNodes.length, edgeCount: selectedEdges.length, clusterCount: clusters.length, totalNodes: nodes.length },
579
+ title: vaultName || 'Knowledge Graph',
580
+ });
581
+ }
582
+ catch (err) {
583
+ console.error(err);
584
+ res.status(500).json({ error: 'Internal server error' });
585
+ }
586
+ });
587
+ // Sync 상태 추적
588
+ let syncState = {
589
+ running: false, startedAt: '', completedAt: '', result: '', output: '',
590
+ };
591
+ // POST /api/sync — Notion → Obsidian 동기화 트리거
592
+ app.post('/api/sync', async (_req, res) => {
593
+ if (syncState.running) {
594
+ res.json({ success: false, error: 'Sync already running', state: syncState });
595
+ return;
596
+ }
597
+ try {
598
+ const { spawn } = await import('node:child_process');
599
+ const { resolve } = await import('node:path');
600
+ const syncScript = resolve(process.cwd(), 'packages/sync/sync-to-obsidian.mjs');
601
+ const syncDir = resolve(process.cwd(), 'packages/sync');
602
+ const { existsSync } = await import('node:fs');
603
+ if (!existsSync(syncScript)) {
604
+ res.json({ success: false, error: 'sync script not found' });
605
+ return;
606
+ }
607
+ if (!existsSync(resolve(syncDir, '.env'))) {
608
+ res.json({ success: false, error: '.env not found' });
609
+ return;
610
+ }
611
+ syncState = { running: true, startedAt: new Date().toISOString(), completedAt: '', result: '', output: '' };
612
+ const child = spawn('node', [syncScript], { cwd: syncDir, stdio: ['ignore', 'pipe', 'pipe'], shell: true });
613
+ let output = '';
614
+ child.stdout.on('data', (d) => { output += d.toString(); });
615
+ child.stderr.on('data', (d) => { output += d.toString(); });
616
+ child.on('close', (code) => {
617
+ syncState.running = false;
618
+ syncState.completedAt = new Date().toISOString();
619
+ syncState.result = code === 0 ? 'success' : 'failed';
620
+ syncState.output = output.slice(-500); // 마지막 500자만
621
+ });
622
+ res.json({ success: true, message: 'Sync started' });
623
+ }
624
+ catch (err) {
625
+ syncState.running = false;
626
+ console.error(err);
627
+ res.status(500).json({ error: 'Sync failed' });
628
+ }
629
+ });
630
+ // GET /api/sync/status — 동기화 상태 조회
631
+ app.get('/api/sync/status', (_req, res) => {
632
+ res.json(syncState);
633
+ });
634
+ // POST /api/clip — 웹 페이지 클리핑
635
+ app.post('/api/clip', async (req, res) => {
636
+ try {
637
+ const { url } = req.body;
638
+ if (!url) {
639
+ res.status(400).json({ error: 'url required' });
640
+ return;
641
+ }
642
+ // HIGH-03: SSRF 방어 — 내부 네트워크 차단
643
+ try {
644
+ const parsed = new URL(url);
645
+ if (!['http:', 'https:'].includes(parsed.protocol)) {
646
+ res.status(400).json({ error: 'Only http/https URLs allowed' });
647
+ return;
648
+ }
649
+ const host = parsed.hostname.toLowerCase();
650
+ if (host === 'localhost' || host === '127.0.0.1' || host === '::1' || host.endsWith('.local') || host.startsWith('192.168.') || host.startsWith('10.') || host.startsWith('172.16.')) {
651
+ res.status(400).json({ error: 'Internal URLs not allowed' });
652
+ return;
653
+ }
654
+ }
655
+ catch {
656
+ res.status(400).json({ error: 'Invalid URL' });
657
+ return;
658
+ }
659
+ const isYT = /youtube\.com\/watch|youtu\.be\//.test(url);
660
+ const response = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0 stellavault-clipper/1.0' }, signal: AbortSignal.timeout(15000) });
661
+ const html = await response.text();
662
+ // 제목 추출
663
+ const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i);
664
+ let title = (titleMatch ? titleMatch[1] : new URL(url).hostname).replace(/ - YouTube$/, '').trim();
665
+ const safeTitle = title.replace(/[<>:"/\\|?*]/g, '').replace(/\s+/g, ' ').trim().slice(0, 80);
666
+ let content;
667
+ if (isYT) {
668
+ const videoId = url.match(/(?:v=|youtu\.be\/)([a-zA-Z0-9_-]+)/)?.[1] ?? '';
669
+ const descMatch = html.match(/"shortDescription":"([\s\S]*?)"/);
670
+ const desc = descMatch ? descMatch[1].replace(/\\n/g, '\n').replace(/\\"/g, '"').slice(0, 3000) : '';
671
+ content = `![thumbnail](https://img.youtube.com/vi/${videoId}/maxresdefault.jpg)\n\n## 설명\n\n${desc}\n\n[YouTube](${url})`;
672
+ }
673
+ else {
674
+ content = html
675
+ .replace(/<script[\s\S]*?<\/script>/gi, '').replace(/<style[\s\S]*?<\/style>/gi, '')
676
+ .replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, '\n# $1\n').replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, '\n## $1\n')
677
+ .replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, '\n### $1\n')
678
+ .replace(/<p[^>]*>([\s\S]*?)<\/p>/gi, '\n$1\n').replace(/<br\s*\/?>/gi, '\n')
679
+ .replace(/<a[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi, '[$2]($1)')
680
+ .replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, '- $1\n')
681
+ .replace(/<(strong|b)[^>]*>([\s\S]*?)<\/\1>/gi, '**$2**')
682
+ .replace(/<(em|i)[^>]*>([\s\S]*?)<\/\1>/gi, '*$2*')
683
+ .replace(/<code[^>]*>([\s\S]*?)<\/code>/gi, '`$1`')
684
+ .replace(/<[^>]+>/g, '')
685
+ .replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&nbsp;/g, ' ')
686
+ .replace(/\n{3,}/g, '\n\n').trim();
687
+ if (content.length > 10000)
688
+ content = content.slice(0, 10000) + '\n\n...(truncated)';
689
+ }
690
+ // vault에 저장
691
+ const { writeFileSync, mkdirSync } = await import('node:fs');
692
+ const { join } = await import('node:path');
693
+ const date = new Date().toISOString().slice(0, 10);
694
+ const clipDir = join(vaultPath || '.', '06_Research', 'clips');
695
+ mkdirSync(clipDir, { recursive: true });
696
+ const fileName = `${date} ${safeTitle}.md`;
697
+ const md = `---\ntitle: "${safeTitle}"\nsource: "${url}"\nclipped: ${date}\ntags: [clip${isYT ? ', youtube' : ''}]\n---\n\n# ${safeTitle}\n\n> Source: ${url}\n\n${content}`;
698
+ writeFileSync(join(clipDir, fileName), md, 'utf-8');
699
+ res.json({ success: true, fileName, path: join(clipDir, fileName) });
700
+ }
701
+ catch (err) {
702
+ console.error(err);
703
+ res.status(500).json({ error: 'Clip failed' });
704
+ }
705
+ });
706
+ return {
707
+ async start() {
708
+ return new Promise((resolve) => {
709
+ app.listen(port, '127.0.0.1', () => {
710
+ console.error(`🌐 API server running at http://127.0.0.1:${port}`);
711
+ resolve();
712
+ });
713
+ });
714
+ },
715
+ app,
716
+ };
717
+ }
718
+ //# sourceMappingURL=server.js.map