@xopcai/xopc 0.0.90 → 0.0.91

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 (290) hide show
  1. package/README.md +36 -12
  2. package/README.zh-CN.md +36 -12
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  5. package/dist/extensions/feishu/src/workflow-progress.js +1 -1
  6. package/dist/extensions/telegram/src/plugin.js +1 -1
  7. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  8. package/dist/extensions/telegram/src/workflow-progress.js +1 -1
  9. package/dist/extensions/telegram/xopc.extension.json +1 -1
  10. package/dist/extensions/weixin/src/api/api.js +2 -2
  11. package/dist/extensions/weixin/src/auth/accounts.js +1 -1
  12. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  13. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  14. package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
  15. package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
  16. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  17. package/dist/extensions/weixin/src/plugin.js +1 -1
  18. package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
  19. package/dist/extensions/weixin/src/workflow-progress.js +1 -1
  20. package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +41 -0
  21. package/dist/gateway/static/root/assets/agents-bVWUlrlD.js +222 -0
  22. package/dist/gateway/static/root/assets/apps-page-CIC8bmvZ.js +1 -0
  23. package/dist/gateway/static/root/assets/{attachment-preview-renderer-CpyoFbs4.js → attachment-preview-renderer-DBAxQXb-.js} +2 -2
  24. package/dist/gateway/static/root/assets/{attachment-process-heavy-CqVriadb.js → attachment-process-heavy-Csq3TrrP.js} +4 -4
  25. package/dist/gateway/static/root/assets/channels-settings-C8G8RAAP.js +1 -0
  26. package/dist/gateway/static/root/assets/{channels-status-swr-BrtH2VzC.js → channels-status-swr-CYWL5DLD.js} +1 -1
  27. package/dist/gateway/static/root/assets/copy-Dv6d4Dvw.js +1 -0
  28. package/dist/gateway/static/root/assets/{cron-api-CyqbgfHM.js → cron-api-TVqLlGAC.js} +1 -1
  29. package/dist/gateway/static/root/assets/cron-page-BtcFYlvv.js +1 -0
  30. package/dist/gateway/static/root/assets/dist-CUV1uY5f.js +1 -0
  31. package/dist/gateway/static/root/assets/{extension-debug-page-D6Ak0STa.js → extension-debug-page-mTLHRDp1.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-page-Q0P3d6DW.js → extension-page-iI8BI7WK.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-settings-page-CL55LwU_.js → extension-settings-page-ByXcdubM.js} +1 -1
  34. package/dist/gateway/static/root/assets/{fetch-Dqa9iTWl.js → fetch-BWtQq_Ys.js} +1 -1
  35. package/dist/gateway/static/root/assets/{field-primitives-HUR6JElP.js → field-primitives-BsZ-4VT5.js} +1 -1
  36. package/dist/gateway/static/root/assets/{heartbeat-config-api-DusckjUX.js → heartbeat-config-api-WjTsRLCU.js} +1 -1
  37. package/dist/gateway/static/root/assets/{index-BYcGfwcE.js → index-CKkR-v9U.js} +85 -81
  38. package/dist/gateway/static/root/assets/index-VlELBY99.css +1 -0
  39. package/dist/gateway/static/root/assets/logs-page-ClnIpxfd.js +1 -0
  40. package/dist/gateway/static/root/assets/note-detail-page-B91pLkEI.css +1 -0
  41. package/dist/gateway/static/root/assets/note-detail-page-DJ2Mb4x7.js +179 -0
  42. package/dist/gateway/static/root/assets/note-time-JLBPSLzK.js +1 -0
  43. package/dist/gateway/static/root/assets/notes-page-BE-75qz9.js +1 -0
  44. package/dist/gateway/static/root/assets/{pdf-BnEvgIXZ.js → pdf-epILhEOn.js} +1 -1
  45. package/dist/gateway/static/root/assets/preload-helper-zJ_50EbN.js +1 -0
  46. package/dist/gateway/static/root/assets/sessions-page-bJJkWtTl.js +1 -0
  47. package/dist/gateway/static/root/assets/{settings-form-section-a0qGVOlr.js → settings-form-section-DSYCknxM.js} +1 -1
  48. package/dist/gateway/static/root/assets/settings-page-WcMXLq2U.js +3 -0
  49. package/dist/gateway/static/root/assets/share-preview-page-awRqs4hV.js +2 -0
  50. package/dist/gateway/static/root/assets/skills-page-Lu-i1JG7.js +2 -0
  51. package/dist/gateway/static/root/assets/{theme-store-C0Ehmdo5.js → theme-store-BC-42BoZ.js} +1 -1
  52. package/dist/gateway/static/root/assets/toast-z0toXu32.js +1 -0
  53. package/dist/gateway/static/root/assets/url-CY1RQKTU.js +3 -0
  54. package/dist/gateway/static/root/assets/{utils-DRQryzdn.js → utils-DX3TQuap.js} +1 -1
  55. package/dist/gateway/static/root/assets/vendor-codemirror-DYoKfS8f.js +45 -0
  56. package/dist/gateway/static/root/assets/{voice-api-key-field-D0viACE2.js → voice-api-key-field-B5uKlDqA.js} +1 -1
  57. package/dist/gateway/static/root/assets/{workflow-page.utils-DnG8JBhV.js → workflow-page.utils-ClC37yEp.js} +1 -1
  58. package/dist/gateway/static/root/assets/workflows-page-C7VhIXtR.js +27 -0
  59. package/dist/gateway/static/root/index.html +10 -8
  60. package/dist/package.js +1 -1
  61. package/dist/src/agent/agent-manager.js +7 -7
  62. package/dist/src/agent/agent-scope.js +1 -1
  63. package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
  64. package/dist/src/agent/context/workspace-seed.js +2 -2
  65. package/dist/src/agent/goals/goal-run-store.js +4 -4
  66. package/dist/src/agent/goals/persistent-goal-service.js +1 -1
  67. package/dist/src/agent/goals/post-turn.js +2 -2
  68. package/dist/src/agent/image/load-image-media.js +2 -2
  69. package/dist/src/agent/ipc/bus.js +1 -1
  70. package/dist/src/agent/ipc/inbox.js +2 -2
  71. package/dist/src/agent/ipc/socket.js +1 -1
  72. package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
  73. package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
  74. package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
  75. package/dist/src/agent/mcp/mcp-transport.js +1 -1
  76. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  77. package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
  78. package/dist/src/agent/memory/dreaming/events.js +1 -1
  79. package/dist/src/agent/memory/dreaming/last-run.js +1 -1
  80. package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
  81. package/dist/src/agent/memory/dreaming/preview.js +1 -1
  82. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  83. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  84. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  85. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  86. package/dist/src/agent/models/manager.js +1 -1
  87. package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
  88. package/dist/src/agent/reply/post-compaction-context.js +1 -1
  89. package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
  90. package/dist/src/agent/sandbox/path-policy.js +2 -2
  91. package/dist/src/agent/service/build-direct-message-content.js +1 -1
  92. package/dist/src/agent/service.js +4 -4
  93. package/dist/src/agent/session/session-inspector.js +1 -1
  94. package/dist/src/agent/skills/config.js +1 -1
  95. package/dist/src/agent/skills/hub-hash.js +2 -2
  96. package/dist/src/agent/skills/hub-lock.js +1 -1
  97. package/dist/src/agent/skills/hub-pull.js +2 -2
  98. package/dist/src/agent/skills/index.js +1 -1
  99. package/dist/src/agent/skills/managed-store.js +1 -1
  100. package/dist/src/agent/skills/scanner.js +1 -1
  101. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  102. package/dist/src/agent/skills/skill-manager.js +1 -1
  103. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  104. package/dist/src/agent/tools/factory.js +1 -1
  105. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  106. package/dist/src/agent/tools/send-media.js +1 -1
  107. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  108. package/dist/src/agent/tools/workflow-tool.js +1 -1
  109. package/dist/src/agent/tools/write.js +1 -1
  110. package/dist/src/agent/workflow/catalog.js +1 -1
  111. package/dist/src/auth/credentials.js +3 -3
  112. package/dist/src/auth/profiles/store.js +1 -1
  113. package/dist/src/auth/sync-provider-auth.js +1 -1
  114. package/dist/src/browser/cache-dir-policy.js +1 -1
  115. package/dist/src/browser/cdp-local-launcher.js +2 -2
  116. package/dist/src/browser/providers/browser-ext-install.js +3 -3
  117. package/dist/src/browser/providers/cloakbrowser.js +4 -4
  118. package/dist/src/browser/providers/playwright-doctor.js +1 -1
  119. package/dist/src/browser/stealth.js +1 -1
  120. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  121. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  122. package/dist/src/channels/outbound/persist-store.js +1 -1
  123. package/dist/src/channels/pairing/allow-from-file.js +1 -1
  124. package/dist/src/channels/pairing/pairing-store.js +2 -2
  125. package/dist/src/chat-commands/agent-edit.js +2 -2
  126. package/dist/src/chat-commands/builtins/config.js +2 -2
  127. package/dist/src/chat-commands/context.js +1 -1
  128. package/dist/src/cli/commands/config.js +1 -1
  129. package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
  130. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  131. package/dist/src/cli/commands/doctor/checks/session-integrity.js +2 -2
  132. package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
  133. package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
  134. package/dist/src/cli/commands/extension-dev.js +1 -1
  135. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  136. package/dist/src/cli/commands/extension-pack.js +1 -1
  137. package/dist/src/cli/commands/gateway/logs.js +1 -1
  138. package/dist/src/cli/commands/image.js +1 -1
  139. package/dist/src/cli/commands/init.js +4 -4
  140. package/dist/src/cli/commands/onboard.js +1 -1
  141. package/dist/src/cli/utils/init-workspace-core.js +2 -2
  142. package/dist/src/commands/agents.config.js +1 -1
  143. package/dist/src/config/agent-profile.js +1 -1
  144. package/dist/src/config/gateway-bind.js +1 -1
  145. package/dist/src/config/index.js +5 -5
  146. package/dist/src/config/loader.js +2 -2
  147. package/dist/src/config/models-json.js +2 -2
  148. package/dist/src/config/paths-state.js +1 -1
  149. package/dist/src/config/profile.js +2 -2
  150. package/dist/src/config/workspace-path.js +1 -1
  151. package/dist/src/cron/executor.js +2 -2
  152. package/dist/src/cron/persistence.js +1 -1
  153. package/dist/src/cron/run-log-store.js +1 -1
  154. package/dist/src/daemon/constants.js +1 -1
  155. package/dist/src/daemon/install-plan.js +2 -2
  156. package/dist/src/daemon/launchd.js +2 -2
  157. package/dist/src/daemon/schtasks.js +2 -2
  158. package/dist/src/daemon/systemd.js +2 -2
  159. package/dist/src/extensions/bundle-mcp.js +1 -1
  160. package/dist/src/extensions/discover-extensions.js +1 -1
  161. package/dist/src/extensions/health.js +1 -1
  162. package/dist/src/extensions/loader.js +1 -1
  163. package/dist/src/extensions/lockfile.js +2 -2
  164. package/dist/src/extensions/update.js +1 -1
  165. package/dist/src/gateway/agents-admin.js +3 -3
  166. package/dist/src/gateway/file-path-classifier.js +2 -2
  167. package/dist/src/gateway/hono/lib/config-payload.d.ts +1 -0
  168. package/dist/src/gateway/hono/lib/config-payload.js +2 -1
  169. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  170. package/dist/src/gateway/hono/lib/extension-store.js +2 -2
  171. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  172. package/dist/src/gateway/hono/middleware/auth.js +1 -2
  173. package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
  174. package/dist/src/gateway/hono/oauth.js +1 -1
  175. package/dist/src/gateway/hono/routes/agents.js +1 -1
  176. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  177. package/dist/src/gateway/hono/routes/config-patch/gateway.d.ts +2 -2
  178. package/dist/src/gateway/hono/routes/config-patch/gateway.js +12 -0
  179. package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -1
  180. package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
  181. package/dist/src/gateway/hono/routes/dreaming.js +1 -1
  182. package/dist/src/gateway/hono/routes/host-fs.js +2 -2
  183. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  184. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  185. package/dist/src/gateway/hono/routes/models.js +1 -1
  186. package/dist/src/gateway/hono/routes/notes.d.ts +3 -0
  187. package/dist/src/gateway/hono/routes/notes.js +274 -0
  188. package/dist/src/gateway/hono/routes/notes.js.map +1 -0
  189. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  190. package/dist/src/gateway/hono/routes/shares.js +1 -1
  191. package/dist/src/gateway/hono/routes/workspace.js +4 -4
  192. package/dist/src/gateway/lock.js +3 -3
  193. package/dist/src/gateway/ports.js +1 -1
  194. package/dist/src/gateway/service/agent-runner.js +2 -2
  195. package/dist/src/gateway/service/marketplace-service.js +2 -2
  196. package/dist/src/gateway/service.d.ts +3 -0
  197. package/dist/src/gateway/service.js +11 -1
  198. package/dist/src/gateway/service.js.map +1 -1
  199. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  200. package/dist/src/gateway/workspace-ripgrep.d.ts +6 -0
  201. package/dist/src/gateway/workspace-ripgrep.js +62 -11
  202. package/dist/src/gateway/workspace-ripgrep.js.map +1 -1
  203. package/dist/src/infra/brew.js +1 -1
  204. package/dist/src/infra/package-json.js +1 -1
  205. package/dist/src/infra/package-update-steps.js +1 -1
  206. package/dist/src/infra/path-env.js +2 -2
  207. package/dist/src/infra/restart.js +2 -2
  208. package/dist/src/infra/stable-node-path.js +1 -1
  209. package/dist/src/infra/update-check.js +1 -1
  210. package/dist/src/infra/update-global.js +1 -1
  211. package/dist/src/infra/update-lock.js +3 -3
  212. package/dist/src/infra/update-runner.js +1 -1
  213. package/dist/src/infra/update-startup.js +2 -2
  214. package/dist/src/infra/write-file-atomic.js +2 -2
  215. package/dist/src/notes/attachment-ref.d.ts +9 -0
  216. package/dist/src/notes/attachment-ref.js +27 -0
  217. package/dist/src/notes/attachment-ref.js.map +1 -0
  218. package/dist/src/notes/index.d.ts +4 -0
  219. package/dist/src/notes/index.js +4 -0
  220. package/dist/src/notes/note-attachment-sync.d.ts +7 -0
  221. package/dist/src/notes/note-attachment-sync.js +46 -0
  222. package/dist/src/notes/note-attachment-sync.js.map +1 -0
  223. package/dist/src/notes/note-index-meta.d.ts +14 -0
  224. package/dist/src/notes/note-index-meta.js +87 -0
  225. package/dist/src/notes/note-index-meta.js.map +1 -0
  226. package/dist/src/notes/paths.d.ts +5 -0
  227. package/dist/src/notes/paths.js +23 -0
  228. package/dist/src/notes/paths.js.map +1 -0
  229. package/dist/src/notes/service.d.ts +42 -0
  230. package/dist/src/notes/service.js +331 -0
  231. package/dist/src/notes/service.js.map +1 -0
  232. package/dist/src/notes/store.d.ts +33 -0
  233. package/dist/src/notes/store.js +317 -0
  234. package/dist/src/notes/store.js.map +1 -0
  235. package/dist/src/notes/types.d.ts +162 -0
  236. package/dist/src/notes/types.js +1 -0
  237. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  238. package/dist/src/providers/index.js +2 -2
  239. package/dist/src/providers/model-registry.js +1 -1
  240. package/dist/src/session/config-store.js +2 -2
  241. package/dist/src/session/init-session-turn.js +2 -2
  242. package/dist/src/session/parity/jsonl-transcript-io.js +2 -2
  243. package/dist/src/session/parity/sessions-json-file.js +1 -1
  244. package/dist/src/session/parity/transcript-file-lock.js +2 -2
  245. package/dist/src/session/parity/transcript-paths.js +1 -1
  246. package/dist/src/session/resolve-session.js +4 -4
  247. package/dist/src/session/search-index-cache.js +1 -1
  248. package/dist/src/session/search-index.js +1 -1
  249. package/dist/src/session/session-title.js +2 -2
  250. package/dist/src/session/store.js +6 -6
  251. package/dist/src/share/share-auto.js +2 -2
  252. package/dist/src/share/share-store.js +3 -3
  253. package/dist/src/share/share-thumbnail.js +2 -2
  254. package/dist/src/share/share-zip.js +1 -1
  255. package/dist/src/share/site-share-store.js +3 -3
  256. package/dist/src/share/site-static-serve.js +1 -1
  257. package/dist/src/tui/clipboard-image.js +3 -3
  258. package/dist/src/tui/theme-manager.js +1 -1
  259. package/dist/src/tui/tui-keybindings-file.js +1 -1
  260. package/dist/src/tui/tui-scoped-models.js +2 -2
  261. package/dist/src/tui/tui-settings.js +1 -1
  262. package/dist/src/tui/tui.js +3 -3
  263. package/dist/src/tunnel/frpc-binary.js +3 -3
  264. package/dist/src/tunnel/frpc-config.js +1 -1
  265. package/dist/src/tunnel/frpc-extract.js +1 -1
  266. package/dist/src/tunnel/tunnel-state.js +1 -1
  267. package/dist/src/utils/logger/audit.js +1 -1
  268. package/dist/src/utils/logger/log-store.js +1 -1
  269. package/dist/src/utils/logger/rotation.js +1 -1
  270. package/dist/src/voice/tts/audio.js +1 -1
  271. package/dist/src/voice/tts/providers/edge-speech.js +2 -2
  272. package/dist/src/workflows/store/event-store.js +1 -1
  273. package/dist/src/workflows/store/run-store.js +1 -1
  274. package/package.json +1 -1
  275. package/dist/gateway/static/root/assets/agents-cPvvYLXo.js +0 -222
  276. package/dist/gateway/static/root/assets/apps-page-Bk1_P5FJ.js +0 -1
  277. package/dist/gateway/static/root/assets/channels-settings-CZoeQwHz.js +0 -1
  278. package/dist/gateway/static/root/assets/cron-page-BpLdiQN8.js +0 -1
  279. package/dist/gateway/static/root/assets/dist-BTWC-BTN.js +0 -45
  280. package/dist/gateway/static/root/assets/dist-BpAiK86n.js +0 -1
  281. package/dist/gateway/static/root/assets/eye-DAfL1U7M.js +0 -1
  282. package/dist/gateway/static/root/assets/index-V7MQ7834.css +0 -1
  283. package/dist/gateway/static/root/assets/logs-page-_HcZ2fgK.js +0 -1
  284. package/dist/gateway/static/root/assets/sessions-page-iezSMjho.js +0 -1
  285. package/dist/gateway/static/root/assets/settings-page-C9_nYQwM.js +0 -3
  286. package/dist/gateway/static/root/assets/share-preview-page-DExl7CJy.js +0 -2
  287. package/dist/gateway/static/root/assets/skills-page-BlgGD93t.js +0 -2
  288. package/dist/gateway/static/root/assets/url-fxyYANfA.js +0 -3
  289. package/dist/gateway/static/root/assets/vendor-codemirror-D0yxdRpg.js +0 -58
  290. package/dist/gateway/static/root/assets/workflows-page-BvMobnJP.js +0 -27
@@ -0,0 +1,331 @@
1
+ import { createLogger } from "../utils/logger/index.js";
2
+ import { init_logger } from "../utils/logger.js";
3
+ import { attachmentIdFromTarget, buildNoteAttachmentRef } from "./attachment-ref.js";
4
+ import { partitionAttachmentsByReference } from "./note-attachment-sync.js";
5
+ import { randomUUID } from "node:crypto";
6
+ //#region src/notes/service.ts
7
+ init_logger();
8
+ const log = createLogger("NotesService");
9
+ function inferKind(text, hasAttachments, attachments) {
10
+ if (hasAttachments && attachments?.length && attachments.every((item) => item.type === "audio")) return "voice";
11
+ if (hasAttachments) return "media";
12
+ if (!text) return "thought";
13
+ const lower = text.toLowerCase();
14
+ if (/^(todo|task|remind|buy|call|email|meet|finish|submit|send)\b/i.test(lower) || /\b(明天|今天|记得|别忘|待办|提醒)\b/.test(text)) return "todo";
15
+ if (/^https?:\/\//.test(text.trim())) return "bookmark";
16
+ return "thought";
17
+ }
18
+ function createBlockId() {
19
+ return `block_${Date.now()}_${randomUUID().slice(0, 8)}`;
20
+ }
21
+ const IMAGE_MARKDOWN = /^!\[([^\]]*)\]\(([^)]+)\)$/;
22
+ function noteTextToBlocks(text, noteId) {
23
+ if (!text?.trim()) return void 0;
24
+ const now = Date.now();
25
+ return text.split(/\n{2,}/).map((part) => {
26
+ const trimmed = part.trim();
27
+ if (noteId) {
28
+ const imageMatch = trimmed.match(IMAGE_MARKDOWN);
29
+ if (imageMatch) {
30
+ const attachmentId = attachmentIdFromTarget(imageMatch[2], noteId);
31
+ if (attachmentId) return {
32
+ id: createBlockId(),
33
+ type: "image",
34
+ attachmentId,
35
+ alt: imageMatch[1] || void 0,
36
+ createdAt: now,
37
+ updatedAt: now
38
+ };
39
+ }
40
+ }
41
+ return {
42
+ id: createBlockId(),
43
+ type: "paragraph",
44
+ text: trimmed,
45
+ createdAt: now,
46
+ updatedAt: now
47
+ };
48
+ });
49
+ }
50
+ function blocksToPlainText(blocks, noteId) {
51
+ if (!blocks?.length) return void 0;
52
+ return blocks.map((block) => {
53
+ if (block.type === "divider") return "---";
54
+ if (block.type === "todo") return `${block.checked ? "[x]" : "[ ]"} ${block.text}`;
55
+ if (block.type === "image") {
56
+ if (noteId) return `![${block.alt ?? ""}](${buildNoteAttachmentRef(noteId, block.attachmentId)})`;
57
+ return block.alt ?? "";
58
+ }
59
+ return block.text;
60
+ }).filter((text) => text.trim().length > 0).join("\n\n");
61
+ }
62
+ function createAiOrganizedBlocks(blocks, instruction) {
63
+ const now = Date.now();
64
+ const lines = (blocksToPlainText(blocks) || "").split(/\n+/).map((line) => line.trim()).filter(Boolean);
65
+ const wantsTodos = /待办|todo|task|行动|提醒/i.test(instruction);
66
+ const wantsSummary = /摘要|总结|summary|压缩/i.test(instruction);
67
+ if (wantsTodos) {
68
+ const candidates = lines.filter((line) => /要|需|记得|todo|task|完成|提交|联系|跟进|提醒/i.test(line));
69
+ return (candidates.length ? candidates : lines).slice(0, 12).map((line) => ({
70
+ id: createBlockId(),
71
+ type: "todo",
72
+ text: line.replace(/^[-*\d.\s\[\]x]+/i, "").trim(),
73
+ checked: false,
74
+ createdAt: now,
75
+ updatedAt: now
76
+ }));
77
+ }
78
+ if (wantsSummary) {
79
+ const summary = lines.join(" ").slice(0, 220);
80
+ return [{
81
+ id: createBlockId(),
82
+ type: "paragraph",
83
+ text: summary,
84
+ createdAt: now,
85
+ updatedAt: now
86
+ }];
87
+ }
88
+ const titleText = lines[0]?.slice(0, 40) || "整理后的笔记";
89
+ const bodyLines = lines.slice(1).length ? lines.slice(1) : lines;
90
+ return [{
91
+ id: createBlockId(),
92
+ type: "heading",
93
+ text: titleText,
94
+ level: 2,
95
+ createdAt: now,
96
+ updatedAt: now
97
+ }, ...bodyLines.map((line) => ({
98
+ id: createBlockId(),
99
+ type: "bulletList",
100
+ text: line.replace(/^[-*\d.\s]+/, "").trim(),
101
+ indent: 0,
102
+ createdAt: now,
103
+ updatedAt: now
104
+ }))];
105
+ }
106
+ const SNAPSHOT_THROTTLE_MS = 6e4;
107
+ const MAX_SNAPSHOTS_PER_NOTE = 30;
108
+ var NotesService = class {
109
+ store;
110
+ lastSnapshotAt = /* @__PURE__ */ new Map();
111
+ constructor(store) {
112
+ this.store = store;
113
+ }
114
+ async initialize() {
115
+ await this.store.initialize();
116
+ log.debug("NotesService initialized");
117
+ }
118
+ async quickCapture(text, source) {
119
+ const now = Date.now();
120
+ const id = randomUUID();
121
+ const blocks = noteTextToBlocks(text, id);
122
+ const note = {
123
+ id,
124
+ kind: inferKind(text),
125
+ status: "inbox",
126
+ text,
127
+ blocks,
128
+ createdAt: now,
129
+ updatedAt: now,
130
+ capturedVia: source,
131
+ localVersion: 0,
132
+ remoteVersion: 1
133
+ };
134
+ await this.store.addNote(note);
135
+ log.debug({
136
+ id: note.id,
137
+ kind: note.kind
138
+ }, "Quick capture");
139
+ return note;
140
+ }
141
+ async createNote(params) {
142
+ const now = Date.now();
143
+ const id = randomUUID();
144
+ const blocks = params.blocks ?? noteTextToBlocks(params.text, id);
145
+ const text = params.text ?? blocksToPlainText(blocks, id);
146
+ const note = {
147
+ id,
148
+ title: params.title,
149
+ kind: params.kind || inferKind(text),
150
+ status: "inbox",
151
+ text,
152
+ blocks,
153
+ createdAt: now,
154
+ updatedAt: now,
155
+ capturedVia: params.capturedVia,
156
+ tags: params.tags,
157
+ pinned: params.pinned,
158
+ localVersion: 0,
159
+ remoteVersion: 1
160
+ };
161
+ await this.store.addNote(note);
162
+ log.debug({
163
+ id: note.id,
164
+ kind: note.kind
165
+ }, "Note created");
166
+ return note;
167
+ }
168
+ async getNote(id) {
169
+ return this.store.getNote(id);
170
+ }
171
+ async updateNote(id, patch, trigger = "edit") {
172
+ const existing = await this.store.getNote(id);
173
+ if (!existing) return null;
174
+ const contentTouched = patch.text !== void 0 || patch.blocks !== void 0 || patch.title !== void 0;
175
+ if (contentTouched) await this.maybeSaveSnapshot(existing, trigger);
176
+ const normalizedPatch = { ...patch };
177
+ if (patch.blocks) normalizedPatch.text = patch.text ?? blocksToPlainText(patch.blocks, existing.id);
178
+ else if (typeof patch.text === "string") normalizedPatch.blocks = patch.blocks ?? noteTextToBlocks(patch.text, existing.id);
179
+ normalizedPatch.remoteVersion = (existing.remoteVersion ?? 0) + 1;
180
+ if (contentTouched) {
181
+ const merged = {
182
+ ...existing,
183
+ ...normalizedPatch,
184
+ id: existing.id,
185
+ createdAt: existing.createdAt,
186
+ updatedAt: Date.now()
187
+ };
188
+ const reconciled = await this.reconcileAttachments(merged);
189
+ normalizedPatch.attachments = reconciled.attachments;
190
+ normalizedPatch.kind = reconciled.kind;
191
+ }
192
+ return this.store.updateNote(id, normalizedPatch);
193
+ }
194
+ async reconcileAttachments(note) {
195
+ const { kept, removed } = partitionAttachmentsByReference(note);
196
+ if (removed.length === 0) return note;
197
+ for (const attachment of removed) await this.store.deleteAttachmentFile(note.id, attachment.relativePath);
198
+ log.debug({
199
+ noteId: note.id,
200
+ removedIds: removed.map((attachment) => attachment.id)
201
+ }, "Pruned orphan note attachments");
202
+ const hasAttachments = kept.length > 0;
203
+ return {
204
+ ...note,
205
+ attachments: hasAttachments ? kept : void 0,
206
+ kind: inferKind(note.text, hasAttachments, kept)
207
+ };
208
+ }
209
+ async syncNote(id, patch, baseRemoteVersion) {
210
+ const existing = await this.store.getNote(id);
211
+ if (!existing) return {
212
+ note: null,
213
+ conflict: false
214
+ };
215
+ const currentRemoteVersion = existing.remoteVersion ?? 0;
216
+ if (baseRemoteVersion !== void 0 && baseRemoteVersion < currentRemoteVersion) return {
217
+ note: existing,
218
+ conflict: true
219
+ };
220
+ return {
221
+ note: await this.updateNote(id, patch, "sync"),
222
+ conflict: false
223
+ };
224
+ }
225
+ async createAiEditPatch(id, instruction, blocks) {
226
+ const note = await this.store.getNote(id);
227
+ if (!note) return null;
228
+ const organizedBlocks = createAiOrganizedBlocks(blocks?.length ? blocks : note.blocks ?? noteTextToBlocks(note.text, note.id) ?? [], instruction);
229
+ return {
230
+ message: "AI edit patch generated",
231
+ patch: {
232
+ id: randomUUID(),
233
+ summary: `已根据「${instruction.slice(0, 40)}」生成可预览的块级整理建议`,
234
+ operations: [{
235
+ type: "replaceBlocks",
236
+ blocks: organizedBlocks
237
+ }]
238
+ }
239
+ };
240
+ }
241
+ async deleteNote(id) {
242
+ const deleted = await this.store.deleteNote(id);
243
+ if (deleted) {
244
+ await this.store.deleteAllSnapshots(id);
245
+ this.lastSnapshotAt.delete(id);
246
+ }
247
+ return deleted;
248
+ }
249
+ async listNotes(query = {}) {
250
+ return this.store.listNotes(query);
251
+ }
252
+ async addAttachment(noteId, file) {
253
+ const note = await this.store.getNote(noteId);
254
+ if (!note) return null;
255
+ const { relativePath, size } = await this.store.saveAttachment(noteId, file.name, file.buffer);
256
+ const attachment = {
257
+ id: randomUUID(),
258
+ type: inferAttachmentType(file.mimeType),
259
+ mimeType: file.mimeType,
260
+ fileName: file.name,
261
+ size,
262
+ relativePath,
263
+ duration: file.duration
264
+ };
265
+ const attachments = [...note.attachments || [], attachment];
266
+ const kind = note.kind === "thought" && attachment.type === "audio" ? "voice" : note.kind === "thought" ? "media" : note.kind;
267
+ await this.store.updateNote(noteId, {
268
+ attachments,
269
+ kind
270
+ });
271
+ return attachment;
272
+ }
273
+ async getAttachmentPath(noteId, attachmentId) {
274
+ const note = await this.store.getNote(noteId);
275
+ if (!note) return null;
276
+ const attachment = note.attachments?.find((a) => a.id === attachmentId);
277
+ if (!attachment) return null;
278
+ return {
279
+ filePath: this.store.resolveAttachmentPath(noteId, attachment.relativePath),
280
+ mimeType: attachment.mimeType,
281
+ fileName: attachment.fileName
282
+ };
283
+ }
284
+ async listNoteHistory(noteId) {
285
+ return this.store.listSnapshots(noteId);
286
+ }
287
+ async getNoteSnapshot(noteId, timestamp) {
288
+ return this.store.getSnapshot(noteId, timestamp);
289
+ }
290
+ async restoreNoteSnapshot(noteId, timestamp) {
291
+ const snapshot = await this.store.getSnapshot(noteId, timestamp);
292
+ if (!snapshot) return null;
293
+ const existing = await this.store.getNote(noteId);
294
+ if (!existing) return null;
295
+ await this.store.saveSnapshot(existing, "restore");
296
+ this.lastSnapshotAt.set(noteId, Date.now());
297
+ await this.store.pruneSnapshots(noteId, MAX_SNAPSHOTS_PER_NOTE);
298
+ return this.store.updateNote(noteId, {
299
+ title: snapshot.title,
300
+ text: snapshot.text,
301
+ blocks: snapshot.blocks,
302
+ tags: snapshot.tags
303
+ });
304
+ }
305
+ async flush() {
306
+ await this.store.flush();
307
+ }
308
+ async maybeSaveSnapshot(note, trigger) {
309
+ if (trigger !== "edit") {
310
+ await this.store.saveSnapshot(note, trigger);
311
+ this.lastSnapshotAt.set(note.id, Date.now());
312
+ await this.store.pruneSnapshots(note.id, MAX_SNAPSHOTS_PER_NOTE);
313
+ return;
314
+ }
315
+ const last = this.lastSnapshotAt.get(note.id) ?? 0;
316
+ if (Date.now() - last < SNAPSHOT_THROTTLE_MS) return;
317
+ await this.store.saveSnapshot(note, trigger);
318
+ this.lastSnapshotAt.set(note.id, Date.now());
319
+ await this.store.pruneSnapshots(note.id, MAX_SNAPSHOTS_PER_NOTE);
320
+ }
321
+ };
322
+ function inferAttachmentType(mimeType) {
323
+ if (mimeType.startsWith("image/")) return "image";
324
+ if (mimeType.startsWith("video/")) return "video";
325
+ if (mimeType.startsWith("audio/")) return "audio";
326
+ return "file";
327
+ }
328
+ //#endregion
329
+ export { NotesService };
330
+
331
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","names":[],"sources":["../../../src/notes/service.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { createLogger } from '../utils/logger.js';\nimport { buildNoteAttachmentRef, attachmentIdFromTarget } from './attachment-ref.js';\nimport { partitionAttachmentsByReference } from './note-attachment-sync.js';\nimport { NotesStore } from './store.js';\nimport type {\n CaptureSource,\n CreateNoteParams,\n Note,\n NoteAiPatch,\n NoteAttachment,\n NoteBlock,\n NoteIndexEntry,\n NoteKind,\n NoteSnapshot,\n NoteSnapshotEntry,\n NotesListQuery,\n SnapshotTrigger,\n} from './types.js';\n\nconst log = createLogger('NotesService');\n\nfunction inferKind(\n text?: string,\n hasAttachments?: boolean,\n attachments?: NoteAttachment[],\n): NoteKind {\n if (hasAttachments && attachments?.length && attachments.every((item) => item.type === 'audio')) {\n return 'voice';\n }\n if (hasAttachments) return 'media';\n if (!text) return 'thought';\n const lower = text.toLowerCase();\n if (/^(todo|task|remind|buy|call|email|meet|finish|submit|send)\\b/i.test(lower) ||\n /\\b(明天|今天|记得|别忘|待办|提醒)\\b/.test(text)) {\n return 'todo';\n }\n if (/^https?:\\/\\//.test(text.trim())) return 'bookmark';\n return 'thought';\n}\n\nfunction createBlockId(): string {\n return `block_${Date.now()}_${randomUUID().slice(0, 8)}`;\n}\n\nconst IMAGE_MARKDOWN = /^!\\[([^\\]]*)\\]\\(([^)]+)\\)$/;\n\nfunction noteTextToBlocks(text?: string, noteId?: string): NoteBlock[] | undefined {\n if (!text?.trim()) return undefined;\n const now = Date.now();\n return text.split(/\\n{2,}/).map((part) => {\n const trimmed = part.trim();\n if (noteId) {\n const imageMatch = trimmed.match(IMAGE_MARKDOWN);\n if (imageMatch) {\n const attachmentId = attachmentIdFromTarget(imageMatch[2], noteId);\n if (attachmentId) {\n return {\n id: createBlockId(),\n type: 'image' as const,\n attachmentId,\n alt: imageMatch[1] || undefined,\n createdAt: now,\n updatedAt: now,\n };\n }\n }\n }\n return {\n id: createBlockId(),\n type: 'paragraph' as const,\n text: trimmed,\n createdAt: now,\n updatedAt: now,\n };\n });\n}\n\nfunction blocksToPlainText(blocks?: NoteBlock[], noteId?: string): string | undefined {\n if (!blocks?.length) return undefined;\n return blocks\n .map((block) => {\n if (block.type === 'divider') return '---';\n if (block.type === 'todo') return `${block.checked ? '[x]' : '[ ]'} ${block.text}`;\n if (block.type === 'image') {\n if (noteId) {\n return `![${block.alt ?? ''}](${buildNoteAttachmentRef(noteId, block.attachmentId)})`;\n }\n return block.alt ?? '';\n }\n return block.text;\n })\n .filter((text) => text.trim().length > 0)\n .join('\\n\\n');\n}\n\nfunction createAiOrganizedBlocks(blocks: NoteBlock[], instruction: string): NoteBlock[] {\n const now = Date.now();\n const plainText = blocksToPlainText(blocks) || '';\n const lines = plainText\n .split(/\\n+/)\n .map((line) => line.trim())\n .filter(Boolean);\n const wantsTodos = /待办|todo|task|行动|提醒/i.test(instruction);\n const wantsSummary = /摘要|总结|summary|压缩/i.test(instruction);\n\n if (wantsTodos) {\n const candidates = lines.filter((line) => /要|需|记得|todo|task|完成|提交|联系|跟进|提醒/i.test(line));\n return (candidates.length ? candidates : lines).slice(0, 12).map((line) => ({\n id: createBlockId(),\n type: 'todo' as const,\n text: line.replace(/^[-*\\d.\\s\\[\\]x]+/i, '').trim(),\n checked: false,\n createdAt: now,\n updatedAt: now,\n }));\n }\n\n if (wantsSummary) {\n const summary = lines.join(' ').slice(0, 220);\n return [{\n id: createBlockId(),\n type: 'paragraph',\n text: summary,\n createdAt: now,\n updatedAt: now,\n }];\n }\n\n const titleText = lines[0]?.slice(0, 40) || '整理后的笔记';\n const bodyLines = lines.slice(1).length ? lines.slice(1) : lines;\n return [\n {\n id: createBlockId(),\n type: 'heading',\n text: titleText,\n level: 2,\n createdAt: now,\n updatedAt: now,\n },\n ...bodyLines.map((line) => ({\n id: createBlockId(),\n type: 'bulletList' as const,\n text: line.replace(/^[-*\\d.\\s]+/, '').trim(),\n indent: 0,\n createdAt: now,\n updatedAt: now,\n })),\n ];\n}\n\nconst SNAPSHOT_THROTTLE_MS = 60_000;\nconst MAX_SNAPSHOTS_PER_NOTE = 30;\n\nexport class NotesService {\n private store: NotesStore;\n private lastSnapshotAt = new Map<string, number>();\n\n constructor(store: NotesStore) {\n this.store = store;\n }\n\n async initialize(): Promise<void> {\n await this.store.initialize();\n log.debug('NotesService initialized');\n }\n\n async quickCapture(text: string, source: CaptureSource): Promise<Note> {\n const now = Date.now();\n const id = randomUUID();\n const blocks = noteTextToBlocks(text, id);\n const note: Note = {\n id,\n kind: inferKind(text),\n status: 'inbox',\n text,\n blocks,\n createdAt: now,\n updatedAt: now,\n capturedVia: source,\n localVersion: 0,\n remoteVersion: 1,\n };\n await this.store.addNote(note);\n log.debug({ id: note.id, kind: note.kind }, 'Quick capture');\n return note;\n }\n\n async createNote(params: CreateNoteParams): Promise<Note> {\n const now = Date.now();\n const id = randomUUID();\n const blocks = params.blocks ?? noteTextToBlocks(params.text, id);\n const text = params.text ?? blocksToPlainText(blocks, id);\n const note: Note = {\n id,\n title: params.title,\n kind: params.kind || inferKind(text),\n status: 'inbox',\n text,\n blocks,\n createdAt: now,\n updatedAt: now,\n capturedVia: params.capturedVia,\n tags: params.tags,\n pinned: params.pinned,\n localVersion: 0,\n remoteVersion: 1,\n };\n await this.store.addNote(note);\n log.debug({ id: note.id, kind: note.kind }, 'Note created');\n return note;\n }\n\n async getNote(id: string): Promise<Note | null> {\n return this.store.getNote(id);\n }\n\n async updateNote(id: string, patch: Partial<Note>, trigger: SnapshotTrigger = 'edit'): Promise<Note | null> {\n const existing = await this.store.getNote(id);\n if (!existing) return null;\n\n const contentTouched = patch.text !== undefined || patch.blocks !== undefined || patch.title !== undefined;\n if (contentTouched) {\n await this.maybeSaveSnapshot(existing, trigger);\n }\n\n const normalizedPatch: Partial<Note> = { ...patch };\n if (patch.blocks) {\n normalizedPatch.text = patch.text ?? blocksToPlainText(patch.blocks, existing.id);\n } else if (typeof patch.text === 'string') {\n normalizedPatch.blocks = patch.blocks ?? noteTextToBlocks(patch.text, existing.id);\n }\n normalizedPatch.remoteVersion = (existing.remoteVersion ?? 0) + 1;\n\n if (contentTouched) {\n const merged: Note = {\n ...existing,\n ...normalizedPatch,\n id: existing.id,\n createdAt: existing.createdAt,\n updatedAt: Date.now(),\n };\n const reconciled = await this.reconcileAttachments(merged);\n normalizedPatch.attachments = reconciled.attachments;\n normalizedPatch.kind = reconciled.kind;\n }\n\n return this.store.updateNote(id, normalizedPatch);\n }\n\n private async reconcileAttachments(note: Note): Promise<Note> {\n const { kept, removed } = partitionAttachmentsByReference(note);\n if (removed.length === 0) return note;\n\n for (const attachment of removed) {\n await this.store.deleteAttachmentFile(note.id, attachment.relativePath);\n }\n\n log.debug(\n { noteId: note.id, removedIds: removed.map((attachment) => attachment.id) },\n 'Pruned orphan note attachments',\n );\n\n const hasAttachments = kept.length > 0;\n return {\n ...note,\n attachments: hasAttachments ? kept : undefined,\n kind: inferKind(note.text, hasAttachments, kept),\n };\n }\n\n async syncNote(\n id: string,\n patch: Partial<Note>,\n baseRemoteVersion?: number,\n ): Promise<{ note: Note | null; conflict: boolean }> {\n const existing = await this.store.getNote(id);\n if (!existing) return { note: null, conflict: false };\n\n const currentRemoteVersion = existing.remoteVersion ?? 0;\n if (baseRemoteVersion !== undefined && baseRemoteVersion < currentRemoteVersion) {\n return { note: existing, conflict: true };\n }\n\n const updated = await this.updateNote(id, patch, 'sync');\n return { note: updated, conflict: false };\n }\n\n async createAiEditPatch(\n id: string,\n instruction: string,\n blocks?: NoteBlock[],\n ): Promise<{ message: string; patch: NoteAiPatch } | null> {\n const note = await this.store.getNote(id);\n if (!note) return null;\n\n const sourceBlocks = blocks?.length ? blocks : note.blocks ?? noteTextToBlocks(note.text, note.id) ?? [];\n const organizedBlocks = createAiOrganizedBlocks(sourceBlocks, instruction);\n const patch: NoteAiPatch = {\n id: randomUUID(),\n summary: `已根据「${instruction.slice(0, 40)}」生成可预览的块级整理建议`,\n operations: [{ type: 'replaceBlocks', blocks: organizedBlocks }],\n };\n\n return {\n message: 'AI edit patch generated',\n patch,\n };\n }\n\n async deleteNote(id: string): Promise<boolean> {\n const deleted = await this.store.deleteNote(id);\n if (deleted) {\n await this.store.deleteAllSnapshots(id);\n this.lastSnapshotAt.delete(id);\n }\n return deleted;\n }\n\n async listNotes(query: NotesListQuery = {}): Promise<{ items: NoteIndexEntry[]; total: number }> {\n return this.store.listNotes(query);\n }\n\n async addAttachment(\n noteId: string,\n file: { name: string; buffer: Buffer; mimeType: string; duration?: number },\n ): Promise<NoteAttachment | null> {\n const note = await this.store.getNote(noteId);\n if (!note) return null;\n\n const { relativePath, size } = await this.store.saveAttachment(noteId, file.name, file.buffer);\n\n const attachment: NoteAttachment = {\n id: randomUUID(),\n type: inferAttachmentType(file.mimeType),\n mimeType: file.mimeType,\n fileName: file.name,\n size,\n relativePath,\n duration: file.duration,\n };\n\n const attachments = [...(note.attachments || []), attachment];\n const kind: NoteKind =\n note.kind === 'thought' && attachment.type === 'audio'\n ? 'voice'\n : note.kind === 'thought'\n ? 'media'\n : note.kind;\n await this.store.updateNote(noteId, { attachments, kind });\n\n return attachment;\n }\n\n async getAttachmentPath(\n noteId: string,\n attachmentId: string,\n ): Promise<{ filePath: string; mimeType: string; fileName: string } | null> {\n const note = await this.store.getNote(noteId);\n if (!note) return null;\n\n const attachment = note.attachments?.find((a) => a.id === attachmentId);\n if (!attachment) return null;\n\n const fullPath = this.store.resolveAttachmentPath(noteId, attachment.relativePath);\n return { filePath: fullPath, mimeType: attachment.mimeType, fileName: attachment.fileName };\n }\n\n async listNoteHistory(noteId: string): Promise<NoteSnapshotEntry[]> {\n return this.store.listSnapshots(noteId);\n }\n\n async getNoteSnapshot(noteId: string, timestamp: number): Promise<NoteSnapshot | null> {\n return this.store.getSnapshot(noteId, timestamp);\n }\n\n async restoreNoteSnapshot(noteId: string, timestamp: number): Promise<Note | null> {\n const snapshot = await this.store.getSnapshot(noteId, timestamp);\n if (!snapshot) return null;\n const existing = await this.store.getNote(noteId);\n if (!existing) return null;\n\n await this.store.saveSnapshot(existing, 'restore');\n this.lastSnapshotAt.set(noteId, Date.now());\n await this.store.pruneSnapshots(noteId, MAX_SNAPSHOTS_PER_NOTE);\n\n return this.store.updateNote(noteId, {\n title: snapshot.title,\n text: snapshot.text,\n blocks: snapshot.blocks,\n tags: snapshot.tags,\n });\n }\n\n async flush(): Promise<void> {\n await this.store.flush();\n }\n\n private async maybeSaveSnapshot(note: Note, trigger: SnapshotTrigger): Promise<void> {\n if (trigger !== 'edit') {\n await this.store.saveSnapshot(note, trigger);\n this.lastSnapshotAt.set(note.id, Date.now());\n await this.store.pruneSnapshots(note.id, MAX_SNAPSHOTS_PER_NOTE);\n return;\n }\n const last = this.lastSnapshotAt.get(note.id) ?? 0;\n if (Date.now() - last < SNAPSHOT_THROTTLE_MS) return;\n await this.store.saveSnapshot(note, trigger);\n this.lastSnapshotAt.set(note.id, Date.now());\n await this.store.pruneSnapshots(note.id, MAX_SNAPSHOTS_PER_NOTE);\n }\n}\n\nfunction inferAttachmentType(mimeType: string): NoteAttachment['type'] {\n if (mimeType.startsWith('image/')) return 'image';\n if (mimeType.startsWith('video/')) return 'video';\n if (mimeType.startsWith('audio/')) return 'audio';\n return 'file';\n}\n"],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { Note, NoteIndexEntry, NoteSnapshot, NoteSnapshotEntry, NotesListQuery, SnapshotTrigger } from './types.js';
2
+ export declare class NotesStore {
3
+ private indexCache;
4
+ private dirty;
5
+ private saveTimeout;
6
+ private initialized;
7
+ initialize(): Promise<void>;
8
+ addNote(note: Note): Promise<void>;
9
+ getNote(id: string): Promise<Note | null>;
10
+ updateNote(id: string, patch: Partial<Note>): Promise<Note | null>;
11
+ deleteNote(id: string): Promise<boolean>;
12
+ listNotes(query?: NotesListQuery): Promise<{
13
+ items: NoteIndexEntry[];
14
+ total: number;
15
+ }>;
16
+ saveAttachment(noteId: string, fileName: string, buffer: Buffer): Promise<{
17
+ relativePath: string;
18
+ size: number;
19
+ }>;
20
+ resolveAttachmentPath(noteId: string, relativePath: string): string;
21
+ deleteAttachmentFile(noteId: string, relativePath: string): Promise<void>;
22
+ saveSnapshot(note: Note, trigger: SnapshotTrigger): Promise<void>;
23
+ listSnapshots(noteId: string): Promise<NoteSnapshotEntry[]>;
24
+ getSnapshot(noteId: string, timestamp: number): Promise<NoteSnapshot | null>;
25
+ pruneSnapshots(noteId: string, maxCount: number): Promise<void>;
26
+ deleteAllSnapshots(noteId: string): Promise<void>;
27
+ flush(): Promise<void>;
28
+ private loadIndex;
29
+ private writeIndex;
30
+ private writeNoteItem;
31
+ private scheduleIndexSave;
32
+ private rebuildIndexFromItems;
33
+ }