@weavecode/weave 0.1.4 → 0.1.6

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 (362) hide show
  1. package/node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/encodeurl/README.md +14 -0
  2. package/node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/encodeurl/index.js +5 -10
  3. package/node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/encodeurl/package.json +10 -6
  4. package/node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/qs/CHANGELOG.md +42 -0
  5. package/node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/qs/README.md +38 -10
  6. package/node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/qs/dist/qs.js +18 -18
  7. package/node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/qs/lib/parse.js +16 -4
  8. package/node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/qs/lib/utils.js +13 -2
  9. package/node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/qs/package.json +1 -1
  10. package/node_modules/@modelcontextprotocol/sdk/node_modules/express-rate-limit/dist/index.cjs +6 -2
  11. package/node_modules/@modelcontextprotocol/sdk/node_modules/express-rate-limit/dist/index.mjs +6 -2
  12. package/node_modules/@modelcontextprotocol/sdk/node_modules/express-rate-limit/package.json +10 -10
  13. package/node_modules/@modelcontextprotocol/sdk/node_modules/jose/dist/types/jwt/decrypt.d.ts +3 -0
  14. package/node_modules/@modelcontextprotocol/sdk/node_modules/jose/dist/types/jwt/unsecured.d.ts +2 -0
  15. package/node_modules/@modelcontextprotocol/sdk/node_modules/jose/dist/types/jwt/verify.d.ts +3 -0
  16. package/node_modules/@modelcontextprotocol/sdk/node_modules/jose/dist/types/types.d.ts +8 -0
  17. package/node_modules/@modelcontextprotocol/sdk/node_modules/jose/dist/types/util/decode_jwt.d.ts +1 -0
  18. package/node_modules/@modelcontextprotocol/sdk/node_modules/jose/dist/webapi/jwks/remote.js +1 -1
  19. package/node_modules/@modelcontextprotocol/sdk/node_modules/jose/package.json +1 -1
  20. package/node_modules/@weavecode/cli/dist/cli.js +4 -4
  21. package/node_modules/@weavecode/cli/dist/commands/chpass.js +1 -1
  22. package/node_modules/@weavecode/cli/dist/commands/daemon-auth.js +1 -1
  23. package/node_modules/@weavecode/cli/dist/commands/daemon.d.ts +2 -0
  24. package/node_modules/@weavecode/cli/dist/commands/daemon.js +2 -2
  25. package/node_modules/@weavecode/cli/dist/commands/dashboard.js +2 -2
  26. package/node_modules/@weavecode/cli/dist/commands/doctor.js +1 -1
  27. package/node_modules/@weavecode/cli/dist/commands/init.js +1 -1
  28. package/node_modules/@weavecode/cli/dist/commands/msg.js +2 -2
  29. package/node_modules/@weavecode/cli/dist/commands/scan.js +1 -1
  30. package/node_modules/@weavecode/cli/dist/commands/status.js +2 -2
  31. package/node_modules/@weavecode/cli/dist/commands/uninstall.js +1 -1
  32. package/node_modules/@weavecode/cli/dist/i18n/dict/en.js +4 -4
  33. package/node_modules/@weavecode/cli/dist/i18n/dict/zh.d.ts +5 -1
  34. package/node_modules/@weavecode/cli/dist/i18n/dict/zh.js +5 -5
  35. package/node_modules/@weavecode/cli/dist/i18n/index.js +1 -1
  36. package/node_modules/@weavecode/cli/dist/i18n/t.js +1 -1
  37. package/node_modules/@weavecode/cli/dist/init/claudemd-gen.js +3 -3
  38. package/node_modules/@weavecode/cli/dist/init/executor.js +3 -3
  39. package/node_modules/@weavecode/cli/dist/init/git-commit.js +2 -2
  40. package/node_modules/@weavecode/cli/dist/init/harnesses/claude.js +7 -7
  41. package/node_modules/@weavecode/cli/dist/init/harnesses/codex.js +1 -1
  42. package/node_modules/@weavecode/cli/dist/init/instruction-link.js +1 -1
  43. package/node_modules/@weavecode/cli/dist/init/plugin-templates.js +1 -1
  44. package/node_modules/@weavecode/cli/dist/init/prompt.js +4 -4
  45. package/node_modules/@weavecode/cli/dist/init/provision.js +1 -1
  46. package/node_modules/@weavecode/cli/dist/init/uninstall.js +6 -6
  47. package/node_modules/@weavecode/cli/dist/init/wiring.js +1 -1
  48. package/node_modules/@weavecode/cli/dist/parser.js +1 -1
  49. package/node_modules/@weavecode/cli/dist/statusline/ansi.js +1 -1
  50. package/node_modules/@weavecode/cli/dist/statusline/mock-input.js +1 -1
  51. package/node_modules/@weavecode/cli/dist/statusline/preview.js +2 -2
  52. package/node_modules/@weavecode/cli/package.json +5 -5
  53. package/node_modules/@weavecode/core/dist/harness-checks.js +1 -1
  54. package/node_modules/@weavecode/core/dist/harness.d.ts +14 -0
  55. package/node_modules/@weavecode/core/dist/harness.js +1 -1
  56. package/node_modules/@weavecode/core/dist/index.d.ts +1 -1
  57. package/node_modules/@weavecode/core/dist/index.js +1 -1
  58. package/node_modules/@weavecode/core/dist/platform.js +1 -1
  59. package/node_modules/@weavecode/core/dist/types.js +1 -1
  60. package/node_modules/@weavecode/core/dist/utils/fs.js +1 -1
  61. package/node_modules/@weavecode/core/dist/utils/logger.js +1 -1
  62. package/node_modules/@weavecode/core/package.json +1 -1
  63. package/node_modules/@weavecode/harness/dist/claude/assets.js +4 -4
  64. package/node_modules/@weavecode/harness/dist/claude/capture.js +9 -9
  65. package/node_modules/@weavecode/harness/dist/claude/commands.d.ts +3 -0
  66. package/node_modules/@weavecode/harness/dist/claude/commands.js +2 -3
  67. package/node_modules/@weavecode/harness/dist/claude/config.js +2 -2
  68. package/node_modules/@weavecode/harness/dist/claude/detect.js +1 -1
  69. package/node_modules/@weavecode/harness/dist/claude/edits.js +1 -1
  70. package/node_modules/@weavecode/harness/dist/claude/event-source.js +1 -1
  71. package/node_modules/@weavecode/harness/dist/claude/images.js +2 -2
  72. package/node_modules/@weavecode/harness/dist/claude/index.d.ts +1 -0
  73. package/node_modules/@weavecode/harness/dist/claude/index.js +1 -1
  74. package/node_modules/@weavecode/harness/dist/claude/liveness.js +1 -1
  75. package/node_modules/@weavecode/harness/dist/claude/mcp-gen.js +1 -1
  76. package/node_modules/@weavecode/harness/dist/claude/messaging.js +2 -2
  77. package/node_modules/@weavecode/harness/dist/claude/model-aliases.js +1 -1
  78. package/node_modules/@weavecode/harness/dist/claude/narration.d.ts +0 -11
  79. package/node_modules/@weavecode/harness/dist/claude/narration.js +1 -1
  80. package/node_modules/@weavecode/harness/dist/claude/peers.js +1 -1
  81. package/node_modules/@weavecode/harness/dist/claude/plan.js +1 -1
  82. package/node_modules/@weavecode/harness/dist/claude/plugins.js +4 -4
  83. package/node_modules/@weavecode/harness/dist/claude/prompt-inbox.js +1 -1
  84. package/node_modules/@weavecode/harness/dist/claude/prompt.js +3 -3
  85. package/node_modules/@weavecode/harness/dist/claude/questions.js +2 -2
  86. package/node_modules/@weavecode/harness/dist/claude/resume.js +1 -1
  87. package/node_modules/@weavecode/harness/dist/claude/rewind.d.ts +8 -0
  88. package/node_modules/@weavecode/harness/dist/claude/rewind.js +1 -0
  89. package/node_modules/@weavecode/harness/dist/claude/settings-gen.js +1 -1
  90. package/node_modules/@weavecode/harness/dist/claude/side-question.js +2 -2
  91. package/node_modules/@weavecode/harness/dist/claude/statusline/generator.js +39 -18
  92. package/node_modules/@weavecode/harness/dist/claude/statusline/git-cache.js +1 -1
  93. package/node_modules/@weavecode/harness/dist/claude/statusline/surface.js +2 -2
  94. package/node_modules/@weavecode/harness/dist/claude/transcript-cursor.d.ts +26 -0
  95. package/node_modules/@weavecode/harness/dist/claude/transcript-cursor.js +2 -0
  96. package/node_modules/@weavecode/harness/dist/claude/transcript.js +7 -7
  97. package/node_modules/@weavecode/harness/dist/codex/assets.js +1 -1
  98. package/node_modules/@weavecode/harness/dist/codex/capture.js +2 -2
  99. package/node_modules/@weavecode/harness/dist/codex/commands.js +4 -4
  100. package/node_modules/@weavecode/harness/dist/codex/config.js +2 -2
  101. package/node_modules/@weavecode/harness/dist/codex/detect.js +1 -1
  102. package/node_modules/@weavecode/harness/dist/codex/event-source.js +2 -2
  103. package/node_modules/@weavecode/harness/dist/codex/images.js +1 -1
  104. package/node_modules/@weavecode/harness/dist/codex/index.js +1 -1
  105. package/node_modules/@weavecode/harness/dist/codex/liveness.js +1 -1
  106. package/node_modules/@weavecode/harness/dist/codex/mcp.js +1 -1
  107. package/node_modules/@weavecode/harness/dist/codex/messaging.js +2 -2
  108. package/node_modules/@weavecode/harness/dist/codex/narration.js +1 -1
  109. package/node_modules/@weavecode/harness/dist/codex/plugin-state.js +1 -1
  110. package/node_modules/@weavecode/harness/dist/codex/plugins.js +3 -3
  111. package/node_modules/@weavecode/harness/dist/codex/provision.js +1 -1
  112. package/node_modules/@weavecode/harness/dist/codex/questions.js +2 -2
  113. package/node_modules/@weavecode/harness/dist/codex/rollout.js +5 -5
  114. package/node_modules/@weavecode/harness/dist/codex/side-question.js +4 -4
  115. package/node_modules/@weavecode/harness/dist/codex/titles.js +4 -4
  116. package/node_modules/@weavecode/harness/dist/codex/toml.js +13 -13
  117. package/node_modules/@weavecode/harness/dist/codex/transcript.js +2 -2
  118. package/node_modules/@weavecode/harness/dist/driver.d.ts +49 -1
  119. package/node_modules/@weavecode/harness/dist/driver.js +1 -1
  120. package/node_modules/@weavecode/harness/dist/init/generated.js +2 -2
  121. package/node_modules/@weavecode/harness/dist/init/pipeline.js +1 -1
  122. package/node_modules/@weavecode/harness/dist/registry-wiring.js +1 -1
  123. package/node_modules/@weavecode/harness/dist/shared/install-link.js +1 -1
  124. package/node_modules/@weavecode/harness/dist/shared/json-cap.js +1 -1
  125. package/node_modules/@weavecode/harness/package.json +2 -2
  126. package/node_modules/@weavecode/server/dist/config/dotted.js +1 -1
  127. package/node_modules/@weavecode/server/dist/config/profiles.d.ts +19 -13
  128. package/node_modules/@weavecode/server/dist/config/profiles.js +2 -2
  129. package/node_modules/@weavecode/server/dist/daemon/ai-config.js +2 -2
  130. package/node_modules/@weavecode/server/dist/daemon/ai-context.js +6 -6
  131. package/node_modules/@weavecode/server/dist/daemon/ai-routes.js +4 -4
  132. package/node_modules/@weavecode/server/dist/daemon/api-log.js +3 -3
  133. package/node_modules/@weavecode/server/dist/daemon/auth-routes.js +1 -1
  134. package/node_modules/@weavecode/server/dist/daemon/auto-start.js +6 -6
  135. package/node_modules/@weavecode/server/dist/daemon/capture-routes.js +1 -1
  136. package/node_modules/@weavecode/server/dist/daemon/config-routes.js +1 -1
  137. package/node_modules/@weavecode/server/dist/daemon/cron.js +1 -1
  138. package/node_modules/@weavecode/server/dist/daemon/dashboard-auth.js +2 -2
  139. package/node_modules/@weavecode/server/dist/daemon/dashboard-pusher.js +1 -1
  140. package/node_modules/@weavecode/server/dist/daemon/data-bus.js +1 -1
  141. package/node_modules/@weavecode/server/dist/daemon/decision-config.js +2 -2
  142. package/node_modules/@weavecode/server/dist/daemon/dev-entry.js +1 -1
  143. package/node_modules/@weavecode/server/dist/daemon/entry.js +7 -1
  144. package/node_modules/@weavecode/server/dist/daemon/fs-browser.js +1 -1
  145. package/node_modules/@weavecode/server/dist/daemon/hook-pipeline.js +1 -1
  146. package/node_modules/@weavecode/server/dist/daemon/hooks-routes.js +1 -1
  147. package/node_modules/@weavecode/server/dist/daemon/http.js +1 -1
  148. package/node_modules/@weavecode/server/dist/daemon/liveness.js +1 -1
  149. package/node_modules/@weavecode/server/dist/daemon/mcp-orchestration.js +1 -1
  150. package/node_modules/@weavecode/server/dist/daemon/narration-watcher.js +2 -2
  151. package/node_modules/@weavecode/server/dist/daemon/opener/backend-ghostty.js +1 -1
  152. package/node_modules/@weavecode/server/dist/daemon/opener/backend-iterm.js +1 -1
  153. package/node_modules/@weavecode/server/dist/daemon/opener/backend-konsole.js +2 -2
  154. package/node_modules/@weavecode/server/dist/daemon/opener/backend-terminal-app.js +1 -1
  155. package/node_modules/@weavecode/server/dist/daemon/opener/backend-wezterm.js +1 -1
  156. package/node_modules/@weavecode/server/dist/daemon/opener/backend-wt.js +1 -1
  157. package/node_modules/@weavecode/server/dist/daemon/opener/backend.js +1 -1
  158. package/node_modules/@weavecode/server/dist/daemon/opener/launch.js +1 -1
  159. package/node_modules/@weavecode/server/dist/daemon/opener/pty-terminals.js +4 -4
  160. package/node_modules/@weavecode/server/dist/daemon/opener/shared.js +1 -1
  161. package/node_modules/@weavecode/server/dist/daemon/opener/wezterm-mux.js +1 -1
  162. package/node_modules/@weavecode/server/dist/daemon/opener/wt.js +3 -3
  163. package/node_modules/@weavecode/server/dist/daemon/opener.d.ts +2 -0
  164. package/node_modules/@weavecode/server/dist/daemon/opener.js +1 -1
  165. package/node_modules/@weavecode/server/dist/daemon/port.js +1 -1
  166. package/node_modules/@weavecode/server/dist/daemon/project-files.d.ts +12 -6
  167. package/node_modules/@weavecode/server/dist/daemon/project-files.js +2 -2
  168. package/node_modules/@weavecode/server/dist/daemon/project-harnesses.js +2 -2
  169. package/node_modules/@weavecode/server/dist/daemon/project-updates-refresher.js +1 -1
  170. package/node_modules/@weavecode/server/dist/daemon/prompt-images.js +1 -1
  171. package/node_modules/@weavecode/server/dist/daemon/routes/hooks-admin.js +1 -1
  172. package/node_modules/@weavecode/server/dist/daemon/routes/market.js +1 -1
  173. package/node_modules/@weavecode/server/dist/daemon/routes/orchestration.js +1 -1
  174. package/node_modules/@weavecode/server/dist/daemon/routes/path-refs.js +1 -1
  175. package/node_modules/@weavecode/server/dist/daemon/routes/project-files.js +1 -1
  176. package/node_modules/@weavecode/server/dist/daemon/routes/projects.js +1 -1
  177. package/node_modules/@weavecode/server/dist/daemon/routes/search.js +1 -1
  178. package/node_modules/@weavecode/server/dist/daemon/routes/session-control.js +1 -1
  179. package/node_modules/@weavecode/server/dist/daemon/routes/session-insights.js +1 -1
  180. package/node_modules/@weavecode/server/dist/daemon/routes/settings-statusline.js +1 -1
  181. package/node_modules/@weavecode/server/dist/daemon/routes/uploads.js +1 -1
  182. package/node_modules/@weavecode/server/dist/daemon/routes/visualizations.d.ts +3 -0
  183. package/node_modules/@weavecode/server/dist/daemon/routes/visualizations.js +1 -0
  184. package/node_modules/@weavecode/server/dist/daemon/routes/watches.js +1 -1
  185. package/node_modules/@weavecode/server/dist/daemon/server.js +8 -8
  186. package/node_modules/@weavecode/server/dist/daemon/session-open.js +1 -1
  187. package/node_modules/@weavecode/server/dist/daemon/session-probe.d.ts +5 -4
  188. package/node_modules/@weavecode/server/dist/daemon/session-probe.js +1 -1
  189. package/node_modules/@weavecode/server/dist/daemon/settings.js +2 -2
  190. package/node_modules/@weavecode/server/dist/daemon/snapshot-refresher.js +1 -1
  191. package/node_modules/@weavecode/server/dist/daemon/sse-hub.js +1 -1
  192. package/node_modules/@weavecode/server/dist/daemon/start.js +1 -1
  193. package/node_modules/@weavecode/server/dist/daemon/state.js +2 -2
  194. package/node_modules/@weavecode/server/dist/daemon/static.js +1 -1
  195. package/node_modules/@weavecode/server/dist/daemon/terminal-topology-refresher.js +1 -1
  196. package/node_modules/@weavecode/server/dist/daemon/tls.js +1 -1
  197. package/node_modules/@weavecode/server/dist/daemon/toolkit-routes.js +1 -1
  198. package/node_modules/@weavecode/server/dist/daemon/upload-sessions.js +1 -1
  199. package/node_modules/@weavecode/server/dist/db/db.js +6 -2
  200. package/node_modules/@weavecode/server/dist/db/layout.js +1 -1
  201. package/node_modules/@weavecode/server/dist/hooks/answer-keys.js +1 -1
  202. package/node_modules/@weavecode/server/dist/hooks/bus.d.ts +3 -1
  203. package/node_modules/@weavecode/server/dist/hooks/bus.js +1 -1
  204. package/node_modules/@weavecode/server/dist/hooks/dashboard-rate.js +1 -1
  205. package/node_modules/@weavecode/server/dist/hooks/decision-judge.js +1 -1
  206. package/node_modules/@weavecode/server/dist/hooks/delivery/ack-gate.d.ts +12 -6
  207. package/node_modules/@weavecode/server/dist/hooks/delivery/ack-gate.js +1 -1
  208. package/node_modules/@weavecode/server/dist/hooks/delivery/channels.d.ts +3 -3
  209. package/node_modules/@weavecode/server/dist/hooks/delivery/channels.js +1 -1
  210. package/node_modules/@weavecode/server/dist/hooks/delivery/keys.d.ts +19 -9
  211. package/node_modules/@weavecode/server/dist/hooks/delivery/keys.js +1 -1
  212. package/node_modules/@weavecode/server/dist/hooks/delivery/queue.js +1 -1
  213. package/node_modules/@weavecode/server/dist/hooks/delivery/relay.d.ts +2 -2
  214. package/node_modules/@weavecode/server/dist/hooks/delivery/relay.js +1 -1
  215. package/node_modules/@weavecode/server/dist/hooks/delivery/send.js +2 -2
  216. package/node_modules/@weavecode/server/dist/hooks/delivery/views.d.ts +3 -1
  217. package/node_modules/@weavecode/server/dist/hooks/delivery/views.js +1 -1
  218. package/node_modules/@weavecode/server/dist/hooks/dispatcher.js +6 -6
  219. package/node_modules/@weavecode/server/dist/hooks/edge-extract.js +1 -1
  220. package/node_modules/@weavecode/server/dist/hooks/handlers/webhook.js +1 -1
  221. package/node_modules/@weavecode/server/dist/hooks/headless-runner.js +2 -2
  222. package/node_modules/@weavecode/server/dist/hooks/inbox-socket.js +2 -2
  223. package/node_modules/@weavecode/server/dist/hooks/inbox-worker.js +3 -3
  224. package/node_modules/@weavecode/server/dist/hooks/match.js +1 -1
  225. package/node_modules/@weavecode/server/dist/hooks/message-deliverer.d.ts +45 -38
  226. package/node_modules/@weavecode/server/dist/hooks/message-deliverer.js +5 -3
  227. package/node_modules/@weavecode/server/dist/hooks/mirror.js +4 -4
  228. package/node_modules/@weavecode/server/dist/hooks/orchestration-engine.js +7 -7
  229. package/node_modules/@weavecode/server/dist/hooks/orchestration-graph.js +1 -1
  230. package/node_modules/@weavecode/server/dist/hooks/plugin-installer.js +3 -3
  231. package/node_modules/@weavecode/server/dist/hooks/plugin-loader.js +1 -1
  232. package/node_modules/@weavecode/server/dist/hooks/plugin-project-state.js +1 -1
  233. package/node_modules/@weavecode/server/dist/hooks/prompt-flusher.js +1 -1
  234. package/node_modules/@weavecode/server/dist/hooks/promptbox.js +8 -8
  235. package/node_modules/@weavecode/server/dist/hooks/push-notify.js +2 -2
  236. package/node_modules/@weavecode/server/dist/hooks/question-tracker.js +1 -1
  237. package/node_modules/@weavecode/server/dist/hooks/registry.js +1 -1
  238. package/node_modules/@weavecode/server/dist/hooks/resume-runner.js +1 -1
  239. package/node_modules/@weavecode/server/dist/hooks/session-purge.js +1 -1
  240. package/node_modules/@weavecode/server/dist/hooks/side-questions.js +1 -1
  241. package/node_modules/@weavecode/server/dist/hooks/synthetic-sessions.js +1 -1
  242. package/node_modules/@weavecode/server/dist/hooks/toolkit-scan.js +1 -1
  243. package/node_modules/@weavecode/server/dist/hooks/toolkit.js +5 -5
  244. package/node_modules/@weavecode/server/dist/hooks/watch-engine.js +1 -1
  245. package/node_modules/@weavecode/server/dist/install/apply-update.js +1 -1
  246. package/node_modules/@weavecode/server/dist/install/capabilities.js +1 -1
  247. package/node_modules/@weavecode/server/dist/install/codex-toml.js +1 -1
  248. package/node_modules/@weavecode/server/dist/install/file-assets.js +1 -1
  249. package/node_modules/@weavecode/server/dist/install/fingerprint.js +1 -1
  250. package/node_modules/@weavecode/server/dist/install/global-config.js +1 -1
  251. package/node_modules/@weavecode/server/dist/install/global-file-assets.js +1 -1
  252. package/node_modules/@weavecode/server/dist/install/init-files.js +2 -2
  253. package/node_modules/@weavecode/server/dist/install/install-ledger.js +1 -1
  254. package/node_modules/@weavecode/server/dist/install/installer.js +1 -1
  255. package/node_modules/@weavecode/server/dist/install/library-index.js +1 -1
  256. package/node_modules/@weavecode/server/dist/install/mcp-rows.js +1 -1
  257. package/node_modules/@weavecode/server/dist/install/plugin-install.js +1 -1
  258. package/node_modules/@weavecode/server/dist/install/plugin-rows.js +1 -1
  259. package/node_modules/@weavecode/server/dist/install/plugin-templates.js +1 -1
  260. package/node_modules/@weavecode/server/dist/install/project-config.js +1 -1
  261. package/node_modules/@weavecode/server/dist/install/reader.js +1 -1
  262. package/node_modules/@weavecode/server/dist/install/scan-cache.js +1 -1
  263. package/node_modules/@weavecode/server/dist/install/scanner.js +1 -1
  264. package/node_modules/@weavecode/server/dist/install/settings-mcp.js +2 -2
  265. package/node_modules/@weavecode/server/dist/install/updates.js +1 -1
  266. package/node_modules/@weavecode/server/dist/market/builtin.js +1 -1
  267. package/node_modules/@weavecode/server/dist/market/identity.js +1 -1
  268. package/node_modules/@weavecode/server/dist/market/import.js +2 -2
  269. package/node_modules/@weavecode/server/dist/market/refresh.js +1 -1
  270. package/node_modules/@weavecode/server/dist/market/resolve.js +1 -1
  271. package/node_modules/@weavecode/server/dist/market/updates.js +1 -1
  272. package/node_modules/@weavecode/server/dist/registry.js +2 -2
  273. package/node_modules/@weavecode/server/dist/repositories/hook-dispatches.js +5 -5
  274. package/node_modules/@weavecode/server/dist/repositories/hook-events.d.ts +1 -1
  275. package/node_modules/@weavecode/server/dist/repositories/hook-events.js +5 -5
  276. package/node_modules/@weavecode/server/dist/repositories/hook-rules.js +4 -4
  277. package/node_modules/@weavecode/server/dist/repositories/libraries.js +1 -1
  278. package/node_modules/@weavecode/server/dist/repositories/orchestrations.js +7 -7
  279. package/node_modules/@weavecode/server/dist/repositories/plugin-states.js +1 -1
  280. package/node_modules/@weavecode/server/dist/repositories/projects.js +2 -2
  281. package/node_modules/@weavecode/server/dist/repositories/push-subscriptions.js +2 -2
  282. package/node_modules/@weavecode/server/dist/repositories/session-watches.js +4 -4
  283. package/node_modules/@weavecode/server/dist/repositories/sessions.d.ts +11 -1
  284. package/node_modules/@weavecode/server/dist/repositories/sessions.js +4 -2
  285. package/node_modules/@weavecode/server/dist/repositories/uploads.d.ts +14 -4
  286. package/node_modules/@weavecode/server/dist/repositories/uploads.js +11 -11
  287. package/node_modules/@weavecode/server/dist/statusline/cache-sweeper.js +1 -1
  288. package/node_modules/@weavecode/server/dist/statusline/generator.js +1 -1
  289. package/node_modules/@weavecode/server/dist/statusline/manager.js +3 -3
  290. package/node_modules/@weavecode/server/dist/surfaces.d.ts +3 -1
  291. package/node_modules/@weavecode/server/dist/surfaces.js +1 -1
  292. package/node_modules/@weavecode/server/dist/watch/cache.js +1 -1
  293. package/node_modules/@weavecode/server/dist/watch/classifier.js +1 -1
  294. package/node_modules/@weavecode/server/dist/watch/plugin-mods.js +1 -1
  295. package/node_modules/@weavecode/server/dist/watch/sync-scheduler.js +1 -1
  296. package/node_modules/@weavecode/server/dist/watch/watch-service.js +1 -1
  297. package/node_modules/@weavecode/server/package.json +4 -4
  298. package/node_modules/@weavecode/server/web/dist/assets/{abnfDiagram-WEYOIA3C-DA-K085F.js → abnfDiagram-WEYOIA3C-CbIFQh0R.js} +1 -1
  299. package/node_modules/@weavecode/server/web/dist/assets/{arc-BI1jnmdZ.js → arc-DH8-rYAa.js} +1 -1
  300. package/node_modules/@weavecode/server/web/dist/assets/{architectureDiagram-ZHVYKT53-DbdBgric.js → architectureDiagram-ZHVYKT53-qnv6S0sc.js} +1 -1
  301. package/node_modules/@weavecode/server/web/dist/assets/{blockDiagram-RJAE7QA3-BPRBmzb-.js → blockDiagram-RJAE7QA3-pT5eO-cB.js} +1 -1
  302. package/node_modules/@weavecode/server/web/dist/assets/{c4Diagram-XDPPPE54-C4D5sRs1.js → c4Diagram-XDPPPE54-DHDBDMb1.js} +1 -1
  303. package/node_modules/@weavecode/server/web/dist/assets/channel-BHPeeuzN.js +1 -0
  304. package/node_modules/@weavecode/server/web/dist/assets/{chunk-2E7WHVMD-9ZSKiNU3.js → chunk-2E7WHVMD-CT951PH5.js} +1 -1
  305. package/node_modules/@weavecode/server/web/dist/assets/{chunk-2Q5K7J3B-i0AOjw_8.js → chunk-2Q5K7J3B-E5AD7_ZY.js} +1 -1
  306. package/node_modules/@weavecode/server/web/dist/assets/{chunk-4EMNKR2T-DTpLfX1H.js → chunk-4EMNKR2T-DWz9qkcy.js} +1 -1
  307. package/node_modules/@weavecode/server/web/dist/assets/{chunk-55XCU76K-COQYRD4q.js → chunk-55XCU76K-D-SgWD8w.js} +1 -1
  308. package/node_modules/@weavecode/server/web/dist/assets/{chunk-5VM5RSS4-VsftEbvo.js → chunk-5VM5RSS4-ci6YzbO5.js} +1 -1
  309. package/node_modules/@weavecode/server/web/dist/assets/{chunk-JWPE2WC7-DonektJN.js → chunk-JWPE2WC7-CY4mr2f4.js} +1 -1
  310. package/node_modules/@weavecode/server/web/dist/assets/{chunk-VQ5W62QI-CaKUwuul.js → chunk-VQ5W62QI-CON8zyg8.js} +1 -1
  311. package/node_modules/@weavecode/server/web/dist/assets/{chunk-XXDRQBXY-DXH3D_MT.js → chunk-XXDRQBXY-DXXXZ4pW.js} +1 -1
  312. package/node_modules/@weavecode/server/web/dist/assets/{chunk-ZCHMQSOR-COwyAuGn.js → chunk-ZCHMQSOR-CukvPvkM.js} +2 -2
  313. package/node_modules/@weavecode/server/web/dist/assets/{classDiagram-v2-4MYWR3UA-DEvMZ6mX.js → classDiagram-v2-4MYWR3UA-GiWZ9xtF.js} +1 -1
  314. package/node_modules/@weavecode/server/web/dist/assets/{cose-bilkent-JH36ORCC-6JBJHfH6.js → cose-bilkent-JH36ORCC-D7aJ-akq.js} +1 -1
  315. package/node_modules/@weavecode/server/web/dist/assets/{cynefin-EF2NZ3EQ-C4GM4sjT.js → cynefin-EF2NZ3EQ-9B__MaRM.js} +1 -1
  316. package/node_modules/@weavecode/server/web/dist/assets/{cynefinDiagram-WF24UDRP-Cqgi0MBP.js → cynefinDiagram-WF24UDRP-B2ujZh2k.js} +1 -1
  317. package/node_modules/@weavecode/server/web/dist/assets/{dagre-NK4GIZVX-CljrnkCY.js → dagre-NK4GIZVX-nHaB6IWf.js} +1 -1
  318. package/node_modules/@weavecode/server/web/dist/assets/{diagram-CBK3MWTC-C3PyOj5J.js → diagram-CBK3MWTC-xKfjDvgr.js} +1 -1
  319. package/node_modules/@weavecode/server/web/dist/assets/{diagram-I5XIIV2D-I5brQlaZ.js → diagram-I5XIIV2D-BHT2-5mY.js} +1 -1
  320. package/node_modules/@weavecode/server/web/dist/assets/{diagram-ODIL2UCW-BX9uFNbU.js → diagram-ODIL2UCW-CckywF4W.js} +1 -1
  321. package/node_modules/@weavecode/server/web/dist/assets/{diagram-PFBEJUXE-y7D-VmqJ.js → diagram-PFBEJUXE-Dpjlc7Wh.js} +1 -1
  322. package/node_modules/@weavecode/server/web/dist/assets/{diagram-RJA3I4VV-DLUL8W42.js → diagram-RJA3I4VV-M6WznOaW.js} +1 -1
  323. package/node_modules/@weavecode/server/web/dist/assets/{diagram-TYUNFSNR-DLCC3ILi.js → diagram-TYUNFSNR-DgRJ3zL1.js} +1 -1
  324. package/node_modules/@weavecode/server/web/dist/assets/{ebnfDiagram-V4P3TTPT-D7vALia7.js → ebnfDiagram-V4P3TTPT-BzzVN__a.js} +1 -1
  325. package/node_modules/@weavecode/server/web/dist/assets/{elk-QONTBCTQ-Ct2_hatz.js → elk-QONTBCTQ-Ch6Gs9G9.js} +1 -1
  326. package/node_modules/@weavecode/server/web/dist/assets/{erDiagram-KYQIGQAA-DxLai58T.js → erDiagram-KYQIGQAA-COxT-Pxb.js} +1 -1
  327. package/node_modules/@weavecode/server/web/dist/assets/{flowDiagram-IUGLLWBU-AWn2IHrF.js → flowDiagram-IUGLLWBU-CJVWnTBa.js} +1 -1
  328. package/node_modules/@weavecode/server/web/dist/assets/{ganttDiagram-R7GPNTJY-DmRHd4Wz.js → ganttDiagram-R7GPNTJY-BkRpWPUl.js} +1 -1
  329. package/node_modules/@weavecode/server/web/dist/assets/{gitGraphDiagram-PDYL7XHY-Ccj-t3zg.js → gitGraphDiagram-PDYL7XHY-Ba8BJcvZ.js} +1 -1
  330. package/node_modules/@weavecode/server/web/dist/assets/index-CtifxEiF.css +11 -0
  331. package/node_modules/@weavecode/server/web/dist/assets/index-FwH9INgS.js +1090 -0
  332. package/node_modules/@weavecode/server/web/dist/assets/{infoDiagram-MVDRKAX5-BHquFlQa.js → infoDiagram-MVDRKAX5-HTQ5FUNK.js} +1 -1
  333. package/node_modules/@weavecode/server/web/dist/assets/{ishikawaDiagram-HPEHV2GE-CVogEbdm.js → ishikawaDiagram-HPEHV2GE-BNjEDIZy.js} +1 -1
  334. package/node_modules/@weavecode/server/web/dist/assets/{journeyDiagram-BOSHW7O7-DC9XG9oh.js → journeyDiagram-BOSHW7O7-Cao6h91a.js} +1 -1
  335. package/node_modules/@weavecode/server/web/dist/assets/{kanban-definition-FNSA4HC5-C_eu7DC_.js → kanban-definition-FNSA4HC5-CXIx2CHQ.js} +1 -1
  336. package/node_modules/@weavecode/server/web/dist/assets/{linear-keTbWHJ-.js → linear-65WmlvQb.js} +1 -1
  337. package/node_modules/@weavecode/server/web/dist/assets/{mermaid.core-4sbg0MnY.js → mermaid.core-D39fi_iR.js} +4 -4
  338. package/node_modules/@weavecode/server/web/dist/assets/{mindmap-definition-6OY57UG3-kMQS0S2l.js → mindmap-definition-6OY57UG3-DHqpz_AK.js} +1 -1
  339. package/node_modules/@weavecode/server/web/dist/assets/{pegDiagram-ECIUJNBX-DcUFoEwP.js → pegDiagram-ECIUJNBX-Cpy1nsER.js} +1 -1
  340. package/node_modules/@weavecode/server/web/dist/assets/{pieDiagram-GNMPY74T-BQJ7WDCx.js → pieDiagram-GNMPY74T-B1IyNqzr.js} +1 -1
  341. package/node_modules/@weavecode/server/web/dist/assets/{quadrantDiagram-D57K4MC3-C7Oi1-sq.js → quadrantDiagram-D57K4MC3-zSM6o7mB.js} +1 -1
  342. package/node_modules/@weavecode/server/web/dist/assets/{railroadDiagram-Y72NRBL7-CEF2QbOF.js → railroadDiagram-Y72NRBL7-oM4XJIwA.js} +1 -1
  343. package/node_modules/@weavecode/server/web/dist/assets/{requirementDiagram-EQYSW5RA-CTjjFS7X.js → requirementDiagram-EQYSW5RA-CbCLBjHf.js} +1 -1
  344. package/node_modules/@weavecode/server/web/dist/assets/{sankeyDiagram-SY24JHO5-j0S5kubS.js → sankeyDiagram-SY24JHO5-BucVk6EP.js} +1 -1
  345. package/node_modules/@weavecode/server/web/dist/assets/{sequenceDiagram-BLEHNHQU-B76EsZp4.js → sequenceDiagram-BLEHNHQU-Dg8CYEnT.js} +1 -1
  346. package/node_modules/@weavecode/server/web/dist/assets/{sizeCapture-INFHLROL-r0DZuqAy.js → sizeCapture-INFHLROL-C51zb7Oc.js} +1 -1
  347. package/node_modules/@weavecode/server/web/dist/assets/{stateDiagram-v2-A6IESKWZ-B4DR9LHz.js → stateDiagram-v2-A6IESKWZ-BvK2l3is.js} +1 -1
  348. package/node_modules/@weavecode/server/web/dist/assets/{swimlanes-B3ENRKFQ-Dbcl85ro.js → swimlanes-B3ENRKFQ-Ds7QOfXW.js} +1 -1
  349. package/node_modules/@weavecode/server/web/dist/assets/swimlanesDiagram-AYQRV4AK-ByBF02UJ.js +8 -0
  350. package/node_modules/@weavecode/server/web/dist/assets/{timeline-definition-JXYKGLGB-BlBXXRNf.js → timeline-definition-JXYKGLGB-CadU4Qq4.js} +1 -1
  351. package/node_modules/@weavecode/server/web/dist/assets/{usecaseDiagram-WPOLBNNX-Ck1M0dpc.js → usecaseDiagram-WPOLBNNX-BYuZwTDZ.js} +1 -1
  352. package/node_modules/@weavecode/server/web/dist/assets/{vennDiagram-Q3Y3IDCW-N8B-ohWk.js → vennDiagram-Q3Y3IDCW-C7zgxe07.js} +1 -1
  353. package/node_modules/@weavecode/server/web/dist/assets/{wardleyDiagram-IVAIDKWA-DX-AMXq_.js → wardleyDiagram-IVAIDKWA-DA4Otf4s.js} +1 -1
  354. package/node_modules/@weavecode/server/web/dist/assets/{xychartDiagram-4URNHDU4-4fgL48Yl.js → xychartDiagram-4URNHDU4-Bs5YGwMU.js} +1 -1
  355. package/node_modules/@weavecode/server/web/dist/index.html +2 -2
  356. package/node_modules/@weavecode/server/web/dist/sw.js +14 -12
  357. package/node_modules/@weavecode/templates/package.json +2 -2
  358. package/package.json +6 -6
  359. package/node_modules/@weavecode/server/web/dist/assets/channel-DcCJrvon.js +0 -1
  360. package/node_modules/@weavecode/server/web/dist/assets/index-BSxzia7U.js +0 -1031
  361. package/node_modules/@weavecode/server/web/dist/assets/index-CEpJpR7P.css +0 -11
  362. package/node_modules/@weavecode/server/web/dist/assets/swimlanesDiagram-AYQRV4AK-CZ_6SSBd.js +0 -8
@@ -1 +1 @@
1
- import{imagesSurfaceFor as N}from"../surfaces.js";const d=/\[Image #\d+\]/gi,b=64,I=64*1024*1024,P=12e4,E=t=>t.replace(d,"").replace(/\s+/g," ").trim(),w={"image/png":".png","image/jpeg":".jpg","image/gif":".gif","image/webp":".webp"};function _(t,e,n){const r=t.hookEvents?.listBySession(e,2e3,void 0,["UserPromptSubmit"])??[],o=[];for(const i of r){if(n.get(i.id)?.length)continue;const c=S(i.payload);!c||!c.match(d)||o.push({eventId:i.id,at:i.createdAt,text:E(c)})}return o}function S(t){try{const e=JSON.parse(t).raw;return typeof e?.prompt=="string"&&e.prompt?e.prompt:null}catch{return null}}function O(t,e,n){for(let r=n.length-1;r>=0;r--)try{const o=JSON.parse(n[r].payload).transcriptPath;if(typeof o=="string"&&o)return o}catch{}return t.sessionProbe?.transcriptPathOf(e)??null}export async function importPromptImages(t,e,n){const r=n?N(n):null;if(!r||!t.hookEvents||!t.uploads)return 0;const o=t.uploads.imagesBySession(e),i=_(t,e,o);if(i.length===0)return 0;const c=t.hookEvents.listBySession(e),u=await r.sessionImages({sessionId:e,transcriptPath:O(t,e,c)});if(!u?.length)return 0;let s=0,a=0;for(const l of u){if(s>=b||a>=I)break;const m=x(i,l.text,l.at);if(!m)continue;const p=[];for(const f of l.images){if(s>=b||a>=I)break;const g=w[f.mediaType];if(!g)continue;const h=Buffer.from(f.bytes),y=t.uploads.save({name:`pasted-${s+1}${g}`,mediaType:f.mediaType,bytes:h,dir:t.uploadDir??null});p.includes(y.id)||p.push(y.id),s+=1,a+=h.length}p.length>0&&t.uploads.linkToEvent(m.eventId,p)}return s}function x(t,e,n){const r=E(e),o=r===""?[]:t.filter(a=>a.text===r),i=o.length>0?o:r===""?t.filter(a=>a.text===""):[];if(i.length===0)return null;const c=n?Date.parse(n):NaN;let u=null,s=Number.POSITIVE_INFINITY;for(const a of i){const l=Number.isNaN(c)||!a.at?Number.POSITIVE_INFINITY:Math.abs(Date.parse(a.at)-c);l<s&&(u=a,s=l)}return!u||o.length===0&&!(s<=P)?null:(t.splice(t.indexOf(u),1),u)}export function needsImageImport(t,e){return t.some(n=>{if(n.eventType!=="UserPromptSubmit"||e?.get(n.id)?.length)return!1;const r=S(n.payload);return!!r&&/\[Image #\d+\]/i.test(r)})}const T=new Set;export async function importPromptImagesOnce(t,e,n){if(T.has(e))return!1;T.add(e);try{return await importPromptImages(t,e,n)>0}catch{return!1}}
1
+ import{imagesSurfaceFor as T}from"../surfaces.js";const I=/\[Image #\d+\]/gi,y=64,b=64*1024*1024,v=12e4,N=t=>t.replace(I,"").replace(/\s+/g," ").trim(),x={"image/png":".png","image/jpeg":".jpg","image/gif":".gif","image/webp":".webp"};function O(t,e,n){const r=t.hookEvents?.listBySession(e,2e3,void 0,["UserPromptSubmit"])??[],o=[];for(const i of r){if(n.get(i.id)?.length)continue;const c=S(i.payload);!c||!c.match(I)||o.push({eventId:i.id,at:i.createdAt,text:N(c)})}return o}function S(t){try{const e=JSON.parse(t).raw;return typeof e?.prompt=="string"&&e.prompt?e.prompt:null}catch{return null}}function w(t,e,n){for(let r=n.length-1;r>=0;r--)try{const o=JSON.parse(n[r].payload).transcriptPath;if(typeof o=="string"&&o)return o}catch{}return t.sessionProbe?.transcriptPathOf(e)??null}export async function importPromptImages(t,e,n){const r=n?T(n):null;if(!r||!t.hookEvents||!t.uploads)return 0;const o=t.uploads.imagesBySession(e),i=O(t,e,o);if(i.length===0)return 0;const c=t.hookEvents.listBySession(e),u=await r.sessionImages({sessionId:e,transcriptPath:w(t,e,c)});if(!u?.length)return 0;let a=0,s=0;for(const p of u){if(a>=y||s>=b)break;const m=E(i,p.text,p.at);if(!m)continue;const l=[];for(const f of p.images){if(a>=y||s>=b)break;const g=x[f.mediaType];if(!g)continue;const d=Buffer.from(f.bytes),h=t.uploads.save({name:`pasted-${a+1}${g}`,mediaType:f.mediaType,bytes:d,dir:t.uploadDir??null});l.includes(h.id)||l.push(h.id),a+=1,s+=d.length}l.length>0&&t.uploads.linkToEvent(m.eventId,l)}return a}function E(t,e,n){const r=N(e),o=r===""?[]:t.filter(s=>s.text===r),i=o.length>0?o:r===""?t.filter(s=>s.text===""):[];if(i.length===0)return null;const c=n?Date.parse(n):NaN;let u=null,a=Number.POSITIVE_INFINITY;for(const s of i){const p=Number.isNaN(c)||!s.at?Number.POSITIVE_INFINITY:Math.abs(Date.parse(s.at)-c);p<a&&(u=s,a=p)}return!u||o.length===0&&!(a<=v)?null:(t.splice(t.indexOf(u),1),u)}export function needsImageImport(t,e){return t.some(n=>{if(n.eventType!=="UserPromptSubmit"||e?.get(n.id)?.length)return!1;const r=S(n.payload);return!!r&&/\[Image #\d+\]/i.test(r)})}const P=new Set;export async function importPromptImagesOnce(t,e,n){if(P.has(e))return!1;P.add(e);try{return await importPromptImages(t,e,n)>0}catch{return!1}}
@@ -1 +1 @@
1
- import{z as u}from"zod";import{HandlerConfigSchema as I,HookDispatchStatusSchema as p,HookRuleFilterSchema as R,HookRuleSourceSchema as j}from"@weavecode/core";import{readJson as h,sendJson as r}from"../http.js";import{installPlugin as v,uninstallPlugin as w}from"../../hooks/plugin-installer.js";import{effective as E,setProjectEnabled as S}from"../../hooks/plugin-project-state.js";const y=u.object({name:u.string().min(1),enabled:u.boolean().optional(),priority:u.number().int().optional(),projectId:u.number().int().nullable(),source:j,filter:R.optional(),handler:I,timeoutMs:u.number().int().positive().optional(),record:u.boolean().optional()}),T=y.partial(),H=u.object({enabled:u.boolean()}),B=u.object({enabled:u.boolean(),projectId:u.number().int().optional()}),O=u.object({sessionId:u.string().min(1)}),U=u.object({src:u.string().min(1)});export async function handleHookAdminRoutes(i,o,e,l,d){if(l==="/hooks/rules"){if(!e.hookRegistry)return r(o,503,{error:"Hooks subsystem unavailable"}),!0;if(i.method==="GET"){const t=d.searchParams.get("projectId"),n=t!=null&&t!==""?Number(t):void 0;return r(o,200,{rules:e.hookRegistry.list(n)}),!0}if(i.method==="POST"){try{const t=y.parse(await h(i)),n=e.hookRegistry.create(t);r(o,201,{rule:n})}catch(t){r(o,400,{error:t instanceof Error?t.message:"Invalid rule body"})}return!0}}const g=l.match(/^\/hooks\/rules\/([^/]+)$/);if(g){if(!e.hookRegistry)return r(o,503,{error:"Hooks subsystem unavailable"}),!0;const t=decodeURIComponent(g[1]);if(i.method==="PUT"){try{const n=T.parse(await h(i)),s=e.hookRegistry.update(t,n);if(!s)return r(o,404,{error:"Rule not found"}),!0;r(o,200,{rule:s})}catch(n){r(o,400,{error:n instanceof Error?n.message:"Invalid rule body"})}return!0}if(i.method==="DELETE")return e.hookRegistry.remove(t)?(r(o,200,{ok:!0}),!0):(r(o,404,{error:"Rule not found"}),!0)}const m=l.match(/^\/hooks\/rules\/([^/]+)\/toggle$/);if(m&&i.method==="POST"){if(!e.hookRegistry)return r(o,503,{error:"Hooks subsystem unavailable"}),!0;try{const t=H.parse(await h(i)),n=e.hookRegistry.setEnabled(decodeURIComponent(m[1]),t.enabled);if(!n)return r(o,404,{error:"Rule not found"}),!0;r(o,200,{rule:n})}catch(t){r(o,400,{error:t instanceof Error?t.message:"Invalid body"})}return!0}if(l==="/hooks/plugins"){if(i.method==="GET"){const t=d.searchParams.get("projectId");if(t==null||t==="")return r(o,200,{plugins:e.hookPlugins?.list()??[]}),!0;const n=e.projects.getById(Number(t));if(!n)return r(o,404,{error:"Project not found"}),!0;if(!e.pluginStates)return r(o,503,{error:"Plugin states unavailable"}),!0;const s=e.pluginStates;return r(o,200,{plugins:(e.hookPlugins?.list()??[]).map(a=>{if(a.status==="rejected")return a;const c=E(n,a.id,{states:s})?"active":"disabled";return c===a.status?a:{...a,status:c}})}),!0}if(i.method==="POST"){if(!e.hookPlugins)return r(o,503,{error:"Plugin loader unavailable"}),!0;try{const t=U.parse(await h(i)),n=await v(t.src);await e.hookPlugins.reloadAll(),r(o,201,n)}catch(t){r(o,400,{error:t instanceof Error?t.message:"Install failed"})}return!0}}const f=l.match(/^\/hooks\/plugins\/([^/]+)\/uninstall$/);if(f&&i.method==="POST"){if(!e.hookPlugins)return r(o,503,{error:"Plugin loader unavailable"}),!0;const t=decodeURIComponent(f[1]);return await w(t)?(await e.hookPlugins.reloadAll(),r(o,200,{ok:!0}),!0):(r(o,404,{error:"Plugin directory not found"}),!0)}if(l==="/hooks/plugins/rescan"&&i.method==="POST"){if(!e.hookPlugins)return r(o,503,{error:"Plugin loader unavailable"}),!0;try{await e.hookPlugins.rescan(),r(o,200,{ok:!0,plugins:e.hookPlugins.list().length})}catch(t){r(o,500,{error:t instanceof Error?t.message:"Rescan failed"})}return!0}const b=l.match(/^\/hooks\/plugins\/([^/]+)\/toggle$/);if(b&&i.method==="POST"){if(!e.hookPlugins)return r(o,503,{error:"Plugin loader unavailable"}),!0;try{const t=B.parse(await h(i)),n=decodeURIComponent(b[1]);if(t.projectId!==void 0){const a=e.projects.getById(t.projectId);return a?e.hookPlugins.list().some(c=>c.id===n)?(S(a,n,t.enabled,{projects:e.projects}),r(o,200,{ok:!0}),!0):(r(o,404,{error:"Plugin not found"}),!0):(r(o,404,{error:"Project not found"}),!0)}if(!e.hookPlugins.setPluginEnabled(n,t.enabled))return r(o,404,{error:"Plugin not found"}),!0;r(o,200,{ok:!0})}catch(t){r(o,400,{error:t instanceof Error?t.message:"Invalid body"})}return!0}if(l==="/hooks/dispatches"&&i.method==="GET"){if(!e.hookDispatches)return r(o,503,{error:"Hooks subsystem unavailable"}),!0;const t=d.searchParams.get("ruleId")??void 0,n=d.searchParams.get("status"),s=n?p.parse(n):void 0,a=d.searchParams.get("sessionId")??void 0,c=d.searchParams.get("limit"),P=c?Number(c):void 0;return r(o,200,{dispatches:e.hookDispatches.list({ruleId:t,status:s,sessionId:a,limit:Number.isFinite(P)?P:void 0})}),!0}const k=l.match(/^\/hooks\/dispatches\/(\d+)\/run$/);if(k&&i.method==="POST"){if(!e.hookRunner)return r(o,503,{error:"Resume runner unavailable"}),!0;let t;try{t=O.parse(await h(i)).sessionId}catch(s){return r(o,400,{error:s instanceof Error?s.message:"Invalid body"}),!0}const n=await e.hookRunner.run(Number(k[1]),t);return n.ok?r(o,200,{ok:!0,dispatchId:n.dispatchId}):r(o,n.status,{error:n.error}),!0}return!1}
1
+ import{z as a}from"zod";import{HandlerConfigSchema as y,HookDispatchStatusSchema as v,HookRuleFilterSchema as I,HookRuleSourceSchema as R}from"@weavecode/core";import{readJson as d,sendJson as t}from"../http.js";import{installPlugin as j,uninstallPlugin as E}from"../../hooks/plugin-installer.js";import{effective as S,setProjectEnabled as w}from"../../hooks/plugin-project-state.js";const P=a.object({name:a.string().min(1),enabled:a.boolean().optional(),priority:a.number().int().optional(),projectId:a.number().int().nullable(),source:R,filter:I.optional(),handler:y,timeoutMs:a.number().int().positive().optional(),record:a.boolean().optional()}),T=P.partial(),H=a.object({enabled:a.boolean()}),O=a.object({enabled:a.boolean(),projectId:a.number().int().optional()}),U=a.object({sessionId:a.string().min(1)}),C=a.object({src:a.string().min(1)});export async function handleHookAdminRoutes(s,e,o,l,c){if(l==="/hooks/rules"){if(!o.hookRegistry)return t(e,503,{error:"Hooks subsystem unavailable"}),!0;if(s.method==="GET"){const r=c.searchParams.get("projectId"),n=r!=null&&r!==""?Number(r):void 0;return t(e,200,{rules:o.hookRegistry.list(n)}),!0}if(s.method==="POST"){try{const r=P.parse(await d(s)),n=o.hookRegistry.create(r);t(e,201,{rule:n})}catch(r){t(e,400,{error:r instanceof Error?r.message:"Invalid rule body"})}return!0}}const m=l.match(/^\/hooks\/rules\/([^/]+)$/);if(m){if(!o.hookRegistry)return t(e,503,{error:"Hooks subsystem unavailable"}),!0;const r=decodeURIComponent(m[1]);if(s.method==="PUT"){try{const n=T.parse(await d(s)),i=o.hookRegistry.update(r,n);if(!i)return t(e,404,{error:"Rule not found"}),!0;t(e,200,{rule:i})}catch(n){t(e,400,{error:n instanceof Error?n.message:"Invalid rule body"})}return!0}if(s.method==="DELETE")return o.hookRegistry.remove(r)?(t(e,200,{ok:!0}),!0):(t(e,404,{error:"Rule not found"}),!0)}const g=l.match(/^\/hooks\/rules\/([^/]+)\/toggle$/);if(g&&s.method==="POST"){if(!o.hookRegistry)return t(e,503,{error:"Hooks subsystem unavailable"}),!0;try{const r=H.parse(await d(s)),n=o.hookRegistry.setEnabled(decodeURIComponent(g[1]),r.enabled);if(!n)return t(e,404,{error:"Rule not found"}),!0;t(e,200,{rule:n})}catch(r){t(e,400,{error:r instanceof Error?r.message:"Invalid body"})}return!0}if(l==="/hooks/plugins"){if(s.method==="GET"){const r=c.searchParams.get("projectId");if(r==null||r==="")return t(e,200,{plugins:o.hookPlugins?.list()??[]}),!0;const n=o.projects.getById(Number(r));if(!n)return t(e,404,{error:"Project not found"}),!0;if(!o.pluginStates)return t(e,503,{error:"Plugin states unavailable"}),!0;const i=o.pluginStates;return t(e,200,{plugins:(o.hookPlugins?.list()??[]).map(u=>{if(u.status==="rejected")return u;const h=S(n,u.id,{states:i})?"active":"disabled";return h===u.status?u:{...u,status:h}})}),!0}if(s.method==="POST"){if(!o.hookPlugins)return t(e,503,{error:"Plugin loader unavailable"}),!0;try{const r=C.parse(await d(s)),n=await j(r.src);await o.hookPlugins.reloadAll(),t(e,201,n)}catch(r){t(e,400,{error:r instanceof Error?r.message:"Install failed"})}return!0}}const f=l.match(/^\/hooks\/plugins\/([^/]+)\/uninstall$/);if(f&&s.method==="POST"){if(!o.hookPlugins)return t(e,503,{error:"Plugin loader unavailable"}),!0;const r=decodeURIComponent(f[1]);return await E(r)?(await o.hookPlugins.reloadAll(),t(e,200,{ok:!0}),!0):(t(e,404,{error:"Plugin directory not found"}),!0)}if(l==="/hooks/plugins/rescan"&&s.method==="POST"){if(!o.hookPlugins)return t(e,503,{error:"Plugin loader unavailable"}),!0;try{await o.hookPlugins.rescan(),t(e,200,{ok:!0,plugins:o.hookPlugins.list().length})}catch(r){t(e,500,{error:r instanceof Error?r.message:"Rescan failed"})}return!0}const p=l.match(/^\/hooks\/plugins\/([^/]+)\/toggle$/);if(p&&s.method==="POST"){if(!o.hookPlugins)return t(e,503,{error:"Plugin loader unavailable"}),!0;try{const r=O.parse(await d(s)),n=decodeURIComponent(p[1]);if(r.projectId!==void 0){const i=o.projects.getById(r.projectId);return i?o.hookPlugins.list().some(u=>u.id===n)?(w(i,n,r.enabled,{projects:o.projects}),t(e,200,{ok:!0}),!0):(t(e,404,{error:"Plugin not found"}),!0):(t(e,404,{error:"Project not found"}),!0)}if(!o.hookPlugins.setPluginEnabled(n,r.enabled))return t(e,404,{error:"Plugin not found"}),!0;t(e,200,{ok:!0})}catch(r){t(e,400,{error:r instanceof Error?r.message:"Invalid body"})}return!0}if(l==="/hooks/dispatches"&&s.method==="GET"){if(!o.hookDispatches)return t(e,503,{error:"Hooks subsystem unavailable"}),!0;const r=c.searchParams.get("ruleId")??void 0,n=c.searchParams.get("status"),i=n?v.parse(n):void 0,u=c.searchParams.get("sessionId")??void 0,h=c.searchParams.get("limit"),k=h?Number(h):void 0;return t(e,200,{dispatches:o.hookDispatches.list({ruleId:r,status:i,sessionId:u,limit:Number.isFinite(k)?k:void 0})}),!0}const b=l.match(/^\/hooks\/dispatches\/(\d+)\/run$/);if(b&&s.method==="POST"){if(!o.hookRunner)return t(e,503,{error:"Resume runner unavailable"}),!0;let r;try{r=U.parse(await d(s)).sessionId}catch(i){return t(e,400,{error:i instanceof Error?i.message:"Invalid body"}),!0}const n=await o.hookRunner.run(Number(b[1]),r);return n.ok?t(e,200,{ok:!0,dispatchId:n.dispatchId}):t(e,n.status,{error:n.error}),!0}return!1}
@@ -1 +1 @@
1
- import{homedir as j}from"node:os";import X from"node:path";import{checkHarness as Y,isHarness as A}from"@weavecode/core";import{readBody as g,sendJson as e}from"../http.js";import{FingerprintCache as z}from"../../install/fingerprint.js";import{installSkill as Z,uninstallSkill as q,installFileAsset as tt,uninstallFileAsset as rt,InstallConflictError as x,AssetNotFoundError as M}from"../../install/installer.js";import{isFileAssetCategory as K}from"../../install/file-assets.js";import{installGlobalSkill as et,uninstallGlobalSkill as at,readGlobalSkillGroups as it,resolveGlobalSkillDir as H,listGlobalSkillFiles as ot,readGlobalSkillFile as nt,ClaudeJsonCorruptError as ct}from"../../install/global-config.js";import{CodexTomlCorruptError as lt}from"../../install/codex-toml.js";import{forgetInstalled as st,recordInstalled as ut}from"../../install/install-ledger.js";import{readProjectFile as mt,readGlobalFile as ft,PathEscapeError as dt,FileTooLargeError as pt}from"../../install/reader.js";import{readMcpRows as gt}from"../../install/mcp-rows.js";import{setMcpEnableOverride as ht}from"../../install/settings-mcp.js";import{applyUpdate as bt,syncAll as yt}from"../../install/apply-update.js";import{buildLibraryIndex as kt}from"../../install/library-index.js";import{detectUpdates as wt}from"../../install/updates.js";import{detectPluginTemplateUpdates as St}from"../../install/plugin-templates.js";import{detectInitFileUpdates as jt,needsInit as vt}from"../../install/init-files.js";import{getLibraryScan as _,invalidateScan as b}from"../../install/scan-cache.js";import{installCapabilities as It}from"../../install/capabilities.js";import{BUILTIN_MCP_SERVERS as Pt}from"../../install/builtin-mcp.js";import{readPluginRows as Mt}from"../../install/plugin-rows.js";import{installPluginPack as Et}from"../../install/plugin-install.js";import{importFromDir as Nt,importFromGit as Ot,importLibraryIntoMarket as Tt}from"../../market/import.js";import{marketDir as E,readManifest as Ct}from"../../market/manifest.js";import{marketIdOf as $t}from"../../market/identity.js";import{isInsideDir as N,toRelPosix as Bt}from"../../market/resolve.js";import{checkMarketUpdates as Ft}from"../../market/updates.js";import{refreshMarketItems as Jt}from"../../market/refresh.js";import{ensureBuiltinMarket as Gt,isBuiltinMarketDir as S}from"../../market/builtin.js";import{removeInstalled as Lt}from"../../market/install-link.js";import{syncPlugins as Rt}from"../../hooks/plugin-project-state.js";import{assetSurfaces as Dt,pluginStateSurfaces as Ut,pluginSurfaces as W,pluginSurfaceFor as V}from"../../surfaces.js";function Q(o){return o.scope==="user"||o.category==="plugin"?!1:o.category==="mcp"?!!v(o.agent)?.mcpScopes().includes("project"):!0}function v(o){const r=o??"claude";return Dt().find(i=>i.harness===r)}function I(o,r){r instanceof x?o.statusCode=409:r instanceof M?o.statusCode=404:r instanceof ct||r instanceof lt?o.statusCode=500:o.statusCode=400,o.setHeader("content-type","application/json"),o.end(JSON.stringify({error:r instanceof Error?r.message:"Install failed"}))}function O(o,r){r instanceof dt?o.statusCode=403:r instanceof pt?o.statusCode=413:r instanceof M?o.statusCode=404:o.statusCode=400,o.setHeader("content-type","application/json"),o.end(JSON.stringify({error:r instanceof Error?r.message:"Read failed"}))}async function P(o,r){const i=N(o.path,E(r)),u=i?await Ct(o.path):null,d=S(o.path,r);return{id:o.id,kind:o.kind,addedAt:o.addedAt,path:o.path,relPath:N(o.path,r)?Bt(o.path,r):null,resourceId:i?X.basename(o.path):null,managed:i,builtin:d,namespace:i?$t(o.path,E(r)):void 0,legacy:!u&&!d,source:d?"builtin":u?.source??"legacy",origin:u?.origin??null,importedAt:u?.importedAt??null,commit:u?.commit??null,tags:await xt(o.path)}}const At={command:"command",agent:"agent",workflow:"workflow",rule:"rules","output-style":"output-style"};async function zt(o){const{plugins:r,skills:i,mcp:u,files:d}=await _(o),w={},h=(p,y)=>{y>0&&(w[p]=(w[p]??0)+y)};h("skill",i.length),h("mcp",u.length);for(const[p,y]of Object.entries(d))h(At[p]??p,y.length);return h("plugin",r.length),h("mods",r.filter(p=>p.kind==="mod"||p.components.includes("mod")).length),w}async function xt(o){try{return await zt(o)}catch{return{}}}export async function handleMarketRoutes(o,r,i,u,d){if(u==="/capabilities"&&o.method==="GET")return e(r,200,It()),!0;if(u==="/libraries"&&o.method==="GET")return e(r,200,{libraries:i.libraries?i.libraries.list():[]}),!0;if(u==="/libraries"&&o.method==="POST"){if(!i.libraries)return e(r,503,{error:"Libraries unavailable"}),!0;let a;try{a=JSON.parse(await g(o))}catch{return e(r,400,{error:"Invalid JSON body"}),!0}if(!a.path||!a.kind)return e(r,400,{error:'Missing "path" or "kind"'}),!0;if(!["skill","mcp","both"].includes(a.kind))return e(r,400,{error:'Invalid "kind" (skill|mcp|both)'}),!0;const t=i.libraries.add({path:a.path,kind:a.kind});return i.dataBus?.emit({key:"libraries"}),e(r,201,t),!0}const w=u.match(/^\/libraries\/(\d+)\/assets$/);if(w&&o.method==="GET"){if(!i.libraries)return e(r,503,{error:"Libraries unavailable"}),!0;const a=i.libraries.getById(Number(w[1]));if(!a)return e(r,404,{error:"Library not found"}),!0;const{plugins:t,skills:n,mcp:c,files:l}=await _(a.path),s=a.kind==="skill"||a.kind==="both"?t:[],m=a.kind==="mcp"||a.kind==="both"?c:[],f=a.kind==="skill"||a.kind==="both"?Object.values(l).flat():null;return e(r,200,{library:a,skills:n,mcp:m,files:f,plugins:s}),!0}const h=u.match(/^\/libraries\/(\d+)$/);if(h&&o.method==="DELETE"){if(!i.libraries)return e(r,503,{error:"Libraries unavailable"}),!0;const a=Number(h[1]),t=i.libraries.getById(a);return t&&await b(t.path),i.libraries.remove(a),i.dataBus?.emit({key:"libraries"}),i.dataBus?.emit({key:"market"}),e(r,200,{ok:!0}),!0}if(u==="/market"&&o.method==="GET"){if(!i.libraries)return e(r,503,{error:"Libraries unavailable"}),!0;const a=await Promise.all(i.libraries.list().map(t=>P(t,i.libraries.dataRoot)));return e(r,200,{items:a}),!0}if(u==="/market"&&o.method==="POST"){if(!i.libraries)return e(r,503,{error:"Libraries unavailable"}),!0;let a;try{a=JSON.parse(await g(o))}catch{return e(r,400,{error:"Invalid JSON body"}),!0}const t={libraries:i.libraries,dataRoot:i.libraries.dataRoot};try{if(a.kind==="dir"&&a.path){const n=await Nt(a.path,t);n.refreshed&&i.fingerprintCache?.invalidate(n.dir),await b(n.dir),i.dataBus?.emit({key:"market"}),e(r,201,{item:await P(i.libraries.getById(n.libraryId),i.libraries.dataRoot)})}else if(a.kind==="git"&&a.url){const n=await Ot(a.url,t);n.refreshed&&i.fingerprintCache?.invalidate(n.dir),await b(n.dir),i.dataBus?.emit({key:"market"}),e(r,201,{item:await P(i.libraries.getById(n.libraryId),i.libraries.dataRoot)})}else e(r,400,{error:'Invalid body (kind: "dir" + path | "git" + url)'})}catch(n){e(r,400,{error:n instanceof Error?n.message:"Import failed"})}return!0}if(u==="/market/check-updates"&&o.method==="POST"){if(!i.libraries)return e(r,503,{error:"Libraries unavailable"}),!0;let a={};const t=(await g(o)).trim();if(t)try{a=JSON.parse(t)}catch{return e(r,400,{error:"Invalid JSON body"}),!0}const n=i.libraries.list().filter(l=>!S(l.path,i.libraries.dataRoot)).filter(l=>!a.ids?.length||a.ids.includes(l.id));for(const l of n)await b(l.path);const c=await Ft(n.map(l=>({id:l.id,dir:l.path})));return i.dataBus?.emit({key:"market"}),e(r,200,{results:c}),!0}if(u==="/market/refresh"&&o.method==="POST"){if(!i.libraries)return e(r,503,{error:"Libraries unavailable"}),!0;let a={};const t=(await g(o)).trim();if(t)try{a=JSON.parse(t)}catch{return e(r,400,{error:"Invalid JSON body"}),!0}const n=i.libraries.list().filter(f=>!a.ids?.length||a.ids.includes(f.id)),c=[];for(const f of n.filter(k=>S(k.path,i.libraries.dataRoot))){const k=await Gt({libraries:i.libraries});c.push({id:f.id,status:k.error?"failed":"refreshed"})}const l=n.filter(f=>!S(f.path,i.libraries.dataRoot)).map(f=>({id:f.id,dir:f.path})),s=[...await Jt(l,{libraries:i.libraries,dataRoot:i.libraries.dataRoot}),...c],m=new Map(l.map(f=>[f.id,f.dir]));for(const f of s)if(f.status==="refreshed"){const k=m.get(f.id);k&&(i.fingerprintCache?.invalidate(k),await b(k))}return i.dataBus?.emit({key:"market"}),e(r,200,{results:s}),!0}const p=u.match(/^\/market\/(\d+)$/);if(p&&o.method==="DELETE"){if(!i.libraries)return e(r,503,{error:"Libraries unavailable"}),!0;const a=i.libraries.getById(Number(p[1]));if(!a)return e(r,404,{error:"Library not found"}),!0;if(S(a.path,i.libraries.dataRoot))return e(r,409,{error:"Built-in market is owned by weave and cannot be deleted",note:"It is re-materialized from @weavecode/templates on every daemon start."}),!0;if(!N(a.path,E(i.libraries.dataRoot)))return e(r,400,{error:"Not a market resource (legacy row \u2014 use DELETE /libraries/:id)"}),!0;let t;try{await(i.pluginSurface??V("claude")).removeMarketplace(a.path)}catch(n){t=`Host marketplace deregistration failed: ${n instanceof Error?n.message:String(n)}`}return await Lt(a.path),i.libraries.remove(a.id),await b(a.path),i.dataBus?.emit({key:"market"}),i.dataBus?.emit({key:"libraries"}),e(r,200,{ok:!0,removed:a.path,installedCopiesKept:!0,...t?{warning:t}:{},note:"The host cascade-uninstalls plugins installed from this market; other installed copies are kept."}),!0}const y=u.match(/^\/libraries\/(\d+)\/market-import$/);if(y&&o.method==="POST"){if(!i.libraries)return e(r,503,{error:"Libraries unavailable"}),!0;const a=i.libraries.getById(Number(y[1]));if(!a)return e(r,404,{error:"Library not found"}),!0;try{const t=a.path,n=await Tt(a,{libraries:i.libraries,dataRoot:i.libraries.dataRoot});await b(t),await b(n.dir),e(r,200,{item:await P(i.libraries.getById(n.libraryId),i.libraries.dataRoot)})}catch(t){e(r,400,{error:t instanceof Error?t.message:"Import failed"})}return!0}const T=u.match(/^\/projects\/(\d+)\/install$/);if(T&&o.method==="POST"){const a=i.projects.getById(Number(T[1]));let t;try{t=JSON.parse(await g(o))}catch{return e(r,400,{error:"Invalid JSON body"}),!0}if(!a&&Q(t))return e(r,404,{error:"Project not found"}),!0;const n=a?.path??"";try{if(t.category==="skill"){if(!t.name||!t.sourceDir)return e(r,400,{error:'Missing "name" or "sourceDir"'}),!0;const c=t.agent??"claude",l=t.scope==="user"?await et(t.sourceDir,t.name,c):await Z(n,t.sourceDir,t.name,c);e(r,201,{category:"skill",name:t.name,scope:t.scope??"project",agent:c,path:l})}else if(t.category&&K(t.category)){if(!t.name||!t.sourceFile)return e(r,400,{error:'Missing "name" or "sourceFile"'}),!0;const c=t.agent??"claude",l=t.scope??"project",s=await tt(n,t.category,t.name,t.sourceFile,c,l);e(r,201,{category:t.category,name:t.name,scope:l,agent:c,path:s})}else if(t.category==="mcp"){if(!t.name||!t.mcpConfig)return e(r,400,{error:'Missing "name" or "mcpConfig"'}),!0;const c=t.agent??"claude",l=v(c);if(!l)return e(r,400,{error:`Unsupported agent: ${c}`}),!0;const s=l.mcpScopes(),m=t.scope??s[0];if(!m||!s.includes(m))return e(r,400,{error:`MCP servers for ${c} are ${s.join("/")}-level only \u2014 scope "${t.scope}" is not available`}),!0;if(t.name in await l.mcpRead(m,n,j()))return I(r,new x(`MCP server "${t.name}" is already configured`)),!0;await l.mcpWrite(m,n,j(),t.name,t.mcpConfig),m==="project"&&await ut(n,{category:"mcp",name:t.name,agent:c}),e(r,201,{category:"mcp",name:t.name,scope:m,agent:c})}else if(t.category==="plugin"){if(!t.name||!t.sourceDir)return e(r,400,{error:'Missing "name" or "sourceDir"'}),!0;if(t.scope&&t.scope!=="user")return e(r,400,{error:'Plugin install currently only supports scope "user"'}),!0;const c=t.agent??"claude",l=W().find(f=>f.harness===c);if(!l)return e(r,400,{error:`No plugin install surface for runtime: ${c}`}),!0;const s=c==="claude"&&i.pluginSurface?i.pluginSurface:l.surface,m=await Et(s,{dir:t.sourceDir,name:t.name,version:t.version,description:t.description});i.dataBus?.emit({key:"toolkit"}),e(r,201,{category:"plugin",key:m.key,scope:"user",agent:c,alreadyInstalled:m.alreadyInstalled})}else e(r,400,{error:'Invalid "category" (skill|mcp|plugin|command|agent|workflow|rule|output-style)'})}catch(c){I(r,c)}return!0}const C=u.match(/^\/projects\/(\d+)\/mcp\/builtin$/);if(C&&o.method==="POST"){const a=i.projects.getById(Number(C[1]));let t;try{t=JSON.parse(await g(o))}catch{return e(r,400,{error:"Invalid JSON body"}),!0}const n=Pt.find(f=>f.name===t.name);if(!n)return e(r,404,{error:`Unknown built-in MCP server: ${t.name??""}`}),!0;if(!a)return e(r,404,{error:"Project not found"}),!0;const c=t.agent??"claude",l=v(c);if(!l||!l.mcpScopes().includes("user"))return e(r,400,{error:`MCP servers for ${c} have no user scope`}),!0;const s=i.auth?.getLocalToken()??"",m={type:"http",url:`http://127.0.0.1:${i.port}${n.path}`,...s?{headers:{"x-weave-local":s}}:{}};try{await l.mcpWrite("user",a.path,j(),n.name,m),e(r,201,{category:"mcp",name:n.name,scope:"user",agent:c})}catch(f){e(r,400,{error:f instanceof Error?f.message:"Install failed"})}return!0}const $=u.match(/^\/projects\/(\d+)\/uninstall$/);if($&&o.method==="POST"){const a=i.projects.getById(Number($[1]));let t;try{t=JSON.parse(await g(o))}catch{return e(r,400,{error:"Invalid JSON body"}),!0}if(!a&&Q(t))return e(r,404,{error:"Project not found"}),!0;const n=a?.path??"";try{if(t.category==="skill"){if(!t.name)return e(r,400,{error:'Missing "name"'}),!0;const c=t.agent??"claude";t.scope==="user"?await at(t.name,c):await q(n,t.name,c)}else if(t.category&&K(t.category)){if(!t.name)return e(r,400,{error:'Missing "name"'}),!0;await rt(n,t.category,t.name,t.agent??"claude",t.scope??"project")}else if(t.category==="mcp"){if(!t.name)return e(r,400,{error:'Missing "name"'}),!0;const c=t.agent??"claude",l=v(c);if(!l)return e(r,400,{error:`Unsupported agent: ${c}`}),!0;const s=l.mcpScopes(),m=t.scope??s[0];if(!m||!s.includes(m))return e(r,400,{error:`MCP servers for ${c} are ${s.join("/")}-level only \u2014 scope "${t.scope}" is not available`}),!0;if(!await l.mcpRemove(m,n,j(),t.name))return I(r,new M(`MCP server "${t.name}" is not configured`)),!0;m==="project"&&await st(n,{category:"mcp",name:t.name,agent:c})}else return e(r,400,{error:'Invalid "category" (skill|mcp|command|agent|workflow|rule|output-style)'}),!0;e(r,200,{ok:!0})}catch(c){I(r,c)}return!0}const B=u.match(/^\/projects\/(\d+)\/asset-file$/);if(B&&o.method==="GET"){const a=i.projects.getById(Number(B[1]));if(!a)return e(r,404,{error:"Project not found"}),!0;const t=d.searchParams.get("path");if(!t)return e(r,400,{error:'Missing "path" query parameter'}),!0;try{const n=await mt(a.path,t);e(r,200,{path:t,content:n.content,size:n.size})}catch(n){O(r,n)}return!0}if(u==="/global-file"&&o.method==="GET"){const a=d.searchParams.get("path");if(!a)return e(r,400,{error:'Missing "path" query parameter'}),!0;try{const t=await ft(a);e(r,200,{path:a,content:t.content,size:t.size})}catch(t){O(r,t)}return!0}const F=u.match(/^\/projects\/(\d+)\/mcp$/);if(F&&o.method==="GET"){const a=i.projects.getById(Number(F[1]));return a?(e(r,200,{rows:await gt(a.path)}),!0):(e(r,404,{error:"Project not found"}),!0)}const J=u.match(/^\/projects\/(\d+)\/harness-checks$/);if(J&&o.method==="GET"){const a=i.projects.getById(Number(J[1]));return a?(e(r,200,{checks:await Y(a.path)}),!0):(e(r,404,{error:"Project not found"}),!0)}if(u.match(/^\/projects\/(\d+)\/skills\/global$/)&&o.method==="GET")return e(r,200,{groups:await it()}),!0;if(u.match(/^\/projects\/(\d+)\/skills\/global\/files$/)&&o.method==="GET"){const a=d.searchParams.get("source"),t=d.searchParams.get("name"),n=d.searchParams.get("pluginKey")??void 0;if(!a||!t)return e(r,400,{error:'Missing "source" or "name" query parameter'}),!0;const c=await H(a,t,n);return c?(e(r,200,{files:await ot(c)}),!0):(e(r,404,{error:"Skill not found"}),!0)}if(u.match(/^\/projects\/(\d+)\/skills\/global\/file$/)&&o.method==="GET"){const a=d.searchParams.get("source"),t=d.searchParams.get("name"),n=d.searchParams.get("pluginKey")??void 0,c=d.searchParams.get("path");if(!a||!t||!c)return e(r,400,{error:'Missing "source", "name" or "path" query parameter'}),!0;const l=await H(a,t,n);if(!l)return e(r,404,{error:"Skill not found"}),!0;try{const s=await nt(l,c);e(r,200,{path:c,content:s.content,size:s.size})}catch(s){O(r,s)}return!0}const G=u.match(/^\/projects\/(\d+)\/plugins\/state$/);if(G){const a=i.projects.getById(Number(G[1]));if(!a)return e(r,404,{error:"Project not found"}),!0;if(o.method==="GET")return e(r,200,{rows:await Mt(a.path)}),!0;if(o.method==="PUT"){let t;try{t=JSON.parse(await g(o))}catch{return e(r,400,{error:"Invalid JSON body"}),!0}if(!A(t.runtime)||!t.key||!t.scope||!["user","project","local"].includes(t.scope)||!t.state||!["on","off","inherit"].includes(t.state))return e(r,400,{error:'Missing or invalid "runtime"/"key"/"scope" (user|project|local)/"state" (on|off|inherit)'}),!0;const n=t.scope,c=t.state;try{const l=W().find(s=>s.harness===t.runtime);if(l)await(t.runtime==="claude"&&i.pluginSurface?i.pluginSurface:l.surface).setEnabledOverride(a.path,n,t.key,c);else{const s=Ut().find(m=>m.harness===t.runtime);if(!s||n!=="user"||c==="inherit")return e(r,400,{error:"This runtime only supports user-scope on/off overrides"}),!0;await s.state.writeGlobalEnabled(t.key,c==="on")}i.dataBus?.emit({key:"toolkit"}),e(r,200,{ok:!0})}catch(l){e(r,400,{error:l instanceof Error?l.message:"Plugin state write failed"})}return!0}}if(u.match(/^\/projects\/(\d+)\/plugins\/uninstall$/)&&o.method==="POST"){let a;try{a=JSON.parse(await g(o))}catch{return e(r,400,{error:"Invalid JSON body"}),!0}if(!A(a.runtime)||!a.key)return e(r,400,{error:'Missing or invalid "runtime"/"key"'}),!0;try{const t=a.runtime==="claude"&&i.pluginSurface?i.pluginSurface:V(a.runtime),{name:n,market:c}=t.parsePluginKey(a.key);await t.uninstall(n,c),e(r,200,{ok:!0})}catch(t){e(r,400,{error:t instanceof Error?t.message:"Plugin uninstall failed"})}return!0}const L=u.match(/^\/projects\/(\d+)\/mcp\/enable$/);if(L){const a=i.projects.getById(Number(L[1]));if(!a)return e(r,404,{error:"Project not found"}),!0;if(o.method==="PUT"){let t;try{t=JSON.parse(await g(o))}catch{return e(r,400,{error:"Invalid JSON body"}),!0}return!t.name||!t.state?(e(r,400,{error:'Missing "name" or "state"'}),!0):["on","off","inherit"].includes(t.state)?(await ht(a.path,t.name,t.state),e(r,200,{ok:!0}),!0):(e(r,400,{error:'Invalid "state" (on|off|inherit)'}),!0)}}const R=u.match(/^\/projects\/(\d+)\/updates$/);if(R&&o.method==="GET"){const a=i.projects.getById(Number(R[1]));if(!a)return e(r,404,{error:"Project not found"}),!0;const t=i.fingerprintCache??new z;if(!i.libraries){const c=await St(a.path,t),l=await jt(a.path);return e(r,200,{skills:[],mcp:[],files:[],pluginTemplates:c,initFiles:l,available:c.filter(s=>s.outdated).length+l.filter(s=>s.outdated&&s.syncClass==="auto").length,needsInit:vt(l)}),!0}const n=await wt(a.path,i.libraries.list(),t);return e(r,200,n),!0}const D=u.match(/^\/projects\/(\d+)\/update$/);if(D&&o.method==="POST"){const a=i.projects.getById(Number(D[1]));if(!a)return e(r,404,{error:"Project not found"}),!0;if(!i.libraries)return e(r,503,{error:"Libraries unavailable"}),!0;let t;try{t=JSON.parse(await g(o))}catch{return e(r,400,{error:"Invalid JSON body"}),!0}if(!t.category||!t.name||!t.scope)return e(r,400,{error:'Missing "category", "name", or "scope"'}),!0;if(!["skill","mcp","command","agent","workflow","rule","output-style"].includes(t.category))return e(r,400,{error:'Invalid "category"'}),!0;try{const c=await kt(i.libraries.list());await bt(a.path,{category:t.category,name:t.name,scope:t.scope,agent:t.agent},c),e(r,200,{ok:!0,category:t.category,name:t.name,scope:t.scope})}catch(c){e(r,400,{error:c instanceof Error?c.message:"Update failed"})}return!0}const U=u.match(/^\/projects\/(\d+)\/sync-all$/);if(U&&o.method==="POST"){const a=i.projects.getById(Number(U[1]));if(!a)return e(r,404,{error:"Project not found"}),!0;if(!i.libraries)return e(r,503,{error:"Libraries unavailable"}),!0;try{const t=i.fingerprintCache??new z,n=await yt(a.path,i.libraries.list(),t);let c=n;try{const l=i.hookPlugins?.list().map(m=>m.id)??[],s=Rt(a,l,{projects:i.projects});c={...n,pluginSync:{syncedAt:s}}}catch{}e(r,200,c),i.updatesRefresher?.refreshNow(!0)}catch(t){e(r,400,{error:t instanceof Error?t.message:"Sync failed"})}return!0}return!1}
1
+ import{homedir as v}from"node:os";import Q from"node:path";import{checkHarness as X,isHarness as A}from"@weavecode/core";import{readBody as h,sendJson as r}from"../http.js";import{FingerprintCache as z}from"../../install/fingerprint.js";import{installSkill as Y,uninstallSkill as Z,installFileAsset as ee,uninstallFileAsset as te,InstallConflictError as x,AssetNotFoundError as E}from"../../install/installer.js";import{isFileAssetCategory as H}from"../../install/file-assets.js";import{installGlobalSkill as re,uninstallGlobalSkill as ae,readGlobalSkillGroups as ie,resolveGlobalSkillDir as q,listGlobalSkillFiles as se,readGlobalSkillFile as ne,ClaudeJsonCorruptError as oe}from"../../install/global-config.js";import{CodexTomlCorruptError as le}from"../../install/codex-toml.js";import{forgetInstalled as ce,recordInstalled as me}from"../../install/install-ledger.js";import{readProjectFile as ue,readGlobalFile as de,PathEscapeError as pe,FileTooLargeError as fe}from"../../install/reader.js";import{readMcpRows as he}from"../../install/mcp-rows.js";import{setMcpEnableOverride as ge}from"../../install/settings-mcp.js";import{applyUpdate as ye,syncAll as be}from"../../install/apply-update.js";import{buildLibraryIndex as ke}from"../../install/library-index.js";import{detectUpdates as we}from"../../install/updates.js";import{detectPluginTemplateUpdates as Se}from"../../install/plugin-templates.js";import{detectInitFileUpdates as ve,needsInit as Ie}from"../../install/init-files.js";import{getLibraryScan as K,invalidateScan as y}from"../../install/scan-cache.js";import{installCapabilities as Pe}from"../../install/capabilities.js";import{BUILTIN_MCP_SERVERS as je}from"../../install/builtin-mcp.js";import{readPluginRows as Ee}from"../../install/plugin-rows.js";import{installPluginPack as Ne}from"../../install/plugin-install.js";import{importFromDir as Oe,importFromGit as Te,importLibraryIntoMarket as $e}from"../../market/import.js";import{marketDir as N,readManifest as Be}from"../../market/manifest.js";import{marketIdOf as Me}from"../../market/identity.js";import{isInsideDir as O,toRelPosix as Ce}from"../../market/resolve.js";import{checkMarketUpdates as Je}from"../../market/updates.js";import{refreshMarketItems as Re}from"../../market/refresh.js";import{ensureBuiltinMarket as Ge,isBuiltinMarketDir as S}from"../../market/builtin.js";import{removeInstalled as Le}from"../../market/install-link.js";import{syncPlugins as Fe}from"../../hooks/plugin-project-state.js";import{assetSurfaces as De,pluginStateSurfaces as Ue,pluginSurfaces as W,pluginSurfaceFor as _}from"../../surfaces.js";function V(s){return s.scope==="user"||s.category==="plugin"?!1:s.category==="mcp"?!!I(s.agent)?.mcpScopes().includes("project"):!0}function I(s){const t=s??"claude";return De().find(i=>i.harness===t)}function P(s,t){t instanceof x?s.statusCode=409:t instanceof E?s.statusCode=404:t instanceof oe||t instanceof le?s.statusCode=500:s.statusCode=400,s.setHeader("content-type","application/json"),s.end(JSON.stringify({error:t instanceof Error?t.message:"Install failed"}))}function T(s,t){t instanceof pe?s.statusCode=403:t instanceof fe?s.statusCode=413:t instanceof E?s.statusCode=404:s.statusCode=400,s.setHeader("content-type","application/json"),s.end(JSON.stringify({error:t instanceof Error?t.message:"Read failed"}))}async function j(s,t){const i=O(s.path,N(t)),m=i?await Be(s.path):null,p=S(s.path,t);return{id:s.id,kind:s.kind,addedAt:s.addedAt,path:s.path,relPath:O(s.path,t)?Ce(s.path,t):null,resourceId:i?Q.basename(s.path):null,managed:i,builtin:p,namespace:i?Me(s.path,N(t)):void 0,legacy:!m&&!p,source:p?"builtin":m?.source??"legacy",origin:m?.origin??null,importedAt:m?.importedAt??null,commit:m?.commit??null,tags:await xe(s.path)}}const Ae={command:"command",agent:"agent",workflow:"workflow",rule:"rules","output-style":"output-style"};async function ze(s){const{plugins:t,skills:i,mcp:m,files:p}=await K(s),w={},g=(f,b)=>{b>0&&(w[f]=(w[f]??0)+b)};g("skill",i.length),g("mcp",m.length);for(const[f,b]of Object.entries(p))g(Ae[f]??f,b.length);return g("plugin",t.length),g("mods",t.filter(f=>f.kind==="mod"||f.components.includes("mod")).length),w}async function xe(s){try{return await ze(s)}catch{return{}}}export async function handleMarketRoutes(s,t,i,m,p){if(m==="/capabilities"&&s.method==="GET")return r(t,200,Pe()),!0;if(m==="/libraries"&&s.method==="GET")return r(t,200,{libraries:i.libraries?i.libraries.list():[]}),!0;if(m==="/libraries"&&s.method==="POST"){if(!i.libraries)return r(t,503,{error:"Libraries unavailable"}),!0;let a;try{a=JSON.parse(await h(s))}catch{return r(t,400,{error:"Invalid JSON body"}),!0}if(!a.path||!a.kind)return r(t,400,{error:'Missing "path" or "kind"'}),!0;if(!["skill","mcp","both"].includes(a.kind))return r(t,400,{error:'Invalid "kind" (skill|mcp|both)'}),!0;const e=i.libraries.add({path:a.path,kind:a.kind});return i.dataBus?.emit({key:"libraries"}),r(t,201,e),!0}const w=m.match(/^\/libraries\/(\d+)\/assets$/);if(w&&s.method==="GET"){if(!i.libraries)return r(t,503,{error:"Libraries unavailable"}),!0;const a=i.libraries.getById(Number(w[1]));if(!a)return r(t,404,{error:"Library not found"}),!0;const{plugins:e,skills:n,mcp:o,files:l}=await K(a.path),c=a.kind==="skill"||a.kind==="both"?e:[],u=a.kind==="mcp"||a.kind==="both"?o:[],d=a.kind==="skill"||a.kind==="both"?Object.values(l).flat():null;return r(t,200,{library:a,skills:n,mcp:u,files:d,plugins:c}),!0}const g=m.match(/^\/libraries\/(\d+)$/);if(g&&s.method==="DELETE"){if(!i.libraries)return r(t,503,{error:"Libraries unavailable"}),!0;const a=Number(g[1]),e=i.libraries.getById(a);return e&&await y(e.path),i.libraries.remove(a),i.dataBus?.emit({key:"libraries"}),i.dataBus?.emit({key:"market"}),r(t,200,{ok:!0}),!0}if(m==="/market"&&s.method==="GET"){if(!i.libraries)return r(t,503,{error:"Libraries unavailable"}),!0;const a=await Promise.all(i.libraries.list().map(e=>j(e,i.libraries.dataRoot)));return r(t,200,{items:a}),!0}if(m==="/market"&&s.method==="POST"){if(!i.libraries)return r(t,503,{error:"Libraries unavailable"}),!0;let a;try{a=JSON.parse(await h(s))}catch{return r(t,400,{error:"Invalid JSON body"}),!0}const e={libraries:i.libraries,dataRoot:i.libraries.dataRoot};try{if(a.kind==="dir"&&a.path){const n=await Oe(a.path,e);n.refreshed&&i.fingerprintCache?.invalidate(n.dir),await y(n.dir),i.dataBus?.emit({key:"market"}),r(t,201,{item:await j(i.libraries.getById(n.libraryId),i.libraries.dataRoot)})}else if(a.kind==="git"&&a.url){const n=await Te(a.url,e);n.refreshed&&i.fingerprintCache?.invalidate(n.dir),await y(n.dir),i.dataBus?.emit({key:"market"}),r(t,201,{item:await j(i.libraries.getById(n.libraryId),i.libraries.dataRoot)})}else r(t,400,{error:'Invalid body (kind: "dir" + path | "git" + url)'})}catch(n){r(t,400,{error:n instanceof Error?n.message:"Import failed"})}return!0}if(m==="/market/check-updates"&&s.method==="POST"){if(!i.libraries)return r(t,503,{error:"Libraries unavailable"}),!0;let a={};const e=(await h(s)).trim();if(e)try{a=JSON.parse(e)}catch{return r(t,400,{error:"Invalid JSON body"}),!0}const n=i.libraries.list().filter(l=>!S(l.path,i.libraries.dataRoot)).filter(l=>!a.ids?.length||a.ids.includes(l.id));for(const l of n)await y(l.path);const o=await Je(n.map(l=>({id:l.id,dir:l.path})));return i.dataBus?.emit({key:"market"}),r(t,200,{results:o}),!0}if(m==="/market/refresh"&&s.method==="POST"){if(!i.libraries)return r(t,503,{error:"Libraries unavailable"}),!0;let a={};const e=(await h(s)).trim();if(e)try{a=JSON.parse(e)}catch{return r(t,400,{error:"Invalid JSON body"}),!0}const n=i.libraries.list().filter(d=>!a.ids?.length||a.ids.includes(d.id)),o=[];for(const d of n.filter(k=>S(k.path,i.libraries.dataRoot))){const k=await Ge({libraries:i.libraries});o.push({id:d.id,status:k.error?"failed":"refreshed"})}const l=n.filter(d=>!S(d.path,i.libraries.dataRoot)).map(d=>({id:d.id,dir:d.path})),c=[...await Re(l,{libraries:i.libraries,dataRoot:i.libraries.dataRoot}),...o],u=new Map(l.map(d=>[d.id,d.dir]));for(const d of c)if(d.status==="refreshed"){const k=u.get(d.id);k&&(i.fingerprintCache?.invalidate(k),await y(k))}return i.dataBus?.emit({key:"market"}),r(t,200,{results:c}),!0}const f=m.match(/^\/market\/(\d+)$/);if(f&&s.method==="DELETE"){if(!i.libraries)return r(t,503,{error:"Libraries unavailable"}),!0;const a=i.libraries.getById(Number(f[1]));if(!a)return r(t,404,{error:"Library not found"}),!0;if(S(a.path,i.libraries.dataRoot))return r(t,409,{error:"Built-in market is owned by weave and cannot be deleted",note:"It is re-materialized from @weavecode/templates on every daemon start."}),!0;if(!O(a.path,N(i.libraries.dataRoot)))return r(t,400,{error:"Not a market resource (legacy row \u2014 use DELETE /libraries/:id)"}),!0;let e;try{await(i.pluginSurface??_("claude")).removeMarketplace(a.path)}catch(n){e=`Host marketplace deregistration failed: ${n instanceof Error?n.message:String(n)}`}return await Le(a.path),i.libraries.remove(a.id),await y(a.path),i.dataBus?.emit({key:"market"}),i.dataBus?.emit({key:"libraries"}),r(t,200,{ok:!0,removed:a.path,installedCopiesKept:!0,...e?{warning:e}:{},note:"The host cascade-uninstalls plugins installed from this market; other installed copies are kept."}),!0}const b=m.match(/^\/libraries\/(\d+)\/market-import$/);if(b&&s.method==="POST"){if(!i.libraries)return r(t,503,{error:"Libraries unavailable"}),!0;const a=i.libraries.getById(Number(b[1]));if(!a)return r(t,404,{error:"Library not found"}),!0;try{const e=a.path,n=await $e(a,{libraries:i.libraries,dataRoot:i.libraries.dataRoot});await y(e),await y(n.dir),r(t,200,{item:await j(i.libraries.getById(n.libraryId),i.libraries.dataRoot)})}catch(e){r(t,400,{error:e instanceof Error?e.message:"Import failed"})}return!0}const $=m.match(/^\/projects\/(\d+)\/install$/);if($&&s.method==="POST"){const a=i.projects.getById(Number($[1]));let e;try{e=JSON.parse(await h(s))}catch{return r(t,400,{error:"Invalid JSON body"}),!0}if(!a&&V(e))return r(t,404,{error:"Project not found"}),!0;const n=a?.path??"";try{if(e.category==="skill"){if(!e.name||!e.sourceDir)return r(t,400,{error:'Missing "name" or "sourceDir"'}),!0;const o=e.agent??"claude",l=e.scope==="user"?await re(e.sourceDir,e.name,o):await Y(n,e.sourceDir,e.name,o);r(t,201,{category:"skill",name:e.name,scope:e.scope??"project",agent:o,path:l})}else if(e.category&&H(e.category)){if(!e.name||!e.sourceFile)return r(t,400,{error:'Missing "name" or "sourceFile"'}),!0;const o=e.agent??"claude",l=e.scope??"project",c=await ee(n,e.category,e.name,e.sourceFile,o,l);r(t,201,{category:e.category,name:e.name,scope:l,agent:o,path:c})}else if(e.category==="mcp"){if(!e.name||!e.mcpConfig)return r(t,400,{error:'Missing "name" or "mcpConfig"'}),!0;const o=e.agent??"claude",l=I(o);if(!l)return r(t,400,{error:`Unsupported agent: ${o}`}),!0;const c=l.mcpScopes(),u=e.scope??c[0];if(!u||!c.includes(u))return r(t,400,{error:`MCP servers for ${o} are ${c.join("/")}-level only \u2014 scope "${e.scope}" is not available`}),!0;if(e.name in await l.mcpRead(u,n,v()))return P(t,new x(`MCP server "${e.name}" is already configured`)),!0;await l.mcpWrite(u,n,v(),e.name,e.mcpConfig),u==="project"&&await me(n,{category:"mcp",name:e.name,agent:o}),r(t,201,{category:"mcp",name:e.name,scope:u,agent:o})}else if(e.category==="plugin"){if(!e.name||!e.sourceDir)return r(t,400,{error:'Missing "name" or "sourceDir"'}),!0;if(e.scope&&e.scope!=="user")return r(t,400,{error:'Plugin install currently only supports scope "user"'}),!0;const o=e.agent??"claude",l=W().find(d=>d.harness===o);if(!l)return r(t,400,{error:`No plugin install surface for runtime: ${o}`}),!0;const c=o==="claude"&&i.pluginSurface?i.pluginSurface:l.surface,u=await Ne(c,{dir:e.sourceDir,name:e.name,version:e.version,description:e.description});i.dataBus?.emit({key:"toolkit"}),r(t,201,{category:"plugin",key:u.key,scope:"user",agent:o,alreadyInstalled:u.alreadyInstalled})}else r(t,400,{error:'Invalid "category" (skill|mcp|plugin|command|agent|workflow|rule|output-style)'})}catch(o){P(t,o)}return!0}const B=m.match(/^\/projects\/(\d+)\/mcp\/builtin$/);if(B&&s.method==="POST"){const a=i.projects.getById(Number(B[1]));let e;try{e=JSON.parse(await h(s))}catch{return r(t,400,{error:"Invalid JSON body"}),!0}const n=je.find(d=>d.name===e.name);if(!n)return r(t,404,{error:`Unknown built-in MCP server: ${e.name??""}`}),!0;if(!a)return r(t,404,{error:"Project not found"}),!0;const o=e.agent??"claude",l=I(o);if(!l||!l.mcpScopes().includes("user"))return r(t,400,{error:`MCP servers for ${o} have no user scope`}),!0;const c=i.auth?.getLocalToken()??"",u={type:"http",url:`http://127.0.0.1:${i.port}${n.path}`,...c?{headers:{"x-weave-local":c}}:{}};try{await l.mcpWrite("user",a.path,v(),n.name,u),r(t,201,{category:"mcp",name:n.name,scope:"user",agent:o})}catch(d){r(t,400,{error:d instanceof Error?d.message:"Install failed"})}return!0}const M=m.match(/^\/projects\/(\d+)\/uninstall$/);if(M&&s.method==="POST"){const a=i.projects.getById(Number(M[1]));let e;try{e=JSON.parse(await h(s))}catch{return r(t,400,{error:"Invalid JSON body"}),!0}if(!a&&V(e))return r(t,404,{error:"Project not found"}),!0;const n=a?.path??"";try{if(e.category==="skill"){if(!e.name)return r(t,400,{error:'Missing "name"'}),!0;const o=e.agent??"claude";e.scope==="user"?await ae(e.name,o):await Z(n,e.name,o)}else if(e.category&&H(e.category)){if(!e.name)return r(t,400,{error:'Missing "name"'}),!0;await te(n,e.category,e.name,e.agent??"claude",e.scope??"project")}else if(e.category==="mcp"){if(!e.name)return r(t,400,{error:'Missing "name"'}),!0;const o=e.agent??"claude",l=I(o);if(!l)return r(t,400,{error:`Unsupported agent: ${o}`}),!0;const c=l.mcpScopes(),u=e.scope??c[0];if(!u||!c.includes(u))return r(t,400,{error:`MCP servers for ${o} are ${c.join("/")}-level only \u2014 scope "${e.scope}" is not available`}),!0;if(!await l.mcpRemove(u,n,v(),e.name))return P(t,new E(`MCP server "${e.name}" is not configured`)),!0;u==="project"&&await ce(n,{category:"mcp",name:e.name,agent:o})}else return r(t,400,{error:'Invalid "category" (skill|mcp|command|agent|workflow|rule|output-style)'}),!0;r(t,200,{ok:!0})}catch(o){P(t,o)}return!0}const C=m.match(/^\/projects\/(\d+)\/asset-file$/);if(C&&s.method==="GET"){const a=i.projects.getById(Number(C[1]));if(!a)return r(t,404,{error:"Project not found"}),!0;const e=p.searchParams.get("path");if(!e)return r(t,400,{error:'Missing "path" query parameter'}),!0;try{const n=await ue(a.path,e);r(t,200,{path:e,content:n.content,size:n.size})}catch(n){T(t,n)}return!0}if(m==="/global-file"&&s.method==="GET"){const a=p.searchParams.get("path");if(!a)return r(t,400,{error:'Missing "path" query parameter'}),!0;try{const e=await de(a);r(t,200,{path:a,content:e.content,size:e.size})}catch(e){T(t,e)}return!0}const J=m.match(/^\/projects\/(\d+)\/mcp$/);if(J&&s.method==="GET"){const a=i.projects.getById(Number(J[1]));return a?(r(t,200,{rows:await he(a.path)}),!0):(r(t,404,{error:"Project not found"}),!0)}const R=m.match(/^\/projects\/(\d+)\/harness-checks$/);if(R&&s.method==="GET"){const a=i.projects.getById(Number(R[1]));return a?(r(t,200,{checks:await X(a.path)}),!0):(r(t,404,{error:"Project not found"}),!0)}if(m.match(/^\/projects\/(\d+)\/skills\/global$/)&&s.method==="GET")return r(t,200,{groups:await ie()}),!0;if(m.match(/^\/projects\/(\d+)\/skills\/global\/files$/)&&s.method==="GET"){const a=p.searchParams.get("source"),e=p.searchParams.get("name"),n=p.searchParams.get("pluginKey")??void 0;if(!a||!e)return r(t,400,{error:'Missing "source" or "name" query parameter'}),!0;const o=await q(a,e,n);return o?(r(t,200,{files:await se(o)}),!0):(r(t,404,{error:"Skill not found"}),!0)}if(m.match(/^\/projects\/(\d+)\/skills\/global\/file$/)&&s.method==="GET"){const a=p.searchParams.get("source"),e=p.searchParams.get("name"),n=p.searchParams.get("pluginKey")??void 0,o=p.searchParams.get("path");if(!a||!e||!o)return r(t,400,{error:'Missing "source", "name" or "path" query parameter'}),!0;const l=await q(a,e,n);if(!l)return r(t,404,{error:"Skill not found"}),!0;try{const c=await ne(l,o);r(t,200,{path:o,content:c.content,size:c.size})}catch(c){T(t,c)}return!0}const G=m.match(/^\/projects\/(\d+)\/plugins\/state$/);if(G){const a=i.projects.getById(Number(G[1]));if(!a)return r(t,404,{error:"Project not found"}),!0;if(s.method==="GET")return r(t,200,{rows:await Ee(a.path)}),!0;if(s.method==="PUT"){let e;try{e=JSON.parse(await h(s))}catch{return r(t,400,{error:"Invalid JSON body"}),!0}if(!A(e.runtime)||!e.key||!e.scope||!["user","project","local"].includes(e.scope)||!e.state||!["on","off","inherit"].includes(e.state))return r(t,400,{error:'Missing or invalid "runtime"/"key"/"scope" (user|project|local)/"state" (on|off|inherit)'}),!0;const n=e.scope,o=e.state;try{const l=W().find(c=>c.harness===e.runtime);if(l)await(e.runtime==="claude"&&i.pluginSurface?i.pluginSurface:l.surface).setEnabledOverride(a.path,n,e.key,o);else{const c=Ue().find(u=>u.harness===e.runtime);if(!c||n!=="user"||o==="inherit")return r(t,400,{error:"This runtime only supports user-scope on/off overrides"}),!0;await c.state.writeGlobalEnabled(e.key,o==="on")}i.dataBus?.emit({key:"toolkit"}),r(t,200,{ok:!0})}catch(l){r(t,400,{error:l instanceof Error?l.message:"Plugin state write failed"})}return!0}}if(m.match(/^\/projects\/(\d+)\/plugins\/uninstall$/)&&s.method==="POST"){let a;try{a=JSON.parse(await h(s))}catch{return r(t,400,{error:"Invalid JSON body"}),!0}if(!A(a.runtime)||!a.key)return r(t,400,{error:'Missing or invalid "runtime"/"key"'}),!0;try{const e=a.runtime==="claude"&&i.pluginSurface?i.pluginSurface:_(a.runtime),{name:n,market:o}=e.parsePluginKey(a.key);await e.uninstall(n,o),r(t,200,{ok:!0})}catch(e){r(t,400,{error:e instanceof Error?e.message:"Plugin uninstall failed"})}return!0}const L=m.match(/^\/projects\/(\d+)\/mcp\/enable$/);if(L){const a=i.projects.getById(Number(L[1]));if(!a)return r(t,404,{error:"Project not found"}),!0;if(s.method==="PUT"){let e;try{e=JSON.parse(await h(s))}catch{return r(t,400,{error:"Invalid JSON body"}),!0}return!e.name||!e.state?(r(t,400,{error:'Missing "name" or "state"'}),!0):["on","off","inherit"].includes(e.state)?(await ge(a.path,e.name,e.state),r(t,200,{ok:!0}),!0):(r(t,400,{error:'Invalid "state" (on|off|inherit)'}),!0)}}const F=m.match(/^\/projects\/(\d+)\/updates$/);if(F&&s.method==="GET"){const a=i.projects.getById(Number(F[1]));if(!a)return r(t,404,{error:"Project not found"}),!0;const e=i.fingerprintCache??new z;if(!i.libraries){const o=await Se(a.path,e),l=await ve(a.path);return r(t,200,{skills:[],mcp:[],files:[],pluginTemplates:o,initFiles:l,available:o.filter(c=>c.outdated).length+l.filter(c=>c.outdated&&c.syncClass==="auto").length,needsInit:Ie(l)}),!0}const n=await we(a.path,i.libraries.list(),e);return r(t,200,n),!0}const D=m.match(/^\/projects\/(\d+)\/update$/);if(D&&s.method==="POST"){const a=i.projects.getById(Number(D[1]));if(!a)return r(t,404,{error:"Project not found"}),!0;if(!i.libraries)return r(t,503,{error:"Libraries unavailable"}),!0;let e;try{e=JSON.parse(await h(s))}catch{return r(t,400,{error:"Invalid JSON body"}),!0}if(!e.category||!e.name||!e.scope)return r(t,400,{error:'Missing "category", "name", or "scope"'}),!0;if(!["skill","mcp","command","agent","workflow","rule","output-style"].includes(e.category))return r(t,400,{error:'Invalid "category"'}),!0;try{const n=await ke(i.libraries.list());await ye(a.path,{category:e.category,name:e.name,scope:e.scope,agent:e.agent},n),r(t,200,{ok:!0,category:e.category,name:e.name,scope:e.scope})}catch(n){r(t,400,{error:n instanceof Error?n.message:"Update failed"})}return!0}const U=m.match(/^\/projects\/(\d+)\/sync-all$/);if(U&&s.method==="POST"){const a=i.projects.getById(Number(U[1]));if(!a)return r(t,404,{error:"Project not found"}),!0;if(!i.libraries)return r(t,503,{error:"Libraries unavailable"}),!0;try{const e=i.fingerprintCache??new z,n=await be(a.path,i.libraries.list(),e);let o=n;try{const l=i.hookPlugins?.list().map(u=>u.id)??[],c=Fe(a,l,{projects:i.projects});o={...n,pluginSync:{syncedAt:c}}}catch{}r(t,200,o),i.updatesRefresher?.refreshNow(!0)}catch(e){r(t,400,{error:e instanceof Error?e.message:"Sync failed"})}return!0}return!1}
@@ -1 +1 @@
1
- import{z as i}from"zod";import{nextRunAt as E,parseCron as j}from"../cron.js";import{validateOrchestration as S}from"../../hooks/orchestration-graph.js";import{graphDirections as B,toRunSummary as w}from"../../repositories/orchestrations.js";import{readBody as R,readJson as k,sendJson as o}from"../http.js";const M=i.object({id:i.string().min(1).max(128),kind:i.enum(["session","webhook","trigger"]),webhookToken:i.string().min(1).max(200).optional(),url:i.string().max(2e3).optional(),secret:i.string().max(400).optional(),autoFire:i.boolean().optional()}),T=i.object({name:i.string().min(1).max(200),startText:i.string().max(2e4).optional(),projectId:i.number().int().optional(),nodes:i.array(M).min(1),edges:i.array(i.object({id:i.string().min(1).max(64),from:i.string().min(1),to:i.string().min(1),text:i.string().max(2e4),extract:i.string().max(4e3).optional(),condition:i.string().max(4e3).optional(),when:i.string().max(500).optional()})),cron:i.string().max(200).nullish(),enabled:i.boolean().optional()}),N=T.partial(),O=i.object({text:i.string().min(1).max(2e4).optional(),entries:i.array(i.string().min(1)).optional()}),P=i.object({nodeId:i.string().min(1).max(128)}),$=new Set(["running","done","failed","cancelled","interrupted"]);function m(s,r){const t=s.searchParams.get(r);if(t==null||t==="")return;const c=Number(t);return Number.isFinite(c)?c:void 0}function I(s){const r=s.cron?j(s.cron):null;return{...s,nextRunAt:r?E(r):null}}function v(s,r){const t=r.name.trim();if(!t)return{ok:!1,error:"Name is required"};const c=S(r);if(!c.ok)return{ok:!1,error:c.error};const u=r.cron?.trim()||null;if(u&&!j(u))return{ok:!1,error:`Invalid cron expression: ${u}`};for(const d of r.nodes)if(d.kind==="session"&&!s.hookSessions?.getBySessionId(d.id))return{ok:!1,error:`Unknown session: ${d.id}`};const{entries:g}=B(r.nodes,r.edges),h=g.filter(d=>d.kind==="session");let l=r.projectId??null;if(h.length>0){const d=s.hookSessions.getBySessionId(h[0].id);if(l!=null&&l!==d.projectId)return{ok:!1,error:"projectId must match the entry sessions"};if(l=d.projectId,l==null)return{ok:!1,error:"Entry session has no project"};for(const f of h)if(s.hookSessions.getBySessionId(f.id).projectId!==l)return{ok:!1,error:"All entry sessions must belong to the same project"}}else{const d=r.nodes.find(f=>f.kind==="session");d&&(l=s.hookSessions.getBySessionId(d.id).projectId??l)}return l==null?{ok:!1,error:"projectId is required for webhook-only orchestrations"}:{ok:!0,value:{name:t,projectId:l,startText:(r.startText??"").trim(),nodes:r.nodes,edges:r.edges,cron:u}}}function _(s,r){for(const t of s.orchestrationRuns?.listRunning()??[])t.orchestrationId===r&&s.orchestrationEngine?.cancel(t.id)}export async function handleOrchestrationRoutes(s,r,t,c,u){if(c==="/messaging/orchestrations"){if(!t.orchestrations)return o(r,503,{error:"Messaging subsystem unavailable"}),!0;if(s.method==="GET"){const e=m(u,"projectId"),n=e===void 0?t.orchestrations.list():t.orchestrations.listByProject(e);return o(r,200,{orchestrations:n.map(I)}),!0}if(s.method==="POST"){let e;try{e=T.parse(await k(s))}catch(a){return o(r,400,{error:a instanceof Error?a.message:"Invalid body"}),!0}const n=v(t,e);return n.ok?(o(r,201,{ok:!0,orchestration:I(t.orchestrations.insert({...n.value,enabled:e.enabled}))}),!0):(o(r,400,{error:n.error}),!0)}}const g=c.match(/^\/messaging\/orchestrations\/(\d+)$/);if(g){const e=Number(g[1]);if(s.method==="PUT"){if(!t.orchestrations)return o(r,503,{error:"Messaging subsystem unavailable"}),!0;const n=t.orchestrations.getById(e);if(!n)return o(r,404,{error:"Orchestration not found"}),!0;let a;try{a=N.parse(await k(s))}catch(b){return o(r,400,{error:b instanceof Error?b.message:"Invalid body"}),!0}const y=v(t,{name:a.name??n.name,startText:a.startText??n.startText,projectId:a.projectId??(n.nodes.some(b=>b.kind==="session")?void 0:n.projectId),nodes:a.nodes??n.nodes,edges:a.edges??n.edges,cron:a.cron===void 0?n.cron:a.cron});return y.ok?(o(r,200,{ok:!0,orchestration:I(t.orchestrations.update(e,{...y.value,enabled:a.enabled}))}),!0):(o(r,400,{error:y.error}),!0)}if(s.method==="DELETE")return t.orchestrations?t.orchestrations.getById(e)?(_(t,e),t.orchestrations.deleteById(e),o(r,200,{ok:!0,orchestrationId:e}),!0):(o(r,404,{error:"Orchestration not found"}),!0):(o(r,503,{error:"Messaging subsystem unavailable"}),!0)}const h=c.match(/^\/messaging\/orchestrations\/(\d+)\/trigger$/);if(h&&s.method==="POST"){if(!t.orchestrationEngine||!t.orchestrations)return o(r,503,{error:"Messaging subsystem unavailable"}),!0;let e={};try{const a=(await R(s)).trim();a&&(e=O.parse(JSON.parse(a)))}catch{return o(r,400,{error:"Invalid body"}),!0}const n=await t.orchestrationEngine.start(Number(h[1]),"manual",e.text,e.entries);return n.ok?(o(r,201,{ok:!0,runId:n.runId}),!0):(o(r,n.status,{error:n.error}),!0)}const l=c.match(/^\/messaging\/orchestrations\/(\d+)\/rotate-webhook$/);if(l&&s.method==="POST"){if(!t.orchestrations)return o(r,503,{error:"Messaging subsystem unavailable"}),!0;let e;try{e=P.parse(await k(s))}catch(a){return o(r,400,{error:a instanceof Error?a.message:"Invalid body"}),!0}const n=t.orchestrations.rotateNodeToken(Number(l[1]),e.nodeId);return n?(o(r,200,{ok:!0,webhookToken:n}),!0):(o(r,404,{error:"Webhook entry node not found"}),!0)}const d=c.match(/^\/webhooks\/orchestrate\/([A-Za-z0-9_-]+)$/);if(d&&s.method==="POST"){if(!t.orchestrations||!t.orchestrationEngine)return o(r,503,{error:"Messaging subsystem unavailable"}),!0;const e=t.orchestrations.getByNodeToken(d[1]);if(!e)return o(r,404,{error:"Unknown webhook token"}),!0;const n=(await R(s)).trim().slice(0,2e4),a=await t.orchestrationEngine.start(e.row.id,"webhook",n||void 0,[e.nodeId]);return a.ok?(o(r,202,{ok:!0,runId:a.runId}),!0):(o(r,a.status,{error:a.error}),!0)}if(c==="/messaging/orchestration-runs"){if(!t.orchestrationRuns)return o(r,503,{error:"Messaging subsystem unavailable"}),!0;if(s.method==="GET"){const e=u.searchParams.get("status")||void 0;if(e&&!$.has(e))return o(r,400,{error:`Unknown run status: ${e}`}),!0;const n=t.orchestrationRuns.query({orchestrationId:m(u,"orchestrationId"),projectId:m(u,"projectId"),status:e,text:u.searchParams.get("text")||void 0,before:m(u,"before"),limit:m(u,"limit")});return o(r,200,{runs:n.runs.map(w),nextBefore:n.nextBefore}),!0}if(s.method==="DELETE"){const e=u.searchParams.get("scope")==="all"?"all":"finished",n=m(u,"projectId");if(e==="all")for(const a of t.orchestrationRuns.listRunning())n!=null&&a.projectId!==n||t.orchestrationEngine?.cancel(a.id);return o(r,200,{ok:!0,deleted:t.orchestrationRuns.clear(e,n)}),!0}}const f=c.match(/^\/messaging\/orchestration-runs\/(\d+)$/);if(f&&s.method==="GET"){if(!t.orchestrationRuns)return o(r,503,{error:"Messaging subsystem unavailable"}),!0;const e=t.orchestrationRuns.getById(Number(f[1]));return e?(o(r,200,{run:{...w(e),state:e.state}}),!0):(o(r,404,{error:"Run not found"}),!0)}const x=c.match(/^\/messaging\/orchestration-runs\/(\d+)\/cancel$/);if(x&&s.method==="POST"){if(!t.orchestrationRuns)return o(r,503,{error:"Messaging subsystem unavailable"}),!0;const e=t.orchestrationRuns.getById(Number(x[1]));return e?e.status!=="running"?(o(r,409,{error:`Run already ${e.status}`}),!0):(t.orchestrationEngine?.cancel(e.id)||t.orchestrationRuns.finish(e.id,"cancelled"),o(r,200,{ok:!0,runId:e.id}),!0):(o(r,404,{error:"Run not found"}),!0)}return!1}
1
+ import{z as i}from"zod";import{nextRunAt as E,parseCron as x}from"../cron.js";import{validateOrchestration as R}from"../../hooks/orchestration-graph.js";import{graphDirections as S,toRunSummary as j}from"../../repositories/orchestrations.js";import{readBody as v,readJson as k,sendJson as n}from"../http.js";const B=i.object({id:i.string().min(1).max(128),kind:i.enum(["session","webhook","trigger"]),webhookToken:i.string().min(1).max(200).optional(),url:i.string().max(2e3).optional(),secret:i.string().max(400).optional(),autoFire:i.boolean().optional()}),w=i.object({name:i.string().min(1).max(200),startText:i.string().max(2e4).optional(),projectId:i.number().int().optional(),nodes:i.array(B).min(1),edges:i.array(i.object({id:i.string().min(1).max(64),from:i.string().min(1),to:i.string().min(1),text:i.string().max(2e4),extract:i.string().max(4e3).optional(),condition:i.string().max(4e3).optional(),when:i.string().max(500).optional()})),cron:i.string().max(200).nullish(),enabled:i.boolean().optional()}),N=w.partial(),P=i.object({text:i.string().min(1).max(2e4).optional(),entries:i.array(i.string().min(1)).optional()}),O=i.object({nodeId:i.string().min(1).max(128)}),$=new Set(["running","done","failed","cancelled","interrupted"]);function g(s,e){const r=s.searchParams.get(e);if(r==null||r==="")return;const c=Number(r);return Number.isFinite(c)?c:void 0}function I(s){const e=s.cron?x(s.cron):null;return{...s,nextRunAt:e?E(e):null}}function T(s,e){const r=e.name.trim();if(!r)return{ok:!1,error:"Name is required"};const c=R(e);if(!c.ok)return{ok:!1,error:c.error};const d=e.cron?.trim()||null;if(d&&!x(d))return{ok:!1,error:`Invalid cron expression: ${d}`};for(const u of e.nodes)if(u.kind==="session"&&!s.hookSessions?.getBySessionId(u.id))return{ok:!1,error:`Unknown session: ${u.id}`};const{entries:f}=S(e.nodes,e.edges),m=f.filter(u=>u.kind==="session");let l=e.projectId??null;if(m.length>0){const u=s.hookSessions.getBySessionId(m[0].id);if(l!=null&&l!==u.projectId)return{ok:!1,error:"projectId must match the entry sessions"};if(l=u.projectId,l==null)return{ok:!1,error:"Entry session has no project"};for(const h of m)if(s.hookSessions.getBySessionId(h.id).projectId!==l)return{ok:!1,error:"All entry sessions must belong to the same project"}}else{const u=e.nodes.find(h=>h.kind==="session");u&&(l=s.hookSessions.getBySessionId(u.id).projectId??l)}return l==null?{ok:!1,error:"projectId is required for webhook-only orchestrations"}:{ok:!0,value:{name:r,projectId:l,startText:(e.startText??"").trim(),nodes:e.nodes,edges:e.edges,cron:d}}}function M(s,e){for(const r of s.orchestrationRuns?.listRunning()??[])r.orchestrationId===e&&s.orchestrationEngine?.cancel(r.id)}export async function handleOrchestrationRoutes(s,e,r,c,d){if(c==="/messaging/orchestrations"){if(!r.orchestrations)return n(e,503,{error:"Messaging subsystem unavailable"}),!0;if(s.method==="GET"){const o=g(d,"projectId"),t=o===void 0?r.orchestrations.list():r.orchestrations.listByProject(o);return n(e,200,{orchestrations:t.map(I)}),!0}if(s.method==="POST"){let o;try{o=w.parse(await k(s))}catch(a){return n(e,400,{error:a instanceof Error?a.message:"Invalid body"}),!0}const t=T(r,o);return t.ok?(n(e,201,{ok:!0,orchestration:I(r.orchestrations.insert({...t.value,enabled:o.enabled}))}),!0):(n(e,400,{error:t.error}),!0)}}const f=c.match(/^\/messaging\/orchestrations\/(\d+)$/);if(f){const o=Number(f[1]);if(s.method==="PUT"){if(!r.orchestrations)return n(e,503,{error:"Messaging subsystem unavailable"}),!0;const t=r.orchestrations.getById(o);if(!t)return n(e,404,{error:"Orchestration not found"}),!0;let a;try{a=N.parse(await k(s))}catch(b){return n(e,400,{error:b instanceof Error?b.message:"Invalid body"}),!0}const p=T(r,{name:a.name??t.name,startText:a.startText??t.startText,projectId:a.projectId??(t.nodes.some(b=>b.kind==="session")?void 0:t.projectId),nodes:a.nodes??t.nodes,edges:a.edges??t.edges,cron:a.cron===void 0?t.cron:a.cron});return p.ok?(n(e,200,{ok:!0,orchestration:I(r.orchestrations.update(o,{...p.value,enabled:a.enabled}))}),!0):(n(e,400,{error:p.error}),!0)}if(s.method==="DELETE")return r.orchestrations?r.orchestrations.getById(o)?(M(r,o),r.orchestrations.deleteById(o),n(e,200,{ok:!0,orchestrationId:o}),!0):(n(e,404,{error:"Orchestration not found"}),!0):(n(e,503,{error:"Messaging subsystem unavailable"}),!0)}const m=c.match(/^\/messaging\/orchestrations\/(\d+)\/trigger$/);if(m&&s.method==="POST"){if(!r.orchestrationEngine||!r.orchestrations)return n(e,503,{error:"Messaging subsystem unavailable"}),!0;let o={};try{const a=(await v(s)).trim();a&&(o=P.parse(JSON.parse(a)))}catch{return n(e,400,{error:"Invalid body"}),!0}const t=await r.orchestrationEngine.start(Number(m[1]),"manual",o.text,o.entries);return t.ok?(n(e,201,{ok:!0,runId:t.runId}),!0):(n(e,t.status,{error:t.error}),!0)}const l=c.match(/^\/messaging\/orchestrations\/(\d+)\/rotate-webhook$/);if(l&&s.method==="POST"){if(!r.orchestrations)return n(e,503,{error:"Messaging subsystem unavailable"}),!0;let o;try{o=O.parse(await k(s))}catch(a){return n(e,400,{error:a instanceof Error?a.message:"Invalid body"}),!0}const t=r.orchestrations.rotateNodeToken(Number(l[1]),o.nodeId);return t?(n(e,200,{ok:!0,webhookToken:t}),!0):(n(e,404,{error:"Webhook entry node not found"}),!0)}const u=c.match(/^\/webhooks\/orchestrate\/([A-Za-z0-9_-]+)$/);if(u&&s.method==="POST"){if(!r.orchestrations||!r.orchestrationEngine)return n(e,503,{error:"Messaging subsystem unavailable"}),!0;const o=r.orchestrations.getByNodeToken(u[1]);if(!o)return n(e,404,{error:"Unknown webhook token"}),!0;const t=(await v(s)).trim().slice(0,2e4),a=await r.orchestrationEngine.start(o.row.id,"webhook",t||void 0,[o.nodeId]);return a.ok?(n(e,202,{ok:!0,runId:a.runId}),!0):(n(e,a.status,{error:a.error}),!0)}if(c==="/messaging/orchestration-runs"){if(!r.orchestrationRuns)return n(e,503,{error:"Messaging subsystem unavailable"}),!0;if(s.method==="GET"){const o=d.searchParams.get("status")||void 0;if(o&&!$.has(o))return n(e,400,{error:`Unknown run status: ${o}`}),!0;const t=r.orchestrationRuns.query({orchestrationId:g(d,"orchestrationId"),projectId:g(d,"projectId"),status:o,text:d.searchParams.get("text")||void 0,before:g(d,"before"),limit:g(d,"limit")});return n(e,200,{runs:t.runs.map(j),nextBefore:t.nextBefore}),!0}if(s.method==="DELETE"){const o=d.searchParams.get("scope")==="all"?"all":"finished",t=g(d,"projectId");if(o==="all")for(const a of r.orchestrationRuns.listRunning())t!=null&&a.projectId!==t||r.orchestrationEngine?.cancel(a.id);return n(e,200,{ok:!0,deleted:r.orchestrationRuns.clear(o,t)}),!0}}const h=c.match(/^\/messaging\/orchestration-runs\/(\d+)$/);if(h&&s.method==="GET"){if(!r.orchestrationRuns)return n(e,503,{error:"Messaging subsystem unavailable"}),!0;const o=r.orchestrationRuns.getById(Number(h[1]));return o?(n(e,200,{run:{...j(o),state:o.state}}),!0):(n(e,404,{error:"Run not found"}),!0)}const y=c.match(/^\/messaging\/orchestration-runs\/(\d+)\/cancel$/);if(y&&s.method==="POST"){if(!r.orchestrationRuns)return n(e,503,{error:"Messaging subsystem unavailable"}),!0;const o=r.orchestrationRuns.getById(Number(y[1]));return o?o.status!=="running"?(n(e,409,{error:`Run already ${o.status}`}),!0):(r.orchestrationEngine?.cancel(o.id)||r.orchestrationRuns.finish(o.id,"cancelled"),n(e,200,{ok:!0,runId:o.id}),!0):(n(e,404,{error:"Run not found"}),!0)}return!1}
@@ -1 +1 @@
1
- import{isHarness as I}from"@weavecode/core";import{readJson as j,sendJson as o}from"../http.js";import{messagingSurfaceFor as P}from"../../surfaces.js";import{projectFileIndex as R}from"../project-files.js";function S(t,e,r){if(!e.hookSessions||!e.hookEvents)return o(t,503,{error:"Hooks subsystem unavailable"}),null;const n=e.hookSessions.getBySessionId(r);if(!n)return o(t,404,{error:"Session not found"}),null;if(n.projectId==null)return o(t,400,{error:"Session is not bound to a project"}),null;const s=e.projects.getById(n.projectId);return s||(o(t,404,{error:"Project not found"}),null)}function b(t,e){if(!t.hookEvents)return null;const r=t.hookEvents.listBySession(e,1),n=r[r.length-1]?.source;return I(n)?n:null}function k(t,e,r){const n=b(e,r),s=n?P(n):null,a=s?.pathRefs??null;return a||(o(t,409,{error:n?s?.unsupportedReasons?.pathRefs??`Harness "${n}" has no path reference syntax`:"Session has no hook events to determine its harness"}),null)}function x(t){if(t==null||t==="")return null;const e=Number(t);return Number.isInteger(e)&&e>0?e:null}function y(t,e){let r=0,n=t.length-1;for(;r<=n;){const s=r+n>>1,a=t[s];if(a===e)return!0;a<e?r=s+1:n=s-1}return!1}export async function handlePathRefRoutes(t,e,r,n,s){const a=n.match(/^\/sessions\/([^/]+)\/ref$/);if(a&&t.method==="GET"){const d=decodeURIComponent(a[1]);if(!S(e,r,d))return!0;const u=k(e,r,d);if(!u)return!0;const c=(s.searchParams.get("path")??"").replace(/^\/+|\/+$/g,"");if(!c)return o(e,400,{error:'Missing "path" query parameter'}),!0;const f=s.searchParams.get("kind")==="dir"?"dir":"file";if(!u.kinds.includes(f))return o(e,409,{error:`Harness does not accept "${f}" references`}),!0;const l=x(s.searchParams.get("start")),h=x(s.searchParams.get("end"));return u.lines?l==null?(o(e,200,{ref:u.expression({path:c,kind:f})}),!0):h!=null&&h<l?(o(e,400,{error:'"end" must not precede "start"'}),!0):(o(e,200,{ref:u.expression({path:c,kind:f,lines:{start:l,end:h??l}})}),!0):(o(e,200,{ref:u.expression({path:c,kind:f})}),!0)}const g=n.match(/^\/sessions\/([^/]+)\/refs\/recognize$/);if(g&&t.method==="POST"){const d=decodeURIComponent(g[1]),u=S(e,r,d);if(!u)return!0;const c=k(e,r,d);if(!c)return!0;let f="";try{const i=await j(t);f=typeof i?.text=="string"?i.text:""}catch{return o(e,400,{error:"Body must be JSON"}),!0}const l=await R(u.id,u.path),h=[];for(const i of c.recognize(f)){const p=i.kind==="dir"&&y(l.dirs,i.path)?"dir":y(l.files,i.path)?"file":null;if(!p||!c.kinds.includes(p))continue;const m=i.lines&&c.lines?i.lines:void 0;h.push({start:i.start,end:i.end,ref:c.expression({path:i.path,kind:p,...m?{lines:m}:{}}),path:i.path,kind:p,...m?{lines:m}:{}})}return o(e,200,{refs:h}),!0}return!1}
1
+ import{isHarness as I}from"@weavecode/core";import{readJson as P,sendJson as t}from"../http.js";import{messagingSurfaceFor as R}from"../../surfaces.js";import{projectFileIndex as b}from"../project-files.js";function k(e,n,s){if(!n.hookSessions||!n.hookEvents)return t(e,503,{error:"Hooks subsystem unavailable"}),null;const r=n.hookSessions.getBySessionId(s);return r?r.projectId==null?(t(e,400,{error:"Session is not bound to a project"}),null):n.projects.getById(r.projectId)||(t(e,404,{error:"Project not found"}),null):(t(e,404,{error:"Session not found"}),null)}function j(e,n){if(!e.hookEvents)return null;const s=e.hookEvents.listBySession(n,1),r=s[s.length-1]?.source;return I(r)?r:null}function y(e,n,s){const r=j(n,s),o=r?R(r):null;return(o?.pathRefs??null)||(t(e,409,{error:r?o?.unsupportedReasons?.pathRefs??`Harness "${r}" has no path reference syntax`:"Session has no hook events to determine its harness"}),null)}function S(e){if(e==null||e==="")return null;const n=Number(e);return Number.isInteger(n)&&n>0?n:null}function x(e,n){let s=0,r=e.length-1;for(;s<=r;){const o=s+r>>1,f=e[o];if(f===n)return!0;f<n?s=o+1:r=o-1}return!1}export async function handlePathRefRoutes(e,n,s,r,o){const f=r.match(/^\/sessions\/([^/]+)\/ref$/);if(f&&e.method==="GET"){const h=decodeURIComponent(f[1]);if(!k(n,s,h))return!0;const a=y(n,s,h);if(!a)return!0;const u=(o.searchParams.get("path")??"").replace(/^\/+|\/+$/g,"");if(!u)return t(n,400,{error:'Missing "path" query parameter'}),!0;const c=o.searchParams.get("kind")==="dir"?"dir":"file";if(!a.kinds.includes(c))return t(n,409,{error:`Harness does not accept "${c}" references`}),!0;const l=S(o.searchParams.get("start")),d=S(o.searchParams.get("end"));return a.lines?l==null?(t(n,200,{ref:a.expression({path:u,kind:c})}),!0):d!=null&&d<l?(t(n,400,{error:'"end" must not precede "start"'}),!0):(t(n,200,{ref:a.expression({path:u,kind:c,lines:{start:l,end:d??l}})}),!0):(t(n,200,{ref:a.expression({path:u,kind:c})}),!0)}const g=r.match(/^\/sessions\/([^/]+)\/refs\/recognize$/);if(g&&e.method==="POST"){const h=decodeURIComponent(g[1]),a=k(n,s,h);if(!a)return!0;const u=y(n,s,h);if(!u)return!0;let c="";try{const i=await P(e);c=typeof i?.text=="string"?i.text:""}catch{return t(n,400,{error:"Body must be JSON"}),!0}const l=await b(a.id,a.path),d=[];for(const i of u.recognize(c)){const p=i.kind==="dir"&&x(l.dirs,i.path)?"dir":x(l.files,i.path)?"file":null;if(!p||!u.kinds.includes(p))continue;const m=i.lines&&u.lines?i.lines:void 0;d.push({start:i.start,end:i.end,ref:u.expression({path:i.path,kind:p,...m?{lines:m}:{}}),path:i.path,kind:p,...m?{lines:m}:{}})}return t(n,200,{refs:d}),!0}return!1}
@@ -1 +1 @@
1
- import{lstat as O,readFile as T,stat as j}from"node:fs/promises";import I from"node:path";import{sendJson as n}from"../http.js";import{listDirOf as E,matchPaths as F,projectFileIndex as S}from"../project-files.js";import{resolveWithinProject as v}from"../../install/reader.js";const N=4*1024*1024,P=1024*1024,_=8192;function k(r){const t=Number(r);return!Number.isFinite(t)||t<=0?100:Math.min(200,Math.floor(t))}function B(r){switch(r.slice(r.lastIndexOf(".")+1).toLowerCase()){case"png":return"image/png";case"jpg":case"jpeg":return"image/jpeg";case"gif":return"image/gif";case"webp":return"image/webp";default:return null}}function M(r){switch(r.slice(r.lastIndexOf(".")+1).toLowerCase()){case"html":case"htm":return"text/html; charset=utf-8";case"svg":return"image/svg+xml";case"pdf":return"application/pdf";default:return null}}const z=new Set(["docx","doc","xlsx","xls","pptx","ppt","zip","gz","tar","rar","7z","exe","dll","bin","woff","woff2","ttf","otf","ico","mp4","mp3","wav","mov"]);function L(r){const t=r.slice(r.lastIndexOf(".")+1).toLowerCase();return z.has(t)?"application/octet-stream":null}export async function handleProjectFileRoutes(r,t,x,w,f){const y=w.match(/^\/projects\/(\d+)\/files$/);if(y&&r.method==="GET"){const i=x.projects.getById(Number(y[1]));if(!i)return n(t,404,{error:"Project not found"}),!0;const a=await S(i.id,i.path,{fresh:f.searchParams.get("fresh")==="1"}),u=f.searchParams.get("q")??"",c=(f.searchParams.get("dir")??"").replace(/^\/+|\/+$/g,""),m=k(f.searchParams.get("limit"));if(u.trim()){const{hits:e,total:o,capped:l}=await F(a,u,m,c);return n(t,200,{root:i.path,dir:c,total:o,capped:l,truncated:a.truncated,entries:e.map(s=>({path:s.path,name:s.path.slice(s.path.lastIndexOf("/")+1),kind:s.kind,ranges:s.ranges,...a.submodules[s.path]?{head:a.submodules[s.path]}:{}}))}),!0}const p=E(a,c),d=await Promise.all(p.slice(0,m).map(async e=>{const o={path:e.path,name:e.path.slice(e.path.lastIndexOf("/")+1),kind:e.kind,...e.head?{head:e.head}:{}},l=I.join(i.path,e.path),[s,h]=await Promise.all([O(l).catch(()=>null),e.kind==="file"?j(l).catch(()=>null):Promise.resolve(null)]),g=s?.isSymbolicLink()?{symlink:!0}:null;return h?{...o,...g,size:h.size,mtime:h.mtime.toISOString()}:g?{...o,...g}:o}));return n(t,200,{root:i.path,dir:c,total:p.length,capped:!1,truncated:a.truncated,entries:d}),!0}const b=w.match(/^\/projects\/(\d+)\/file$/);if(b&&r.method==="GET"){const i=x.projects.getById(Number(b[1]));if(!i)return n(t,404,{error:"Project not found"}),!0;const a=f.searchParams.get("path");if(!a)return n(t,400,{error:'Missing "path" query parameter'}),!0;let u;try{u=v(i.path,a)}catch{return n(t,403,{error:"Path escapes the project"}),!0}let c;try{c=await j(u)}catch{return n(t,404,{error:"File not found"}),!0}if(!c.isFile())return n(t,404,{error:"Not a file"}),!0;if(c.size>N)return n(t,413,{error:"File too large to preview"}),!0;const m=B(a),p=M(a),d=L(a);let e=await T(u);if(!m&&!p&&!d){if(e.subarray(0,_).includes(0))return n(t,415,{error:"Binary file"}),!0;if(e.length>P){const o=e.subarray(0,P),l=o.lastIndexOf(10);e=l>0?o.subarray(0,l):o,t.setHeader("x-weave-truncated","1")}}return p&&t.setHeader("content-security-policy","sandbox"),d&&t.setHeader("content-disposition",`attachment; filename*=UTF-8''${encodeURI(I.basename(a))}`),t.writeHead(200,{"content-type":m??p??d??"text/plain; charset=utf-8","content-length":String(e.length),"cache-control":"private, max-age=60"}),t.end(e),!0}return!1}
1
+ import{lstat as v,readFile as F,stat as j}from"node:fs/promises";import P from"node:path";import{sendJson as n}from"../http.js";import{listDirOf as O,matchPaths as k,projectFileIndex as z}from"../project-files.js";import{resolveWithinProject as N}from"../../install/reader.js";const S=4*1024*1024,I=1024*1024,L=8192;function B(r){const e=Number(r);return!Number.isFinite(e)||e<=0?100:Math.min(200,Math.floor(e))}function H(r){switch(r.slice(r.lastIndexOf(".")+1).toLowerCase()){case"png":return"image/png";case"jpg":case"jpeg":return"image/jpeg";case"gif":return"image/gif";case"webp":return"image/webp";default:return null}}function M(r){switch(r.slice(r.lastIndexOf(".")+1).toLowerCase()){case"html":case"htm":return"text/html; charset=utf-8";case"svg":return"image/svg+xml";case"pdf":return"application/pdf";default:return null}}const T=new Set(["docx","doc","xlsx","xls","pptx","ppt","zip","gz","tar","rar","7z","exe","dll","bin","woff","woff2","ttf","otf","ico","mp4","mp3","wav","mov"]);function $(r){const e=r.slice(r.lastIndexOf(".")+1).toLowerCase();return T.has(e)?"application/octet-stream":null}export async function handleProjectFileRoutes(r,e,w,x,m){const y=x.match(/^\/projects\/(\d+)\/files$/);if(y&&r.method==="GET"){const i=w.projects.getById(Number(y[1]));if(!i)return n(e,404,{error:"Project not found"}),!0;const a=await z(i.id,i.path,{fresh:m.searchParams.get("fresh")==="1"}),p=m.searchParams.get("q")??"",c=(m.searchParams.get("dir")??"").replace(/^\/+|\/+$/g,""),h=B(m.searchParams.get("limit"));if(p.trim()){const{hits:t,total:s,capped:l}=await k(a,p,h,c);return n(e,200,{root:i.path,dir:c,total:s,capped:l,truncated:a.truncated,entries:t.map(o=>({path:o.path,name:o.path.slice(o.path.lastIndexOf("/")+1),kind:o.kind,ranges:o.ranges,...a.submodules[o.path]?{head:a.submodules[o.path]}:{}}))}),!0}const u=O(a,c),d=await Promise.all(u.slice(0,h).map(async t=>{const s={path:t.path,name:t.path.slice(t.path.lastIndexOf("/")+1),kind:t.kind,...t.head?{head:t.head}:{}},l=P.join(i.path,t.path),[o,f]=await Promise.all([v(l).catch(()=>null),t.kind==="file"?j(l).catch(()=>null):Promise.resolve(null)]),g=o?.isSymbolicLink()?{symlink:!0}:null;return f?{...s,...g,size:f.size,mtime:f.mtime.toISOString()}:g?{...s,...g}:s}));return n(e,200,{root:i.path,dir:c,total:u.length,capped:!1,truncated:a.truncated,entries:d}),!0}const b=x.match(/^\/projects\/(\d+)\/file$/);if(b&&r.method==="GET"){const i=w.projects.getById(Number(b[1]));if(!i)return n(e,404,{error:"Project not found"}),!0;const a=m.searchParams.get("path");if(!a)return n(e,400,{error:'Missing "path" query parameter'}),!0;let p;try{p=N(i.path,a)}catch{return n(e,403,{error:"Path escapes the project"}),!0}let c;try{c=await j(p)}catch{return n(e,404,{error:"File not found"}),!0}if(!c.isFile())return n(e,404,{error:"Not a file"}),!0;if(c.size>S)return n(e,413,{error:"File too large to preview"}),!0;const h=H(a),u=M(a),d=$(a);let t=await F(p);if(!h&&!u&&!d){if(t.subarray(0,L).includes(0))return n(e,415,{error:"Binary file"}),!0;if(t.length>I){const s=t.subarray(0,I),l=s.lastIndexOf(10);t=l>0?s.subarray(0,l):s,e.setHeader("x-weave-truncated","1")}}return u&&e.setHeader("content-security-policy","sandbox"),d&&e.setHeader("content-disposition",`attachment; filename*=UTF-8''${encodeURI(P.basename(a))}`),e.writeHead(200,{"content-type":h??u??d??"text/plain; charset=utf-8","content-length":String(t.length),"cache-control":"private, max-age=60"}),e.end(t),!0}return!1}
@@ -1 +1 @@
1
- import y from"node:fs";import $ from"node:path";import{isHarness as J}from"@weavecode/core";import{messagingSurfaceFor as M}from"../../surfaces.js";import{readBody as p,sendJson as o}from"../http.js";import{readDaemonSettings as j}from"../settings.js";import{closeTerminalsIn as A,defaultProjectOpener as d,readTerminalCounts as x,terminalBackendFor as D}from"../opener.js";import{purgeProjectPipeline as R}from"../hook-pipeline.js";import{hooksRoot as G}from"../../hooks/mirror.js";import{projectHarnesses as H,withProjectHarnesses as E}from"../project-harnesses.js";import{scheduleBootstrapInjection as C}from"../session-open.js";import{getUploadsDir as U}from"../../db/paths.js";export async function handleProjectRoutes(a,e,r,l){const I=l.match(/^\/projects\/(\d+)\/open$/);if(I&&a.method==="POST"){const t=r.projects.getById(Number(I[1]));if(!t)return o(e,404,{error:"Project not found"}),!0;let n;try{n=JSON.parse(await p(a))}catch{return o(e,400,{error:"Invalid JSON body"}),!0}if(n.target!=="explorer"&&n.target!=="terminal")return o(e,400,{error:'Invalid "target" (explorer|terminal)'}),!0;const s=r.opener??d;try{if(!(await y.promises.stat(t.path)).isDirectory())throw new Error(`Not a directory: ${t.path}`);if(n.target==="explorer")await s.openExplorer(t.path);else{const c=await j();await s.openTerminal(t.path,c.terminal),setTimeout(()=>{r.terminalRefresher?.refreshNow()},600)}o(e,200,{ok:!0,target:n.target,path:t.path})}catch(i){o(e,400,{error:i instanceof Error?i.message:"Open failed"})}return!0}const v=l.match(/^\/projects\/(\d+)\/close-terminal$/);if(v&&a.method==="POST"){const t=r.projects.getById(Number(v[1]));if(!t)return o(e,404,{error:"Project not found"}),!0;let n;try{n=JSON.parse(await p(a)||"{}")}catch{return o(e,400,{error:"Invalid JSON body"}),!0}if(!Array.isArray(n.paneIds)||n.paneIds.length===0||n.paneIds.some(c=>typeof c!="string"))return o(e,400,{error:"paneIds must be a non-empty array of strings"}),!0;const s=r.opener??d,i=await j();try{const u=await(s.closeTerminals??A)(t.path,i.terminal,n.paneIds);setTimeout(()=>{r.terminalRefresher?.refreshNow()},600),o(e,200,{ok:!0,closed:u})}catch(c){o(e,400,{error:c instanceof Error?c.message:"Close failed"})}return!0}const b=l.match(/^\/projects\/(\d+)\/focus-terminal$/);if(b&&a.method==="POST"){const t=r.projects.getById(Number(b[1]));if(!t)return o(e,404,{error:"Project not found"}),!0;let n;try{n=JSON.parse(await p(a)||"{}")}catch{return o(e,400,{error:"Invalid JSON body"}),!0}if(typeof n.targetId!="string"||!n.targetId)return o(e,400,{error:"targetId must be a non-empty string"}),!0;const s=r.opener??d,i=await j();try{await(s.focusTerminal??d.focusTerminal)(t.path,n.targetId,i.terminal),o(e,200,{ok:!0,targetId:n.targetId})}catch(c){o(e,409,{error:c instanceof Error?c.message:"Cannot focus terminal"})}return!0}const P=l.match(/^\/projects\/(\d+)\/new-session$/);if(P&&a.method==="POST"){const t=r.projects.getById(Number(P[1]));if(!t)return o(e,404,{error:"Project not found"}),!0;let n;try{n=JSON.parse(await p(a)||"{}")}catch{return o(e,400,{error:"Invalid JSON body"}),!0}const s=await H(t.path,r.harnesses);if(n.harness!==void 0&&!J(n.harness))return o(e,400,{error:'Invalid "harness"'}),!0;const i=n.harness;if(i&&!s.some(m=>m.id===i))return o(e,409,{error:`Harness "${i}" is not integrated in this project`}),!0;const c=i?[i]:s.map(m=>m.id),u=s.find(m=>c.includes(m.id)&&m.canStartSession),f=u?r.harnesses?.get(u.id)?.resume:void 0;if(!f?.bin)return o(e,501,{error:`Harness "${c[0]}" cannot start an interactive session`}),!0;const h=r.opener??d;try{if(!(await y.promises.stat(t.path)).isDirectory())throw new Error(`Not a directory: ${t.path}`);const g=await j(),k=[f.bin,...f.launchArgs??[]],w=await h.runInTerminal(t.path,k,g.terminal);w&&u&&M(u.id).paneEnvSource==="launch"&&r.messageDeliverer?.notePendingOrigin({harness:u.id,projectId:t.id,env:w.env}),w&&u&&f?.bootstrapText&&C(r,w,f.bootstrapText(t.name)),setTimeout(()=>{r.terminalRefresher?.refreshNow()},600),o(e,200,{ok:!0,command:k.join(" "),cwd:t.path})}catch(m){o(e,400,{error:m instanceof Error?m.message:"Open failed"})}return!0}if(l==="/projects"&&a.method==="GET")return o(e,200,{projects:await E(r.projects.list(),r.harnesses)}),!0;if(l==="/projects/archived"&&a.method==="GET")return o(e,200,{projects:await E(r.projects.listArchived(),r.harnesses)}),!0;const T=l.match(/^\/projects\/(\d+)\/archive$/);if(T&&a.method==="POST"){const t=r.projects.getById(Number(T[1]));if(!t)return o(e,404,{error:"Project not found"}),!0;if(t.archivedAt)return o(e,200,{project:t,purged:{events:0,dispatches:0,sessions:0,messages:0}}),!0;const n=r.hookDispatches?.deleteByProject(t.id)??0,s=r.hookEvents?.deleteByProject(t.id)??0;r.uploads?.pruneUnreferenced();const i=r.messageDeliverer?.forgetProject(t.id)??0;r.sessionWatches?.deleteByProject(t.id),r.orchestrations?.deleteByProject(t.id),r.orchestrationRuns?.clear("all",t.id),r.sideQuestions?.repo.deleteByProject(t.id);const c=r.hookSessions?.removeByProject(t.id)??0;return r.projects.setArchived(t.id,!0),R(r.hooksDir??G(),t.token),r.watch?.reconcile(),r.dataBus?.emit({key:"projects"}),o(e,200,{project:r.projects.getById(t.id),purged:{events:s,dispatches:n,sessions:c,messages:i}}),!0}const S=l.match(/^\/projects\/(\d+)\/restore$/);if(S&&a.method==="POST"){const t=r.projects.getById(Number(S[1]));return t?t.archivedAt?(r.projects.setArchived(t.id,!1),r.hookMirror?.syncProject(t.id),r.watch?.reconcile(),r.dataBus?.emit({key:"projects"}),o(e,200,{project:r.projects.getById(t.id)}),!0):(o(e,200,{project:t}),!0):(o(e,404,{error:"Project not found"}),!0)}if(l==="/projects/order"&&a.method==="PUT"){let t;try{t=JSON.parse(await p(a))}catch{return o(e,400,{error:"Invalid JSON body"}),!0}return!Array.isArray(t.ids)||!t.ids.every(n=>Number.isInteger(n))?(o(e,400,{error:'Invalid "ids" (integer array expected)'}),!0):(r.projects.setOrder(t.ids),r.dataBus?.emit({key:"projects"}),o(e,200,{projects:r.projects.list()}),!0)}const N=l.match(/^\/projects\/(\d+)\/panes$/);if(N&&a.method==="GET"){const t=r.projects.getById(Number(N[1]));if(!t)return o(e,404,{error:"Project not found"}),!0;const n=await j(),s=r.opener?.listPanes??d.listPanes;try{o(e,200,{projectId:t.id,panes:await s(t.path,n.terminal)})}catch(i){o(e,409,{error:i instanceof Error?i.message:"Cannot list panes"})}return!0}const B=l.match(/^\/projects\/(\d+)\/terminals$/);if(B&&a.method==="GET"){const t=r.projects.getById(Number(B[1]));if(!t)return o(e,404,{error:"Project not found"}),!0;const n=await j(),s=r.opener?.listTerminals??d.listTerminals,i=D(n.terminal).enumerable?await s(t.path,n.terminal):[];return o(e,200,{projectId:t.id,terminals:i}),!0}if(l==="/terminals/counts"&&a.method==="GET"){if(r.terminalRefresher)o(e,200,{counts:r.terminalRefresher.lastSnapshot?.counts??{}}),r.terminalRefresher.tick(!0);else{const t=r.opener??d;o(e,200,{counts:await x(r.projects.list(),t)})}return!0}const O=l.match(/^\/projects\/(\d+)\/storage$/);if(O){const t=r.projects.getById(Number(O[1]));if(!t)return o(e,404,{error:"Project not found"}),!0;const s=r.projects.getMeta(t.id)?.storage??{},i=r.uploads?.defaultDir??U();if(a.method==="GET")return o(e,200,{storage:s,effective:{images:s.images||i,files:s.files||i}}),!0;if(a.method==="PUT"){let c;try{c=JSON.parse(await p(a))}catch{return o(e,400,{error:"Invalid JSON body"}),!0}const u={};for(const f of["images","files"]){const h=c[f];if(h==null||h===""){u[f]=null;continue}if(typeof h!="string")return o(e,400,{error:`"${f}" must be a string or null`}),!0;const m=h.trim();if(!$.isAbsolute(m))return o(e,400,{error:`"${f}" must be an absolute path`}),!0;try{y.mkdirSync(m,{recursive:!0}),y.accessSync(m,y.constants.W_OK)}catch(g){return o(e,400,{error:`"${f}" is not writable: ${g instanceof Error?g.message:String(g)}`}),!0}u[f]=m}return r.projects.setMeta(t.id,{storage:u}),r.dataBus?.emit({key:"projects"}),o(e,200,{storage:u,effective:{images:u.images||i,files:u.files||i}}),!0}return!1}return!1}
1
+ import g from"node:fs";import E from"node:path";import{isHarness as $}from"@weavecode/core";import{messagingSurfaceFor as J}from"../../surfaces.js";import{readBody as l,sendJson as s}from"../http.js";import{readDaemonSettings as j}from"../settings.js";import{closeTerminalsIn as A,defaultProjectOpener as f,readTerminalCounts as D,terminalBackendFor as R}from"../opener.js";import{purgeProjectPipeline as x}from"../hook-pipeline.js";import{hooksRoot as G}from"../../hooks/mirror.js";import{projectHarnesses as H,withProjectHarnesses as k}from"../project-harnesses.js";import{scheduleBootstrapInjection as C}from"../session-open.js";import{getUploadsDir as U}from"../../db/paths.js";export async function handleProjectRoutes(c,r,t,p){const w=p.match(/^\/projects\/(\d+)\/open$/);if(w&&c.method==="POST"){const e=t.projects.getById(Number(w[1]));if(!e)return s(r,404,{error:"Project not found"}),!0;let o;try{o=JSON.parse(await l(c))}catch{return s(r,400,{error:"Invalid JSON body"}),!0}if(o.target!=="explorer"&&o.target!=="terminal")return s(r,400,{error:'Invalid "target" (explorer|terminal)'}),!0;const a=t.opener??f;try{if(!(await g.promises.stat(e.path)).isDirectory())throw new Error(`Not a directory: ${e.path}`);if(o.target==="explorer")await a.openExplorer(e.path);else{const n=await j();await a.openTerminal(e.path,n.terminal),setTimeout(()=>{t.terminalRefresher?.refreshNow()},600)}s(r,200,{ok:!0,target:o.target,path:e.path})}catch(n){s(r,400,{error:n instanceof Error?n.message:"Open failed"})}return!0}const I=p.match(/^\/projects\/(\d+)\/close-terminal$/);if(I&&c.method==="POST"){const e=t.projects.getById(Number(I[1]));if(!e)return s(r,404,{error:"Project not found"}),!0;let o;try{o=JSON.parse(await l(c)||"{}")}catch{return s(r,400,{error:"Invalid JSON body"}),!0}if(!Array.isArray(o.paneIds)||o.paneIds.length===0||o.paneIds.some(i=>typeof i!="string"))return s(r,400,{error:"paneIds must be a non-empty array of strings"}),!0;const a=t.opener??f,n=await j();try{const i=await(a.closeTerminals??A)(e.path,n.terminal,o.paneIds);setTimeout(()=>{t.terminalRefresher?.refreshNow()},600),s(r,200,{ok:!0,closed:i})}catch(i){s(r,400,{error:i instanceof Error?i.message:"Close failed"})}return!0}const P=p.match(/^\/projects\/(\d+)\/focus-terminal$/);if(P&&c.method==="POST"){const e=t.projects.getById(Number(P[1]));if(!e)return s(r,404,{error:"Project not found"}),!0;let o;try{o=JSON.parse(await l(c)||"{}")}catch{return s(r,400,{error:"Invalid JSON body"}),!0}if(typeof o.targetId!="string"||!o.targetId)return s(r,400,{error:"targetId must be a non-empty string"}),!0;const a=t.opener??f,n=await j();try{await(a.focusTerminal??f.focusTerminal)(e.path,o.targetId,n.terminal),s(r,200,{ok:!0,targetId:o.targetId})}catch(i){s(r,409,{error:i instanceof Error?i.message:"Cannot focus terminal"})}return!0}const v=p.match(/^\/projects\/(\d+)\/new-session$/);if(v&&c.method==="POST"){const e=t.projects.getById(Number(v[1]));if(!e)return s(r,404,{error:"Project not found"}),!0;let o;try{o=JSON.parse(await l(c)||"{}")}catch{return s(r,400,{error:"Invalid JSON body"}),!0}const a=await H(e.path,t.harnesses);if(o.harness!==void 0&&!$(o.harness))return s(r,400,{error:'Invalid "harness"'}),!0;const n=o.harness;if(n&&!a.some(d=>d.id===n))return s(r,409,{error:`Harness "${n}" is not integrated in this project`}),!0;const i=n?[n]:a.map(d=>d.id),m=a.find(d=>i.includes(d.id)&&d.canStartSession),u=m?t.harnesses?.get(m.id)?.resume:void 0;if(!u?.bin)return s(r,501,{error:`Harness "${i[0]}" cannot start an interactive session`}),!0;const h=t.opener??f;try{if(!(await g.promises.stat(e.path)).isDirectory())throw new Error(`Not a directory: ${e.path}`);const d=await j(),O=[u.bin,...u.launchArgs??[]],y=await h.runInTerminal(e.path,O,d.terminal);y&&m&&J(m.id).paneEnvSource==="launch"&&t.messageDeliverer?.notePendingOrigin({harness:m.id,projectId:e.id,env:y.env}),y&&m&&u?.bootstrapText&&C(t,y,u.bootstrapText(e.name)),setTimeout(()=>{t.terminalRefresher?.refreshNow()},600),s(r,200,{ok:!0,command:O.join(" "),cwd:e.path})}catch(d){s(r,400,{error:d instanceof Error?d.message:"Open failed"})}return!0}if(p==="/projects"&&c.method==="GET")return s(r,200,{projects:await k(t.projects.list(),t.harnesses)}),!0;if(p==="/projects/archived"&&c.method==="GET")return s(r,200,{projects:await k(t.projects.listArchived(),t.harnesses)}),!0;const b=p.match(/^\/projects\/(\d+)\/archive$/);if(b&&c.method==="POST"){const e=t.projects.getById(Number(b[1]));if(!e)return s(r,404,{error:"Project not found"}),!0;if(e.archivedAt)return s(r,200,{project:e,purged:{events:0,dispatches:0,sessions:0,messages:0}}),!0;const o=t.hookDispatches?.deleteByProject(e.id)??0,a=t.hookEvents?.deleteByProject(e.id)??0;t.uploads?.pruneUnreferenced();const n=t.messageDeliverer?.forgetProject(e.id)??0;t.sessionWatches?.deleteByProject(e.id),t.orchestrations?.deleteByProject(e.id),t.orchestrationRuns?.clear("all",e.id),t.sideQuestions?.repo.deleteByProject(e.id);const i=t.hookSessions?.removeByProject(e.id)??0;return t.projects.setArchived(e.id,!0),x(t.hooksDir??G(),e.token),t.watch?.reconcile(),t.dataBus?.emit({key:"projects"}),s(r,200,{project:t.projects.getById(e.id),purged:{events:a,dispatches:o,sessions:i,messages:n}}),!0}const S=p.match(/^\/projects\/(\d+)\/restore$/);if(S&&c.method==="POST"){const e=t.projects.getById(Number(S[1]));return e?e.archivedAt?(t.projects.setArchived(e.id,!1),t.hookMirror?.syncProject(e.id),t.watch?.reconcile(),t.dataBus?.emit({key:"projects"}),s(r,200,{project:t.projects.getById(e.id)}),!0):(s(r,200,{project:e}),!0):(s(r,404,{error:"Project not found"}),!0)}if(p==="/projects/order"&&c.method==="PUT"){let e;try{e=JSON.parse(await l(c))}catch{return s(r,400,{error:"Invalid JSON body"}),!0}return!Array.isArray(e.ids)||!e.ids.every(o=>Number.isInteger(o))?(s(r,400,{error:'Invalid "ids" (integer array expected)'}),!0):(t.projects.setOrder(e.ids),t.dataBus?.emit({key:"projects"}),s(r,200,{projects:t.projects.list()}),!0)}const T=p.match(/^\/projects\/(\d+)\/panes$/);if(T&&c.method==="GET"){const e=t.projects.getById(Number(T[1]));if(!e)return s(r,404,{error:"Project not found"}),!0;const o=await j(),a=t.opener?.listPanes??f.listPanes;try{s(r,200,{projectId:e.id,panes:await a(e.path,o.terminal)})}catch(n){s(r,409,{error:n instanceof Error?n.message:"Cannot list panes"})}return!0}const N=p.match(/^\/projects\/(\d+)\/terminals$/);if(N&&c.method==="GET"){const e=t.projects.getById(Number(N[1]));if(!e)return s(r,404,{error:"Project not found"}),!0;const o=await j(),a=t.opener?.listTerminals??f.listTerminals,n=R(o.terminal).enumerable?await a(e.path,o.terminal):[];return s(r,200,{projectId:e.id,terminals:n}),!0}if(p==="/terminals/counts"&&c.method==="GET"){if(t.terminalRefresher)s(r,200,{counts:t.terminalRefresher.lastSnapshot?.counts??{}}),t.terminalRefresher.tick(!0);else{const e=t.opener??f;s(r,200,{counts:await D(t.projects.list(),e)})}return!0}const B=p.match(/^\/projects\/(\d+)\/storage$/);if(B){const e=t.projects.getById(Number(B[1]));if(!e)return s(r,404,{error:"Project not found"}),!0;const o=t.projects.getMeta(e.id)?.storage??{},a=t.uploads?.defaultDir??U();if(c.method==="GET")return s(r,200,{storage:o,effective:{images:o.images||a,files:o.files||a}}),!0;if(c.method==="PUT"){let n;try{n=JSON.parse(await l(c))}catch{return s(r,400,{error:"Invalid JSON body"}),!0}const i={};for(const m of["images","files"]){const u=n[m];if(u==null||u===""){i[m]=null;continue}if(typeof u!="string")return s(r,400,{error:`"${m}" must be a string or null`}),!0;const h=u.trim();if(!E.isAbsolute(h))return s(r,400,{error:`"${m}" must be an absolute path`}),!0;try{g.mkdirSync(h,{recursive:!0}),g.accessSync(h,g.constants.W_OK)}catch(d){return s(r,400,{error:`"${m}" is not writable: ${d instanceof Error?d.message:String(d)}`}),!0}i[m]=h}return t.projects.setMeta(e.id,{storage:i}),t.dataBus?.emit({key:"projects"}),s(r,200,{storage:i,effective:{images:i.images||a,files:i.files||a}}),!0}return!1}return!1}
@@ -1 +1 @@
1
- import{sendJson as i}from"../http.js";import{matchPaths as T,peekCachedIndex as E}from"../project-files.js";const _=8,p=12,g=6,m=18,v=1440*6e4,l=80;export async function handleSearchRoutes(e,n,a,o,r){if(o!=="/search"||e.method!=="GET")return!1;if(!a.hookEvents||!a.hookSessions)return i(n,503,{error:"Hooks subsystem unavailable"}),!0;const s=(r.searchParams.get("q")??"").trim();if(!s)return i(n,200,{q:s,sessions:[],messages:[],files:[],truncated:{messages:!1,files:!1}}),!0;const[u,d]=await Promise.all([a.hookSessions.searchByTitle(s,_),a.hookEvents.searchConversationTexts(s,p)]),S=a.projects.list().filter(t=>!t.archivedAt),I=(await Promise.all(S.map(async t=>{const f=await E(t.id,t.path,{maxStaleMs:v});if(!f)return[];const{hits:x}=await T(f,s,g,"");return x.map(c=>({projectId:t.id,path:c.path,kind:c.kind,ranges:c.ranges}))}))).flat().slice(0,m);return i(n,200,{q:s,sessions:u.map(t=>({sessionId:t.sessionId,projectId:t.projectId,title:t.title})),messages:d.map(t=>M(t,s)),files:I,truncated:{messages:d.length>=p,files:I.length>=m}}),!0}function M(e,n){const o=e.text.toLowerCase().indexOf(n.toLowerCase());if(o<0||e.text.length<=l*2)return{eventId:e.eventId,sessionId:e.sessionId,projectId:e.projectId,source:e.source,eventType:e.eventType,at:e.at,text:e.text,offset:Math.max(o,0),clipped:!1};const r=Math.max(0,o-l),s=Math.min(e.text.length,o+n.length+l);return{eventId:e.eventId,sessionId:e.sessionId,projectId:e.projectId,source:e.source,eventType:e.eventType,at:e.at,text:e.text.slice(r,s),offset:o-r,clipped:r>0||s<e.text.length}}
1
+ import{sendJson as c}from"../http.js";import{matchPaths as g,peekCachedIndex as v}from"../project-files.js";const j=8,m=12,k=6,f=18,y=1440*6e4,d=80;export async function handleSearchRoutes(e,n,t,a,r){if(a!=="/search"||e.method!=="GET")return!1;if(!t.hookEvents||!t.hookSessions)return c(n,503,{error:"Hooks subsystem unavailable"}),!0;const o=(r.searchParams.get("q")??"").trim();if(!o)return c(n,200,{q:o,sessions:[],messages:[],files:[],truncated:{messages:!1,files:!1}}),!0;const[u,l]=await Promise.all([t.hookSessions.searchByTitle(o,j),t.hookEvents.searchConversationTexts(o,m)]),I=t.projects.list().filter(s=>!s.archivedAt),h=(await Promise.all(I.map(async s=>{const p=await v(s.id,s.path,{maxStaleMs:y});if(!p)return[];const{hits:x}=await g(p,o,k,"");return x.map(i=>({projectId:s.id,path:i.path,kind:i.kind,ranges:i.ranges}))}))).flat().slice(0,f);return c(n,200,{q:o,sessions:u.map(s=>({sessionId:s.sessionId,projectId:s.projectId,title:s.title})),messages:l.map(s=>T(s,o)),files:h,truncated:{messages:l.length>=m,files:h.length>=f}}),!0}function T(e,n){const t=e.text.toLowerCase().indexOf(n.toLowerCase());if(t<0||e.text.length<=d*2)return{eventId:e.eventId,sessionId:e.sessionId,projectId:e.projectId,source:e.source,eventType:e.eventType,at:e.at,text:e.text,offset:Math.max(t,0),clipped:!1};const a=Math.max(0,t-d),r=Math.min(e.text.length,t+n.length+d);return{eventId:e.eventId,sessionId:e.sessionId,projectId:e.projectId,source:e.source,eventType:e.eventType,at:e.at,text:e.text.slice(a,r),offset:t-a,clipped:a>0||r<e.text.length}}
@@ -1 +1 @@
1
- import{z as a}from"zod";import{readJson as g,sendJson as s}from"../http.js";import{composeAnswerAtoms as C}from"../../hooks/answer-keys.js";import{readSideQuestions as O}from"../../hooks/side-questions.js";const Q=a.object({question:a.string().min(1).max(4e3)}),U=a.object({model:a.string().min(1).max(128)}),$=a.object({level:a.string().min(1).max(32)}),j=a.object({answers:a.array(a.object({questionKey:a.string().min(1),options:a.array(a.number().int().min(0)).max(32),text:a.string().max(2e3).optional()})).min(1)}),A=1200;async function P(i,r){await new Promise(u=>setTimeout(u,A));const e=i();return e!==null&&e>r}export async function handleSessionControlRoutes(i,r,e,u,b){const h=u.match(/^\/sessions\/([^/]+)\/interrupt$/);if(h&&i.method==="POST"){if(!e.messageDeliverer)return s(r,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(h[1]),o=e.sessionProbe,n=e.hookSessions?.getBySessionId(t)??null,c=!!o&&!!n&&o.transcriptPresent(t)!==null,m=()=>c?o.interruptedAt(t,n.lastSeenAt)??null:null,l=m()??0,f=await e.messageDeliverer.interrupt(t);if(!f.ok)return s(r,f.status,{error:f.error}),!0;let d=null;if(c&&(d=await P(m,l),d===!1)){const D=await e.messageDeliverer.interrupt(t,"heuristic",f.paneId);D.ok&&D.sent&&(d=await P(m,l))}return setTimeout(()=>e.interruptRefresher?.tick(),1500),s(r,200,{ok:!0,sessionId:t,interrupted:d}),!0}const k=u.match(/^\/sessions\/([^/]+)\/close-pane$/);if(k&&i.method==="POST"){if(!e.messageDeliverer)return s(r,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(k[1]),o=await e.messageDeliverer.closePane(t);return o.ok?(setTimeout(()=>{e.livenessRefresher?.tick(),e.terminalRefresher?.refreshNow()},600),s(r,200,{ok:!0,sessionId:t}),!0):(s(r,o.status,{error:o.error}),!0)}const v=u.match(/^\/sessions\/([^/]+)\/mode$/);if(v&&i.method==="POST"){if(!e.messageDeliverer)return s(r,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(v[1]),o=await e.messageDeliverer.cycleMode(t);return o.ok?(s(r,200,{ok:!0,sessionId:t}),!0):(s(r,o.status,{error:o.error}),!0)}const I=u.match(/^\/sessions\/([^/]+)\/enter$/);if(I&&i.method==="POST"){if(!e.messageDeliverer)return s(r,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(I[1]),o=await e.messageDeliverer.pressEnter(t);return o.ok?(s(r,200,{ok:!0,sessionId:t}),!0):(s(r,o.status,{error:o.error}),!0)}const w=u.match(/^\/sessions\/([^/]+)\/ctrl-c$/);if(w&&i.method==="POST"){if(!e.messageDeliverer)return s(r,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(w[1]),o=await e.messageDeliverer.pressCtrlC(t);return o.ok?(s(r,200,{ok:!0,sessionId:t}),!0):(s(r,o.status,{error:o.error}),!0)}const y=u.match(/^\/sessions\/([^/]+)\/model$/);if(y&&i.method==="POST"){if(!e.messageDeliverer)return s(r,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(y[1]),o=U.parse(await g(i)),n=await e.messageDeliverer.setModel(t,o.model);return n.ok?(setTimeout(()=>e.interruptRefresher?.tick(),1500),s(r,200,{ok:!0,sessionId:t}),!0):(s(r,n.status,{error:n.error}),!0)}const M=u.match(/^\/sessions\/([^/]+)\/effort$/);if(M&&i.method==="POST"){if(!e.messageDeliverer)return s(r,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(M[1]),o=$.parse(await g(i)),n=await e.messageDeliverer.setEffort(t,o.level);return n.ok?(s(r,200,{ok:!0,sessionId:t}),!0):(s(r,n.status,{error:n.error}),!0)}if(u==="/messaging/questions"&&i.method==="GET")return e.questionTracker?(s(r,200,{questions:e.questionTracker.snapshot()}),!0):(s(r,503,{error:"Question tracking unavailable"}),!0);const T=u.match(/^\/sessions\/([^/]+)\/answer$/);if(T&&i.method==="POST"){if(!e.questionTracker||!e.messageDeliverer)return s(r,503,{error:"Question tracking unavailable"}),!0;const t=decodeURIComponent(T[1]);let o;try{o=j.parse(await g(i))}catch(l){return s(r,400,{error:l instanceof Error?l.message:"Invalid body"}),!0}const n=e.questionTracker.get(t);if(!n)return s(r,409,{error:"Session has no pending question"}),!0;const c=C(n.questions,o.answers);if(!c.ok)return s(r,400,{error:c.error}),!0;const m=await e.messageDeliverer.answer(t,c.atoms);return m.ok?(e.questionTracker.markAnswered(t),s(r,200,{ok:!0,sessionId:t}),!0):(s(r,m.status,{error:m.error}),!0)}const S=u.match(/^\/sessions\/([^/]+)\/side-question$/);if(S&&i.method==="POST"){if(!e.messageDeliverer||!e.sideQuestions)return s(r,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(S[1]);let o;try{o=Q.parse(await g(i))}catch(m){return s(r,400,{error:m instanceof Error?m.message:"Invalid body"}),!0}const n=await e.messageDeliverer.askSideQuestion(t,o.question);if(!n.ok)return s(r,n.status,{error:n.error}),!0;const c=e.sideQuestions.repo.insert({sessionId:t,projectId:e.hookSessions?.getBySessionId(t)?.projectId??null,question:n.question});return s(r,200,{ok:!0,sideQuestion:c}),!0}const R=u.match(/^\/sessions\/([^/]+)\/side-questions$/);if(R&&i.method==="GET"){if(!e.sideQuestions)return s(r,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(R[1]),o=Number(b.searchParams.get("limit")),n=Number(b.searchParams.get("beforeId")),c=O(e.sideQuestions,t,{limit:Number.isFinite(o)&&o>0?Math.min(o,100):20,beforeId:Number.isFinite(n)&&n>0?n:void 0});return s(r,200,{sideQuestions:c}),!0}return!1}
1
+ import{z as a}from"zod";import{readJson as d,sendJson as r}from"../http.js";import{composeAnswerAtoms as D}from"../../hooks/answer-keys.js";import{readSideQuestions as R}from"../../hooks/side-questions.js";const q=a.object({question:a.string().min(1).max(4e3)}),S=a.object({model:a.string().min(1).max(128)}),C=a.object({level:a.string().min(1).max(32)}),M=a.object({answers:a.array(a.object({questionKey:a.string().min(1),options:a.array(a.number().int().min(0)).max(32),text:a.string().max(2e3).optional()})).min(1)});export async function handleSessionControlRoutes(i,e,o,m,l){const g=m.match(/^\/sessions\/([^/]+)\/interrupt$/);if(g&&i.method==="POST"){if(!o.messageDeliverer)return r(e,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(g[1]),s=await o.messageDeliverer.interrupt(t);return s.ok?(setTimeout(()=>o.interruptRefresher?.tick(),1500),setTimeout(()=>o.interruptRefresher?.tick(),12e3),r(e,200,{ok:!0,sessionId:t,confirmed:s.confirmed}),!0):(r(e,s.status,{error:s.error}),!0)}const f=m.match(/^\/sessions\/([^/]+)\/close-pane$/);if(f&&i.method==="POST"){if(!o.messageDeliverer)return r(e,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(f[1]),s=await o.messageDeliverer.closePane(t);return s.ok?(setTimeout(()=>{o.livenessRefresher?.tick(),o.terminalRefresher?.refreshNow()},600),r(e,200,{ok:!0,sessionId:t}),!0):(r(e,s.status,{error:s.error}),!0)}const h=m.match(/^\/sessions\/([^/]+)\/mode$/);if(h&&i.method==="POST"){if(!o.messageDeliverer)return r(e,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(h[1]),s=await o.messageDeliverer.cycleMode(t);return s.ok?(r(e,200,{ok:!0,sessionId:t}),!0):(r(e,s.status,{error:s.error}),!0)}const v=m.match(/^\/sessions\/([^/]+)\/enter$/);if(v&&i.method==="POST"){if(!o.messageDeliverer)return r(e,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(v[1]),s=await o.messageDeliverer.pressEnter(t);return s.ok?(r(e,200,{ok:!0,sessionId:t}),!0):(r(e,s.status,{error:s.error}),!0)}const b=m.match(/^\/sessions\/([^/]+)\/ctrl-c$/);if(b&&i.method==="POST"){if(!o.messageDeliverer)return r(e,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(b[1]),s=await o.messageDeliverer.pressCtrlC(t);return s.ok?(r(e,200,{ok:!0,sessionId:t}),!0):(r(e,s.status,{error:s.error}),!0)}const p=m.match(/^\/sessions\/([^/]+)\/model$/);if(p&&i.method==="POST"){if(!o.messageDeliverer)return r(e,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(p[1]),s=S.parse(await d(i)),n=await o.messageDeliverer.setModel(t,s.model);return n.ok?(setTimeout(()=>o.interruptRefresher?.tick(),1500),r(e,200,{ok:!0,sessionId:t}),!0):(r(e,n.status,{error:n.error}),!0)}const k=m.match(/^\/sessions\/([^/]+)\/effort$/);if(k&&i.method==="POST"){if(!o.messageDeliverer)return r(e,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(k[1]),s=C.parse(await d(i)),n=await o.messageDeliverer.setEffort(t,s.level);return n.ok?(r(e,200,{ok:!0,sessionId:t}),!0):(r(e,n.status,{error:n.error}),!0)}if(m==="/messaging/questions"&&i.method==="GET")return o.questionTracker?(r(e,200,{questions:o.questionTracker.snapshot()}),!0):(r(e,503,{error:"Question tracking unavailable"}),!0);const I=m.match(/^\/sessions\/([^/]+)\/answer$/);if(I&&i.method==="POST"){if(!o.questionTracker||!o.messageDeliverer)return r(e,503,{error:"Question tracking unavailable"}),!0;const t=decodeURIComponent(I[1]);let s;try{s=M.parse(await d(i))}catch(y){return r(e,400,{error:y instanceof Error?y.message:"Invalid body"}),!0}const n=o.questionTracker.get(t);if(!n)return r(e,409,{error:"Session has no pending question"}),!0;const u=D(n.questions,s.answers);if(!u.ok)return r(e,400,{error:u.error}),!0;const c=await o.messageDeliverer.answer(t,u.atoms);return c.ok?(o.questionTracker.markAnswered(t),r(e,200,{ok:!0,sessionId:t}),!0):(r(e,c.status,{error:c.error}),!0)}const w=m.match(/^\/sessions\/([^/]+)\/side-question$/);if(w&&i.method==="POST"){if(!o.messageDeliverer||!o.sideQuestions)return r(e,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(w[1]);let s;try{s=q.parse(await d(i))}catch(c){return r(e,400,{error:c instanceof Error?c.message:"Invalid body"}),!0}const n=await o.messageDeliverer.askSideQuestion(t,s.question);if(!n.ok)return r(e,n.status,{error:n.error}),!0;const u=o.sideQuestions.repo.insert({sessionId:t,projectId:o.hookSessions?.getBySessionId(t)?.projectId??null,question:n.question});return r(e,200,{ok:!0,sideQuestion:u}),!0}const T=m.match(/^\/sessions\/([^/]+)\/side-questions$/);if(T&&i.method==="GET"){if(!o.sideQuestions)return r(e,503,{error:"Messaging subsystem unavailable"}),!0;const t=decodeURIComponent(T[1]),s=Number(l.searchParams.get("limit")),n=Number(l.searchParams.get("beforeId")),u=R(o.sideQuestions,t,{limit:Number.isFinite(s)&&s>0?Math.min(s,100):20,beforeId:Number.isFinite(n)&&n>0?n:void 0});return r(e,200,{sideQuestions:u}),!0}return!1}
@@ -1 +1 @@
1
- import{readFileSync as C}from"node:fs";import y from"node:path";import{isHarness as _}from"@weavecode/core";import{sendJson as i}from"../http.js";import{blockingPromptFor as R,commandsSurfaceFor as j,editSurfaceFor as F,narrationSurfaceFor as N}from"../../surfaces.js";function E(o,t,n){if(!t.hookSessions||!t.hookEvents)return i(o,503,{error:"Hooks subsystem unavailable"}),null;const r=t.hookSessions.getBySessionId(n);if(!r)return i(o,404,{error:"Session not found"}),null;if(r.projectId==null)return i(o,400,{error:"Session is not bound to a project"}),null;const f=t.projects.getById(r.projectId);return f||(i(o,404,{error:"Project not found"}),null)}function S(o,t){if(!o.hookEvents)return null;const n=o.hookEvents.listBySession(t,1),r=n[n.length-1]?.source;return _(r)?r:null}const v=20,O=2e5;function H(o){try{const t=JSON.parse(o);return[typeof t.toolName=="string"?t.toolName:null,t.toolInput??null,t.toolResponse??null]}catch{return[null,null,null]}}function w(o){if(!o)return null;try{const t=C(o,"utf8");return t.trim()?t:null}catch{return null}}const x=2e5,B=4e6;function G(o,t){const n=y.relative(t,o);return(!!n&&!n.startsWith("..")&&!y.isAbsolute(n)?n:o).split(y.sep).join("/")}function M(o){if(!o)return null;const t=Date.parse(o);return Number.isFinite(t)?t:null}function U(o,t,n){if(t===null&&n===null)return!0;if(!o)return!1;const r=Date.parse(o);return!(!Number.isFinite(r)||t!==null&&r<=t||n!==null&&r>n)}function $(o,t){const n=[],r=new Map;let f=B,I=0,h=!1;for(const c of o){const d=c.hunk.before.slice(0,x),p=c.hunk.after.slice(0,x),P=d.length<c.hunk.before.length||p.length<c.hunk.after.length;P&&(h=!0);const e=d.length+p.length;if(e>f){h=!0;continue}f-=e;const s=G(c.path,t);let u=r.get(s);u||(u={path:s,hunks:[]},r.set(s,u),n.push(u)),u.hunks.push({before:d,after:p,at:c.at,...P?{clamped:!0}:{}}),I++}return{files:n,hunks:I,truncated:h}}export async function handleSessionInsightRoutes(o,t,n,r,f){const I=r.match(/^\/sessions\/([^/]+)\/plan$/);if(I&&o.method==="GET"){const e=decodeURIComponent(I[1]);if(!n.hookEvents)return i(t,503,{error:"Hook ingestion unavailable"}),!0;const s=n.hookEvents.listBySession(e,1),u=s[s.length-1]?.source,l=_(u)?R(u)?.plan??null:null;if(!l)return i(t,200,{plan:null}),!0;const m=g=>g.flatMap(T=>{const A=l.draftOf(...H(T.payload));return A?[{row:T,draft:A}]:[]}),[a]=m(n.hookEvents.listByTools(e,[l.tool],null,v));if(!a)return i(t,200,{plan:null}),!0;const[k]=m(n.hookEvents.listByTools(e,l.writeTools,l.fileHint,v)).filter(g=>g.row.id<a.row.id),b=a.draft.text??w(a.draft.filePath)??k?.draft.text??w(k?.draft.filePath??null);return b?(i(t,200,{plan:{eventId:a.row.id,at:a.row.createdAt,text:b.slice(0,O)}}),!0):(i(t,200,{plan:null}),!0)}const h=r.match(/^\/sessions\/([^/]+)\/pre-question$/);if(h&&o.method==="GET"){const e=decodeURIComponent(h[1]);if(!E(t,n,e))return!0;const s=S(n,e),l=await(s?R(s):null)?.preQuestion?.(n.sessionProbe?.transcriptPathOf(e)??null);return i(t,200,{preQuestion:l??null}),!0}const c=r.match(/^\/sessions\/([^/]+)\/narration$/);if(c&&o.method==="GET"){const e=decodeURIComponent(c[1]);if(!E(t,n,e))return!0;const s=S(n,e),u=s?N(s):null;if(!u)return i(t,200,{supported:!1,narration:null}),!0;const l=await u.sessionNarration({sessionId:e,transcriptPath:n.sessionProbe?.transcriptPathOf(e)??null});return i(t,200,{supported:l!==null,narration:l}),!0}const d=r.match(/^\/sessions\/([^/]+)\/commands$/);if(d&&o.method==="GET"){const e=decodeURIComponent(d[1]),s=E(t,n,e);if(!s)return!0;const u=S(n,e),l=u?j(u):null;return l?(i(t,200,{supported:!0,commands:await l.list({projectPath:s.path})}),!0):(i(t,200,{supported:!1,commands:[]}),!0)}const p=r.match(/^\/sessions\/([^/]+)\/command-executions$/);if(p&&o.method==="GET"){const e=decodeURIComponent(p[1]);if(!E(t,n,e))return!0;const s=S(n,e),u=s?j(s):null;if(!u)return i(t,200,{supported:!1,executions:null}),!0;const l=await u.executions(n.sessionProbe?.transcriptPathOf(e)??null);return i(t,200,{supported:l!==null,executions:l}),!0}const P=r.match(/^\/sessions\/([^/]+)\/edits$/);if(P&&o.method==="GET"){const e=decodeURIComponent(P[1]),s=E(t,n,e);if(!s)return!0;const u=S(n,e),l=u?F(u):null;if(!l)return i(t,200,{files:null,hunks:0,truncated:!1,reason:"unsupported"}),!0;const m=await l.sessionEdits({sessionId:e,transcriptPath:n.sessionProbe?.transcriptPathOf(e)??null});if(!m)return i(t,200,{files:null,hunks:0,truncated:!1,reason:"unreadable"}),!0;const a=M(f.searchParams.get("since")),k=M(f.searchParams.get("until")),b=a!==null||k!==null?m.filter(g=>U(g.at,a,k)):m;return i(t,200,$(b,s.path)),!0}return!1}
1
+ import{readFileSync as M}from"node:fs";import F from"node:path";import{isHarness as O}from"@weavecode/core";import{sendJson as i}from"../http.js";import{blockingPromptFor as U,commandsSurfaceFor as $,editSurfaceFor as D,narrationSurfaceFor as J,rewindSurfaceFor as Q}from"../../surfaces.js";function w(s,n,t){if(!n.hookSessions||!n.hookEvents)return i(s,503,{error:"Hooks subsystem unavailable"}),null;const o=n.hookSessions.getBySessionId(t);return o?o.projectId==null?(i(s,400,{error:"Session is not bound to a project"}),null):n.projects.getById(o.projectId)||(i(s,404,{error:"Project not found"}),null):(i(s,404,{error:"Session not found"}),null)}function I(s,n){if(!s.hookEvents)return null;const t=s.hookEvents.listBySession(n,1),o=t[t.length-1]?.source;return O(o)?o:null}const B=20,z=2e3,W=200,x=new Map,q=2e5;function K(s){try{const n=JSON.parse(s);return[typeof n.toolName=="string"?n.toolName:null,n.toolInput??null,n.toolResponse??null]}catch{return[null,null,null]}}function N(s){if(!s)return null;try{const n=M(s,"utf8");return n.trim()?n:null}catch{return null}}const H=2e5,L=4e6;function V(s,n){const t=F.relative(n,s);return(t&&!t.startsWith("..")&&!F.isAbsolute(t)?t:s).split(F.sep).join("/")}function A(s){if(!s)return null;const n=Date.parse(s);return Number.isFinite(n)?n:null}function X(s,n,t){if(n===null&&t===null)return!0;if(!s)return!1;const o=Date.parse(s);return!(!Number.isFinite(o)||n!==null&&o<=n||t!==null&&o>t)}function Y(s,n){const t=[],o=new Map;let f=L,P=0,d=!1;for(const a of s){const p=a.hunk.before.slice(0,H),h=a.hunk.after.slice(0,H),b=p.length<a.hunk.before.length||h.length<a.hunk.after.length;b&&(d=!0);const E=p.length+h.length;if(E>f){d=!0;continue}f-=E;const e=V(a.path,n);let r=o.get(e);r||(r={path:e,hunks:[]},o.set(e,r),t.push(r)),r.hunks.push({before:p,after:h,at:a.at,...b?{clamped:!0}:{}}),P++}return{files:t,hunks:P,truncated:d}}export async function handleSessionInsightRoutes(s,n,t,o,f){const P=o.match(/^\/sessions\/([^/]+)\/plan$/);if(P&&s.method==="GET"){const e=decodeURIComponent(P[1]);if(!t.hookEvents)return i(n,503,{error:"Hook ingestion unavailable"}),!0;const r=t.hookEvents.listBySession(e,1),u=r[r.length-1]?.source,l=O(u)?U(u)?.plan??null:null;if(!l)return i(n,200,{plan:null}),!0;const m=c=>c.flatMap(v=>{const R=l.draftOf(...K(v.payload));return R?[{row:v,draft:R}]:[]}),g=r[r.length-1]?.id??0,S=t.hookEvents,y=x.get(e);if(y&&y.latestId===g)return i(n,200,{plan:y.plan}),!0;const T=c=>(x.size>=W&&x.clear(),x.set(e,{latestId:g,plan:c}),i(n,200,{plan:c}),!0),j=(c,v)=>{const R=S.listByTools(e,c,v,B,g-z);return R.length>0?R:S.listByTools(e,c,v,B)},[k]=m(j([l.tool],null));if(!k)return T(null);const[C]=m(j(l.writeTools,l.fileHint)).filter(c=>c.row.id<k.row.id),G=k.draft.text??N(k.draft.filePath)??C?.draft.text??N(C?.draft.filePath??null);return T(G?{eventId:k.row.id,at:k.row.createdAt,text:G.slice(0,q)}:null)}const d=o.match(/^\/sessions\/([^/]+)\/pre-question$/);if(d&&s.method==="GET"){const e=decodeURIComponent(d[1]);if(!w(n,t,e))return!0;const r=I(t,e),u=await(r?U(r):null)?.preQuestion?.(t.sessionProbe?.transcriptPathOf(e)??null);return i(n,200,{preQuestion:u??null}),!0}const a=o.match(/^\/sessions\/([^/]+)\/narration$/);if(a&&s.method==="GET"){const e=decodeURIComponent(a[1]);if(!w(n,t,e))return!0;const r=I(t,e),u=r?J(r):null;if(!u)return i(n,200,{supported:!1,narration:null}),!0;const l=await u.sessionNarration({sessionId:e,transcriptPath:t.sessionProbe?.transcriptPathOf(e)??null});return i(n,200,{supported:l!==null,narration:l}),!0}const p=o.match(/^\/sessions\/([^/]+)\/rewinds$/);if(p&&s.method==="GET"){const e=decodeURIComponent(p[1]);if(!w(n,t,e))return!0;const r=I(t,e),u=r?Q(r):null;if(!u)return i(n,200,{supported:!1,rewinds:null}),!0;const l=await u.sessionRewinds({sessionId:e,transcriptPath:t.sessionProbe?.transcriptPathOf(e)??null});return i(n,200,{supported:l!==null,rewinds:l}),!0}const h=o.match(/^\/sessions\/([^/]+)\/commands$/);if(h&&s.method==="GET"){const e=decodeURIComponent(h[1]),r=w(n,t,e);if(!r)return!0;const u=I(t,e),l=u?$(u):null;return l?(i(n,200,{supported:!0,commands:await l.list({projectPath:r.path}),inputModes:l.inputModes}),!0):(i(n,200,{supported:!1,commands:[],inputModes:[]}),!0)}const b=o.match(/^\/sessions\/([^/]+)\/command-executions$/);if(b&&s.method==="GET"){const e=decodeURIComponent(b[1]);if(!w(n,t,e))return!0;const r=I(t,e),u=r?$(r):null;if(!u)return i(n,200,{supported:!1,executions:null}),!0;const l=await u.executions(t.sessionProbe?.transcriptPathOf(e)??null);return i(n,200,{supported:l!==null,executions:l}),!0}const E=o.match(/^\/sessions\/([^/]+)\/edits$/);if(E&&s.method==="GET"){const e=decodeURIComponent(E[1]),r=w(n,t,e);if(!r)return!0;const u=I(t,e),l=u?D(u):null;if(!l)return i(n,200,{files:null,hunks:0,truncated:!1,reason:"unsupported"}),!0;const m=await l.sessionEdits({sessionId:e,transcriptPath:t.sessionProbe?.transcriptPathOf(e)??null});if(!m)return i(n,200,{files:null,hunks:0,truncated:!1,reason:"unreadable"}),!0;const g=A(f.searchParams.get("since")),S=A(f.searchParams.get("until")),y=g!==null||S!==null?m.filter(T=>X(T.at,g,S)):m;return i(n,200,Y(y,r.path)),!0}return!1}
@@ -1 +1 @@
1
- import{readBody as m,sendJson as i}from"../http.js";import{DEFAULT_AI_SETTINGS as P,DEFAULT_DECISION_SETTINGS as K,readDaemonSettings as y,writeDaemonSettings as E,terminalPresetsFor as T}from"../settings.js";import{readAiKey as N,writeAiKey as G}from"../ai-config.js";import{readDecisionKey as j,writeDecisionKey as A}from"../decision-config.js";import{accessUrls as I}from"../tls.js";import{applyLaunchOnLogin as O}from"../auto-start.js";import{WEAVE_VERSION as U}from"../../version.js";import{readProjectConfig as h,writeProjectConfig as C}from"../../install/project-config.js";import{readStatuslineConfig as p,readStatuslineScript as v,readStatuslineFacts as D,applyStatuslineConfig as g,readGlobalStatuslineConfig as S,writeGlobalStatuslineConfig as J}from"../../statusline/manager.js";import{DEFAULT_STATUSLINE_CONFIG as F}from"../../statusline/config.js";import{listBrowseRoots as _,listDirectoryChildren as B}from"../fs-browser.js";import{projectFileNamespaces as L}from"../../hooks/toolkit.js";import{listGlobalFileAssets as k}from"../../install/global-file-assets.js";function w(a,r,o){const s=a.ai??P,u=a.decision??K,l=N(r),f=j(o);return{...a,ai:{baseUrl:s.baseUrl,model:s.model,apiKey:l,apiKeySet:l!==null},decision:{baseUrl:u.baseUrl,model:u.model,apiKey:f,apiKeySet:f!==null}}}function b(a){if(!a)return;const{apiKey:r,...o}=a;return o}export async function handleSettingsRoutes(a,r,o,s,u){if(s==="/settings"){if(a.method==="GET")return i(r,200,{settings:w(await y(),o.aiKeyPath,o.decisionKeyPath),presets:T(process.platform),platform:process.platform,urls:I(o.port,o.tlsPort??0),version:U,repoUrl:"https://github.com/zijing66/weave"}),!0;if(a.method==="PUT"){let t;try{t=JSON.parse(await m(a))}catch{return i(r,400,{error:"Invalid JSON body"}),!0}const e=t.ai?.apiKey;e!==void 0&&G(typeof e=="string"?e:null,o.aiKeyPath);const n=t.decision?.apiKey;n!==void 0&&A(typeof n=="string"?n:null,o.decisionKeyPath);const c=await y(),d=await E({...c,...t,ai:t.ai?b(t.ai):c.ai,decision:t.decision?b(t.decision):c.decision});return o.hookEvents?.resetPrune(),(o.autoStart??O)(d.startup?.launchOnLogin??!1),o.dataBus?.emit({key:"settings"}),i(r,200,w(d,o.aiKeyPath,o.decisionKeyPath)),!0}}if(s==="/fs/roots"&&a.method==="GET"){const t=o.libraries?o.libraries.list().map(n=>n.path):[],e=await _(t);return i(r,200,{roots:e}),!0}if(s==="/fs/children"&&a.method==="GET"){const t=u.searchParams.get("path");if(!t)return i(r,400,{error:'Missing "path" query parameter'}),!0;try{const e=await B(t);i(r,200,{children:e})}catch(e){i(r,400,{error:`Cannot list directory: ${String(e)}`})}return!0}const l=s.match(/^\/projects\/(\d+)\/config$/);if(l){const t=o.projects.getById(Number(l[1]));if(!t)return i(r,404,{error:"Project not found"}),!0;if(a.method==="GET")return i(r,200,await h(t.path)),!0;if(a.method==="PUT"){let e;try{e=JSON.parse(await m(a))}catch{return i(r,400,{error:"Invalid JSON body"}),!0}const n=await h(t.path),c={lastSyncAt:e.lastSyncAt??n.lastSyncAt};return await C(t.path,c),i(r,200,c),!0}}if(s==="/statusline/global"){if(a.method==="GET")return i(r,200,{config:await S()}),!0;if(a.method==="PUT"){let t;try{t=JSON.parse(await m(a))}catch{return i(r,400,{error:"Invalid JSON body"}),!0}try{await J(t);for(const e of o.projects.list()){const n=await p(e.path);n.source==="global"&&await g(e.path,n)}o.dataBus?.emit({key:"statusline"}),i(r,200,{ok:!0})}catch(e){i(r,400,{error:e instanceof Error?e.message:"Global statusline update failed"})}return!0}}if(s==="/statusline/default"&&a.method==="GET")return i(r,200,{config:F}),!0;const f=s.match(/^\/projects\/(\d+)\/statusline$/);if(f){const t=o.projects.getById(Number(f[1]));if(!t)return i(r,404,{error:"Project not found"}),!0;if(a.method==="GET"){const[e,n,c]=await Promise.all([p(t.path),v(t.path),S()]);return i(r,200,{config:e,script:n,globalConfig:c,facts:D(t.path)}),!0}if(a.method==="PUT"){let e;try{e=JSON.parse(await m(a))}catch{return i(r,400,{error:"Invalid JSON body"}),!0}try{await g(t.path,e),o.dataBus?.emit({key:"statusline",projectPath:t.path}),i(r,200,{ok:!0})}catch(n){i(r,400,{error:n instanceof Error?n.message:"Statusline update failed"})}return!0}}if(s==="/assets"&&a.method==="GET"){const t=u.searchParams.get("path");if(!t)return i(r,400,{error:'Missing "path" query parameter'}),!0;if(!o.watch)return i(r,503,{error:"Watch service unavailable"}),!0;const e=await L(t),n=o.watch.getAssets(t).map(c=>{const d=e.get(c.relPath);return d?{...c,namespace:d}:c});return i(r,200,{projectPath:t,assets:n}),!0}return s==="/global-file-assets"&&a.method==="GET"?(i(r,200,{assets:await k()}),!0):!1}
1
+ import{readBody as m,sendJson as r}from"../http.js";import{DEFAULT_AI_SETTINGS as b,DEFAULT_DECISION_SETTINGS as E,readDaemonSettings as u,writeDaemonSettings as T,terminalPresetsFor as K}from"../settings.js";import{readAiKey as N,writeAiKey as G}from"../ai-config.js";import{readDecisionKey as I,writeDecisionKey as U}from"../decision-config.js";import{accessUrls as j}from"../tls.js";import{applyLaunchOnLogin as A}from"../auto-start.js";import{WEAVE_VERSION as O}from"../../version.js";import{readProjectConfig as h,writeProjectConfig as v}from"../../install/project-config.js";import{readStatuslineConfig as y,readStatuslineScript as C,readStatuslineFacts as D,applyStatuslineConfig as g,readGlobalStatuslineConfig as S,writeGlobalStatuslineConfig as J}from"../../statusline/manager.js";import{DEFAULT_STATUSLINE_CONFIG as F}from"../../statusline/config.js";import{listBrowseRoots as B,listDirectoryChildren as L}from"../fs-browser.js";import{projectFileNamespaces as _}from"../../hooks/toolkit.js";import{listGlobalFileAssets as k}from"../../install/global-file-assets.js";function w(i,e,s){const n=i.ai??b,l=i.decision??E,p=N(e),d=I(s);return{...i,ai:{baseUrl:n.baseUrl,model:n.model,apiKey:p,apiKeySet:p!==null},decision:{baseUrl:l.baseUrl,model:l.model,apiKey:d,apiKeySet:d!==null}}}function P(i){if(!i)return;const{apiKey:e,...s}=i;return s}export async function handleSettingsRoutes(i,e,s,n,l){if(n==="/settings"){if(i.method==="GET")return r(e,200,{settings:w(await u(),s.aiKeyPath,s.decisionKeyPath),presets:K(process.platform),platform:process.platform,urls:j(s.port,s.tlsPort??0),version:O,repoUrl:"https://github.com/zijing66/weave"}),!0;if(i.method==="PUT"){let t;try{t=JSON.parse(await m(i))}catch{return r(e,400,{error:"Invalid JSON body"}),!0}const a=t.ai?.apiKey;a!==void 0&&G(typeof a=="string"?a:null,s.aiKeyPath);const o=t.decision?.apiKey;o!==void 0&&U(typeof o=="string"?o:null,s.decisionKeyPath);const c=await u(),f=await T({...c,...t,ai:t.ai?P(t.ai):c.ai,decision:t.decision?P(t.decision):c.decision});return s.hookEvents?.resetPrune(),(s.autoStart??A)(f.startup?.launchOnLogin??!1),s.dataBus?.emit({key:"settings"}),r(e,200,w(f,s.aiKeyPath,s.decisionKeyPath)),!0}}if(n==="/fs/roots"&&i.method==="GET"){const t=s.libraries?s.libraries.list().map(o=>o.path):[],a=await B(t);return r(e,200,{roots:a}),!0}if(n==="/fs/children"&&i.method==="GET"){const t=l.searchParams.get("path");if(!t)return r(e,400,{error:'Missing "path" query parameter'}),!0;try{const a=await L(t);r(e,200,{children:a})}catch(a){r(e,400,{error:`Cannot list directory: ${String(a)}`})}return!0}const p=n.match(/^\/projects\/(\d+)\/config$/);if(p){const t=s.projects.getById(Number(p[1]));if(!t)return r(e,404,{error:"Project not found"}),!0;if(i.method==="GET")return r(e,200,await h(t.path)),!0;if(i.method==="PUT"){let a;try{a=JSON.parse(await m(i))}catch{return r(e,400,{error:"Invalid JSON body"}),!0}const o=await h(t.path),c={lastSyncAt:a.lastSyncAt??o.lastSyncAt};return await v(t.path,c),r(e,200,c),!0}}if(n==="/statusline/global"){if(i.method==="GET")return r(e,200,{config:await S()}),!0;if(i.method==="PUT"){let t;try{t=JSON.parse(await m(i))}catch{return r(e,400,{error:"Invalid JSON body"}),!0}try{await J(t);for(const a of s.projects.list()){const o=await y(a.path);o.source==="global"&&await g(a.path,o)}s.dataBus?.emit({key:"statusline"}),r(e,200,{ok:!0})}catch(a){r(e,400,{error:a instanceof Error?a.message:"Global statusline update failed"})}return!0}}if(n==="/statusline/default"&&i.method==="GET")return r(e,200,{config:F}),!0;const d=n.match(/^\/projects\/(\d+)\/statusline$/);if(d){const t=s.projects.getById(Number(d[1]));if(!t)return r(e,404,{error:"Project not found"}),!0;if(i.method==="GET"){const[a,o,c]=await Promise.all([y(t.path),C(t.path),S()]);return r(e,200,{config:a,script:o,globalConfig:c,facts:D(t.path)}),!0}if(i.method==="PUT"){let a;try{a=JSON.parse(await m(i))}catch{return r(e,400,{error:"Invalid JSON body"}),!0}try{await g(t.path,a),s.dataBus?.emit({key:"statusline",projectPath:t.path}),r(e,200,{ok:!0})}catch(o){r(e,400,{error:o instanceof Error?o.message:"Statusline update failed"})}return!0}}if(n==="/assets"&&i.method==="GET"){const t=l.searchParams.get("path");if(!t)return r(e,400,{error:'Missing "path" query parameter'}),!0;if(!s.watch)return r(e,503,{error:"Watch service unavailable"}),!0;const a=await _(t),o=s.watch.getAssets(t).map(c=>{const f=a.get(c.relPath);return f?{...c,namespace:f}:c});return r(e,200,{projectPath:t,assets:o}),!0}return n==="/global-file-assets"&&i.method==="GET"?(r(e,200,{assets:await k()}),!0):!1}
@@ -1 +1 @@
1
- import{createReadStream as B}from"node:fs";import{z as d}from"zod";import{readJson as p,readRawBody as E,sendJson as r}from"../http.js";import{isImageMediaType as I,maxUploadBytes as g}from"../../repositories/uploads.js";import{defaultUploadSessions as S}from"../upload-sessions.js";const w=d.object({name:d.string().min(1).max(200),mediaType:d.string().min(1).max(100),data:d.string().min(1),projectId:d.number().int().positive().optional()}),j=d.object({name:d.string().min(1).max(200),mediaType:d.string().min(1).max(100),size:d.number().int().positive(),projectId:d.number().int().positive().optional()});function z(i,e){return(I(e)?i?.images:i?.files)??null}const x=16*1024*1024;function y(i,e){return{id:e.id,name:e.name,mediaType:e.mediaType,size:e.size,path:i.uploads.filePath(e.id,e.mediaType,e.name,e.dir??i.uploads.defaultDir)}}function T(i,e,o){if(!i.projects.getById(e))return null;const c=i.projects.getMeta(e);return z(c?.storage,o)}export async function handleUploadRoutes(i,e,o,s,c){const l=o.uploadSessions??S;if(s==="/uploads/begin"&&i.method==="POST"){if(!o.uploads)return r(e,503,{error:"Upload subsystem unavailable"}),!0;let t;try{t=j.parse(await p(i))}catch(n){return r(e,400,{error:n instanceof Error?n.message:"Invalid body"}),!0}if(t.size>g(t.mediaType))return r(e,413,{error:"File too large"}),!0;const a=t.projectId?T(o,t.projectId,t.mediaType):null,u=await l.begin({...t,dir:a});return r(e,201,{uploadId:u.id,received:0,size:u.size}),!0}const f=s.match(/^\/uploads\/([0-9a-f-]{36})\/finish$/);if(f&&i.method==="POST"){if(!o.uploads)return r(e,503,{error:"Upload subsystem unavailable"}),!0;const t=await l.finish(f[1]);if(!t)return r(e,409,{error:"Upload incomplete"}),!0;const a=await o.uploads.adoptFromFile(t);return await l.abort(f[1]),r(e,201,{upload:y(o,a)}),!0}const b=s.match(/^\/uploads\/([0-9a-f-]{36})$/);if(b){const t=b[1];if(i.method==="GET"){const a=await l.get(t);return a?(r(e,200,{received:a.received,size:a.size,name:a.name,mediaType:a.mediaType}),!0):(r(e,404,{error:"Upload not found"}),!0)}if(i.method==="PUT"){const a=Number(c.searchParams.get("offset")??"");if(!Number.isInteger(a)||a<0)return r(e,400,{error:"Invalid offset"}),!0;const u=await E(i,x).catch(()=>null);if(!u)return r(e,413,{error:"Chunk too large"}),!0;const n=await l.append(t,a,u);return n.kind==="missing"?(r(e,404,{error:"Upload not found"}),!0):n.kind==="conflict"?(r(e,409,{received:n.received}),!0):n.kind==="overflow"?(r(e,413,{received:n.received,error:"Exceeds declared size"}),!0):(r(e,200,{received:n.received}),!0)}if(i.method==="DELETE")return await l.abort(t),r(e,200,{ok:!0}),!0}if(s==="/uploads"&&i.method==="POST"){if(!o.uploads)return r(e,503,{error:"Upload subsystem unavailable"}),!0;let t;try{t=w.parse(await p(i))}catch(n){return r(e,400,{error:n instanceof Error?n.message:"Invalid body"}),!0}const a=Buffer.from(t.data,"base64");if(a.length===0)return r(e,400,{error:"Empty upload"}),!0;if(a.length>g(t.mediaType))return r(e,413,{error:"File too large"}),!0;const u=o.uploads.save({name:t.name,mediaType:t.mediaType,bytes:a,dir:t.projectId?T(o,t.projectId,t.mediaType):null});return r(e,201,{upload:y(o,u)}),!0}const m=s.match(/^\/sessions\/([^/]+)\/uploads$/);if(m&&i.method==="GET"){if(!o.uploads)return r(e,503,{error:"Upload subsystem unavailable"}),!0;const t=o.uploads.uploadsBySession(decodeURIComponent(m[1]));return r(e,200,{uploads:(t??[]).map(a=>({...y(o,a),createdAt:a.createdAt})),totalBytes:(t??[]).reduce((a,u)=>a+u.size,0)}),!0}if(m&&i.method==="DELETE"){if(!o.uploads)return r(e,503,{error:"Upload subsystem unavailable"}),!0;const t=o.uploads.uploadsBySession(decodeURIComponent(m[1]));if(!t)return r(e,200,{removed:0,freedBytes:0}),!0;const a=t.reduce((n,U)=>n+U.size,0),u=o.uploads.removeMany(t.map(n=>n.id));return o.dataBus?.emit({key:"sessions"}),r(e,200,{removed:u,freedBytes:a}),!0}if(s==="/uploads/unreferenced"&&i.method==="GET")return o.uploads?(r(e,200,o.uploads.unreferencedStats()),!0):(r(e,503,{error:"Upload subsystem unavailable"}),!0);if(s==="/uploads/overview"&&i.method==="GET")return o.uploads?(r(e,200,o.uploads.overview()),!0):(r(e,503,{error:"Upload subsystem unavailable"}),!0);const h=d.object({ids:d.array(d.string().regex(/^[a-f0-9]{64}$/)).min(1).max(500)});if(s==="/uploads/remove"&&i.method==="POST"){if(!o.uploads)return r(e,503,{error:"Upload subsystem unavailable"}),!0;let t;try{t=h.parse(await p(i))}catch(u){return r(e,400,{error:u instanceof Error?u.message:"Invalid body"}),!0}const a=o.uploads.removeMany(t.ids);return o.dataBus?.emit({key:"sessions"}),r(e,200,{removed:a}),!0}if(s==="/uploads/usage"&&i.method==="GET")return o.uploads?(r(e,200,{sessions:o.uploads.usageBySession()}),!0):(r(e,503,{error:"Upload subsystem unavailable"}),!0);if(s==="/uploads/unreferenced"&&i.method==="POST"){if(!o.uploads)return r(e,503,{error:"Upload subsystem unavailable"}),!0;let t={done:0,total:0};const a=o.uploads.cleanupUnreferenced((u,n)=>{t={done:u,total:n}});return o.dataBus?.emit({key:"sessions"}),r(e,200,{removed:a,...t}),!0}const v=s.match(/^\/images\/([a-f0-9]{64})$/);if(v&&i.method==="GET"){if(!o.uploads)return r(e,503,{error:"Upload subsystem unavailable"}),!0;const t=o.uploads.fileStatOf(v[1]);if(!t)return r(e,404,{error:"Image not found"}),!0;e.writeHead(200,{"content-type":t.row.mediaType,"content-length":String(t.size),"cache-control":"private, max-age=31536000, immutable"});const a=B(t.file);return a.on("error",()=>{typeof e.destroy=="function"&&e.destroy()}),a.pipe(e),!0}return!1}
1
+ import{createReadStream as w}from"node:fs";import{z as u}from"zod";import{readJson as c,readRawBody as E,sendJson as a}from"../http.js";import{isImageMediaType as I,maxUploadBytes as b}from"../../repositories/uploads.js";import{defaultUploadSessions as S}from"../upload-sessions.js";const z=u.object({name:u.string().min(1).max(200),mediaType:u.string().min(1).max(100),data:u.string().min(1),projectId:u.number().int().positive().optional()}),B=u.object({name:u.string().min(1).max(200),mediaType:u.string().min(1).max(100),size:u.number().int().positive(),projectId:u.number().int().positive().optional()});function j(o,e){return(I(e)?o?.images:o?.files)??null}const x=16*1024*1024;function f(o,e){return{id:e.id,name:e.name,mediaType:e.mediaType,size:e.size,path:o.uploads.pathOf(e)}}function g(o,e,t){if(!o.projects.getById(e))return null;const d=o.projects.getMeta(e);return j(d?.storage,t)}export async function handleUploadRoutes(o,e,t,d,h){const l=t.uploadSessions??S;if(d==="/uploads/begin"&&o.method==="POST"){if(!t.uploads)return a(e,503,{error:"Upload subsystem unavailable"}),!0;let r;try{r=B.parse(await c(o))}catch(n){return a(e,400,{error:n instanceof Error?n.message:"Invalid body"}),!0}if(r.size>b(r.mediaType))return a(e,413,{error:"File too large"}),!0;const s=r.projectId?g(t,r.projectId,r.mediaType):null,i=await l.begin({...r,dir:s});return a(e,201,{uploadId:i.id,received:0,size:i.size}),!0}const m=d.match(/^\/uploads\/([0-9a-f-]{36})\/finish$/);if(m&&o.method==="POST"){if(!t.uploads)return a(e,503,{error:"Upload subsystem unavailable"}),!0;const r=await l.finish(m[1]);if(!r)return a(e,409,{error:"Upload incomplete"}),!0;const s=await t.uploads.adoptFromFile(r);return await l.abort(m[1]),a(e,201,{upload:f(t,s)}),!0}const y=d.match(/^\/uploads\/([0-9a-f-]{36})$/);if(y){const r=y[1];if(o.method==="GET"){const s=await l.get(r);return s?(a(e,200,{received:s.received,size:s.size,name:s.name,mediaType:s.mediaType}),!0):(a(e,404,{error:"Upload not found"}),!0)}if(o.method==="PUT"){const s=Number(h.searchParams.get("offset")??"");if(!Number.isInteger(s)||s<0)return a(e,400,{error:"Invalid offset"}),!0;const i=await E(o,x).catch(()=>null);if(!i)return a(e,413,{error:"Chunk too large"}),!0;const n=await l.append(r,s,i);return n.kind==="missing"?(a(e,404,{error:"Upload not found"}),!0):n.kind==="conflict"?(a(e,409,{received:n.received}),!0):n.kind==="overflow"?(a(e,413,{received:n.received,error:"Exceeds declared size"}),!0):(a(e,200,{received:n.received}),!0)}if(o.method==="DELETE")return await l.abort(r),a(e,200,{ok:!0}),!0}if(d==="/uploads"&&o.method==="POST"){if(!t.uploads)return a(e,503,{error:"Upload subsystem unavailable"}),!0;let r;try{r=z.parse(await c(o))}catch(n){return a(e,400,{error:n instanceof Error?n.message:"Invalid body"}),!0}const s=Buffer.from(r.data,"base64");if(s.length===0)return a(e,400,{error:"Empty upload"}),!0;if(s.length>b(r.mediaType))return a(e,413,{error:"File too large"}),!0;const i=t.uploads.save({name:r.name,mediaType:r.mediaType,bytes:s,dir:r.projectId?g(t,r.projectId,r.mediaType):null});return a(e,201,{upload:f(t,i)}),!0}const p=d.match(/^\/sessions\/([^/]+)\/uploads$/);if(p&&o.method==="GET"){if(!t.uploads)return a(e,503,{error:"Upload subsystem unavailable"}),!0;const r=t.uploads.uploadsBySession(decodeURIComponent(p[1]));return a(e,200,{uploads:(r??[]).map(s=>({...f(t,s),createdAt:s.createdAt})),totalBytes:(r??[]).reduce((s,i)=>s+i.size,0)}),!0}if(p&&o.method==="DELETE"){if(!t.uploads)return a(e,503,{error:"Upload subsystem unavailable"}),!0;const r=t.uploads.uploadsBySession(decodeURIComponent(p[1]));if(!r)return a(e,200,{removed:0,freedBytes:0}),!0;const s=r.reduce((n,U)=>n+U.size,0),i=t.uploads.removeMany(r.map(n=>n.id));return t.dataBus?.emit({key:"sessions"}),a(e,200,{removed:i,freedBytes:s}),!0}if(d==="/uploads/unreferenced"&&o.method==="GET")return t.uploads?(a(e,200,t.uploads.unreferencedStats()),!0):(a(e,503,{error:"Upload subsystem unavailable"}),!0);if(d==="/uploads/overview"&&o.method==="GET")return t.uploads?(a(e,200,t.uploads.overview()),!0):(a(e,503,{error:"Upload subsystem unavailable"}),!0);const T=u.object({ids:u.array(u.string().regex(/^[a-f0-9]{64}$/)).min(1).max(500)});if(d==="/uploads/remove"&&o.method==="POST"){if(!t.uploads)return a(e,503,{error:"Upload subsystem unavailable"}),!0;let r;try{r=T.parse(await c(o))}catch(i){return a(e,400,{error:i instanceof Error?i.message:"Invalid body"}),!0}const s=t.uploads.removeMany(r.ids);return t.dataBus?.emit({key:"sessions"}),a(e,200,{removed:s}),!0}if(d==="/uploads/usage"&&o.method==="GET")return t.uploads?(a(e,200,{sessions:t.uploads.usageBySession()}),!0):(a(e,503,{error:"Upload subsystem unavailable"}),!0);if(d==="/uploads/unreferenced"&&o.method==="POST"){if(!t.uploads)return a(e,503,{error:"Upload subsystem unavailable"}),!0;let r={done:0,total:0};const s=t.uploads.cleanupUnreferenced((i,n)=>{r={done:i,total:n}});return t.dataBus?.emit({key:"sessions"}),a(e,200,{removed:s,...r}),!0}const v=d.match(/^\/images\/([a-f0-9]{64})$/);if(v&&o.method==="GET"){if(!t.uploads)return a(e,503,{error:"Upload subsystem unavailable"}),!0;const r=t.uploads.fileStatOf(v[1]);if(!r)return a(e,404,{error:"Image not found"}),!0;e.writeHead(200,{"content-type":r.row.mediaType,"content-length":String(r.size),"cache-control":"private, max-age=31536000, immutable"});const s=w(r.file);return s.on("error",()=>{typeof e.destroy=="function"&&e.destroy()}),s.pipe(e),!0}return!1}
@@ -0,0 +1,3 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import type { WeaveServerDeps } from '../server.js';
3
+ export declare function handleVisualizationRoutes(req: IncomingMessage, res: ServerResponse, deps: WeaveServerDeps, pathname: string, url: URL): Promise<boolean>;
@@ -0,0 +1 @@
1
+ import t from"node:path";import d from"node:os";import{promises as l}from"node:fs";import{HARNESS_DIRS as u,VISUALIZATION_PROTOCOL as g}from"@weavecode/core";import{sendJson as e}from"../http.js";import{isInsideDir as F}from"../../market/resolve.js";function j(s){const r=d.homedir(),o=[t.join(r,u.codex.userDir,"visualizations"),t.join(r,u.claude.userDir),t.join(r,u.codex.userDir,"plugins")];for(const a of s.projects.list())o.push(a.path);return o}export async function handleVisualizationRoutes(s,r,o,a,c){if(a!=="/visualizations/fragment"||s.method!=="GET")return!1;const n=c.searchParams.get("path")??"",i=t.resolve(n);if(!n||!t.isAbsolute(n))return e(r,400,{error:'Missing or relative "path" query parameter'}),!0;const f=j(o);if(!f.some(h=>F(i,h)))return e(r,403,{error:`Fragment path is outside the allowed roots: ${f.join(", ")}`}),!0;if(t.extname(i).toLowerCase()!==".html")return e(r,403,{error:"Only .html fragments are served"}),!0;let m;try{m=await l.stat(i)}catch{return e(r,404,{error:"Fragment file not found"}),!0}if(!m.isFile())return e(r,404,{error:"Fragment path is not a file"}),!0;if(m.size>g.maxFragmentBytes)return e(r,413,{error:"Fragment exceeds the 1 MB size limit"}),!0;const p=await l.readFile(i,"utf8");return e(r,200,{html:p}),!0}