@weavecode/weave 0.1.0 → 0.1.2

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 (300) hide show
  1. package/node_modules/@weavecode/cli/dist/cli.js +3 -3
  2. package/node_modules/@weavecode/cli/dist/commands/daemon.d.ts +7 -0
  3. package/node_modules/@weavecode/cli/dist/commands/daemon.js +2 -2
  4. package/node_modules/@weavecode/cli/dist/commands/dashboard.d.ts +0 -2
  5. package/node_modules/@weavecode/cli/dist/commands/dashboard.js +2 -1
  6. package/node_modules/@weavecode/cli/dist/commands/doctor.js +1 -1
  7. package/node_modules/@weavecode/cli/dist/commands/init.js +1 -1
  8. package/node_modules/@weavecode/cli/dist/commands/msg.js +2 -2
  9. package/node_modules/@weavecode/cli/dist/commands/scan.js +1 -1
  10. package/node_modules/@weavecode/cli/dist/commands/status.js +2 -2
  11. package/node_modules/@weavecode/cli/dist/i18n/dict/en.js +6 -12
  12. package/node_modules/@weavecode/cli/dist/i18n/dict/zh.d.ts +44 -2
  13. package/node_modules/@weavecode/cli/dist/i18n/dict/zh.js +6 -12
  14. package/node_modules/@weavecode/cli/dist/init/claudemd-gen.d.ts +2 -3
  15. package/node_modules/@weavecode/cli/dist/init/claudemd-gen.js +4 -4
  16. package/node_modules/@weavecode/cli/dist/init/executor.js +3 -3
  17. package/node_modules/@weavecode/cli/dist/init/git-commit.js +1 -1
  18. package/node_modules/@weavecode/cli/dist/init/harnesses/claude.js +7 -7
  19. package/node_modules/@weavecode/cli/dist/init/harnesses/zcode.d.ts +29 -0
  20. package/node_modules/@weavecode/cli/dist/init/harnesses/zcode.js +1 -0
  21. package/node_modules/@weavecode/cli/dist/init/helpers-gen.d.ts +4 -0
  22. package/node_modules/@weavecode/cli/dist/init/helpers-gen.js +1 -20
  23. package/node_modules/@weavecode/cli/dist/init/mcp-gen.d.ts +3 -20
  24. package/node_modules/@weavecode/cli/dist/init/mcp-gen.js +1 -1
  25. package/node_modules/@weavecode/cli/dist/init/prompt.js +4 -4
  26. package/node_modules/@weavecode/cli/dist/init/settings-gen.d.ts +3 -30
  27. package/node_modules/@weavecode/cli/dist/init/settings-gen.js +1 -1
  28. package/node_modules/@weavecode/cli/package.json +5 -5
  29. package/node_modules/@weavecode/core/dist/harness.d.ts +45 -2
  30. package/node_modules/@weavecode/core/dist/harness.js +1 -1
  31. package/node_modules/@weavecode/core/dist/index.d.ts +2 -2
  32. package/node_modules/@weavecode/core/dist/index.js +1 -1
  33. package/node_modules/@weavecode/core/dist/terminal.d.ts +28 -4
  34. package/node_modules/@weavecode/core/dist/terminal.js +1 -1
  35. package/node_modules/@weavecode/core/dist/types.d.ts +188 -19
  36. package/node_modules/@weavecode/core/dist/types.js +1 -1
  37. package/node_modules/@weavecode/core/package.json +1 -1
  38. package/node_modules/@weavecode/harness/dist/claude/assets.d.ts +10 -0
  39. package/node_modules/@weavecode/harness/dist/claude/assets.js +4 -1
  40. package/node_modules/@weavecode/harness/dist/claude/capture.d.ts +15 -3
  41. package/node_modules/@weavecode/harness/dist/claude/capture.js +15 -9
  42. package/node_modules/@weavecode/harness/dist/claude/event-source.d.ts +1 -1
  43. package/node_modules/@weavecode/harness/dist/claude/event-source.js +1 -1
  44. package/node_modules/@weavecode/harness/dist/claude/helpers-gen.d.ts +5 -0
  45. package/node_modules/@weavecode/harness/dist/claude/helpers-gen.js +20 -0
  46. package/node_modules/@weavecode/harness/dist/claude/index.d.ts +10 -1
  47. package/node_modules/@weavecode/harness/dist/claude/index.js +1 -1
  48. package/node_modules/@weavecode/harness/dist/claude/mcp-gen.d.ts +22 -0
  49. package/node_modules/@weavecode/harness/dist/claude/mcp-gen.js +1 -0
  50. package/node_modules/@weavecode/harness/dist/claude/messaging.d.ts +11 -2
  51. package/node_modules/@weavecode/harness/dist/claude/messaging.js +4 -1
  52. package/node_modules/@weavecode/harness/dist/claude/model-aliases.d.ts +39 -0
  53. package/node_modules/@weavecode/harness/dist/claude/model-aliases.js +1 -0
  54. package/node_modules/@weavecode/harness/dist/claude/plan.d.ts +2 -0
  55. package/node_modules/@weavecode/harness/dist/claude/plan.js +1 -0
  56. package/node_modules/@weavecode/harness/dist/claude/questions.js +2 -1
  57. package/node_modules/@weavecode/harness/dist/claude/settings-gen.d.ts +32 -0
  58. package/node_modules/@weavecode/harness/dist/claude/settings-gen.js +1 -0
  59. package/node_modules/@weavecode/harness/dist/claude/side-question.d.ts +12 -0
  60. package/node_modules/@weavecode/harness/dist/claude/side-question.js +1 -0
  61. package/node_modules/@weavecode/harness/dist/claude/statusline/config.d.ts +200 -0
  62. package/node_modules/@weavecode/harness/dist/claude/statusline/config.js +1 -0
  63. package/node_modules/@weavecode/harness/dist/claude/statusline/generator.d.ts +12 -0
  64. package/node_modules/@weavecode/harness/dist/claude/statusline/generator.js +745 -0
  65. package/node_modules/@weavecode/harness/dist/claude/statusline/git-cache.d.ts +10 -0
  66. package/node_modules/@weavecode/harness/dist/claude/statusline/git-cache.js +1 -0
  67. package/node_modules/@weavecode/harness/dist/claude/statusline/surface.d.ts +20 -0
  68. package/node_modules/@weavecode/harness/dist/claude/statusline/surface.js +2 -0
  69. package/node_modules/@weavecode/harness/dist/claude/transcript.d.ts +16 -0
  70. package/node_modules/@weavecode/harness/dist/claude/transcript.js +4 -2
  71. package/node_modules/@weavecode/harness/dist/codex/assets.js +1 -1
  72. package/node_modules/@weavecode/harness/dist/codex/index.d.ts +1 -0
  73. package/node_modules/@weavecode/harness/dist/codex/index.js +1 -1
  74. package/node_modules/@weavecode/harness/dist/codex/mcp.d.ts +30 -0
  75. package/node_modules/@weavecode/harness/dist/codex/mcp.js +1 -0
  76. package/node_modules/@weavecode/harness/dist/codex/messaging.js +1 -1
  77. package/node_modules/@weavecode/harness/dist/driver.d.ts +168 -3
  78. package/node_modules/@weavecode/harness/dist/index.d.ts +1 -0
  79. package/node_modules/@weavecode/harness/dist/index.js +1 -1
  80. package/node_modules/@weavecode/harness/dist/init/generated.d.ts +37 -0
  81. package/node_modules/@weavecode/harness/dist/init/generated.js +2 -0
  82. package/node_modules/@weavecode/harness/dist/registry-wiring.d.ts +3 -1
  83. package/node_modules/@weavecode/harness/dist/zcode/assets.d.ts +8 -0
  84. package/node_modules/@weavecode/harness/dist/zcode/assets.js +1 -0
  85. package/node_modules/@weavecode/harness/dist/zcode/capture.d.ts +36 -0
  86. package/node_modules/@weavecode/harness/dist/zcode/capture.js +3 -0
  87. package/node_modules/@weavecode/harness/dist/zcode/detect.d.ts +2 -0
  88. package/node_modules/@weavecode/harness/dist/zcode/detect.js +1 -0
  89. package/node_modules/@weavecode/harness/dist/zcode/event-source.d.ts +8 -0
  90. package/node_modules/@weavecode/harness/dist/zcode/event-source.js +1 -0
  91. package/node_modules/@weavecode/harness/dist/zcode/index.d.ts +13 -0
  92. package/node_modules/@weavecode/harness/dist/zcode/index.js +1 -0
  93. package/node_modules/@weavecode/harness/dist/zcode/mcp.d.ts +6 -0
  94. package/node_modules/@weavecode/harness/dist/zcode/mcp.js +2 -0
  95. package/node_modules/@weavecode/harness/dist/zcode/messaging.d.ts +13 -0
  96. package/node_modules/@weavecode/harness/dist/zcode/messaging.js +1 -0
  97. package/node_modules/@weavecode/harness/dist/zcode/resume.d.ts +7 -0
  98. package/node_modules/@weavecode/harness/dist/zcode/resume.js +1 -0
  99. package/node_modules/@weavecode/harness/package.json +2 -2
  100. package/node_modules/@weavecode/server/dist/daemon/ai-config.js +2 -2
  101. package/node_modules/@weavecode/server/dist/daemon/ai-context.js +6 -6
  102. package/node_modules/@weavecode/server/dist/daemon/ai-routes.js +4 -4
  103. package/node_modules/@weavecode/server/dist/daemon/api-log.d.ts +48 -0
  104. package/node_modules/@weavecode/server/dist/daemon/api-log.js +3 -0
  105. package/node_modules/@weavecode/server/dist/daemon/auth-routes.js +1 -1
  106. package/node_modules/@weavecode/server/dist/daemon/auto-start.js +6 -6
  107. package/node_modules/@weavecode/server/dist/daemon/capture-routes.js +1 -1
  108. package/node_modules/@weavecode/server/dist/daemon/claude-liveness.js +1 -1
  109. package/node_modules/@weavecode/server/dist/daemon/dashboard-auth.js +2 -2
  110. package/node_modules/@weavecode/server/dist/daemon/data-bus.js +1 -1
  111. package/node_modules/@weavecode/server/dist/daemon/dev-entry.js +1 -1
  112. package/node_modules/@weavecode/server/dist/daemon/fs-browser.js +1 -1
  113. package/node_modules/@weavecode/server/dist/daemon/hook-pipeline.d.ts +13 -1
  114. package/node_modules/@weavecode/server/dist/daemon/hook-pipeline.js +1 -1
  115. package/node_modules/@weavecode/server/dist/daemon/hooks-routes.js +1 -1
  116. package/node_modules/@weavecode/server/dist/daemon/http.js +1 -1
  117. package/node_modules/@weavecode/server/dist/daemon/opener.d.ts +102 -19
  118. package/node_modules/@weavecode/server/dist/daemon/opener.js +97 -7
  119. package/node_modules/@weavecode/server/dist/daemon/port.js +1 -1
  120. package/node_modules/@weavecode/server/dist/daemon/project-harnesses.js +2 -2
  121. package/node_modules/@weavecode/server/dist/daemon/project-updates-refresher.d.ts +4 -3
  122. package/node_modules/@weavecode/server/dist/daemon/project-updates-refresher.js +1 -1
  123. package/node_modules/@weavecode/server/dist/daemon/server.d.ts +15 -0
  124. package/node_modules/@weavecode/server/dist/daemon/server.js +8 -6
  125. package/node_modules/@weavecode/server/dist/daemon/session-probe.d.ts +72 -2
  126. package/node_modules/@weavecode/server/dist/daemon/session-probe.js +1 -1
  127. package/node_modules/@weavecode/server/dist/daemon/settings.d.ts +10 -0
  128. package/node_modules/@weavecode/server/dist/daemon/settings.js +2 -2
  129. package/node_modules/@weavecode/server/dist/daemon/snapshot-refresher.d.ts +7 -1
  130. package/node_modules/@weavecode/server/dist/daemon/snapshot-refresher.js +1 -1
  131. package/node_modules/@weavecode/server/dist/daemon/sse-hub.d.ts +40 -0
  132. package/node_modules/@weavecode/server/dist/daemon/sse-hub.js +1 -0
  133. package/node_modules/@weavecode/server/dist/daemon/start.js +1 -1
  134. package/node_modules/@weavecode/server/dist/daemon/state.js +2 -2
  135. package/node_modules/@weavecode/server/dist/daemon/static.js +1 -1
  136. package/node_modules/@weavecode/server/dist/daemon/terminal-topology-refresher.d.ts +4 -1
  137. package/node_modules/@weavecode/server/dist/daemon/tls.js +1 -1
  138. package/node_modules/@weavecode/server/dist/daemon/toolkit-routes.js +1 -1
  139. package/node_modules/@weavecode/server/dist/db/db.js +38 -2
  140. package/node_modules/@weavecode/server/dist/db/paths.d.ts +4 -0
  141. package/node_modules/@weavecode/server/dist/db/paths.js +1 -1
  142. package/node_modules/@weavecode/server/dist/hooks/answer-keys.js +1 -1
  143. package/node_modules/@weavecode/server/dist/hooks/bus.d.ts +5 -0
  144. package/node_modules/@weavecode/server/dist/hooks/bus.js +1 -1
  145. package/node_modules/@weavecode/server/dist/hooks/dispatcher.js +6 -6
  146. package/node_modules/@weavecode/server/dist/hooks/handlers/webhook.js +1 -1
  147. package/node_modules/@weavecode/server/dist/hooks/headless-runner.js +2 -2
  148. package/node_modules/@weavecode/server/dist/hooks/inbox-socket.d.ts +17 -4
  149. package/node_modules/@weavecode/server/dist/hooks/inbox-socket.js +2 -1
  150. package/node_modules/@weavecode/server/dist/hooks/inbox-worker.d.ts +27 -6
  151. package/node_modules/@weavecode/server/dist/hooks/inbox-worker.js +3 -3
  152. package/node_modules/@weavecode/server/dist/hooks/match.js +1 -1
  153. package/node_modules/@weavecode/server/dist/hooks/message-deliverer.d.ts +200 -13
  154. package/node_modules/@weavecode/server/dist/hooks/message-deliverer.js +3 -2
  155. package/node_modules/@weavecode/server/dist/hooks/mirror.d.ts +9 -4
  156. package/node_modules/@weavecode/server/dist/hooks/mirror.js +4 -4
  157. package/node_modules/@weavecode/server/dist/hooks/plugin-installer.js +3 -3
  158. package/node_modules/@weavecode/server/dist/hooks/plugin-loader.js +1 -1
  159. package/node_modules/@weavecode/server/dist/hooks/plugin-project-state.js +1 -1
  160. package/node_modules/@weavecode/server/dist/hooks/prompt-flusher.js +1 -1
  161. package/node_modules/@weavecode/server/dist/hooks/promptbox.d.ts +13 -0
  162. package/node_modules/@weavecode/server/dist/hooks/promptbox.js +8 -8
  163. package/node_modules/@weavecode/server/dist/hooks/push-notify.js +2 -2
  164. package/node_modules/@weavecode/server/dist/hooks/question-tracker.d.ts +6 -4
  165. package/node_modules/@weavecode/server/dist/hooks/question-tracker.js +1 -1
  166. package/node_modules/@weavecode/server/dist/hooks/registry.js +1 -1
  167. package/node_modules/@weavecode/server/dist/hooks/resume-runner.js +1 -1
  168. package/node_modules/@weavecode/server/dist/hooks/sequence-engine.js +1 -1
  169. package/node_modules/@weavecode/server/dist/hooks/session-purge.d.ts +7 -0
  170. package/node_modules/@weavecode/server/dist/hooks/session-purge.js +1 -1
  171. package/node_modules/@weavecode/server/dist/hooks/session-sweeper.d.ts +5 -8
  172. package/node_modules/@weavecode/server/dist/hooks/session-sweeper.js +1 -1
  173. package/node_modules/@weavecode/server/dist/hooks/side-questions.d.ts +41 -0
  174. package/node_modules/@weavecode/server/dist/hooks/side-questions.js +1 -0
  175. package/node_modules/@weavecode/server/dist/hooks/toolkit-scan.js +1 -1
  176. package/node_modules/@weavecode/server/dist/hooks/toolkit.d.ts +8 -0
  177. package/node_modules/@weavecode/server/dist/hooks/toolkit.js +5 -5
  178. package/node_modules/@weavecode/server/dist/hooks/watch-engine.d.ts +7 -0
  179. package/node_modules/@weavecode/server/dist/hooks/watch-engine.js +1 -1
  180. package/node_modules/@weavecode/server/dist/index.d.ts +1 -1
  181. package/node_modules/@weavecode/server/dist/install/apply-update.d.ts +1 -1
  182. package/node_modules/@weavecode/server/dist/install/apply-update.js +1 -1
  183. package/node_modules/@weavecode/server/dist/install/capabilities.d.ts +10 -0
  184. package/node_modules/@weavecode/server/dist/install/capabilities.js +1 -1
  185. package/node_modules/@weavecode/server/dist/install/codex-toml.d.ts +8 -26
  186. package/node_modules/@weavecode/server/dist/install/codex-toml.js +1 -1
  187. package/node_modules/@weavecode/server/dist/install/file-assets.js +1 -1
  188. package/node_modules/@weavecode/server/dist/install/fingerprint.d.ts +5 -1
  189. package/node_modules/@weavecode/server/dist/install/fingerprint.js +1 -1
  190. package/node_modules/@weavecode/server/dist/install/git-classify.d.ts +30 -0
  191. package/node_modules/@weavecode/server/dist/install/git-classify.js +1 -0
  192. package/node_modules/@weavecode/server/dist/install/global-config.d.ts +2 -4
  193. package/node_modules/@weavecode/server/dist/install/global-config.js +1 -2
  194. package/node_modules/@weavecode/server/dist/install/global-file-assets.js +1 -1
  195. package/node_modules/@weavecode/server/dist/install/init-files.d.ts +46 -0
  196. package/node_modules/@weavecode/server/dist/install/init-files.js +2 -0
  197. package/node_modules/@weavecode/server/dist/install/install-ledger.js +1 -1
  198. package/node_modules/@weavecode/server/dist/install/installer.d.ts +3 -5
  199. package/node_modules/@weavecode/server/dist/install/installer.js +1 -3
  200. package/node_modules/@weavecode/server/dist/install/library-index.js +1 -1
  201. package/node_modules/@weavecode/server/dist/install/mcp-rows.d.ts +5 -5
  202. package/node_modules/@weavecode/server/dist/install/mcp-rows.js +1 -1
  203. package/node_modules/@weavecode/server/dist/install/plugin-install.js +1 -1
  204. package/node_modules/@weavecode/server/dist/install/plugin-rows.js +1 -1
  205. package/node_modules/@weavecode/server/dist/install/plugin-templates.js +1 -1
  206. package/node_modules/@weavecode/server/dist/install/project-config.d.ts +0 -2
  207. package/node_modules/@weavecode/server/dist/install/project-config.js +2 -2
  208. package/node_modules/@weavecode/server/dist/install/reader.js +1 -1
  209. package/node_modules/@weavecode/server/dist/install/scan-cache.js +1 -1
  210. package/node_modules/@weavecode/server/dist/install/scanner.js +1 -1
  211. package/node_modules/@weavecode/server/dist/install/settings-mcp.js +2 -2
  212. package/node_modules/@weavecode/server/dist/install/updates.d.ts +17 -5
  213. package/node_modules/@weavecode/server/dist/install/updates.js +1 -1
  214. package/node_modules/@weavecode/server/dist/market/builtin.js +1 -1
  215. package/node_modules/@weavecode/server/dist/market/identity.js +1 -1
  216. package/node_modules/@weavecode/server/dist/market/import.js +2 -2
  217. package/node_modules/@weavecode/server/dist/market/refresh.js +1 -1
  218. package/node_modules/@weavecode/server/dist/market/resolve.js +1 -1
  219. package/node_modules/@weavecode/server/dist/market/updates.js +1 -1
  220. package/node_modules/@weavecode/server/dist/registry.js +2 -2
  221. package/node_modules/@weavecode/server/dist/repositories/hook-dispatches.js +5 -5
  222. package/node_modules/@weavecode/server/dist/repositories/hook-events.d.ts +7 -0
  223. package/node_modules/@weavecode/server/dist/repositories/hook-events.js +2 -2
  224. package/node_modules/@weavecode/server/dist/repositories/hook-rules.js +4 -4
  225. package/node_modules/@weavecode/server/dist/repositories/libraries.js +1 -1
  226. package/node_modules/@weavecode/server/dist/repositories/plugin-states.js +1 -1
  227. package/node_modules/@weavecode/server/dist/repositories/projects.d.ts +7 -0
  228. package/node_modules/@weavecode/server/dist/repositories/projects.js +2 -2
  229. package/node_modules/@weavecode/server/dist/repositories/push-subscriptions.js +2 -2
  230. package/node_modules/@weavecode/server/dist/repositories/session-watches.js +4 -4
  231. package/node_modules/@weavecode/server/dist/repositories/sessions.d.ts +32 -8
  232. package/node_modules/@weavecode/server/dist/repositories/sessions.js +2 -2
  233. package/node_modules/@weavecode/server/dist/repositories/side-questions.d.ts +50 -0
  234. package/node_modules/@weavecode/server/dist/repositories/side-questions.js +6 -0
  235. package/node_modules/@weavecode/server/dist/repositories/uploads.d.ts +66 -0
  236. package/node_modules/@weavecode/server/dist/repositories/uploads.js +7 -0
  237. package/node_modules/@weavecode/server/dist/statusline/cache-sweeper.js +1 -1
  238. package/node_modules/@weavecode/server/dist/statusline/config.d.ts +4 -198
  239. package/node_modules/@weavecode/server/dist/statusline/config.js +1 -1
  240. package/node_modules/@weavecode/server/dist/statusline/generator.d.ts +5 -0
  241. package/node_modules/@weavecode/server/dist/statusline/generator.js +1 -676
  242. package/node_modules/@weavecode/server/dist/statusline/manager.d.ts +7 -5
  243. package/node_modules/@weavecode/server/dist/statusline/manager.js +3 -4
  244. package/node_modules/@weavecode/server/dist/surfaces.d.ts +11 -1
  245. package/node_modules/@weavecode/server/dist/surfaces.js +1 -1
  246. package/node_modules/@weavecode/server/dist/watch/cache.js +1 -1
  247. package/node_modules/@weavecode/server/dist/watch/classifier.js +1 -1
  248. package/node_modules/@weavecode/server/dist/watch/plugin-mods.js +1 -1
  249. package/node_modules/@weavecode/server/dist/watch/sync-scheduler.d.ts +8 -4
  250. package/node_modules/@weavecode/server/dist/watch/sync-scheduler.js +1 -1
  251. package/node_modules/@weavecode/server/dist/watch/watch-service.js +1 -1
  252. package/node_modules/@weavecode/server/package.json +4 -4
  253. package/node_modules/@weavecode/server/web/dist/assets/index-CHWbLAje.js +594 -0
  254. package/node_modules/@weavecode/server/web/dist/assets/index-D0s5v8QS.css +11 -0
  255. package/node_modules/@weavecode/server/web/dist/index.html +14 -2
  256. package/node_modules/@weavecode/templates/dist/resources/weave-market/plugins/core-skills/weave-plugin-creator/skills/weave-plugin-creator/SKILL.md +2 -2
  257. package/node_modules/@weavecode/templates/package.json +2 -2
  258. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-cms/node_modules/asn1js/README.md +50 -32
  259. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-cms/node_modules/asn1js/build/index.d.ts +86 -40
  260. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-cms/node_modules/asn1js/build/index.es.js +860 -353
  261. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-cms/node_modules/asn1js/build/index.js +860 -353
  262. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-cms/node_modules/asn1js/package.json +20 -17
  263. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-csr/node_modules/asn1js/README.md +50 -32
  264. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-csr/node_modules/asn1js/build/index.d.ts +86 -40
  265. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-csr/node_modules/asn1js/build/index.es.js +860 -353
  266. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-csr/node_modules/asn1js/build/index.js +860 -353
  267. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-csr/node_modules/asn1js/package.json +20 -17
  268. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-ecc/node_modules/asn1js/README.md +50 -32
  269. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-ecc/node_modules/asn1js/build/index.d.ts +86 -40
  270. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-ecc/node_modules/asn1js/build/index.es.js +860 -353
  271. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-ecc/node_modules/asn1js/build/index.js +860 -353
  272. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-ecc/node_modules/asn1js/package.json +20 -17
  273. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-pkcs9/node_modules/asn1js/README.md +50 -32
  274. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-pkcs9/node_modules/asn1js/build/index.d.ts +86 -40
  275. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-pkcs9/node_modules/asn1js/build/index.es.js +860 -353
  276. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-pkcs9/node_modules/asn1js/build/index.js +860 -353
  277. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-pkcs9/node_modules/asn1js/package.json +20 -17
  278. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-rsa/node_modules/asn1js/README.md +50 -32
  279. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-rsa/node_modules/asn1js/build/index.d.ts +86 -40
  280. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-rsa/node_modules/asn1js/build/index.es.js +860 -353
  281. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-rsa/node_modules/asn1js/build/index.js +860 -353
  282. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-rsa/node_modules/asn1js/package.json +20 -17
  283. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-schema/node_modules/asn1js/README.md +50 -32
  284. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-schema/node_modules/asn1js/build/index.d.ts +86 -40
  285. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-schema/node_modules/asn1js/build/index.es.js +860 -353
  286. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-schema/node_modules/asn1js/build/index.js +860 -353
  287. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-schema/node_modules/asn1js/package.json +20 -17
  288. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-x509/node_modules/asn1js/README.md +50 -32
  289. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-x509/node_modules/asn1js/build/index.d.ts +86 -40
  290. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-x509/node_modules/asn1js/build/index.es.js +860 -353
  291. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-x509/node_modules/asn1js/build/index.js +860 -353
  292. package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-x509/node_modules/asn1js/package.json +20 -17
  293. package/node_modules/selfsigned/node_modules/pkijs/node_modules/asn1js/README.md +50 -32
  294. package/node_modules/selfsigned/node_modules/pkijs/node_modules/asn1js/build/index.d.ts +86 -40
  295. package/node_modules/selfsigned/node_modules/pkijs/node_modules/asn1js/build/index.es.js +860 -353
  296. package/node_modules/selfsigned/node_modules/pkijs/node_modules/asn1js/build/index.js +860 -353
  297. package/node_modules/selfsigned/node_modules/pkijs/node_modules/asn1js/package.json +20 -17
  298. package/package.json +6 -6
  299. package/node_modules/@weavecode/server/web/dist/assets/index-BOzaRlQE.js +0 -560
  300. package/node_modules/@weavecode/server/web/dist/assets/index-ChX8G8cT.css +0 -11
@@ -4,6 +4,7 @@ import { type LibraryIndex } from './library-index.js';
4
4
  import { readGlobalSkills } from './global-config.js';
5
5
  import type { FileAssetCategory } from './file-assets.js';
6
6
  import { type PluginTemplateUpdate } from './plugin-templates.js';
7
+ import { type InitFileUpdate } from './init-files.js';
7
8
  import type { LibraryRow } from '../repositories/libraries.js';
8
9
  /**
9
10
  * Update detection — compares installed assets against their library sources.
@@ -65,8 +66,12 @@ export interface UpdateReport {
65
66
  files: FileAssetUpdate[];
66
67
  /** 项目自有 weave 源(`.weave/plugins` 的 manifest.templates)的物化漂移。 */
67
68
  pluginTemplates: PluginTemplateUpdate[];
68
- /** Count of outdated, non-custom assets (plugin templates included). */
69
+ /** init 生成物的漂移(不进 git 的可自愈;会进 git 的只报不写)。 */
70
+ initFiles: InitFileUpdate[];
71
+ /** Count of outdated assets sync can actually apply(含 auto 归类的 init 文件)。 */
69
72
  available: number;
73
+ /** 会进 git 的 init 文件有漂移 → dashboard 提醒用户跑 `weave init`。 */
74
+ needsInit: boolean;
70
75
  }
71
76
  /**
72
77
  * Detect skill + MCP + file-asset updates for a project against the registered
@@ -74,15 +79,22 @@ export interface UpdateReport {
74
79
  * rebuild it.
75
80
  */
76
81
  export declare function detectUpdates(projectPath: string, libraries: LibraryRow[], cache: FingerprintCache, index?: LibraryIndex): Promise<UpdateReport>;
82
+ /** 项目行推送面的一行摘要:角标条数 + 「待 init」提醒(同一次检测的两个面)。 */
83
+ export interface ProjectUpdateSummary {
84
+ /** 可代写同步的过期项数(含 auto 归类的 init 文件)。 */
85
+ available: number;
86
+ /** 会进 git 的 init 文件有漂移 → 提醒用户跑 `weave init`。 */
87
+ needsInit: boolean;
88
+ }
77
89
  /**
78
- * 全项目「有更新待同步」计数(projectId → available,含 0):项目行角标推送面
79
- * 的数据源。与 `GET /projects/:id/updates` 同一检测函数(同源不双算),库索引
80
- * 一轮构建一次、整轮复用。
90
+ * 全项目更新摘要(projectId → 摘要,含 0/false):项目行角标推送面的数据源。
91
+ * 与 `GET /projects/:id/updates` 同一检测函数(同源不双算),库索引一轮构建
92
+ * 一次、整轮复用。
81
93
  */
82
94
  export declare function detectProjectUpdateCounts(projects: ReadonlyArray<{
83
95
  id: number;
84
96
  path: string;
85
- }>, libraries: LibraryRow[], cache: FingerprintCache): Promise<Record<number, number>>;
97
+ }>, libraries: LibraryRow[], cache: FingerprintCache): Promise<Record<number, ProjectUpdateSummary>>;
86
98
  /** Detect skill updates (project + user, Claude + Codex) against the index. */
87
99
  export declare function detectSkillUpdates(projectPath: string, cache: FingerprintCache, idx: LibraryIndex): Promise<SkillUpdate[]>;
88
100
  /** Detect single-file asset updates (commands/agents/workflows/rules/output-styles). */
@@ -1 +1 @@
1
- import{readdir as g,stat as I}from"node:fs/promises";import{homedir as k}from"node:os";import u from"node:path";import{hashMcpConfig as l}from"./fingerprint.js";import{buildLibraryIndex as h,findSkillSource as y,findMcpSource as f,findFileAssetSource as b}from"./library-index.js";import{readMcpServers as x}from"./installer.js";import{readGlobalMcpServers as F,readGlobalSkills as P}from"./global-config.js";import{readCodexMcpServers as E}from"./codex-toml.js";import{FILE_ASSET_CATEGORIES as T,FILE_ASSET_SPECS as v}from"./file-assets.js";import{assetSurfaces as j}from"../surfaces.js";import{readInstalledLedger as C}from"./install-ledger.js";import{detectPluginTemplateUpdates as A}from"./plugin-templates.js";export async function detectUpdates(r,a,i,o){const s=o??await h(a),t=await detectSkillUpdates(r,i,s),e=await U(r,s),n=await detectFileAssetUpdates(r,i,s);await L(r,{skills:t,mcp:e,files:n},s);const c=await A(r,i),p=t.filter(m=>!m.custom&&m.outdated).length+e.filter(m=>!m.custom&&m.outdated).length+n.filter(m=>!m.custom&&m.outdated).length+c.filter(m=>m.outdated).length;return{skills:t,mcp:e,files:n,pluginTemplates:c,available:p}}export async function detectProjectUpdateCounts(r,a,i){const o=await h(a),s={};for(const t of r){const e=await detectUpdates(t.path,a,i,o);s[t.id]=e.available}return s}async function L(r,a,i){for(const o of await C(r)){if(o.category==="skill"){if(!y(i,o.name)||a.skills.some(t=>t.scope==="project"&&t.name===o.name&&t.agent===o.agent))continue;a.skills.push({name:o.name,scope:"project",agent:o.agent,outdated:!0,custom:!1,missing:!0});continue}if(o.category==="mcp"){if(!f(i,o.name)||a.mcp.some(t=>t.scope==="project"&&t.name===o.name))continue;a.mcp.push({name:o.name,scope:"project",outdated:!0,custom:!1,missing:!0});continue}const s=o.assetCategory;s===void 0||!b(i,s,o.name)||a.files.some(t=>t.scope==="project"&&t.category===s&&t.name===o.name)||a.files.push({category:s,name:o.name,scope:"project",outdated:!0,custom:!1,missing:!0})}}async function M(r){let a;try{a=await g(r,{withFileTypes:!0})}catch{return[]}const i=[];for(const o of a)if(!(!o.isDirectory()&&!o.isSymbolicLink()))try{(await I(u.join(r,o.name))).isDirectory()&&i.push(o.name)}catch{}return i}export async function detectSkillUpdates(r,a,i){const o=k(),s=[...j().map(e=>({dir:u.join(r,...e.projectSkillsDir().split("/")),scope:"project",agent:e.harness})),...j().map(e=>({dir:e.globalSkillsRoots(o)[0],scope:"user",agent:e.harness}))],t=[];for(const e of s)for(const n of await M(e.dir)){const c=u.join(e.dir,n),p=y(i,n);if(!p){t.push({name:n,scope:e.scope,agent:e.agent,outdated:!1,custom:!0});continue}const[m,d]=await Promise.all([a.get(c),a.get(p.dirPath)]);t.push({name:n,scope:e.scope,agent:e.agent,outdated:m!==d,custom:!1,sourceDir:p.dirPath,libraryId:p.libraryId})}return t}export async function detectFileAssetUpdates(r,a,i){const o=[];for(const s of T){const t=v[s],e=t.dirs.claude?.project;if(!e)continue;const n=u.join(r,...e.split("/"));let c;try{c=(await g(n,{withFileTypes:!0})).filter(p=>p.isFile()&&p.name.endsWith(t.ext)).map(p=>p.name)}catch{continue}for(const p of c){const m=p.slice(0,-t.ext.length),d=b(i,s,m);if(!d){o.push({category:s,name:m,scope:"project",outdated:!1,custom:!0});continue}const[S,w]=await Promise.all([a.getFile(u.join(n,p)),a.getFile(d.filePath)]);o.push({category:s,name:m,scope:"project",outdated:S!==w,custom:!1,libraryId:d.libraryId})}}return o}async function U(r,a){const[i,o,s]=await Promise.all([x(r).catch(()=>({})),F().catch(()=>({})),E().catch(()=>({}))]),t=[];for(const[e,n]of Object.entries(i)){const c=f(a,e);c?t.push({name:e,scope:"project",outdated:l(n)!==l(c.config),custom:!1,libraryId:c.libraryId}):t.push({name:e,scope:"project",outdated:!1,custom:!0})}for(const[e,n]of Object.entries(o)){const c=f(a,e);c?t.push({name:e,scope:"user",outdated:l(n)!==l(c.config),custom:!1,libraryId:c.libraryId}):t.push({name:e,scope:"user",outdated:!1,custom:!0})}for(const[e,n]of Object.entries(s)){const c=f(a,e);c?t.push({name:e,scope:"user",agent:"codex",outdated:l(n)!==l(c.config),custom:!1,libraryId:c.libraryId}):t.push({name:e,scope:"user",agent:"codex",outdated:!1,custom:!0})}return t}export{P as readGlobalSkills};
1
+ import{readdir as g,stat as w}from"node:fs/promises";import{homedir as F}from"node:os";import f from"node:path";import{hashMcpConfig as p}from"./fingerprint.js";import{buildLibraryIndex as h,findSkillSource as y,findMcpSource as m,findFileAssetSource as S}from"./library-index.js";import{readMcpServers as k}from"./installer.js";import{readGlobalMcpServers as x,readGlobalSkills as v}from"./global-config.js";import{readCodexMcpServers as C}from"./codex-toml.js";import{FILE_ASSET_CATEGORIES as E,FILE_ASSET_SPECS as M}from"./file-assets.js";import{assetSurfaces as b}from"../surfaces.js";import{readInstalledLedger as T}from"./install-ledger.js";import{detectPluginTemplateUpdates as U}from"./plugin-templates.js";import{detectInitFileUpdates as D,needsInit as L}from"./init-files.js";export async function detectUpdates(a,o,l,t){const n=t??await h(o),[s,e,r,i,u]=await Promise.all([detectSkillUpdates(a,l,n),H(a,n),detectFileAssetUpdates(a,l,n),U(a,l),D(a)]);await A(a,{skills:s,mcp:e,files:r},n);const d=s.filter(c=>!c.custom&&c.outdated).length+e.filter(c=>!c.custom&&c.outdated).length+r.filter(c=>!c.custom&&c.outdated).length+i.filter(c=>c.outdated).length+u.filter(c=>c.outdated&&c.syncClass==="auto").length;return{skills:s,mcp:e,files:r,pluginTemplates:i,initFiles:u,available:d,needsInit:L(u)}}export async function detectProjectUpdateCounts(a,o,l){const t=await h(o),n={};for(const s of a){const e=await detectUpdates(s.path,o,l,t);n[s.id]={available:e.available,needsInit:e.needsInit}}return n}async function A(a,o,l){for(const t of await T(a)){if(t.category==="skill"){if(!y(l,t.name)||o.skills.some(r=>r.scope==="project"&&r.name===t.name&&r.agent===t.agent))continue;o.skills.push({name:t.name,scope:"project",agent:t.agent,outdated:!0,custom:!1,missing:!0});continue}if(t.category==="mcp"){if(!m(l,t.name)||o.mcp.some(e=>e.scope==="project"&&e.name===t.name))continue;o.mcp.push({name:t.name,scope:"project",outdated:!0,custom:!1,missing:!0});continue}const n=t.assetCategory;n===void 0||!S(l,n,t.name)||o.files.some(e=>e.scope==="project"&&e.category===n&&e.name===t.name)||o.files.push({category:n,name:t.name,scope:"project",outdated:!0,custom:!1,missing:!0})}}async function P(a){let o;try{o=await g(a,{withFileTypes:!0})}catch{return[]}const l=[];for(const t of o)if(!(!t.isDirectory()&&!t.isSymbolicLink()))try{(await w(f.join(a,t.name))).isDirectory()&&l.push(t.name)}catch{}return l}export async function detectSkillUpdates(a,o,l){const t=F(),n=[...b().map(e=>({dir:f.join(a,...e.projectSkillsDir().split("/")),scope:"project",agent:e.harness})),...b().map(e=>({dir:e.globalSkillsRoots(t)[0],scope:"user",agent:e.harness}))],s=[];for(const e of n)for(const r of await P(e.dir)){const i=f.join(e.dir,r),u=y(l,r);if(!u){s.push({name:r,scope:e.scope,agent:e.agent,outdated:!1,custom:!0});continue}const[d,c]=await Promise.all([o.get(i),o.get(u.dirPath)]);s.push({name:r,scope:e.scope,agent:e.agent,outdated:d!==c,custom:!1,sourceDir:u.dirPath,libraryId:u.libraryId})}return s}export async function detectFileAssetUpdates(a,o,l){const t=[];for(const n of E){const s=M[n],e=s.dirs.claude?.project;if(!e)continue;const r=f.join(a,...e.split("/"));let i;try{i=(await g(r,{withFileTypes:!0})).filter(u=>u.isFile()&&u.name.endsWith(s.ext)).map(u=>u.name)}catch{continue}for(const u of i){const d=u.slice(0,-s.ext.length),c=S(l,n,d);if(!c){t.push({category:n,name:d,scope:"project",outdated:!1,custom:!0});continue}const[j,I]=await Promise.all([o.getFile(f.join(r,u)),o.getFile(c.filePath)]);t.push({category:n,name:d,scope:"project",outdated:j!==I,custom:!1,libraryId:c.libraryId})}}return t}async function H(a,o){const[l,t,n]=await Promise.all([k(a).catch(()=>({})),x().catch(()=>({})),C().catch(()=>({}))]),s=[];for(const[e,r]of Object.entries(l)){const i=m(o,e);i?s.push({name:e,scope:"project",outdated:p(r)!==p(i.config),custom:!1,libraryId:i.libraryId}):s.push({name:e,scope:"project",outdated:!1,custom:!0})}for(const[e,r]of Object.entries(t)){const i=m(o,e);i?s.push({name:e,scope:"user",outdated:p(r)!==p(i.config),custom:!1,libraryId:i.libraryId}):s.push({name:e,scope:"user",outdated:!1,custom:!0})}for(const[e,r]of Object.entries(n)){const i=m(o,e);i?s.push({name:e,scope:"user",agent:"codex",outdated:p(r)!==p(i.config),custom:!1,libraryId:i.libraryId}):s.push({name:e,scope:"user",agent:"codex",outdated:!1,custom:!0})}return s}export{v as readGlobalSkills};
@@ -1 +1 @@
1
- import i from"node:path";import{lstat as u,realpath as s}from"node:fs/promises";import{builtinMarketRoot as c}from"@weavecode/templates";import{WEAVE_MARKETPLACE as m}from"@weavecode/harness";import{marketDir as f}from"./manifest.js";import{linkOrCopy as p,removeInstalled as h}from"./install-link.js";export const BUILTIN_MARKET_ID=m;export function builtinMarketDir(r){return i.join(f(r),BUILTIN_MARKET_ID)}export function builtinPacksDir(r){return i.join(builtinMarketDir(r),"plugins")}export function builtinRulesDir(r){return i.join(builtinMarketDir(r),"rules")}export function isBuiltinMarketDir(r,n){return i.resolve(r)===i.resolve(builtinMarketDir(n))}export async function ensureBuiltinMarket(r){const n=r.libraries.dataDir,o=c(),t=builtinMarketDir(n);r.libraries.add({path:t,kind:"both"});const l=await s(o),e=await u(t).catch(()=>null);if(e){if(e.isSymbolicLink()&&await s(t).catch(()=>null)===l)return{changed:!1,form:"link"};await h(t)}try{return{changed:!0,form:await p(o,t)}}catch(a){return{changed:!1,error:a instanceof Error?a.message:String(a)}}}
1
+ import n from"node:path";import{lstat as l,realpath as s}from"node:fs/promises";import{builtinMarketRoot as u}from"@weavecode/templates";import{WEAVE_MARKETPLACE as f}from"@weavecode/harness";import{marketDir as m}from"./manifest.js";import{linkOrCopy as p,removeInstalled as h}from"./install-link.js";export const BUILTIN_MARKET_ID=f;export function builtinMarketDir(t){return n.join(m(t),BUILTIN_MARKET_ID)}export function builtinPacksDir(t){return n.join(builtinMarketDir(t),"plugins")}export function builtinRulesDir(t){return n.join(builtinMarketDir(t),"rules")}export function isBuiltinMarketDir(t,o){return n.resolve(t)===n.resolve(builtinMarketDir(o))}export async function ensureBuiltinMarket(t){const o=t.libraries.dataDir,e=u(),r=builtinMarketDir(o);t.libraries.add({path:r,kind:"both"});const c=await s(e),a=await l(r).catch(()=>null);if(a){if(a.isSymbolicLink()&&await s(r).catch(()=>null)===c)return{changed:!1,form:"link"};await h(r)}try{return{changed:!0,form:await p(e,r)}}catch(i){return{changed:!1,error:i instanceof Error?i.message:String(i)}}}
@@ -1 +1 @@
1
- import t from"node:path";import{marketDir as s}from"./manifest.js";export function marketIdOf(e,o=s()){const r=t.relative(t.resolve(o),t.resolve(e));return r===""||r.startsWith("..")||t.isAbsolute(r)?void 0:r.split(t.sep)[0]||void 0}
1
+ import e from"node:path";import{marketDir as n}from"./manifest.js";export function marketIdOf(i,o=n()){const r=e.relative(e.resolve(o),e.resolve(i));if(r===""||r.startsWith("..")||e.isAbsolute(r))return;const t=r.split(e.sep)[0];return t||void 0}
@@ -1,2 +1,2 @@
1
- import{execFile as M}from"node:child_process";import{cp as F,mkdir as v,readdir as P,rename as b,rm as p,stat as I}from"node:fs/promises";import s from"node:path";import{promisify as z}from"node:util";import{getDataDir as h}from"../db/paths.js";import{isInsideDir as A,toRelPosix as g}from"./resolve.js";import{isBuiltinMarketDir as C}from"./builtin.js";import{marketDir as D,readManifest as y,writeManifest as w}from"./manifest.js";const O=z(M),k=6e4,B=2e3;export function slugifyResourceId(r){const t=r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"");return/^[a-z0-9][a-z0-9-]*$/.test(t)?t:"resource"}export function assertSafeGitUrl(r){const t=r.trim();if(!t)throw new Error("Missing git URL");if(t.includes("--upload-pack"))throw new Error("Invalid git URL (--upload-pack rejected)");if(!(t.startsWith("https://")||t.startsWith("ssh://")||/^git@[^:/\s]+:/.test(t)))throw new Error(`Unsupported git URL (allowed: https/ssh): ${t}`);if(/[\s\r\n\t]/.test(t))throw new Error("Invalid git URL (whitespace)");return t}function l(){return new Date().toISOString()}function S(r){return r.length>B?`${r.slice(0,B)}\u2026(truncated)`:r}function d(r,t){if(!A(r,t))throw new Error(`Import target escapes the market directory: ${r}`)}async function j(r){try{return await I(r),!0}catch{return!1}}async function x(r,t){let a=s.join(t,r),i=1;for(;await j(a);)i+=1,a=s.join(t,`${r}-${i}`);return a}async function L(r,t){let a;try{a=await P(r,{withFileTypes:!0})}catch{return null}for(const i of a){if(!i.isDirectory())continue;const e=s.join(r,i.name);if((await y(e))?.origin===t)return e}return null}async function E(r,t,a,i,e=!1){const n=a.dataDir??h(),o=g(r,n),u=a.libraries.add({path:o,kind:"both"});return{id:s.basename(r),dir:r,relPath:o,manifest:t,libraryId:u.id,reused:i,refreshed:e}}async function H(r,t,a){const i=`${t}.refresh-tmp`;await p(i,{recursive:!0,force:!0}),await F(r,i,{recursive:!0}),d(s.resolve(i),a),await p(t,{recursive:!0,force:!0}),await b(i,t)}export async function importFromDir(r,t){const a=t.dataDir??h(),i=s.resolve(r);if(!(await I(i).catch(()=>null))?.isDirectory())throw new Error(`Not a directory: ${r}`);const e=D(a),n=await L(e,i);if(n){const c=await y(n);await H(i,n,e);const f={...c,importedAt:l()};return await w(n,f),E(n,f,t,!0,!0)}const o=await x(slugifyResourceId(s.basename(i)),e);d(o,e),await v(s.dirname(o),{recursive:!0}),await F(i,o,{recursive:!0});const u={source:"dir",origin:i,importedAt:l()};return await w(o,u),d(s.resolve(o),e),E(o,u,t,!1)}export async function importFromGit(r,t){const a=t.dataDir??h(),i=assertSafeGitUrl(r),e=D(a),n=await L(e,i);if(n){const f=await y(n),m=`${n}.refresh-tmp`;await p(m,{recursive:!0,force:!0});const U=["clone","--depth","1",i,m];try{await(t.execGit??defaultGitExec)("git",U,{timeout:k,maxBuffer:4*1024*1024,windowsHide:!0})}catch(G){throw new Error(`git clone failed: ${S(R(G))}`)}d(s.resolve(m),e),await p(n,{recursive:!0,force:!0}),await b(m,n);const $={...f,importedAt:l()};return await w(n,$),E(n,$,t,!0,!0)}const o=await x(slugifyResourceId(T(i)),e);d(o,e),await v(e,{recursive:!0});const u=["clone","--depth","1",i,o];try{await(t.execGit??defaultGitExec)("git",u,{timeout:k,maxBuffer:4*1024*1024,windowsHide:!0})}catch(f){throw new Error(`git clone failed: ${S(R(f))}`)}d(s.resolve(o),e);const c={source:"git",origin:i,importedAt:l()};return await w(o,c),E(o,c,t,!1)}export async function importLibraryIntoMarket(r,t){const a=t.dataDir??h(),i=s.isAbsolute(r.path)?r.path:s.join(a,r.path),e=s.resolve(i),n=D(a);if(!await j(e))throw new Error(`Source directory not found: ${e}`);if(C(e,a))throw new Error("\u5185\u7F6E\u4ED3\u4E0D\u53EF\u8FC1\u5165\uFF1A\u5B83\u968F\u5305\u53D1\u5E03\u3001\u65E0\u5916\u90E8\u6765\u6E90\uFF0C\u4E14\u843D\u70B9\u662F\u94FE\u63A5\uFF08\u5199\u5165\u4F1A\u6539\u5230\u5305\u5185\u5BB9\uFF09");if(A(e,n)){let c=await y(e);return c||(c={source:"dir",origin:e,importedAt:l()},await w(e,c)),{id:s.basename(e),dir:e,relPath:g(e,a),manifest:c,libraryId:r.id,reused:!0,refreshed:!1}}const o=await x(slugifyResourceId(s.basename(e)),n);d(o,n),await v(s.dirname(o),{recursive:!0}),await F(e,o,{recursive:!0});const u={source:"dir",origin:e,importedAt:l()};return await w(o,u),t.libraries.setPath(r.id,g(o,a)),{id:s.basename(o),dir:o,relPath:g(o,a),manifest:u,libraryId:r.id,reused:!1,refreshed:!1}}function T(r){const t=r.split(/[?#]/)[0],a=t.lastIndexOf("/"),i=t.lastIndexOf(":");return(a>i?t.slice(a+1):t.slice(i+1)).replace(/\.git$/,"")||"repo"}function R(r){const t=r,a=[t?.stderr,t?.stdout].filter(Boolean).map(String);return(a.length?a.join(`
2
- `):r instanceof Error?r.message:String(r)).trim()||"unknown error"}export const defaultGitExec=async(r,t,a)=>{const{stdout:i,stderr:e}=await O(r,t,a);return{stdout:String(i),stderr:String(e)}};
1
+ import{execFile as G}from"node:child_process";import{cp as x,mkdir as D,readdir as R,rename as k,rm as p,stat as A}from"node:fs/promises";import s from"node:path";import{promisify as _}from"node:util";import{getDataDir as h}from"../db/paths.js";import{isInsideDir as E,toRelPosix as g}from"./resolve.js";import{isBuiltinMarketDir as B}from"./builtin.js";import{marketDir as I,readManifest as y,writeManifest as d}from"./manifest.js";const C=_(G),M=6e4,S=2e3;export function slugifyResourceId(r){const t=r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"");return/^[a-z0-9][a-z0-9-]*$/.test(t)?t:"resource"}export function assertSafeGitUrl(r){const t=r.trim();if(!t)throw new Error("Missing git URL");if(t.includes("--upload-pack"))throw new Error("Invalid git URL (--upload-pack rejected)");if(!(t.startsWith("https://")||t.startsWith("ssh://")||/^git@[^:/\s]+:/.test(t)))throw new Error(`Unsupported git URL (allowed: https/ssh): ${t}`);if(/[\s\r\n\t]/.test(t))throw new Error("Invalid git URL (whitespace)");return t}function w(){return new Date().toISOString()}function U(r){return r.length>S?`${r.slice(0,S)}\u2026(truncated)`:r}function l(r,t){if(!E(r,t))throw new Error(`Import target escapes the market directory: ${r}`)}async function L(r){try{return await A(r),!0}catch{return!1}}async function b(r,t){let i=s.join(t,r),e=1;for(;await L(i);)e+=1,i=s.join(t,`${r}-${e}`);return i}async function O(r,t){let i;try{i=await R(r,{withFileTypes:!0})}catch{return null}for(const e of i){if(!e.isDirectory())continue;const a=s.join(r,e.name);if((await y(a))?.origin===t)return a}return null}async function v(r,t,i,e,a=!1){const o=i.dataDir??h(),n=g(r,o),c=i.libraries.add({path:n,kind:"both"});return{id:s.basename(r),dir:r,relPath:n,manifest:t,libraryId:c.id,reused:e,refreshed:a}}async function P(r,t,i){const e=`${t}.refresh-tmp`;await p(e,{recursive:!0,force:!0}),await x(r,e,{recursive:!0}),l(s.resolve(e),i),await p(t,{recursive:!0,force:!0}),await k(e,t)}export async function importFromDir(r,t){const i=t.dataDir??h(),e=s.resolve(r);if(!(await A(e).catch(()=>null))?.isDirectory())throw new Error(`Not a directory: ${r}`);const o=I(i);if(E(e,o))throw new Error("\u4E0D\u80FD\u628A\u8D44\u6E90\u5E02\u573A\u7684\u5B58\u50A8\u76EE\u5F55\u5F53\u6765\u6E90\u5BFC\u5165\uFF1A\u5176\u4E2D\u7684\u8D44\u6E90\u5DF2\u5728\u5217\u8868\u4E2D\uFF08\u5185\u7F6E\u4ED3\u843D\u70B9 / \u5DF2\u5BFC\u5165\u7684\u526F\u672C\uFF09");const n=await O(o,e);if(n){const m=await y(n);await P(e,n,o);const f={...m,importedAt:w()};return await d(n,f),v(n,f,t,!0,!0)}const c=await b(slugifyResourceId(s.basename(e)),o);l(c,o),await D(s.dirname(c),{recursive:!0}),await x(e,c,{recursive:!0});const u={source:"dir",origin:e,importedAt:w()};return await d(c,u),l(s.resolve(c),o),v(c,u,t,!1)}export async function importFromGit(r,t){const i=t.dataDir??h(),e=assertSafeGitUrl(r),a=I(i),o=await O(a,e);if(o){const m=await y(o),f=`${o}.refresh-tmp`;await p(f,{recursive:!0,force:!0});const j=["clone","--depth","1",e,f];try{await(t.execGit??defaultGitExec)("git",j,{timeout:M,maxBuffer:4*1024*1024,windowsHide:!0})}catch(F){throw new Error(`git clone failed: ${U(T(F))}`)}l(s.resolve(f),a),await p(o,{recursive:!0,force:!0}),await k(f,o);const $={...m,importedAt:w()};return await d(o,$),v(o,$,t,!0,!0)}const n=await b(slugifyResourceId(z(e)),a);l(n,a),await D(a,{recursive:!0});const c=["clone","--depth","1",e,n];try{await(t.execGit??defaultGitExec)("git",c,{timeout:M,maxBuffer:4*1024*1024,windowsHide:!0})}catch(m){throw new Error(`git clone failed: ${U(T(m))}`)}l(s.resolve(n),a);const u={source:"git",origin:e,importedAt:w()};return await d(n,u),v(n,u,t,!1)}export async function importLibraryIntoMarket(r,t){const i=t.dataDir??h(),e=s.isAbsolute(r.path)?r.path:s.join(i,r.path),a=s.resolve(e),o=I(i);if(!await L(a))throw new Error(`Source directory not found: ${a}`);if(B(a,i))throw new Error("\u5185\u7F6E\u4ED3\u4E0D\u53EF\u8FC1\u5165\uFF1A\u5B83\u968F\u5305\u53D1\u5E03\u3001\u65E0\u5916\u90E8\u6765\u6E90\uFF0C\u4E14\u843D\u70B9\u662F\u94FE\u63A5\uFF08\u5199\u5165\u4F1A\u6539\u5230\u5305\u5185\u5BB9\uFF09");if(E(a,o)){let u=await y(a);return u||(u={source:"dir",origin:a,importedAt:w()},await d(a,u)),{id:s.basename(a),dir:a,relPath:g(a,i),manifest:u,libraryId:r.id,reused:!0,refreshed:!1}}const n=await b(slugifyResourceId(s.basename(a)),o);l(n,o),await D(s.dirname(n),{recursive:!0}),await x(a,n,{recursive:!0});const c={source:"dir",origin:a,importedAt:w()};return await d(n,c),t.libraries.setPath(r.id,g(n,i)),{id:s.basename(n),dir:n,relPath:g(n,i),manifest:c,libraryId:r.id,reused:!1,refreshed:!1}}function z(r){const t=r.split(/[?#]/)[0],i=t.lastIndexOf("/"),e=t.lastIndexOf(":");return(i>e?t.slice(i+1):t.slice(e+1)).replace(/\.git$/,"")||"repo"}function T(r){const t=r,i=[t?.stderr,t?.stdout].filter(Boolean).map(String);return(i.length?i.join(`
2
+ `):r instanceof Error?r.message:String(r)).trim()||"unknown error"}export const defaultGitExec=async(r,t,i)=>{const{stdout:e,stderr:a}=await C(r,t,i);return{stdout:String(e),stderr:String(a)}};
@@ -1 +1 @@
1
- import{stat as d}from"node:fs/promises";import{defaultGitExec as u,importFromDir as c,importFromGit as m}from"./import.js";import{readManifest as n,writeManifest as f}from"./manifest.js";async function w(i){try{return(await d(i)).isDirectory()}catch{return!1}}export async function refreshMarketItems(i,a){return Promise.all(i.map(async t=>{const r=await n(t.dir);if(!r)return{id:t.id,status:"skipped"};if(r.source==="dir"&&!await w(r.origin))return{id:t.id,status:"origin-missing"};try{if(r.source==="git"){const s=await m(r.origin,a),e=await p(s.dir,a.execGit??u);if(e){const o={...await n(s.dir),commit:e};return await f(s.dir,o),{id:t.id,status:"refreshed",commit:e}}return{id:t.id,status:"refreshed"}}return await c(r.origin,a),{id:t.id,status:"refreshed"}}catch{return{id:t.id,status:"failed"}}}))}async function p(i,a){try{return(await a("git",["-C",i,"rev-parse","HEAD"],{timeout:15e3,maxBuffer:1048576,windowsHide:!0})).stdout.trim()||void 0}catch{return}}
1
+ import{stat as d}from"node:fs/promises";import{defaultGitExec as u,importFromDir as c,importFromGit as f}from"./import.js";import{readManifest as n,writeManifest as m}from"./manifest.js";async function w(t){try{return(await d(t)).isDirectory()}catch{return!1}}export async function refreshMarketItems(t,e){return Promise.all(t.map(async i=>{const r=await n(i.dir);if(!r)return{id:i.id,status:"skipped"};if(r.source==="dir"&&!await w(r.origin))return{id:i.id,status:"origin-missing"};try{if(r.source==="git"){const a=await f(r.origin,e),s=await h(a.dir,e.execGit??u);if(s){const o={...await n(a.dir),commit:s};return await m(a.dir,o),{id:i.id,status:"refreshed",commit:s}}return{id:i.id,status:"refreshed"}}return await c(r.origin,e),{id:i.id,status:"refreshed"}}catch{return{id:i.id,status:"failed"}}}))}async function h(t,e){try{return(await e("git",["-C",t,"rev-parse","HEAD"],{timeout:15e3,maxBuffer:1048576,windowsHide:!0})).stdout.trim()||void 0}catch{return}}
@@ -1 +1 @@
1
- import r from"node:path";import{getDataDir as i}from"../db/paths.js";import{marketDir as l}from"./manifest.js";import{isBuiltinMarketDir as p}from"./builtin.js";export function resolveLibraryPath(t,o=i()){if(!r.isAbsolute(t.path))return r.join(o,t.path);const e=r.resolve(t.path);return isInsideDir(e,o)?e:t.path}export function isInsideDir(t,o){const e=r.relative(r.resolve(o),r.resolve(t));return r.isAbsolute(e)?!1:e===""||e!==".."&&!e.startsWith(`..${r.sep}`)}export function toRelPosix(t,o=i()){return r.relative(r.resolve(o),r.resolve(t)).split(r.sep).join("/")}export function marketLibraryRoots(t,o=i()){const e=r.resolve(l(o)),n=new Set;for(const a of t.list()){const s=r.resolve(a.path);s===e||p(s,o)||isInsideDir(s,e)&&n.add(s)}return[...n]}
1
+ import t from"node:path";import{getDataDir as s}from"../db/paths.js";import{marketDir as a}from"./manifest.js";import{isBuiltinMarketDir as p}from"./builtin.js";export function resolveLibraryPath(e,o=s()){if(!t.isAbsolute(e.path))return t.join(o,e.path);const r=t.resolve(e.path);return isInsideDir(r,o)?r:e.path}export function isInsideDir(e,o){const r=t.relative(t.resolve(o),t.resolve(e));return t.isAbsolute(r)?!1:r===""||r!==".."&&!r.startsWith(`..${t.sep}`)}export function toRelPosix(e,o=s()){return t.relative(t.resolve(o),t.resolve(e)).split(t.sep).join("/")}export function marketLibraryRoots(e,o=s()){const r=t.resolve(a(o)),n=new Set;for(const l of e.list()){const i=t.resolve(l.path);i===r||p(i,o)||isInsideDir(i,r)&&n.add(i)}return[...n]}
@@ -1 +1 @@
1
- import{stat as u}from"node:fs/promises";import m from"node:path";import{hashSkillDir as c}from"../install/fingerprint.js";import{defaultGitExec as d}from"./import.js";import{readManifest as w}from"./manifest.js";const l=15e3;export const MARKET_SKIP_NAMES=new Set([".weave",".git","node_modules",".weave-market.json"]);async function p(e){try{return(await u(e)).isDirectory()}catch{return!1}}async function f(e,n){const r=m.resolve(n);if(!await p(r))return"origin-missing";const[t,a]=await Promise.all([c(e,MARKET_SKIP_NAMES),c(r,MARKET_SKIP_NAMES)]);return t===a?"uptodate":"update-available"}async function g(e,n,r){const t={timeout:l,maxBuffer:1048576,windowsHide:!0};try{const a=await r("git",["-C",e,"rev-parse","HEAD"],t),i=await r("git",["ls-remote",n,"HEAD"],t),o=a.stdout.trim(),s=i.stdout.trim().split(/\s+/)[0]??"";return!o||!s?"unknown":o===s?"uptodate":"update-available"}catch{return"unknown"}}export async function checkMarketUpdates(e,n){const r=n?.execGit??d;return Promise.all(e.map(async t=>{const a=await w(t.dir);let i;return a?a.source==="git"?i=await g(t.dir,a.origin,r):i=await f(t.dir,a.origin):i="unknown",{id:t.id,status:i,checkedAt:new Date().toISOString()}}))}
1
+ import{stat as u}from"node:fs/promises";import m from"node:path";import{hashSkillDir as c}from"../install/fingerprint.js";import{defaultGitExec as l}from"./import.js";import{readManifest as d}from"./manifest.js";const f=15e3;export const MARKET_SKIP_NAMES=new Set([".weave",".git","node_modules",".weave-market.json"]);async function p(i){try{return(await u(i)).isDirectory()}catch{return!1}}async function w(i,o){const r=m.resolve(o);if(!await p(r))return"origin-missing";const[t,e]=await Promise.all([c(i,MARKET_SKIP_NAMES),c(r,MARKET_SKIP_NAMES)]);return t===e?"uptodate":"update-available"}async function h(i,o,r){const t={timeout:f,maxBuffer:1048576,windowsHide:!0};try{const e=await r("git",["-C",i,"rev-parse","HEAD"],t),a=await r("git",["ls-remote",o,"HEAD"],t),n=e.stdout.trim(),s=a.stdout.trim().split(/\s+/)[0]??"";return!n||!s?"unknown":n===s?"uptodate":"update-available"}catch{return"unknown"}}export async function checkMarketUpdates(i,o){const r=o?.execGit??l;return Promise.all(i.map(async t=>{const e=await d(t.dir);let a;return e?e.source==="git"?a=await h(t.dir,e.origin,r):a=await w(t.dir,e.origin):a="unknown",{id:t.id,status:a,checkedAt:new Date().toISOString()}}))}
@@ -1,2 +1,2 @@
1
- import{basename as y,join as a,resolve as d}from"node:path";import{mkdirSync as l,writeFileSync as u}from"node:fs";import{openDatabase as m}from"./db/db.js";import{ProjectRepository as p}from"./repositories/projects.js";import{HookDispatchRepository as h}from"./repositories/hook-dispatches.js";import{HookEventRepository as P}from"./repositories/hook-events.js";import{SessionRepository as k}from"./repositories/sessions.js";import{generateToken as g}from"./daemon/auth.js";import{purgeProjectPipeline as j}from"./daemon/hook-pipeline.js";import{hooksRoot as v}from"./hooks/mirror.js";export function registerProject(t,n,s){const o=m(s);try{const r=new p(o),e=r.getByPath(t)?.token??g(),i=r.register({path:t,name:y(t),token:e,source:n});return l(a(t,".weave"),{recursive:!0}),u(a(t,".weave","token"),e+`
2
- `,"utf-8"),{id:i.id,token:e,path:i.path}}finally{o.close()}}export function unregisterProject(t,n,s){const o=m(n);try{const r=new p(o),e=r.getByPath(t)??r.getByPath(d(t));if(!e)return null;const i=new h(o).deleteByProject(e.id),c=new P(o).deleteByProject(e.id),f=new k(o).removeByProject(e.id);return j(s??v(),e.token),r.remove(e.id),{id:e.id,name:e.name,path:e.path,purged:{events:c,dispatches:i,sessions:f}}}finally{o.close()}}
1
+ import{basename as a,join as c}from"node:path";import{mkdirSync as f,writeFileSync as l}from"node:fs";import{openDatabase as m}from"./db/db.js";import{ProjectRepository as d}from"./repositories/projects.js";import{HookDispatchRepository as u}from"./repositories/hook-dispatches.js";import{HookEventRepository as j}from"./repositories/hook-events.js";import{SessionRepository as w}from"./repositories/sessions.js";import{SessionWatchRepository as P}from"./repositories/session-watches.js";import{SessionSequenceRepository as h}from"./repositories/session-sequences.js";import{SideQuestionRepository as k}from"./repositories/side-questions.js";import{generateToken as B}from"./daemon/auth.js";import{purgeProjectPipeline as R}from"./daemon/hook-pipeline.js";import{hooksRoot as v}from"./hooks/mirror.js";export function registerProject(t,s,p){const o=m(p);try{const r=new d(o),n=r.getByPath(t)?.token??B(),i=r.register({path:t,name:a(t),token:n,source:s});return f(c(t,".weave"),{recursive:!0}),l(c(t,".weave","token"),n+`
2
+ `,"utf-8"),{id:i.id,token:n,path:i.path}}finally{o.close()}}export function unregisterProject(t,s,p){const o=m(s);try{const r=new d(o),e=r.getByPath(t);if(!e)return null;const n=new u(o).deleteByProject(e.id),i=new j(o).deleteByProject(e.id);new P(o).deleteByProject(e.id),new h(o).deleteByProject(e.id),new k(o).deleteByProject(e.id);const y=new w(o).removeByProject(e.id);return R(p??v(),e.token),r.remove(e.id),{id:e.id,name:e.name,path:e.path,purged:{events:i,dispatches:n,sessions:y}}}finally{o.close()}}
@@ -1,8 +1,8 @@
1
- export class HookDispatchRepository{db;constructor(t){this.db=t}insert(t){const s=this.db.prepare(`INSERT INTO hook_dispatches (rule_id, event_id, session_id, prompt, status, mode, started_at)
2
- VALUES (?, ?, ?, ?, ?, ?, ?)`).run(t.ruleId,t.eventId??null,t.sessionId??null,t.prompt,t.status,t.mode,new Date().toISOString());return this.getById(Number(s.lastInsertRowid))}getById(t){const s=this.db.prepare("SELECT * FROM hook_dispatches WHERE id = ?").get(t);return s?n(s):void 0}updateStatus(t,s){const r=new Date().toISOString();return this.db.prepare(`UPDATE hook_dispatches
1
+ export class HookDispatchRepository{db;constructor(t){this.db=t}insert(t){const e=this.db.prepare(`INSERT INTO hook_dispatches (rule_id, event_id, session_id, prompt, status, mode, started_at)
2
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(t.ruleId,t.eventId??null,t.sessionId??null,t.prompt,t.status,t.mode,new Date().toISOString());return this.getById(Number(e.lastInsertRowid))}getById(t){const e=this.db.prepare("SELECT * FROM hook_dispatches WHERE id = ?").get(t);return e?r(e):void 0}updateStatus(t,e){const n=new Date().toISOString();return this.db.prepare(`UPDATE hook_dispatches
3
3
  SET status = ?, output = COALESCE(?, output), error = COALESCE(?, error),
4
4
  finished_at = CASE WHEN ? = 1 THEN ? ELSE finished_at END
5
- WHERE id = ?`).run(s.status,s.output??null,s.error??null,s.finished?1:0,r,t),this.getById(t)}list(t={}){const s=Math.min(Math.max(t.limit??50,1),200),r=[],i=[];t.ruleId&&(r.push("rule_id = ?"),i.push(t.ruleId)),t.status&&(r.push("status = ?"),i.push(t.status)),t.sessionId&&(r.push("session_id = ?"),i.push(t.sessionId));const d=r.length>0?`WHERE ${r.join(" AND ")}`:"";return this.db.prepare(`SELECT * FROM hook_dispatches ${d} ORDER BY started_at DESC, id DESC LIMIT ?`).all(...i,s).map(n)}deleteBySession(t){return Number(this.db.prepare("DELETE FROM hook_dispatches WHERE session_id = ?").run(t).changes)}deleteByProject(t){return Number(this.db.prepare(`DELETE FROM hook_dispatches WHERE session_id IN (SELECT session_id FROM sessions WHERE project_id = ?)
6
- OR event_id IN (SELECT id FROM hook_events WHERE project_id = ?)`).run(t,t).changes)}findRecentAuto(t,s,r){const i=this.db.prepare(`SELECT * FROM hook_dispatches
5
+ WHERE id = ?`).run(e.status,e.output??null,e.error??null,e.finished?1:0,n,t),this.getById(t)}list(t={}){const e=Math.min(Math.max(t.limit??50,1),200),n=[],i=[];t.ruleId&&(n.push("rule_id = ?"),i.push(t.ruleId)),t.status&&(n.push("status = ?"),i.push(t.status)),t.sessionId&&(n.push("session_id = ?"),i.push(t.sessionId));const d=n.length>0?`WHERE ${n.join(" AND ")}`:"";return this.db.prepare(`SELECT * FROM hook_dispatches ${d} ORDER BY started_at DESC, id DESC LIMIT ?`).all(...i,e).map(r)}deleteBySession(t){return Number(this.db.prepare("DELETE FROM hook_dispatches WHERE session_id = ?").run(t).changes)}deleteByProject(t){return Number(this.db.prepare(`DELETE FROM hook_dispatches WHERE session_id IN (SELECT session_id FROM sessions WHERE project_id = ?)
6
+ OR event_id IN (SELECT id FROM hook_events WHERE project_id = ?)`).run(t,t).changes)}findRecentAuto(t,e,n){const i=this.db.prepare(`SELECT * FROM hook_dispatches
7
7
  WHERE rule_id = ? AND session_id = ? AND mode = 'auto' AND started_at >= ?
8
- ORDER BY started_at DESC LIMIT 1`).get(t,s,r);return i?n(i):void 0}}function n(e){return{id:Number(e.id),ruleId:String(e.rule_id),eventId:e.event_id==null?null:Number(e.event_id),sessionId:e.session_id==null?null:String(e.session_id),prompt:String(e.prompt),status:String(e.status),mode:String(e.mode),output:e.output==null?null:String(e.output),error:e.error==null?null:String(e.error),startedAt:String(e.started_at),finishedAt:e.finished_at==null?null:String(e.finished_at)}}
8
+ ORDER BY started_at DESC LIMIT 1`).get(t,e,n);return i?r(i):void 0}}function r(s){return{id:Number(s.id),ruleId:String(s.rule_id),eventId:s.event_id==null?null:Number(s.event_id),sessionId:s.session_id==null?null:String(s.session_id),prompt:String(s.prompt),status:String(s.status),mode:String(s.mode),output:s.output==null?null:String(s.output),error:s.error==null?null:String(s.error),startedAt:String(s.started_at),finishedAt:s.finished_at==null?null:String(s.finished_at)}}
@@ -52,6 +52,13 @@ export declare class HookEventRepository {
52
52
  deleteBySession(sessionId: string): number;
53
53
  /** 归档项目时清空其全部采集事件(含 session_id 为空的散行)。 */
54
54
  deleteByProject(projectId: number): number;
55
+ /**
56
+ * 按工具名择行(最近的 limit 条,倒序):计划正文只藏在 ExitPlanMode 与「写计划
57
+ * 文件」两类调用里,`needle` 再按路径特征收一道——不筛就得把整轮工具调用连同
58
+ * 几 MB 的 tool_input 一起捞回来。`payload` 是归一化 JSON(toolName 顶格),故
59
+ * 直接按 `"toolName":"<名>"` 匹配,不必解 JSON。
60
+ */
61
+ listByTools(sessionId: string, tools: readonly string[], needle: string | null, limit: number): HookEventRow[];
55
62
  /** `types` 非空只取这些事件类型(对话视图按开/闭轮类型翻页,不拉整条事件链)。 */
56
63
  listBySession(sessionId: string, limit?: number, beforeId?: number, types?: readonly string[]): HookEventRow[];
57
64
  }
@@ -1,2 +1,2 @@
1
- import{readDaemonSettingsSync as u,DEFAULT_EVENT_RETENTION_DAYS as d}from"../daemon/settings.js";export class HookEventRepository{db;lastPruneDay=-1;insertEvent;pruneByCutoff;constructor(e){this.db=e,this.insertEvent=e.prepare(`INSERT INTO hook_events (project_id, source, event_type, session_id, payload, created_at)
2
- VALUES (?, ?, ?, ?, ?, ?)`),this.pruneByCutoff=e.prepare("DELETE FROM hook_events WHERE created_at < ?")}resetPrune(){this.lastPruneDay=-1}insert(e){this.pruneExpired();const r=this.insertEvent.run(e.projectId,e.source,e.eventType,e.sessionId??null,e.payload,new Date().toISOString());return Number(r.lastInsertRowid)}insertMany(e){this.pruneExpired();const r=[];this.db.exec("BEGIN");try{for(const t of e){const n=this.insertEvent.run(t.projectId,t.source,t.eventType,t.sessionId??null,t.payload,new Date().toISOString());r.push(Number(n.lastInsertRowid))}this.db.exec("COMMIT")}catch(t){throw this.db.exec("ROLLBACK"),t}return r}pruneExpired(){const e=Math.floor(Date.now()/864e5);if(e===this.lastPruneDay)return;this.lastPruneDay=e;const r=u().events?.retentionDays??d,t=new Date((e-r)*864e5).toISOString();this.pruneByCutoff.run(t)}listByProject(e,r=100){return this.db.prepare("SELECT * FROM hook_events WHERE project_id = ? ORDER BY created_at DESC LIMIT ?").all(e,r).map(t=>a(t))}deleteBySession(e){return Number(this.db.prepare("DELETE FROM hook_events WHERE session_id = ?").run(e).changes)}deleteByProject(e){return Number(this.db.prepare("DELETE FROM hook_events WHERE project_id = ?").run(e).changes)}listBySession(e,r=300,t,n){const o=["session_id = ?"],i=[e];return t!=null&&(o.push("id < ?"),i.push(t)),n?.length&&(o.push(`event_type IN (${n.map(()=>"?").join(",")})`),i.push(...n)),this.db.prepare(`SELECT * FROM hook_events WHERE ${o.join(" AND ")} ORDER BY id DESC LIMIT ?`).all(...i,r).map(p=>a(p)).reverse()}}function a(s){return{id:Number(s.id),projectId:Number(s.project_id),source:String(s.source),eventType:String(s.event_type),sessionId:s.session_id==null?null:String(s.session_id),payload:String(s.payload),createdAt:String(s.created_at)}}
1
+ import{readDaemonSettingsSync as E,DEFAULT_EVENT_RETENTION_DAYS as d}from"../daemon/settings.js";export class HookEventRepository{db;lastPruneDay=-1;insertEvent;pruneByCutoff;constructor(e){this.db=e,this.insertEvent=e.prepare(`INSERT INTO hook_events (project_id, source, event_type, session_id, payload, created_at)
2
+ VALUES (?, ?, ?, ?, ?, ?)`),this.pruneByCutoff=e.prepare("DELETE FROM hook_events WHERE created_at < ?")}resetPrune(){this.lastPruneDay=-1}insert(e){this.pruneExpired();const s=this.insertEvent.run(e.projectId,e.source,e.eventType,e.sessionId??null,e.payload,new Date().toISOString());return Number(s.lastInsertRowid)}insertMany(e){this.pruneExpired();const s=[];this.db.exec("BEGIN");try{for(const t of e){const r=this.insertEvent.run(t.projectId,t.source,t.eventType,t.sessionId??null,t.payload,new Date().toISOString());s.push(Number(r.lastInsertRowid))}this.db.exec("COMMIT")}catch(t){throw this.db.exec("ROLLBACK"),t}return s}pruneExpired(){const e=Math.floor(Date.now()/864e5);if(e===this.lastPruneDay)return;this.lastPruneDay=e;const s=E().events?.retentionDays??d,t=new Date((e-s)*864e5).toISOString();this.pruneByCutoff.run(t)}listByProject(e,s=100){return this.db.prepare("SELECT * FROM hook_events WHERE project_id = ? ORDER BY created_at DESC LIMIT ?").all(e,s).map(t=>p(t))}deleteBySession(e){return Number(this.db.prepare("DELETE FROM hook_events WHERE session_id = ?").run(e).changes)}deleteByProject(e){return Number(this.db.prepare("DELETE FROM hook_events WHERE project_id = ?").run(e).changes)}listByTools(e,s,t,r){if(s.length===0)return[];const o=["session_id = ?","event_type IN ('PreToolUse','PostToolUse')",`(${s.map(()=>"payload LIKE ?").join(" OR ")})`],i=[e,...s.map(a=>`%"toolName":"${a}"%`)];return t&&(o.push("payload LIKE ?"),i.push(`%${t}%`)),this.db.prepare(`SELECT * FROM hook_events WHERE ${o.join(" AND ")} ORDER BY id DESC LIMIT ?`).all(...i,r).map(a=>p(a))}listBySession(e,s=300,t,r){const o=["session_id = ?"],i=[e];return t!=null&&(o.push("id < ?"),i.push(t)),r?.length&&(o.push(`event_type IN (${r.map(()=>"?").join(",")})`),i.push(...r)),this.db.prepare(`SELECT * FROM hook_events WHERE ${o.join(" AND ")} ORDER BY id DESC LIMIT ?`).all(...i,s).map(a=>p(a)).reverse()}}function p(n){return{id:Number(n.id),projectId:Number(n.project_id),source:String(n.source),eventType:String(n.event_type),sessionId:n.session_id==null?null:String(n.session_id),payload:String(n.payload),createdAt:String(n.created_at)}}
@@ -1,8 +1,8 @@
1
- import{randomUUID as s}from"node:crypto";import{HookRuleSchema as d}from"@weavecode/core";export class HookRuleRepository{db;constructor(r){this.db=r}create(r){const e=new Date().toISOString(),o=d.parse({id:s(),name:r.name,enabled:r.enabled??!0,priority:r.priority??0,projectId:r.projectId,source:r.source,filter:r.filter??{},handler:r.handler,timeoutMs:r.timeoutMs??1e4,record:r.record??r.handler.type!=="log",origin:r.origin??"user",pluginId:r.pluginId??null});return this.insertRow(o,e,e),o}upsertPluginRule(r){const e=new Date().toISOString(),o=this.getById(r.id);if(!o)return this.insertRow(r,e,e),r;const t=d.parse({...r,enabled:o.enabled});return this.db.prepare(`UPDATE hook_rules SET name = ?, enabled = ?, priority = ?, project_id = ?,
1
+ import{randomUUID as s}from"node:crypto";import{HookRuleSchema as o}from"@weavecode/core";export class HookRuleRepository{db;constructor(e){this.db=e}create(e){const t=new Date().toISOString(),i=o.parse({id:s(),name:e.name,enabled:e.enabled??!0,priority:e.priority??0,projectId:e.projectId,source:e.source,filter:e.filter??{},handler:e.handler,timeoutMs:e.timeoutMs??1e4,record:e.record??e.handler.type!=="log",origin:e.origin??"user",pluginId:e.pluginId??null});return this.insertRow(i,t,t),i}upsertPluginRule(e){const t=new Date().toISOString(),i=this.getById(e.id);if(!i)return this.insertRow(e,t,t),e;const r=o.parse({...e,enabled:i.enabled});return this.db.prepare(`UPDATE hook_rules SET name = ?, enabled = ?, priority = ?, project_id = ?,
2
2
  source = ?, filter = ?, handler = ?, timeout_ms = ?, record = ?,
3
3
  origin = ?, plugin_id = ?, updated_at = ?
4
- WHERE id = ?`).run(t.name,t.enabled?1:0,t.priority,t.projectId,JSON.stringify(t.source),JSON.stringify(t.filter),JSON.stringify(t.handler),t.timeoutMs,t.record?1:0,t.origin,t.pluginId,e,t.id),t}removeByPlugin(r){const e=this.db.prepare("DELETE FROM hook_rules WHERE plugin_id = ?").run(r);return Number(e.changes)}listByPlugin(r){return this.db.prepare("SELECT * FROM hook_rules WHERE plugin_id = ? ORDER BY created_at ASC").all(r).map(n).filter(e=>e!==void 0)}getById(r){const e=this.db.prepare("SELECT * FROM hook_rules WHERE id = ?").get(r);return e?n(e):void 0}list(r){return(r===void 0?this.db.prepare("SELECT * FROM hook_rules ORDER BY priority DESC, created_at ASC").all():this.db.prepare("SELECT * FROM hook_rules WHERE project_id IS NULL OR project_id = ? ORDER BY priority DESC, created_at ASC").all(r)).map(n).filter(e=>e!==void 0)}update(r,e){const o=this.getById(r);if(!o)return;const t=d.parse({...o,...e,id:r,projectId:"projectId"in e?e.projectId:o.projectId});return this.db.prepare(`UPDATE hook_rules SET name = ?, enabled = ?, priority = ?, project_id = ?,
4
+ WHERE id = ?`).run(r.name,r.enabled?1:0,r.priority,r.projectId,JSON.stringify(r.source),JSON.stringify(r.filter),JSON.stringify(r.handler),r.timeoutMs,r.record?1:0,r.origin,r.pluginId,t,r.id),r}removeByPlugin(e){const t=this.db.prepare("DELETE FROM hook_rules WHERE plugin_id = ?").run(e);return Number(t.changes)}listByPlugin(e){return this.db.prepare("SELECT * FROM hook_rules WHERE plugin_id = ? ORDER BY created_at ASC").all(e).map(d).filter(i=>i!==void 0)}getById(e){const t=this.db.prepare("SELECT * FROM hook_rules WHERE id = ?").get(e);return t?d(t):void 0}list(e){return(e===void 0?this.db.prepare("SELECT * FROM hook_rules ORDER BY priority DESC, created_at ASC").all():this.db.prepare("SELECT * FROM hook_rules WHERE project_id IS NULL OR project_id = ? ORDER BY priority DESC, created_at ASC").all(e)).map(d).filter(i=>i!==void 0)}update(e,t){const i=this.getById(e);if(!i)return;const r=o.parse({...i,...t,id:e,projectId:"projectId"in t?t.projectId:i.projectId});return this.db.prepare(`UPDATE hook_rules SET name = ?, enabled = ?, priority = ?, project_id = ?,
5
5
  source = ?, filter = ?, handler = ?, timeout_ms = ?, record = ?, updated_at = ?
6
- WHERE id = ?`).run(t.name,t.enabled?1:0,t.priority,t.projectId,JSON.stringify(t.source),JSON.stringify(t.filter),JSON.stringify(t.handler),t.timeoutMs,t.record?1:0,new Date().toISOString(),r),t}setEnabled(r,e){const o=this.db.prepare("UPDATE hook_rules SET enabled = ?, updated_at = ? WHERE id = ?").run(e?1:0,new Date().toISOString(),r);if(Number(o.changes)!==0)return this.getById(r)}remove(r){const e=this.db.prepare("DELETE FROM hook_rules WHERE id = ?").run(r);return Number(e.changes)>0}insertRow(r,e,o){this.db.prepare(`INSERT INTO hook_rules (id, name, enabled, priority, project_id, source, filter, handler,
6
+ WHERE id = ?`).run(r.name,r.enabled?1:0,r.priority,r.projectId,JSON.stringify(r.source),JSON.stringify(r.filter),JSON.stringify(r.handler),r.timeoutMs,r.record?1:0,new Date().toISOString(),e),r}setEnabled(e,t){const i=this.db.prepare("UPDATE hook_rules SET enabled = ?, updated_at = ? WHERE id = ?").run(t?1:0,new Date().toISOString(),e);if(Number(i.changes)!==0)return this.getById(e)}remove(e){const t=this.db.prepare("DELETE FROM hook_rules WHERE id = ?").run(e);return Number(t.changes)>0}insertRow(e,t,i){this.db.prepare(`INSERT INTO hook_rules (id, name, enabled, priority, project_id, source, filter, handler,
7
7
  timeout_ms, record, origin, plugin_id, created_at, updated_at)
8
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(r.id,r.name,r.enabled?1:0,r.priority,r.projectId,JSON.stringify(r.source),JSON.stringify(r.filter),JSON.stringify(r.handler),r.timeoutMs,r.record?1:0,r.origin,r.pluginId,e,o)}}function n(i){try{return d.parse({id:String(i.id),name:String(i.name),enabled:Number(i.enabled)===1,priority:Number(i.priority),projectId:i.project_id==null?null:Number(i.project_id),source:JSON.parse(String(i.source)),filter:JSON.parse(String(i.filter??"{}")),handler:JSON.parse(String(i.handler)),timeoutMs:Number(i.timeout_ms),record:Number(i.record)===1,origin:i.origin??"user",pluginId:i.plugin_id==null?null:String(i.plugin_id)})}catch{return}}
8
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(e.id,e.name,e.enabled?1:0,e.priority,e.projectId,JSON.stringify(e.source),JSON.stringify(e.filter),JSON.stringify(e.handler),e.timeoutMs,e.record?1:0,e.origin,e.pluginId,t,i)}}function d(n){try{return o.parse({id:String(n.id),name:String(n.name),enabled:Number(n.enabled)===1,priority:Number(n.priority),projectId:n.project_id==null?null:Number(n.project_id),source:JSON.parse(String(n.source)),filter:JSON.parse(String(n.filter??"{}")),handler:JSON.parse(String(n.handler)),timeoutMs:Number(n.timeout_ms),record:Number(n.record)===1,origin:n.origin??"user",pluginId:n.plugin_id==null?null:String(n.plugin_id)})}catch{return}}
@@ -1 +1 @@
1
- import e from"node:path";import{getDataDir as o}from"../db/paths.js";import{isInsideDir as d,resolveLibraryPath as p,toRelPosix as h}from"../market/resolve.js";export class LibraryRepository{db;dataDir;constructor(r,t=o()){this.db=r,this.dataDir=t}add(r){return this.getByPath(r.path)||(this.db.prepare("INSERT OR IGNORE INTO libraries (path, kind, added_at) VALUES (?, ?, ?)").run(this.writeForm(r.path),r.kind,new Date().toISOString()),this.getByPath(r.path))}getByPath(r){for(const t of new Set([r,this.writeForm(r)])){const s=this.db.prepare("SELECT * FROM libraries WHERE path = ?").get(t);if(s)return this.resolveRow(a(s))}}getById(r){const t=this.db.prepare("SELECT * FROM libraries WHERE id = ?").get(r);return t?this.resolveRow(a(t)):void 0}list(){return this.db.prepare("SELECT * FROM libraries ORDER BY added_at DESC").all().map(r=>this.resolveRow(a(r)))}setPath(r,t){this.db.prepare("UPDATE libraries SET path = ? WHERE id = ?").run(this.writeForm(t),r)}remove(r){this.db.prepare("DELETE FROM libraries WHERE id = ?").run(r)}writeForm(r){return e.isAbsolute(r)&&d(r,this.dataDir)?h(r,this.dataDir):r}resolveRow(r){if(!e.isAbsolute(r.path))return{...r,path:p(r,this.dataDir)};const t=e.resolve(r.path);return d(t,this.dataDir)?(this.db.prepare("UPDATE libraries SET path = ? WHERE id = ?").run(h(t,this.dataDir),r.id),{...r,path:t}):r}}function a(i){return{id:Number(i.id),path:String(i.path),kind:i.kind,addedAt:String(i.added_at)}}
1
+ import e from"node:path";import{getDataDir as n}from"../db/paths.js";import{isInsideDir as d,resolveLibraryPath as o,toRelPosix as h}from"../market/resolve.js";export class LibraryRepository{db;dataDir;constructor(t,r=n()){this.db=t,this.dataDir=r}add(t){const r=this.getByPath(t.path);return r||(this.db.prepare("INSERT OR IGNORE INTO libraries (path, kind, added_at) VALUES (?, ?, ?)").run(this.writeForm(t.path),t.kind,new Date().toISOString()),this.getByPath(t.path))}getByPath(t){for(const r of new Set([t,this.writeForm(t)])){const s=this.db.prepare("SELECT * FROM libraries WHERE path = ?").get(r);if(s)return this.resolveRow(a(s))}}getById(t){const r=this.db.prepare("SELECT * FROM libraries WHERE id = ?").get(t);return r?this.resolveRow(a(r)):void 0}list(){return this.db.prepare("SELECT * FROM libraries ORDER BY added_at DESC").all().map(t=>this.resolveRow(a(t)))}setPath(t,r){this.db.prepare("UPDATE libraries SET path = ? WHERE id = ?").run(this.writeForm(r),t)}remove(t){this.db.prepare("DELETE FROM libraries WHERE id = ?").run(t)}writeForm(t){return e.isAbsolute(t)&&d(t,this.dataDir)?h(t,this.dataDir):t}resolveRow(t){if(!e.isAbsolute(t.path))return{...t,path:o(t,this.dataDir)};const r=e.resolve(t.path);return d(r,this.dataDir)?(this.db.prepare("UPDATE libraries SET path = ? WHERE id = ?").run(h(r,this.dataDir),t.id),{...t,path:r}):t}}function a(i){return{id:Number(i.id),path:String(i.path),kind:i.kind,addedAt:String(i.added_at)}}
@@ -1 +1 @@
1
- export class PluginStateRepository{db;constructor(t){this.db=t}get(t){const e=this.db.prepare("SELECT * FROM plugin_states WHERE id = ?").get(t);if(e)return{id:String(e.id),enabled:Number(e.enabled)===1,config:JSON.parse(String(e.config??"{}")),updatedAt:String(e.updated_at)}}list(){return this.db.prepare("SELECT * FROM plugin_states ORDER BY id ASC").all().map(t=>({id:String(t.id),enabled:Number(t.enabled)===1,config:JSON.parse(String(t.config??"{}")),updatedAt:String(t.updated_at)}))}setEnabled(t,e){this.upsert(t,{enabled:e})}setConfig(t,e){this.upsert(t,{config:e})}remove(t){this.db.prepare("DELETE FROM plugin_states WHERE id = ?").run(t)}upsert(t,e){const i=this.get(t),n=new Date().toISOString(),r=e.enabled??i?.enabled??!0,d=e.config??i?.config??{};i?this.db.prepare("UPDATE plugin_states SET enabled = ?, config = ?, updated_at = ? WHERE id = ?").run(r?1:0,JSON.stringify(d),n,t):this.db.prepare("INSERT INTO plugin_states (id, enabled, config, updated_at) VALUES (?, ?, ?, ?)").run(t,r?1:0,JSON.stringify(d),n)}}
1
+ export class PluginStateRepository{db;constructor(t){this.db=t}get(t){const e=this.db.prepare("SELECT * FROM plugin_states WHERE id = ?").get(t);if(e)return{id:String(e.id),enabled:Number(e.enabled)===1,config:JSON.parse(String(e.config??"{}")),updatedAt:String(e.updated_at)}}list(){return this.db.prepare("SELECT * FROM plugin_states ORDER BY id ASC").all().map(e=>({id:String(e.id),enabled:Number(e.enabled)===1,config:JSON.parse(String(e.config??"{}")),updatedAt:String(e.updated_at)}))}setEnabled(t,e){this.upsert(t,{enabled:e})}setConfig(t,e){this.upsert(t,{config:e})}remove(t){this.db.prepare("DELETE FROM plugin_states WHERE id = ?").run(t)}upsert(t,e){const n=this.get(t),i=new Date().toISOString(),s=e.enabled??n?.enabled??!0,r=e.config??n?.config??{};n?this.db.prepare("UPDATE plugin_states SET enabled = ?, config = ?, updated_at = ? WHERE id = ?").run(s?1:0,JSON.stringify(r),i,t):this.db.prepare("INSERT INTO plugin_states (id, enabled, config, updated_at) VALUES (?, ?, ?, ?)").run(t,s?1:0,JSON.stringify(r),i)}}
@@ -19,6 +19,13 @@ export interface RegisterProjectInput {
19
19
  token: string;
20
20
  source: 'init' | 'scan';
21
21
  }
22
+ /**
23
+ * 项目注册键:绝对化 + 归一分隔符/尾斜杠,落**原生**形态。`D:/a/b` 与 `D:\a\b`
24
+ * 必须同键——否则 `ON CONFLICT(path)` 认不出、重复登记还会另发 token 把 hook 事件
25
+ * 拐到新行上。刻意保留原生分隔符:项目根要拿去和 payload / 请求里的 file_path 做
26
+ * 包含判断(asset 越界闸、crossProjectOnly),折成 posix 会让那些闸把合法路径判越界。
27
+ */
28
+ export declare function projectKey(path: string): string;
22
29
  export declare class ProjectRepository {
23
30
  private readonly db;
24
31
  constructor(db: DatabaseSync);
@@ -1,3 +1,3 @@
1
- export class ProjectRepository{db;constructor(t){this.db=t}register(t){const e=new Date().toISOString();return this.db.prepare(`INSERT INTO projects (path, name, token, source, registered_at, last_seen_at)
1
+ import{resolve as o}from"node:path";export function projectKey(r){return o(r)}export class ProjectRepository{db;constructor(e){this.db=e}register(e){const t=new Date().toISOString(),s=projectKey(e.path);return this.db.prepare(`INSERT INTO projects (path, name, token, source, registered_at, last_seen_at)
2
2
  VALUES (?, ?, ?, ?, ?, ?)
3
- ON CONFLICT(path) DO UPDATE SET last_seen_at = excluded.last_seen_at`).run(t.path,t.name,t.token,t.source,e,e),this.getByPath(t.path)}getByPath(t){const e=this.db.prepare("SELECT * FROM projects WHERE path = ?").get(t);return e?a(e):void 0}getByToken(t){const e=this.db.prepare("SELECT * FROM projects WHERE token = ?").get(t);return e?a(e):void 0}getById(t){const e=this.db.prepare("SELECT * FROM projects WHERE id = ?").get(t);return e?a(e):void 0}list(){return this.db.prepare("SELECT * FROM projects WHERE archived_at IS NULL ORDER BY sort_order IS NULL, sort_order ASC, registered_at DESC").all().map(t=>a(t))}listArchived(){return this.db.prepare("SELECT * FROM projects WHERE archived_at IS NOT NULL ORDER BY archived_at DESC").all().map(t=>a(t))}setOrder(t){this.db.exec("BEGIN");try{this.db.exec("UPDATE projects SET sort_order = NULL");const e=this.db.prepare("UPDATE projects SET sort_order = ? WHERE id = ?");t.forEach((s,n)=>{e.run(n,s)}),this.db.exec("COMMIT")}catch(e){throw this.db.exec("ROLLBACK"),e}}touch(t){this.db.prepare("UPDATE projects SET last_seen_at = ? WHERE id = ?").run(new Date().toISOString(),t)}remove(t){this.db.prepare("DELETE FROM projects WHERE id = ?").run(t)}setArchived(t,e){return(e?this.db.prepare("UPDATE projects SET archived_at = ?, sort_order = NULL WHERE id = ?").run(new Date().toISOString(),t):this.db.prepare("UPDATE projects SET archived_at = NULL WHERE id = ?").run(t)).changes>0}getMeta(t){const e=this.db.prepare("SELECT meta FROM projects WHERE id = ?").get(t);if(!e||e.meta==null)return null;try{const s=JSON.parse(String(e.meta));return s&&typeof s=="object"&&!Array.isArray(s)?s:null}catch{return null}}setMeta(t,e){const s={...this.getMeta(t)??{},...e};this.db.prepare("UPDATE projects SET meta = ? WHERE id = ?").run(JSON.stringify(s),t)}}function a(r){return{id:Number(r.id),path:String(r.path),name:String(r.name),token:String(r.token),source:r.source,registeredAt:String(r.registered_at),lastSeenAt:r.last_seen_at==null?null:String(r.last_seen_at),meta:r.meta==null?null:String(r.meta),sortOrder:r.sort_order==null?null:Number(r.sort_order),archivedAt:r.archived_at==null?null:String(r.archived_at)}}
3
+ ON CONFLICT(path) DO UPDATE SET last_seen_at = excluded.last_seen_at`).run(s,e.name,e.token,e.source,t,t),this.getByPath(s)}getByPath(e){const t=this.db.prepare("SELECT * FROM projects WHERE path = ?").get(projectKey(e));return t?n(t):void 0}getByToken(e){const t=this.db.prepare("SELECT * FROM projects WHERE token = ?").get(e);return t?n(t):void 0}getById(e){const t=this.db.prepare("SELECT * FROM projects WHERE id = ?").get(e);return t?n(t):void 0}list(){return this.db.prepare("SELECT * FROM projects WHERE archived_at IS NULL ORDER BY sort_order IS NULL, sort_order ASC, registered_at DESC").all().map(e=>n(e))}listArchived(){return this.db.prepare("SELECT * FROM projects WHERE archived_at IS NOT NULL ORDER BY archived_at DESC").all().map(e=>n(e))}setOrder(e){this.db.exec("BEGIN");try{this.db.exec("UPDATE projects SET sort_order = NULL");const t=this.db.prepare("UPDATE projects SET sort_order = ? WHERE id = ?");e.forEach((s,a)=>{t.run(a,s)}),this.db.exec("COMMIT")}catch(t){throw this.db.exec("ROLLBACK"),t}}touch(e){this.db.prepare("UPDATE projects SET last_seen_at = ? WHERE id = ?").run(new Date().toISOString(),e)}remove(e){this.db.prepare("DELETE FROM projects WHERE id = ?").run(e)}setArchived(e,t){return(t?this.db.prepare("UPDATE projects SET archived_at = ?, sort_order = NULL WHERE id = ?").run(new Date().toISOString(),e):this.db.prepare("UPDATE projects SET archived_at = NULL WHERE id = ?").run(e)).changes>0}getMeta(e){const t=this.db.prepare("SELECT meta FROM projects WHERE id = ?").get(e);if(!t||t.meta==null)return null;try{const s=JSON.parse(String(t.meta));return s&&typeof s=="object"&&!Array.isArray(s)?s:null}catch{return null}}setMeta(e,t){const s={...this.getMeta(e)??{},...t};this.db.prepare("UPDATE projects SET meta = ? WHERE id = ?").run(JSON.stringify(s),e)}}function n(r){return{id:Number(r.id),path:String(r.path),name:String(r.name),token:String(r.token),source:r.source,registeredAt:String(r.registered_at),lastSeenAt:r.last_seen_at==null?null:String(r.last_seen_at),meta:r.meta==null?null:String(r.meta),sortOrder:r.sort_order==null?null:Number(r.sort_order),archivedAt:r.archived_at==null?null:String(r.archived_at)}}
@@ -1,4 +1,4 @@
1
- export class PushSubscriptionRepository{db;constructor(t){this.db=t}upsert(t){return this.db.prepare(`INSERT INTO push_subscriptions (endpoint, p256dh, auth, user_agent, created_at)
1
+ export class PushSubscriptionRepository{db;constructor(e){this.db=e}upsert(e){return this.db.prepare(`INSERT INTO push_subscriptions (endpoint, p256dh, auth, user_agent, created_at)
2
2
  VALUES (?, ?, ?, ?, ?)
3
3
  ON CONFLICT(endpoint) DO UPDATE SET p256dh = excluded.p256dh, auth = excluded.auth,
4
- user_agent = excluded.user_agent`).run(t.endpoint,t.p256dh,t.auth,t.userAgent??null,new Date().toISOString()),this.getById(t.endpoint)}getById(t){const n=this.db.prepare("SELECT * FROM push_subscriptions WHERE endpoint = ?").get(t);return n?r(n):void 0}list(){return this.db.prepare("SELECT * FROM push_subscriptions ORDER BY created_at, endpoint").all().map(r)}deleteByEndpoint(t){return Number(this.db.prepare("DELETE FROM push_subscriptions WHERE endpoint = ?").run(t).changes)===1}}function r(e){return{endpoint:String(e.endpoint),p256dh:String(e.p256dh),auth:String(e.auth),userAgent:e.user_agent==null?null:String(e.user_agent),createdAt:String(e.created_at)}}
4
+ user_agent = excluded.user_agent`).run(e.endpoint,e.p256dh,e.auth,e.userAgent??null,new Date().toISOString()),this.getById(e.endpoint)}getById(e){const n=this.db.prepare("SELECT * FROM push_subscriptions WHERE endpoint = ?").get(e);return n?r(n):void 0}list(){return this.db.prepare("SELECT * FROM push_subscriptions ORDER BY created_at, endpoint").all().map(r)}deleteByEndpoint(e){return Number(this.db.prepare("DELETE FROM push_subscriptions WHERE endpoint = ?").run(e).changes)===1}}function r(t){return{endpoint:String(t.endpoint),p256dh:String(t.p256dh),auth:String(t.auth),userAgent:t.user_agent==null?null:String(t.user_agent),createdAt:String(t.created_at)}}
@@ -1,5 +1,5 @@
1
- export const WATCH_ON_VALUES=["idle","turnEnd","messageFailed"],DEFAULT_WATCH_ON=["idle"];export class SessionWatchRepository{db;constructor(e){this.db=e}insert(e){const t=this.db.prepare(`INSERT INTO session_watches (watcher_session_id, target_session_id, notify, on_events, remaining, created_at)
2
- VALUES (?, ?, ?, ?, ?, ?)`).run(e.watcherSessionId,e.targetSessionId,e.notify,JSON.stringify(e.onEvents?.length?e.onEvents:DEFAULT_WATCH_ON),e.times===void 0?1:e.times,new Date().toISOString());return this.getById(Number(t.lastInsertRowid))}getById(e){const t=this.db.prepare("SELECT * FROM session_watches WHERE id = ?").get(e);return t?n(t):void 0}listPendingByTarget(e){return this.db.prepare("SELECT * FROM session_watches WHERE target_session_id = ? AND fired_at IS NULL").all(e).map(n)}listPending(){return this.db.prepare("SELECT * FROM session_watches WHERE fired_at IS NULL ORDER BY id").all().map(n)}consumeHit(e){if(Number(this.db.prepare(`UPDATE session_watches SET remaining = remaining - 1
3
- WHERE id = ? AND fired_at IS NULL AND remaining > 0`).run(e).changes)===1)return this.db.prepare("UPDATE session_watches SET fired_at = ? WHERE id = ? AND remaining = 0 AND fired_at IS NULL").run(new Date().toISOString(),e),this.getById(e)??null;const t=this.getById(e);return t&&t.remaining===null&&t.firedAt===null?t:null}updateById(e,t){const i=this.getById(e);if(!(!i||i.firedAt!==null))return this.db.prepare("UPDATE session_watches SET on_events = ?, remaining = ? WHERE id = ? AND fired_at IS NULL").run(JSON.stringify(t.onEvents??i.onEvents),t.remaining,e),this.getById(e)}deleteById(e){return Number(this.db.prepare("DELETE FROM session_watches WHERE id = ?").run(e).changes)===1}deleteBySession(e){return Number(this.db.prepare("DELETE FROM session_watches WHERE target_session_id = ? OR watcher_session_id = ?").run(e,e).changes)}deleteByProject(e){return Number(this.db.prepare(`DELETE FROM session_watches
1
+ export const WATCH_ON_VALUES=["idle","turnEnd","messageFailed"],DEFAULT_WATCH_ON=["idle"];export class SessionWatchRepository{db;constructor(e){this.db=e}insert(e){const s=this.db.prepare(`INSERT INTO session_watches (watcher_session_id, target_session_id, notify, on_events, remaining, created_at)
2
+ VALUES (?, ?, ?, ?, ?, ?)`).run(e.watcherSessionId,e.targetSessionId,e.notify,JSON.stringify(e.onEvents?.length?e.onEvents:DEFAULT_WATCH_ON),e.times===void 0?1:e.times,new Date().toISOString());return this.getById(Number(s.lastInsertRowid))}getById(e){const s=this.db.prepare("SELECT * FROM session_watches WHERE id = ?").get(e);return s?i(s):void 0}listPendingByTarget(e){return this.db.prepare("SELECT * FROM session_watches WHERE target_session_id = ? AND fired_at IS NULL").all(e).map(i)}listPending(){return this.db.prepare("SELECT * FROM session_watches WHERE fired_at IS NULL ORDER BY id").all().map(i)}consumeHit(e){if(Number(this.db.prepare(`UPDATE session_watches SET remaining = remaining - 1
3
+ WHERE id = ? AND fired_at IS NULL AND remaining > 0`).run(e).changes)===1)return this.db.prepare("UPDATE session_watches SET fired_at = ? WHERE id = ? AND remaining = 0 AND fired_at IS NULL").run(new Date().toISOString(),e),this.getById(e)??null;const t=this.getById(e);return t&&t.remaining===null&&t.firedAt===null?t:null}updateById(e,s){const t=this.getById(e);if(!(!t||t.firedAt!==null))return this.db.prepare("UPDATE session_watches SET on_events = ?, remaining = ? WHERE id = ? AND fired_at IS NULL").run(JSON.stringify(s.onEvents??t.onEvents),s.remaining,e),this.getById(e)}deleteById(e){return Number(this.db.prepare("DELETE FROM session_watches WHERE id = ?").run(e).changes)===1}deleteBySession(e){return Number(this.db.prepare("DELETE FROM session_watches WHERE target_session_id = ? OR watcher_session_id = ?").run(e,e).changes)}deleteByProject(e){return Number(this.db.prepare(`DELETE FROM session_watches
4
4
  WHERE target_session_id IN (SELECT session_id FROM sessions WHERE project_id = ?)
5
- OR watcher_session_id IN (SELECT session_id FROM sessions WHERE project_id = ?)`).run(e,e).changes)}}function n(s){return{id:Number(s.id),watcherSessionId:s.watcher_session_id==null?null:String(s.watcher_session_id),targetSessionId:String(s.target_session_id),onEvents:parseOn(s.on_events),remaining:s.remaining==null?null:Number(s.remaining),notify:s.notify==null?null:String(s.notify),firedAt:s.fired_at==null?null:String(s.fired_at),createdAt:String(s.created_at)}}export function parseOn(s){if(typeof s!="string"||!s)return[...DEFAULT_WATCH_ON];try{const e=JSON.parse(s);if(!Array.isArray(e))return[...DEFAULT_WATCH_ON];const t=e.filter(i=>WATCH_ON_VALUES.includes(i));return t.length?t:[...DEFAULT_WATCH_ON]}catch{return[...DEFAULT_WATCH_ON]}}
5
+ OR watcher_session_id IN (SELECT session_id FROM sessions WHERE project_id = ?)`).run(e,e).changes)}}function i(n){return{id:Number(n.id),watcherSessionId:n.watcher_session_id==null?null:String(n.watcher_session_id),targetSessionId:String(n.target_session_id),onEvents:parseOn(n.on_events),remaining:n.remaining==null?null:Number(n.remaining),notify:n.notify==null?null:String(n.notify),firedAt:n.fired_at==null?null:String(n.fired_at),createdAt:String(n.created_at)}}export function parseOn(n){if(typeof n!="string"||!n)return[...DEFAULT_WATCH_ON];try{const e=JSON.parse(n);if(!Array.isArray(e))return[...DEFAULT_WATCH_ON];const s=e.filter(t=>WATCH_ON_VALUES.includes(t));return s.length?s:[...DEFAULT_WATCH_ON]}catch{return[...DEFAULT_WATCH_ON]}}
@@ -13,18 +13,23 @@ export interface SessionRow {
13
13
  eventCount?: number;
14
14
  /** 会话归属 harness = 最新一条 hook 事件的 source(listWithCounts 查询填充)。 */
15
15
  harness?: Harness | null;
16
- /** 回合开合的服务端基线:最后一条 UserPromptSubmit 晚于最后一条 Stop/SessionEnd
17
- * (listWithCounts 查询填充)——前端帧态丢失/重挂载时的权威兜底。 */
16
+ /** 回合开合的服务端基线:最后一条 UserPromptSubmit 晚于最后一条回合收尾事件
17
+ * (Stop/StopFailure/SessionEnd,以及非压缩的 SessionStart——终端挂掉后 resume
18
+ * 起来不会有 Stop;listWithCounts 查询填充)——前端帧态丢失/重挂载时的权威兜底。 */
18
19
  turnOpen?: boolean;
19
20
  /** 最新一条 hook 事件类型(listWithCounts 查询填充)——web 状态总线判「关闭」
20
21
  * (SessionEnd)不必再为每个会话拉事件。 */
21
22
  lastEventType?: string | null;
22
- /** 无 prompt 幽灵会话的自动清理时刻(= lastSeenAt + 保活窗;listWithCounts
23
- * 派生,null = 收到过 prompt 的正常会话,只走全局事件保留期)。 */
23
+ /** 这个会话真跑过(有过 UserPromptSubmit 或收尾事件;listWithCounts 查询填充)。
24
+ * 「有没有转录」的判据要用它:没跑过的会话本来就还没写过转录,没内容不是丢失。 */
25
+ hasRun?: boolean;
26
+ /** 自动移除时刻(= lastSeenAt + 该档保留窗;listWithCounts 派生,两档——没等到
27
+ * 用户 prompt 的新会话快过期,收到过 prompt 的走设置页「无操作会话 n 天后自动
28
+ * 移除」那条线;会话行剩余存活倒计时即读它)。 */
24
29
  autoDeleteAt?: string | null;
25
30
  }
26
- /** 无 prompt(纯会话引导事件)会话的保活窗:静默超过该窗即自动清理——刚开的
27
- * 会话在第一条 prompt 落下前正是这个形状,窗口就是「延迟删除」的分界线。 */
31
+ /** 没等到用户 prompt 的新会话快过期窗(ms)= 1 小时:新建会话要看得见,但无 prompt
32
+ * 的会话不配占 90 天窗口。 */
28
33
  export declare const PROMPTLESS_GRACE_MS: number;
29
34
  /**
30
35
  * sessions 表:daemon 自存的会话标题(不写回 Claude Code 内部文件)。
@@ -44,10 +49,29 @@ export declare class SessionRepository {
44
49
  listWithCounts(projectId?: number): SessionRow[];
45
50
  /**
46
51
  * 单会话 turnOpen(消息域 auto 裁决的忙/闲基准):语义与 listWithCounts 的
47
- * 计算字段完全一致(最后一条 UserPromptSubmit 晚于最后一条 Stop/SessionEnd)。
48
- * 会话不存在 → null;存在但零事件 → false(同列表口径:无 ups 即不判开)。
52
+ * 计算字段完全一致(最后一条 UserPromptSubmit 晚于最后一条回合收尾事件,
53
+ * 判据同 turnCloseSql)。会话不存在 → null;存在但零事件 → false(同列表口径:
54
+ * 无 ups 即不判开)。
49
55
  */
50
56
  turnOpenOf(sessionId: string): boolean | null;
57
+ /**
58
+ * 单会话「真跑过吗」(有过 UPS 或收尾事件),与 listWithCounts 的 `hasRun` 同一
59
+ * 判据。打开终端前判转录是否**丢过**要用它:刚建、还没发第一条 prompt 的会话
60
+ * 本来就还没有转录文件,那不算丢失。
61
+ */
62
+ hasRunOf(sessionId: string): boolean;
63
+ /**
64
+ * 单会话判活输入(`daemon/claude-liveness.ts` 的 `pidAliveOf` 消费形状)——
65
+ * harness / lastEventType / eventCount 都是 listWithCounts 的计算字段,单行版与
66
+ * 列表版同源同判据(同 turnOpenOf 的先例)。会话不存在 → null。
67
+ */
68
+ livenessRowOf(sessionId: string): {
69
+ sessionId: string;
70
+ harness: Harness | null;
71
+ lastSeenAt: string;
72
+ lastEventType: string | null;
73
+ eventCount: number;
74
+ } | null;
51
75
  /**
52
76
  * dashboard 改名:display 名(title)立即落,pending_title 负责把新名同步给
53
77
  * Claude 侧(下次 SessionStart(resume)/UserPromptSubmit 经 hook 下发)——dashboard
@@ -1,5 +1,5 @@
1
- import{isHarness as E}from"@weavecode/core";export const PROMPTLESS_GRACE_MS=10*6e4;export class SessionRepository{db;constructor(e){this.db=e}touch(e,s){const t=new Date().toISOString();return this.db.prepare(`INSERT INTO sessions (session_id, project_id, last_seen_at, created_at)
1
+ import{HARNESS_COMPACTION as S,HARNESS_LIFECYCLE as p,isHarness as _}from"@weavecode/core";import{readDaemonSettingsSync as c,DEFAULT_SESSION_IDLE_DAYS as R}from"../daemon/settings.js";export const PROMPTLESS_GRACE_MS=60*6e4;function T(){return(c().sessions?.idleDays??R)*864e5}function d(i){const e=new Set;for(const n of Object.values(p)){for(const o of n.close)e.add(o);for(const o of n.sessionMarkers)e.add(o);n.sessionExit&&e.add(n.sessionExit)}const s=[...e].map(n=>`'${n}'`).join(","),t=Object.values(S).filter(n=>n.end&&n.endSource).map(n=>`(${i}.event_type = '${n.end}' AND COALESCE(json_extract(${i}.payload, '$.raw.source'), '') = '${n.endSource}')`).join(" OR ");return`${i}.event_type IN (${s})${t?` AND NOT (${t})`:""}`}export class SessionRepository{db;constructor(e){this.db=e}touch(e,s){const t=new Date().toISOString();return this.db.prepare(`INSERT INTO sessions (session_id, project_id, last_seen_at, created_at)
2
2
  VALUES (?, ?, ?, ?)
3
3
  ON CONFLICT(session_id) DO UPDATE SET
4
4
  last_seen_at = excluded.last_seen_at,
5
- project_id = COALESCE(sessions.project_id, excluded.project_id)`).run(e,s??null,t,t),this.getBySessionId(e)}getBySessionId(e){const s=this.db.prepare("SELECT * FROM sessions WHERE session_id = ?").get(e);return s?o(s):void 0}list(e){return(e===void 0?this.db.prepare("SELECT * FROM sessions ORDER BY created_at DESC LIMIT 200").all():this.db.prepare("SELECT * FROM sessions WHERE project_id = ? ORDER BY created_at DESC LIMIT 200").all(e)).map(o)}listWithCounts(e){const s="SELECT s.*, COUNT(e.id) AS event_count, (SELECT e2.source FROM hook_events e2 WHERE e2.session_id = s.session_id ORDER BY e2.id DESC LIMIT 1) AS harness, (SELECT e5.event_type FROM hook_events e5 WHERE e5.session_id = s.session_id ORDER BY e5.id DESC LIMIT 1) AS last_event_type, (SELECT MAX(e3.id) FROM hook_events e3 WHERE e3.session_id = s.session_id AND e3.event_type = 'UserPromptSubmit') AS last_ups, (SELECT MAX(e4.id) FROM hook_events e4 WHERE e4.session_id = s.session_id AND e4.event_type IN ('Stop', 'SessionEnd')) AS last_stop FROM sessions s LEFT JOIN hook_events e ON e.session_id = s.session_id";return(e===void 0?this.db.prepare(`${s} GROUP BY s.session_id ORDER BY s.created_at DESC LIMIT 200`).all():this.db.prepare(`${s} WHERE s.project_id = ? GROUP BY s.session_id ORDER BY s.created_at DESC LIMIT 200`).all(e)).map(t=>{const i=t.harness,r=t.last_ups,l=t.last_stop,d=t.last_event_type;return{...o(t),eventCount:Number(t.event_count??0),harness:E(i)?i:null,turnOpen:r!=null&&(l==null||Number(r)>Number(l)),lastEventType:d??null,autoDeleteAt:r==null?new Date(Date.parse(String(t.last_seen_at))+PROMPTLESS_GRACE_MS).toISOString():null}})}turnOpenOf(e){const s=this.db.prepare("SELECT (SELECT MAX(e3.id) FROM hook_events e3 WHERE e3.session_id = ? AND e3.event_type = 'UserPromptSubmit') AS last_ups, (SELECT MAX(e4.id) FROM hook_events e4 WHERE e4.session_id = ? AND e4.event_type IN ('Stop', 'SessionEnd')) AS last_stop").get(e,e);if(!this.getBySessionId(e))return null;const t=s?.last_ups??null,i=s?.last_stop??null;return t!=null&&(i==null||Number(t)>Number(i))}setPendingTitle(e,s){const t=this.getBySessionId(e);if(t)return t.pendingTitle===s?t:t.title===s?(t.pendingTitle!=null&&this.db.prepare("UPDATE sessions SET pending_title = NULL WHERE session_id = ?").run(e),this.getBySessionId(e)):(this.db.prepare("UPDATE sessions SET title = ?, pending_title = ? WHERE session_id = ?").run(s,s,e),this.getBySessionId(e))}applyReportedTitle(e,s){const t=this.getBySessionId(e);return t?t.pendingTitle!=null?s===t.pendingTitle?(this.db.prepare("UPDATE sessions SET title = ?, pending_title = NULL WHERE session_id = ?").run(s,e),t.title!==s):!1:s!=null&&s!==t.title?(this.db.prepare("UPDATE sessions SET title = ? WHERE session_id = ?").run(s,e),!0):!1:!1}confirmPendingTitle(e,s){return this.db.prepare("UPDATE sessions SET title = ?, pending_title = NULL WHERE session_id = ? AND pending_title = ?").run(s,e,s).changes>0}remove(e){this.db.prepare("DELETE FROM sessions WHERE session_id = ?").run(e)}removeByProject(e){return Number(this.db.prepare("DELETE FROM sessions WHERE project_id = ?").run(e).changes)}}function o(n){return{sessionId:String(n.session_id),projectId:n.project_id==null?null:Number(n.project_id),title:n.title==null?null:String(n.title),pendingTitle:n.pending_title==null?null:String(n.pending_title),lastSeenAt:String(n.last_seen_at),createdAt:String(n.created_at)}}
5
+ project_id = COALESCE(sessions.project_id, excluded.project_id)`).run(e,s??null,t,t),this.getBySessionId(e)}getBySessionId(e){const s=this.db.prepare("SELECT * FROM sessions WHERE session_id = ?").get(e);return s?l(s):void 0}list(e){return(e===void 0?this.db.prepare("SELECT * FROM sessions ORDER BY created_at DESC LIMIT 200").all():this.db.prepare("SELECT * FROM sessions WHERE project_id = ? ORDER BY created_at DESC LIMIT 200").all(e)).map(l)}listWithCounts(e){const s=`SELECT s.*, COUNT(e.id) AS event_count, (SELECT e2.source FROM hook_events e2 WHERE e2.session_id = s.session_id ORDER BY e2.id DESC LIMIT 1) AS harness, (SELECT e5.event_type FROM hook_events e5 WHERE e5.session_id = s.session_id ORDER BY e5.id DESC LIMIT 1) AS last_event_type, (SELECT MAX(e3.id) FROM hook_events e3 WHERE e3.session_id = s.session_id AND e3.event_type = 'UserPromptSubmit') AS last_ups, (SELECT MAX(e6.id) FROM hook_events e6 WHERE e6.session_id = s.session_id AND ${d("e6")}) AS last_close, (SELECT MAX(e4.id) FROM hook_events e4 WHERE e4.session_id = s.session_id AND e4.event_type IN ('Stop', 'StopFailure', 'SessionEnd')) AS last_stop FROM sessions s LEFT JOIN hook_events e ON e.session_id = s.session_id`;return(e===void 0?this.db.prepare(`${s} GROUP BY s.session_id ORDER BY s.created_at DESC LIMIT 200`).all():this.db.prepare(`${s} WHERE s.project_id = ? GROUP BY s.session_id ORDER BY s.created_at DESC LIMIT 200`).all(e)).map(n=>{const o=n.harness,r=n.last_ups,E=n.last_close,a=n.last_stop,u=n.last_event_type;return{...l(n),eventCount:Number(n.event_count??0),harness:_(o)?o:null,turnOpen:r!=null&&(E==null||Number(r)>Number(E)),lastEventType:u??null,hasRun:r!=null||a!=null,autoDeleteAt:new Date(Date.parse(String(n.last_seen_at))+(r==null?PROMPTLESS_GRACE_MS:T())).toISOString()}})}turnOpenOf(e){const s=this.db.prepare(`SELECT (SELECT MAX(e3.id) FROM hook_events e3 WHERE e3.session_id = ? AND e3.event_type = 'UserPromptSubmit') AS last_ups, (SELECT MAX(e6.id) FROM hook_events e6 WHERE e6.session_id = ? AND ${d("e6")}) AS last_close`).get(e,e);if(!this.getBySessionId(e))return null;const t=s?.last_ups??null,n=s?.last_close??null;return t!=null&&(n==null||Number(t)>Number(n))}hasRunOf(e){const s=this.db.prepare("SELECT EXISTS(SELECT 1 FROM hook_events e WHERE e.session_id = ? AND e.event_type IN ('UserPromptSubmit','Stop','StopFailure','SessionEnd')) AS hit").get(e);return Number(s?.hit??0)>0}livenessRowOf(e){const s=this.db.prepare("SELECT s.session_id, s.last_seen_at, COUNT(e.id) AS event_count, (SELECT e2.source FROM hook_events e2 WHERE e2.session_id = s.session_id ORDER BY e2.id DESC LIMIT 1) AS harness, (SELECT e5.event_type FROM hook_events e5 WHERE e5.session_id = s.session_id ORDER BY e5.id DESC LIMIT 1) AS last_event_type FROM sessions s LEFT JOIN hook_events e ON e.session_id = s.session_id WHERE s.session_id = ? GROUP BY s.session_id").get(e);if(!s)return null;const t=s.harness;return{sessionId:String(s.session_id),harness:_(t)?t:null,lastSeenAt:String(s.last_seen_at),lastEventType:s.last_event_type==null?null:String(s.last_event_type),eventCount:Number(s.event_count??0)}}setPendingTitle(e,s){const t=this.getBySessionId(e);if(t)return t.pendingTitle===s?t:t.title===s?(t.pendingTitle!=null&&this.db.prepare("UPDATE sessions SET pending_title = NULL WHERE session_id = ?").run(e),this.getBySessionId(e)):(this.db.prepare("UPDATE sessions SET title = ?, pending_title = ? WHERE session_id = ?").run(s,s,e),this.getBySessionId(e))}applyReportedTitle(e,s){const t=this.getBySessionId(e);return t?t.pendingTitle!=null?s===t.pendingTitle?(this.db.prepare("UPDATE sessions SET title = ?, pending_title = NULL WHERE session_id = ?").run(s,e),t.title!==s):!1:s!=null&&s!==t.title?(this.db.prepare("UPDATE sessions SET title = ? WHERE session_id = ?").run(s,e),!0):!1:!1}confirmPendingTitle(e,s){return this.db.prepare("UPDATE sessions SET title = ?, pending_title = NULL WHERE session_id = ? AND pending_title = ?").run(s,e,s).changes>0}remove(e){this.db.prepare("DELETE FROM sessions WHERE session_id = ?").run(e)}removeByProject(e){return Number(this.db.prepare("DELETE FROM sessions WHERE project_id = ?").run(e).changes)}}function l(i){return{sessionId:String(i.session_id),projectId:i.project_id==null?null:Number(i.project_id),title:i.title==null?null:String(i.title),pendingTitle:i.pending_title==null?null:String(i.pending_title),lastSeenAt:String(i.last_seen_at),createdAt:String(i.created_at)}}
@@ -0,0 +1,50 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
2
+ /** pending = 已打进会话、等回答;answered = 认领到回答;timeout = 超窗没等到。 */
3
+ export type SideQuestionStatus = 'pending' | 'answered' | 'timeout';
4
+ export interface SideQuestionRow {
5
+ id: number;
6
+ sessionId: string;
7
+ projectId: number | null;
8
+ question: string;
9
+ answer: string | null;
10
+ /** 认领到的那条采集事件(SubagentStop);事件清掉后只留 answer 原文。 */
11
+ answerEventId: number | null;
12
+ status: SideQuestionStatus;
13
+ askedAt: string;
14
+ answeredAt: string | null;
15
+ }
16
+ /**
17
+ * 旁路会话域(claude = `/btw`):宿主侧问不留任何痕迹——问题只有 weave 自己
18
+ * 发出去时才知道,回答只能从采集事件认领。故这是**记录系统**而不是递送管道:
19
+ * 消息域「不落库」的规矩在这里不适用,不记就永远没有。
20
+ */
21
+ export declare class SideQuestionRepository {
22
+ private readonly db;
23
+ constructor(db: DatabaseSync);
24
+ insert(input: {
25
+ sessionId: string;
26
+ projectId: number | null;
27
+ question: string;
28
+ }): SideQuestionRow;
29
+ getById(id: number): SideQuestionRow | undefined;
30
+ /**
31
+ * 认领一条回答:该会话**最早一条**仍未落定、且提问时刻不早于 `since` 的行。
32
+ * 条件更新(`status = 'pending'`)天然幂等,并发只赢一次;返回 null = 没有
33
+ * 待认领的行(人工在宿主面板里发起的侧问、或回答来得太晚)。
34
+ */
35
+ claim(sessionId: string, answer: string, eventId: number, since: string): SideQuestionRow | null;
36
+ /**
37
+ * 待答超窗落定(时间窗过了宿主不会再作答:面板可能被 Esc 关掉、主回合可能
38
+ * 已经收尾)。惰性执行——在列表读取前跑一次,不另起定时器。
39
+ */
40
+ expirePending(before: string): number;
41
+ /** 游标向前翻页(`beforeId` 缺省 = 最新一页),倒序。 */
42
+ list(sessionId: string, opts: {
43
+ beforeId?: number;
44
+ limit: number;
45
+ }): SideQuestionRow[];
46
+ /** 删会话级联(与 events / 队列行同口径,不计数)。 */
47
+ deleteBySession(sessionId: string): number;
48
+ /** 归档/注销项目连带清(行上自带 project_id,不必赶在会话行删除之前)。 */
49
+ deleteByProject(projectId: number): number;
50
+ }
@@ -0,0 +1,6 @@
1
+ export class SideQuestionRepository{db;constructor(e){this.db=e}insert(e){const t=this.db.prepare(`INSERT INTO side_questions (session_id, project_id, question, status, asked_at)
2
+ VALUES (?, ?, ?, 'pending', ?)`).run(e.sessionId,e.projectId,e.question,new Date().toISOString());return this.getById(Number(t.lastInsertRowid))}getById(e){const t=this.db.prepare("SELECT * FROM side_questions WHERE id = ?").get(e);return t?d(t):void 0}claim(e,t,n,i){const r=this.db.prepare(`SELECT id FROM side_questions
3
+ WHERE session_id = ? AND status = 'pending' AND asked_at >= ?
4
+ ORDER BY id LIMIT 1`).get(e,i);return r&&Number(this.db.prepare(`UPDATE side_questions SET answer = ?, answer_event_id = ?, status = 'answered', answered_at = ?
5
+ WHERE id = ? AND status = 'pending'`).run(t,n,new Date().toISOString(),r.id).changes)===1?this.getById(r.id)??null:null}expirePending(e){return Number(this.db.prepare(`UPDATE side_questions SET status = 'timeout'
6
+ WHERE status = 'pending' AND asked_at < ?`).run(e).changes)}list(e,t){const{beforeId:n,limit:i}=t;return(n?this.db.prepare("SELECT * FROM side_questions WHERE session_id = ? AND id < ? ORDER BY id DESC LIMIT ?").all(e,n,i):this.db.prepare("SELECT * FROM side_questions WHERE session_id = ? ORDER BY id DESC LIMIT ?").all(e,i)).map(d)}deleteBySession(e){return Number(this.db.prepare("DELETE FROM side_questions WHERE session_id = ?").run(e).changes)}deleteByProject(e){return Number(this.db.prepare("DELETE FROM side_questions WHERE project_id = ?").run(e).changes)}}function d(s){return{id:Number(s.id),sessionId:String(s.session_id),projectId:s.project_id==null?null:Number(s.project_id),question:String(s.question),answer:s.answer==null?null:String(s.answer),answerEventId:s.answer_event_id==null?null:Number(s.answer_event_id),status:String(s.status),askedAt:String(s.asked_at),answeredAt:s.answered_at==null?null:String(s.answered_at)}}