stellavault 0.1.0 → 0.2.1

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