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