@agent-spaces/server 0.3.64 → 0.3.65

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 (294) hide show
  1. package/dist/adapters/claude-code-runtime/index.js +68 -2
  2. package/dist/adapters/claude-code-runtime/message-format.js +26 -0
  3. package/dist/adapters/claude-code-runtime/sdk-config.js +12 -0
  4. package/dist/adapters/git.js +13 -6
  5. package/dist/agents/agent-message-parts.js +1 -1
  6. package/dist/agents/commit-agent.js +4 -2
  7. package/dist/agents/issue-task-controller.js +6 -2
  8. package/dist/app.js +6 -68
  9. package/dist/routes/agent-sse.js +1 -0
  10. package/dist/routes/agent.js +3 -0
  11. package/dist/routes/database.js +1 -0
  12. package/dist/routes/git.js +197 -317
  13. package/dist/routes/robot-account.js +68 -0
  14. package/dist/services/agent.js +36 -2
  15. package/dist/services/builtin-tools/kanban-tools.js +4 -0
  16. package/dist/services/git-operation-log.js +40 -0
  17. package/dist/services/global-wechat-qr.js +72 -0
  18. package/dist/services/hook-engine.js +175 -17
  19. package/dist/services/notification-hub/bot-agent.js +3 -1
  20. package/dist/services/notification-hub/service.js +12 -2
  21. package/dist/services/robot-account.js +34 -0
  22. package/dist/services/workspace.js +2 -0
  23. package/dist/storage/robot-account-store.js +39 -0
  24. package/dist/web/404.html +1 -1
  25. package/dist/web/__next.__PAGE__.txt +4 -4
  26. package/dist/web/__next._full.txt +23 -25
  27. package/dist/web/__next._head.txt +4 -4
  28. package/dist/web/__next._index.txt +13 -13
  29. package/dist/web/__next._tree.txt +2 -2
  30. package/dist/web/_next/static/chunks/{04a4vsq2wknm4.js → 0-a.-hl3wv99k.js} +1 -1
  31. package/dist/web/_next/static/chunks/{0ctg5qmw7uu.1.js → 0-pi.67w.ymsr.js} +3 -3
  32. package/dist/web/_next/static/chunks/0-v_p6j5u3_9l.js +1 -0
  33. package/dist/web/_next/static/chunks/0.-x29t-8gk0r.js +1 -0
  34. package/dist/web/_next/static/chunks/0.6b64sy0g5.3.js +3 -0
  35. package/dist/web/_next/static/chunks/0.l5e8~r7_0~p.js +1 -0
  36. package/dist/web/_next/static/chunks/00-syqzi-6zxr.js +1 -0
  37. package/dist/web/_next/static/chunks/02802taez976w.js +67 -0
  38. package/dist/web/_next/static/chunks/02j4sr9psucf-.js +1 -0
  39. package/dist/web/_next/static/chunks/033.awk90g6eh.js +1 -0
  40. package/dist/web/_next/static/chunks/06.kdsa9rr2h1.js +1 -0
  41. package/dist/web/_next/static/chunks/06si8l0doqe8p.js +1 -0
  42. package/dist/web/_next/static/chunks/08ugbvzr20drp.js +2 -0
  43. package/dist/web/_next/static/chunks/{0n6_4so_afuwk.js → 08x85faoz58i7.js} +2 -2
  44. package/dist/web/_next/static/chunks/{0v3a_x4vt42kx.js → 0_cqojbge48kq.js} +1 -1
  45. package/dist/web/_next/static/chunks/{0a5y-vhf4i5id.js → 0d6~~lv0-6kex.js} +1 -1
  46. package/dist/web/_next/static/chunks/{0vbqaynjq8ga..js → 0db14450oqzhg.js} +1 -1
  47. package/dist/web/_next/static/chunks/0gdwop9cnpmtu.js +1 -0
  48. package/dist/web/_next/static/chunks/0j-l7l_k9jt45.js +1 -0
  49. package/dist/web/_next/static/chunks/0kbvoxla7f751.js +1 -0
  50. package/dist/web/_next/static/chunks/{1442stwa0gbxe.js → 0l2w0qz1ea7ql.js} +2 -2
  51. package/dist/web/_next/static/chunks/0p3h-h3zj5ujg.js +30 -0
  52. package/dist/web/_next/static/chunks/0paaat8y96ayc.js +7 -0
  53. package/dist/web/_next/static/chunks/0pm-un~-sy3rl.js +1 -0
  54. package/dist/web/_next/static/chunks/0pq318f8xgafg.js +1 -0
  55. package/dist/web/_next/static/chunks/0t6s8of0qr6nc.js +1 -0
  56. package/dist/web/_next/static/chunks/0u6h-hqc_i52e.js +1 -0
  57. package/dist/web/_next/static/chunks/0uxzi~9b~k4mn.js +31 -0
  58. package/dist/web/_next/static/chunks/{08ckpwmmwlors.js → 0vis~lx0nxgn-.js} +2 -2
  59. package/dist/web/_next/static/chunks/{10l-9myrdo3x9.js → 0w7.tb29958zj.js} +1 -1
  60. package/dist/web/_next/static/chunks/0yd.m1afqg.2t.js +3 -0
  61. package/dist/web/_next/static/chunks/0yhbr8hl09b-5.js +1 -0
  62. package/dist/web/_next/static/chunks/0zncg76rgtbm-.js +1 -0
  63. package/dist/web/_next/static/chunks/0~~_ltu6dx_ch.js +1 -0
  64. package/dist/web/_next/static/chunks/{0iuf30t0rjsjv.js → 11cchuwoupmbf.js} +1 -1
  65. package/dist/web/_next/static/chunks/12oxcgyhpaxw~.js +1 -0
  66. package/dist/web/_next/static/chunks/1452eibz62gs6.css +1 -0
  67. package/dist/web/_next/static/chunks/16zlreiems5cn.js +1 -0
  68. package/dist/web/_next/static/chunks/18awqjk9-o5m0.js +1 -0
  69. package/dist/web/_next/static/chunks/18c7g84y01mj0.js +1 -0
  70. package/dist/web/_next/static/chunks/{turbopack-0ndm2zc_pdptk.js → turbopack-11qjwlynmfc-n.js} +1 -1
  71. package/dist/web/_not-found/__next._full.txt +30 -29
  72. package/dist/web/_not-found/__next._head.txt +4 -4
  73. package/dist/web/_not-found/__next._index.txt +13 -13
  74. package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
  75. package/dist/web/_not-found/__next._not-found.txt +3 -3
  76. package/dist/web/_not-found/__next._tree.txt +2 -2
  77. package/dist/web/_not-found.html +1 -1
  78. package/dist/web/_not-found.txt +30 -29
  79. package/dist/web/index.html +1 -1
  80. package/dist/web/index.txt +23 -25
  81. package/dist/web/login/__next._full.txt +28 -28
  82. package/dist/web/login/__next._head.txt +4 -4
  83. package/dist/web/login/__next._index.txt +13 -13
  84. package/dist/web/login/__next._tree.txt +2 -2
  85. package/dist/web/login/__next.login.__PAGE__.txt +4 -4
  86. package/dist/web/login/__next.login.txt +3 -3
  87. package/dist/web/login.html +1 -1
  88. package/dist/web/login.txt +28 -28
  89. package/dist/web/settings/__next._full.txt +33 -32
  90. package/dist/web/settings/__next._head.txt +4 -4
  91. package/dist/web/settings/__next._index.txt +13 -13
  92. package/dist/web/settings/__next._tree.txt +2 -2
  93. package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
  94. package/dist/web/settings/__next.settings.txt +5 -5
  95. package/dist/web/settings/agents/__next._full.txt +24 -24
  96. package/dist/web/settings/agents/__next._head.txt +4 -4
  97. package/dist/web/settings/agents/__next._index.txt +13 -13
  98. package/dist/web/settings/agents/__next._tree.txt +2 -2
  99. package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
  100. package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
  101. package/dist/web/settings/agents/__next.settings.txt +5 -5
  102. package/dist/web/settings/agents.html +1 -1
  103. package/dist/web/settings/agents.txt +24 -24
  104. package/dist/web/settings/mcps/__next._full.txt +24 -24
  105. package/dist/web/settings/mcps/__next._head.txt +4 -4
  106. package/dist/web/settings/mcps/__next._index.txt +13 -13
  107. package/dist/web/settings/mcps/__next._tree.txt +2 -2
  108. package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
  109. package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
  110. package/dist/web/settings/mcps/__next.settings.txt +5 -5
  111. package/dist/web/settings/mcps.html +1 -1
  112. package/dist/web/settings/mcps.txt +24 -24
  113. package/dist/web/settings/models/__next._full.txt +24 -24
  114. package/dist/web/settings/models/__next._head.txt +4 -4
  115. package/dist/web/settings/models/__next._index.txt +13 -13
  116. package/dist/web/settings/models/__next._tree.txt +2 -2
  117. package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
  118. package/dist/web/settings/models/__next.settings.models.txt +3 -3
  119. package/dist/web/settings/models/__next.settings.txt +5 -5
  120. package/dist/web/settings/models.html +1 -1
  121. package/dist/web/settings/models.txt +24 -24
  122. package/dist/web/settings/output-styles/__next._full.txt +24 -24
  123. package/dist/web/settings/output-styles/__next._head.txt +4 -4
  124. package/dist/web/settings/output-styles/__next._index.txt +13 -13
  125. package/dist/web/settings/output-styles/__next._tree.txt +2 -2
  126. package/dist/web/settings/output-styles/__next.settings.output-styles.__PAGE__.txt +4 -4
  127. package/dist/web/settings/output-styles/__next.settings.output-styles.txt +3 -3
  128. package/dist/web/settings/output-styles/__next.settings.txt +5 -5
  129. package/dist/web/settings/output-styles.html +1 -1
  130. package/dist/web/settings/output-styles.txt +24 -24
  131. package/dist/web/settings/prompts/__next._full.txt +24 -24
  132. package/dist/web/settings/prompts/__next._head.txt +4 -4
  133. package/dist/web/settings/prompts/__next._index.txt +13 -13
  134. package/dist/web/settings/prompts/__next._tree.txt +2 -2
  135. package/dist/web/settings/prompts/__next.settings.prompts.__PAGE__.txt +4 -4
  136. package/dist/web/settings/prompts/__next.settings.prompts.txt +3 -3
  137. package/dist/web/settings/prompts/__next.settings.txt +5 -5
  138. package/dist/web/settings/prompts.html +1 -1
  139. package/dist/web/settings/prompts.txt +24 -24
  140. package/dist/web/settings/providers/__next._full.txt +24 -24
  141. package/dist/web/settings/providers/__next._head.txt +4 -4
  142. package/dist/web/settings/providers/__next._index.txt +13 -13
  143. package/dist/web/settings/providers/__next._tree.txt +2 -2
  144. package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
  145. package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
  146. package/dist/web/settings/providers/__next.settings.txt +5 -5
  147. package/dist/web/settings/providers.html +1 -1
  148. package/dist/web/settings/providers.txt +24 -24
  149. package/dist/web/settings/skills/__next._full.txt +24 -24
  150. package/dist/web/settings/skills/__next._head.txt +4 -4
  151. package/dist/web/settings/skills/__next._index.txt +13 -13
  152. package/dist/web/settings/skills/__next._tree.txt +2 -2
  153. package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
  154. package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
  155. package/dist/web/settings/skills/__next.settings.txt +5 -5
  156. package/dist/web/settings/skills.html +1 -1
  157. package/dist/web/settings/skills.txt +24 -24
  158. package/dist/web/settings/tools/__next._full.txt +24 -24
  159. package/dist/web/settings/tools/__next._head.txt +4 -4
  160. package/dist/web/settings/tools/__next._index.txt +13 -13
  161. package/dist/web/settings/tools/__next._tree.txt +2 -2
  162. package/dist/web/settings/tools/__next.settings.tools.__PAGE__.txt +4 -4
  163. package/dist/web/settings/tools/__next.settings.tools.txt +3 -3
  164. package/dist/web/settings/tools/__next.settings.txt +5 -5
  165. package/dist/web/settings/tools.html +1 -1
  166. package/dist/web/settings/tools.txt +24 -24
  167. package/dist/web/settings.html +1 -1
  168. package/dist/web/settings.txt +33 -32
  169. package/dist/web/workflows/__next._full.txt +31 -33
  170. package/dist/web/workflows/__next._head.txt +4 -4
  171. package/dist/web/workflows/__next._index.txt +13 -13
  172. package/dist/web/workflows/__next._tree.txt +2 -2
  173. package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
  174. package/dist/web/workflows/__next.workflows.txt +3 -3
  175. package/dist/web/workflows.html +1 -1
  176. package/dist/web/workflows.txt +31 -33
  177. package/dist/web/workspace/_/__next._full.txt +28 -29
  178. package/dist/web/workspace/_/__next._head.txt +4 -4
  179. package/dist/web/workspace/_/__next._index.txt +13 -13
  180. package/dist/web/workspace/_/__next._tree.txt +2 -2
  181. package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
  182. package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
  183. package/dist/web/workspace/_/__next.workspace.txt +3 -3
  184. package/dist/web/workspace/_.html +1 -1
  185. package/dist/web/workspace/_.txt +28 -29
  186. package/dist/web/workspaces/__next._full.txt +29 -31
  187. package/dist/web/workspaces/__next._head.txt +4 -4
  188. package/dist/web/workspaces/__next._index.txt +13 -13
  189. package/dist/web/workspaces/__next._tree.txt +2 -2
  190. package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
  191. package/dist/web/workspaces/__next.workspaces.txt +3 -3
  192. package/dist/web/workspaces.html +1 -1
  193. package/dist/web/workspaces.txt +29 -31
  194. package/dist/ws/agent-runner.js +82 -6
  195. package/package.json +10 -11
  196. package/dist/web/_next/static/chunks/0-9194nbxy_6d.js +0 -1
  197. package/dist/web/_next/static/chunks/02mf61htij8g0.js +0 -31
  198. package/dist/web/_next/static/chunks/04dg4l4hv11q2.js +0 -3
  199. package/dist/web/_next/static/chunks/04fxddw-y20~-.js +0 -7
  200. package/dist/web/_next/static/chunks/04w7~5ooz_pwm.js +0 -1
  201. package/dist/web/_next/static/chunks/05oc21sj24wtq.js +0 -1
  202. package/dist/web/_next/static/chunks/08k~meqbd8d6v.js +0 -1
  203. package/dist/web/_next/static/chunks/099h96wjsdb25.js +0 -2
  204. package/dist/web/_next/static/chunks/09h23logvq65m.js +0 -1
  205. package/dist/web/_next/static/chunks/0ake2f3x2uan4.js +0 -24
  206. package/dist/web/_next/static/chunks/0ck8dw_9-1cdx.js +0 -1
  207. package/dist/web/_next/static/chunks/0ewceguykiut5.js +0 -1
  208. package/dist/web/_next/static/chunks/0f7cb9li~3jra.js +0 -67
  209. package/dist/web/_next/static/chunks/0ghs9on7wodrm.js +0 -1
  210. package/dist/web/_next/static/chunks/0h9cu-k6z5o08.js +0 -1
  211. package/dist/web/_next/static/chunks/0jb4~3qqkw2ps.js +0 -1
  212. package/dist/web/_next/static/chunks/0nb8.iteznuvx.js +0 -1
  213. package/dist/web/_next/static/chunks/0os58q93stt7j.js +0 -3
  214. package/dist/web/_next/static/chunks/0ppr271hz0xoq.js +0 -1
  215. package/dist/web/_next/static/chunks/0s9lmk8iauou3.js +0 -1
  216. package/dist/web/_next/static/chunks/0sp_zbx~w0am7.js +0 -1
  217. package/dist/web/_next/static/chunks/0tel~zsv4z8l_.js +0 -1
  218. package/dist/web/_next/static/chunks/0twamv4wr_dbv.js +0 -1
  219. package/dist/web/_next/static/chunks/0uhol5wafdbl~.js +0 -1
  220. package/dist/web/_next/static/chunks/0v7q4mys.mnm..css +0 -1
  221. package/dist/web/_next/static/chunks/0~3xm80~j9ddg.js +0 -1
  222. package/dist/web/_next/static/chunks/14s37n6h0_q3x.js +0 -16
  223. package/dist/web/_next/static/chunks/161ik5kfyuwtd.js +0 -1
  224. package/dist/web/_next/static/chunks/17k~zbv0_4rep.js +0 -1
  225. package/public/output-styles/carmack-mode.md +0 -381
  226. package/public/output-styles/codex-rigor-mode.md +0 -114
  227. package/public/output-styles/dhh-mode.md +0 -265
  228. package/public/output-styles/evan-you-mode.md +0 -539
  229. package/public/output-styles/index.json +0 -37
  230. package/public/output-styles/jobs-mode.md +0 -369
  231. package/public/output-styles/linus-mode.md +0 -135
  232. package/public/output-styles/uncle-bob-mode.md +0 -221
  233. package/public/prompt/andrej-karpathy-skills.md +0 -65
  234. package/public/prompt/claude-token-efficient-coding.md +0 -35
  235. package/public/prompt/index.json +0 -12
  236. package/public/skills/index.json +0 -92
  237. package/public/skills/planning-with-files/planning-with-files-zh/SKILL.md +0 -243
  238. package/public/skills/planning-with-files/planning-with-files-zh/scripts/check-complete.ps1 +0 -44
  239. package/public/skills/planning-with-files/planning-with-files-zh/scripts/check-complete.sh +0 -46
  240. package/public/skills/planning-with-files/planning-with-files-zh/scripts/init-session.ps1 +0 -124
  241. package/public/skills/planning-with-files/planning-with-files-zh/scripts/init-session.sh +0 -124
  242. package/public/skills/planning-with-files/planning-with-files-zh/scripts/session-catchup.py +0 -438
  243. package/public/skills/planning-with-files/planning-with-files-zh/templates/findings.md +0 -29
  244. package/public/skills/planning-with-files/planning-with-files-zh/templates/progress.md +0 -40
  245. package/public/skills/planning-with-files/planning-with-files-zh/templates/task_plan.md +0 -58
  246. package/public/skills/superpowers/brainstorming/SKILL.md +0 -164
  247. package/public/skills/superpowers/brainstorming/scripts/frame-template.html +0 -214
  248. package/public/skills/superpowers/brainstorming/scripts/helper.js +0 -88
  249. package/public/skills/superpowers/brainstorming/scripts/server.cjs +0 -354
  250. package/public/skills/superpowers/brainstorming/scripts/start-server.sh +0 -148
  251. package/public/skills/superpowers/brainstorming/scripts/stop-server.sh +0 -56
  252. package/public/skills/superpowers/brainstorming/spec-document-reviewer-prompt.md +0 -49
  253. package/public/skills/superpowers/brainstorming/visual-companion.md +0 -287
  254. package/public/skills/superpowers/dispatching-parallel-agents/SKILL.md +0 -182
  255. package/public/skills/superpowers/executing-plans/SKILL.md +0 -70
  256. package/public/skills/superpowers/finishing-a-development-branch/SKILL.md +0 -251
  257. package/public/skills/superpowers/receiving-code-review/SKILL.md +0 -213
  258. package/public/skills/superpowers/requesting-code-review/SKILL.md +0 -103
  259. package/public/skills/superpowers/requesting-code-review/code-reviewer.md +0 -168
  260. package/public/skills/superpowers/subagent-driven-development/SKILL.md +0 -279
  261. package/public/skills/superpowers/subagent-driven-development/code-quality-reviewer-prompt.md +0 -25
  262. package/public/skills/superpowers/subagent-driven-development/implementer-prompt.md +0 -113
  263. package/public/skills/superpowers/subagent-driven-development/spec-reviewer-prompt.md +0 -61
  264. package/public/skills/superpowers/systematic-debugging/CREATION-LOG.md +0 -119
  265. package/public/skills/superpowers/systematic-debugging/SKILL.md +0 -296
  266. package/public/skills/superpowers/systematic-debugging/condition-based-waiting-example.ts +0 -158
  267. package/public/skills/superpowers/systematic-debugging/condition-based-waiting.md +0 -115
  268. package/public/skills/superpowers/systematic-debugging/defense-in-depth.md +0 -122
  269. package/public/skills/superpowers/systematic-debugging/find-polluter.sh +0 -63
  270. package/public/skills/superpowers/systematic-debugging/root-cause-tracing.md +0 -169
  271. package/public/skills/superpowers/systematic-debugging/test-academic.md +0 -14
  272. package/public/skills/superpowers/systematic-debugging/test-pressure-1.md +0 -58
  273. package/public/skills/superpowers/systematic-debugging/test-pressure-2.md +0 -68
  274. package/public/skills/superpowers/systematic-debugging/test-pressure-3.md +0 -69
  275. package/public/skills/superpowers/test-driven-development/SKILL.md +0 -371
  276. package/public/skills/superpowers/test-driven-development/testing-anti-patterns.md +0 -299
  277. package/public/skills/superpowers/using-git-worktrees/SKILL.md +0 -215
  278. package/public/skills/superpowers/using-superpowers/SKILL.md +0 -117
  279. package/public/skills/superpowers/using-superpowers/references/codex-tools.md +0 -59
  280. package/public/skills/superpowers/using-superpowers/references/copilot-tools.md +0 -42
  281. package/public/skills/superpowers/using-superpowers/references/gemini-tools.md +0 -51
  282. package/public/skills/superpowers/verification-before-completion/SKILL.md +0 -139
  283. package/public/skills/superpowers/writing-plans/SKILL.md +0 -152
  284. package/public/skills/superpowers/writing-plans/plan-document-reviewer-prompt.md +0 -49
  285. package/public/skills/superpowers/writing-skills/SKILL.md +0 -655
  286. package/public/skills/superpowers/writing-skills/anthropic-best-practices.md +0 -1150
  287. package/public/skills/superpowers/writing-skills/examples/CLAUDE_MD_TESTING.md +0 -189
  288. package/public/skills/superpowers/writing-skills/graphviz-conventions.dot +0 -172
  289. package/public/skills/superpowers/writing-skills/persuasion-principles.md +0 -187
  290. package/public/skills/superpowers/writing-skills/render-graphs.js +0 -168
  291. package/public/skills/superpowers/writing-skills/testing-skills-with-subagents.md +0 -384
  292. /package/dist/web/_next/static/{e8w3vd9Eov_LDYWj_0zsE → x4-CsKe6qUQyFDWVtfvUg}/_buildManifest.js +0 -0
  293. /package/dist/web/_next/static/{e8w3vd9Eov_LDYWj_0zsE → x4-CsKe6qUQyFDWVtfvUg}/_clientMiddlewareManifest.js +0 -0
  294. /package/dist/web/_next/static/{e8w3vd9Eov_LDYWj_0zsE → x4-CsKe6qUQyFDWVtfvUg}/_ssgManifest.js +0 -0
@@ -1,5 +1,6 @@
1
1
  import { BUILT_IN_AGENT_TOOLS } from '@agent-spaces/shared';
2
2
  import * as kanbanStore from '../../storage/kanban-store.js';
3
+ import { broadcastToWorkspace } from '../../ws/connection-manager.js';
3
4
  import { assertRecord, readOptionalString, readRequiredString, readString } from './input-helpers.js';
4
5
  const kanbanLayoutModes = ['horizontal', 'vertical'];
5
6
  const kanbanPriorities = ['low', 'medium', 'high'];
@@ -158,6 +159,7 @@ function createKanbanBoard(workspaceId, input) {
158
159
  const created = kanbanStore.getBoard(workspaceId);
159
160
  if (!created)
160
161
  throw new Error('Failed to create Kanban board.');
162
+ broadcastToWorkspace(workspaceId, 'kanban.updated', created);
161
163
  return created;
162
164
  }
163
165
  function updateKanbanBoard(workspaceId, input) {
@@ -185,6 +187,7 @@ function updateKanbanBoard(workspaceId, input) {
185
187
  const updated = kanbanStore.listBoards(workspaceId).find((item) => item.id === board.id);
186
188
  if (!updated)
187
189
  throw new Error(`Kanban board not found: ${board.id}`);
190
+ broadcastToWorkspace(workspaceId, 'kanban.updated', updated);
188
191
  return updated;
189
192
  }
190
193
  function deleteKanbanBoard(workspaceId, input) {
@@ -193,6 +196,7 @@ function deleteKanbanBoard(workspaceId, input) {
193
196
  const ok = kanbanStore.deleteBoard(workspaceId, board.id);
194
197
  if (!ok)
195
198
  throw new Error(`Kanban board not found: ${board.id}`);
199
+ broadcastToWorkspace(workspaceId, 'kanban.deleted', { boardId: board.id });
196
200
  return { ok: true, deletedBoardId: board.id };
197
201
  }
198
202
  function resolveKanbanBoard(workspaceId, boardId) {
@@ -0,0 +1,40 @@
1
+ const MAX_ENTRIES = 1000;
2
+ const store = new Map();
3
+ function getList(workspaceId) {
4
+ let list = store.get(workspaceId);
5
+ if (!list) {
6
+ list = [];
7
+ store.set(workspaceId, list);
8
+ }
9
+ return list;
10
+ }
11
+ let counter = 0;
12
+ export function logGitOperation(workspaceId, operation, input, result, error, duration = 0) {
13
+ const list = getList(workspaceId);
14
+ const entry = {
15
+ id: `${Date.now()}-${++counter}`,
16
+ operation,
17
+ input,
18
+ output: truncateOutput(result),
19
+ error,
20
+ timestamp: new Date().toISOString(),
21
+ duration,
22
+ };
23
+ list.unshift(entry);
24
+ if (list.length > MAX_ENTRIES)
25
+ list.length = MAX_ENTRIES;
26
+ }
27
+ export function getGitOperations(workspaceId) {
28
+ return getList(workspaceId);
29
+ }
30
+ function truncateOutput(value) {
31
+ if (value === undefined || value === null)
32
+ return value;
33
+ if (typeof value === 'string')
34
+ return value.length > 5000 ? value.slice(0, 5000) + '...(truncated)' : value;
35
+ const s = JSON.stringify(value);
36
+ if (s && s.length > 5000)
37
+ return JSON.parse(s.slice(0, 5000) + '...(truncated)');
38
+ return value;
39
+ }
40
+ //# sourceMappingURL=git-operation-log.js.map
@@ -0,0 +1,72 @@
1
+ import { WECHAT_BASE_URL, WECHAT_BOT_TYPE, WECHAT_QR_STATUS_TIMEOUT_MS, } from './notification-hub/types.js';
2
+ import { createAccount } from './robot-account.js';
3
+ const globalSessions = new Map();
4
+ export async function getGlobalWeChatQRCode() {
5
+ // cleanup old sessions
6
+ const now = Date.now();
7
+ for (const [key, s] of globalSessions) {
8
+ if (now - s.createdAt > 5 * 60_000)
9
+ globalSessions.delete(key);
10
+ }
11
+ const res = await fetch(`${WECHAT_BASE_URL}/ilink/bot/get_bot_qrcode?bot_type=${WECHAT_BOT_TYPE}`);
12
+ if (!res.ok)
13
+ throw new Error(`Failed to get WeChat QR code: ${res.status}`);
14
+ const qr = await res.json();
15
+ const sessionId = `global-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
16
+ globalSessions.set(sessionId, {
17
+ qrcode: qr.qrcode,
18
+ qrcodeImgContent: qr.qrcode_img_content,
19
+ createdAt: now,
20
+ });
21
+ return { status: 'wait', qrcodeImgContent: qr.qrcode_img_content, sessionId };
22
+ }
23
+ export async function pollGlobalWeChatQRCode(sessionId) {
24
+ if (!sessionId)
25
+ throw new Error('sessionId is required');
26
+ const session = globalSessions.get(sessionId);
27
+ if (!session)
28
+ throw new Error('Session not found or expired');
29
+ const status = await fetchQRStatus(session.qrcode);
30
+ if (status.status === 'confirmed') {
31
+ if (!status.bot_token || !status.ilink_bot_id) {
32
+ throw new Error('WeChat login confirmed but token or bot id is missing');
33
+ }
34
+ globalSessions.delete(sessionId);
35
+ const account = createAccount({
36
+ name: `微信 ${status.ilink_bot_id}`,
37
+ type: 'wechat',
38
+ wechat: {
39
+ token: status.bot_token,
40
+ baseUrl: status.baseurl || WECHAT_BASE_URL,
41
+ accountId: status.ilink_bot_id,
42
+ userId: status.ilink_user_id,
43
+ },
44
+ });
45
+ return { status: 'confirmed', account };
46
+ }
47
+ if (status.status === 'expired') {
48
+ globalSessions.delete(sessionId);
49
+ }
50
+ return { status: status.status, qrcodeImgContent: session.qrcodeImgContent, sessionId };
51
+ }
52
+ async function fetchQRStatus(qrcode) {
53
+ const controller = new AbortController();
54
+ const timer = setTimeout(() => controller.abort(), WECHAT_QR_STATUS_TIMEOUT_MS);
55
+ try {
56
+ const res = await fetch(`${WECHAT_BASE_URL}/ilink/bot/get_qrcode_status?qrcode=${encodeURIComponent(qrcode)}`, {
57
+ headers: { 'iLink-App-ClientVersion': '1' },
58
+ signal: controller.signal,
59
+ });
60
+ clearTimeout(timer);
61
+ if (!res.ok)
62
+ throw new Error(`Failed to poll QR status: ${res.status}`);
63
+ return await res.json();
64
+ }
65
+ catch (err) {
66
+ clearTimeout(timer);
67
+ if (err instanceof Error && err.name === 'AbortError')
68
+ return { status: 'wait' };
69
+ throw err;
70
+ }
71
+ }
72
+ //# sourceMappingURL=global-wechat-qr.js.map
@@ -1,5 +1,12 @@
1
1
  import { exec } from 'node:child_process';
2
2
  import { listHooks } from '../storage/hook-store.js';
3
+ function logDebug(message, details) {
4
+ if (details) {
5
+ console.debug(`[HookEngine] ${message}`, details);
6
+ return;
7
+ }
8
+ console.debug(`[HookEngine] ${message}`);
9
+ }
3
10
  function matchToolName(matcher, toolName) {
4
11
  if (matcher === '*')
5
12
  return true;
@@ -8,7 +15,12 @@ function matchToolName(matcher, toolName) {
8
15
  const regex = new RegExp(matcher.slice(1, -1));
9
16
  return regex.test(toolName);
10
17
  }
11
- catch {
18
+ catch (error) {
19
+ console.warn(`[HookEngine] invalid matcher regex, falling back to exact match`, {
20
+ matcher,
21
+ toolName,
22
+ error: error.message,
23
+ });
12
24
  return matcher === toolName;
13
25
  }
14
26
  }
@@ -16,7 +28,8 @@ function matchToolName(matcher, toolName) {
16
28
  }
17
29
  function executeCommand(command, env, timeout) {
18
30
  return new Promise((resolve) => {
19
- exec(command, { env: { ...process.env, ...env }, timeout: Math.min(timeout, 30000) }, (error) => {
31
+ const cappedTimeout = Math.min(timeout, 30000);
32
+ exec(command, { env: { ...process.env, ...env }, timeout: cappedTimeout }, (error) => {
20
33
  if (error)
21
34
  console.warn(`[HookEngine] command error: ${error.message}`);
22
35
  resolve();
@@ -26,43 +39,101 @@ function executeCommand(command, env, timeout) {
26
39
  async function executeWebhook(url, body, timeout) {
27
40
  try {
28
41
  const controller = new AbortController();
29
- const timer = setTimeout(() => controller.abort(), Math.min(timeout, 30000));
30
- await fetch(url, {
42
+ const cappedTimeout = Math.min(timeout, 30000);
43
+ const timer = setTimeout(() => controller.abort(), cappedTimeout);
44
+ const response = await fetch(url, {
31
45
  method: 'POST',
32
46
  headers: { 'Content-Type': 'application/json' },
33
47
  body: JSON.stringify(body),
34
48
  signal: controller.signal,
35
49
  });
36
50
  clearTimeout(timer);
51
+ if (!response.ok) {
52
+ console.warn(`[HookEngine] webhook non-2xx response`, {
53
+ url,
54
+ status: response.status,
55
+ statusText: response.statusText,
56
+ });
57
+ }
37
58
  }
38
59
  catch (error) {
39
60
  console.warn(`[HookEngine] webhook error: ${error.message}`);
40
61
  }
41
62
  }
42
- async function executeRule(rule, phase, toolName, workspaceId, context) {
63
+ async function executeRule(rule, phase, matcherValue, workspaceId, context, hookName) {
43
64
  const timeout = rule.timeout ?? 10000;
65
+ const startedAt = Date.now();
66
+ logDebug('rule start', {
67
+ workspaceId,
68
+ phase,
69
+ hookName,
70
+ matcherValue,
71
+ matcher: rule.matcher,
72
+ type: rule.type,
73
+ timeout,
74
+ });
44
75
  if (rule.type === 'command' && rule.command) {
45
76
  await executeCommand(rule.command, {
46
- HOOK_TOOL_NAME: toolName,
77
+ HOOK_NAME: hookName ?? '',
78
+ HOOK_RULE_MATCHER: rule.matcher,
79
+ HOOK_RULE_TYPE: rule.type,
80
+ HOOK_RULE_TIMEOUT: String(timeout),
81
+ HOOK_EVENT_NAME: context.eventName ?? phase,
82
+ HOOK_EVENT_PAYLOAD: JSON.stringify(context.eventPayload ?? {}),
83
+ HOOK_MATCHER_VALUE: matcherValue,
84
+ HOOK_TOOL_NAME: matcherValue,
85
+ HOOK_TOOL_USE_ID: context.toolUseId ?? '',
47
86
  HOOK_TOOL_INPUT: JSON.stringify(context.toolInput ?? {}),
48
87
  HOOK_TOOL_RESULT: JSON.stringify(context.toolResult ?? ''),
49
88
  HOOK_WORKSPACE_ID: workspaceId,
50
89
  HOOK_PHASE: phase,
90
+ HOOK_TRIGGER_CHECKED_RULES: String(context.checkedRules ?? ''),
91
+ HOOK_TRIGGER_MATCHED_RULES: String(context.matchedRules ?? ''),
92
+ HOOK_TRIGGERED_AT: new Date().toISOString(),
51
93
  }, timeout);
52
94
  }
53
95
  else if (rule.type === 'webhook' && rule.url) {
54
96
  await executeWebhook(rule.url, {
55
97
  event: phase,
56
- toolName,
98
+ toolName: matcherValue,
99
+ matcherValue,
57
100
  toolInput: context.toolInput,
58
101
  toolResult: context.toolResult ?? undefined,
102
+ payload: context.eventPayload,
59
103
  timestamp: new Date().toISOString(),
60
104
  workspaceId,
61
105
  }, timeout);
62
106
  }
63
107
  else if (rule.type === 'script') {
64
- console.warn(`[HookEngine] script type not implemented, skipping rule in ${phase}`);
108
+ console.warn(`[HookEngine] script type not implemented, skipping rule`, {
109
+ workspaceId,
110
+ phase,
111
+ hookName,
112
+ matcherValue,
113
+ matcher: rule.matcher,
114
+ });
65
115
  }
116
+ else {
117
+ console.warn(`[HookEngine] invalid rule configuration, skipping rule`, {
118
+ workspaceId,
119
+ phase,
120
+ hookName,
121
+ matcherValue,
122
+ matcher: rule.matcher,
123
+ type: rule.type,
124
+ hasCommand: Boolean(rule.command),
125
+ hasUrl: Boolean(rule.url),
126
+ });
127
+ }
128
+ logDebug('rule finished', {
129
+ workspaceId,
130
+ phase,
131
+ hookName,
132
+ matcherValue,
133
+ matcher: rule.matcher,
134
+ type: rule.type,
135
+ durationMs: Date.now() - startedAt,
136
+ });
66
137
  }
67
138
  export class HookEngine {
68
139
  hooks = [];
@@ -72,6 +143,12 @@ export class HookEngine {
72
143
  }
73
144
  load() {
74
145
  this.hooks = listHooks(this.workspaceId);
146
+ logDebug('loaded hooks', {
147
+ workspaceId: this.workspaceId,
148
+ totalHooks: this.hooks.length,
149
+ enabledHooks: this.hooks.filter(h => h.enabled).length,
150
+ hookNames: this.hooks.map(h => h.name),
151
+ });
75
152
  }
76
153
  reload() {
77
154
  this.load();
@@ -79,40 +156,121 @@ export class HookEngine {
79
156
  getHooks() {
80
157
  return this.hooks;
81
158
  }
82
- async executeHooks(phase, toolName, context) {
159
+ async executeHooks(phase, matcherValue, context) {
83
160
  const enabledHooks = this.hooks.filter(h => h.enabled);
84
161
  const promises = [];
162
+ const matchedRuleEntries = [];
163
+ let checkedRules = 0;
164
+ let matchedRules = 0;
165
+ logDebug('trigger received', {
166
+ workspaceId: this.workspaceId,
167
+ phase,
168
+ matcherValue,
169
+ totalHooks: this.hooks.length,
170
+ enabledHooks: enabledHooks.length,
171
+ hasToolInput: context.toolInput !== undefined,
172
+ hasToolResult: context.toolResult !== undefined,
173
+ });
85
174
  for (const hook of enabledHooks) {
86
175
  const rules = hook.hooks[phase];
87
176
  if (!rules)
88
177
  continue;
89
178
  for (const rule of rules) {
90
- if (matchToolName(rule.matcher, toolName)) {
91
- promises.push(executeRule(rule, phase, toolName, this.workspaceId, context));
179
+ checkedRules += 1;
180
+ if (matchToolName(rule.matcher, matcherValue)) {
181
+ matchedRules += 1;
182
+ logDebug('rule matched', {
183
+ workspaceId: this.workspaceId,
184
+ phase,
185
+ hookName: hook.name,
186
+ matcherValue,
187
+ matcher: rule.matcher,
188
+ type: rule.type,
189
+ });
190
+ matchedRuleEntries.push({ hookName: hook.name, rule });
92
191
  }
93
192
  }
94
193
  }
95
- await Promise.allSettled(promises);
194
+ for (const { hookName, rule } of matchedRuleEntries) {
195
+ promises.push(executeRule(rule, phase, matcherValue, this.workspaceId, {
196
+ ...context,
197
+ checkedRules,
198
+ matchedRules,
199
+ }, hookName));
200
+ }
201
+ const results = await Promise.allSettled(promises);
202
+ const rejectedRules = results.filter(result => result.status === 'rejected').length;
203
+ logDebug('trigger finished', {
204
+ workspaceId: this.workspaceId,
205
+ phase,
206
+ matcherValue,
207
+ checkedRules,
208
+ matchedRules,
209
+ rejectedRules,
210
+ });
96
211
  }
97
212
  }
98
213
  export function wrapOnEventWithHooks(onEvent, workspaceId, hooksEnabled) {
99
- if (!hooksEnabled)
214
+ if (!hooksEnabled) {
215
+ logDebug('hooks disabled for workspace', { workspaceId });
100
216
  return onEvent;
217
+ }
101
218
  const engine = new HookEngine(workspaceId);
102
219
  engine.load();
103
220
  const toolNameById = new Map();
221
+ let lastToolUse = null;
104
222
  return (event) => {
105
- onEvent(event);
223
+ if (event.type !== 'hook_event')
224
+ onEvent(event);
106
225
  if (event.type === 'tool_use') {
107
226
  toolNameById.set(event.id, event.name);
108
- engine.executeHooks('PreToolUse', event.name, { toolInput: event.input });
227
+ lastToolUse = { id: event.id, name: event.name };
228
+ logDebug('tool_use event mapped', {
229
+ workspaceId,
230
+ toolUseId: event.id,
231
+ toolName: event.name,
232
+ });
233
+ engine.executeHooks('PreToolUse', event.name, { toolInput: event.input, toolUseId: event.id });
109
234
  }
110
235
  if (event.type === 'tool_result') {
111
- const toolName = event.toolUseId ? (toolNameById.get(event.toolUseId) ?? '') : '';
236
+ const resolvedToolUseId = event.toolUseId || lastToolUse?.id;
237
+ const toolName = resolvedToolUseId ? (toolNameById.get(resolvedToolUseId) ?? '') : '';
112
238
  if (toolName) {
113
- engine.executeHooks('PostToolUse', toolName, { toolResult: event.result });
239
+ logDebug('tool_result event resolved', {
240
+ workspaceId,
241
+ toolUseId: resolvedToolUseId,
242
+ originalToolUseId: event.toolUseId,
243
+ toolName,
244
+ });
245
+ engine.executeHooks('PostToolUse', toolName, { toolResult: event.result, toolUseId: resolvedToolUseId });
246
+ if (isToolFailureResult(event.result)) {
247
+ engine.executeHooks('PostToolUseFailure', toolName, { toolResult: event.result, toolUseId: resolvedToolUseId });
248
+ }
249
+ }
250
+ else {
251
+ logDebug('tool_result event skipped without matching tool_use', {
252
+ workspaceId,
253
+ toolUseId: event.toolUseId,
254
+ });
114
255
  }
115
256
  }
257
+ if (event.type === 'hook_event') {
258
+ logDebug('hook_event received', {
259
+ workspaceId,
260
+ event: event.event,
261
+ matcher: event.matcher ?? '*',
262
+ });
263
+ engine.executeHooks(event.event, event.matcher ?? '*', {
264
+ eventName: event.event,
265
+ eventPayload: event.payload,
266
+ });
267
+ }
116
268
  };
117
269
  }
270
+ function isToolFailureResult(result) {
271
+ if (!result || typeof result !== 'object')
272
+ return false;
273
+ const record = result;
274
+ return record.error !== undefined || record.is_error === true || record.success === false;
275
+ }
118
276
  //# sourceMappingURL=hook-engine.js.map
@@ -28,7 +28,8 @@ export async function runBotAgent(workspaceId, preset, message) {
28
28
  const workingDir = agentService.resolveWorkingDir(workspaceId, preset);
29
29
  const workspace = workspaceService.getById(workspaceId);
30
30
  try {
31
- const result = await runtime.execute(prependPersistentAgentContext(buildBotPrompt(message), {
31
+ const userPrompt = buildBotPrompt(message);
32
+ const result = await runtime.execute(prependPersistentAgentContext(userPrompt, {
32
33
  workspaceId,
33
34
  workingDir,
34
35
  boundDirs: workspace?.boundDirs,
@@ -36,6 +37,7 @@ export async function runBotAgent(workspaceId, preset, message) {
36
37
  excludeNativeClaudeMd: preset.runtimeKind === 'claude-code',
37
38
  }), workingDir, {
38
39
  maxTurns: 20,
40
+ userPrompt,
39
41
  mcpServers: agentService.getMcpServers(preset.mcps),
40
42
  skills: agentService.getAvailableSkillNames(agentService.getAgentConfigDir(workspaceId, preset), preset.skills),
41
43
  configDir: agentService.getAgentConfigDir(workspaceId, preset),
@@ -1,4 +1,5 @@
1
1
  import * as workspaceService from '../workspace.js';
2
+ import { resolveCredentials } from '../robot-account.js';
2
3
  import { adapters } from './types.js';
3
4
  import { persistServiceRunning } from './helpers.js';
4
5
  import { WeChatNotificationAdapter } from './wechat-adapter.js';
@@ -9,15 +10,24 @@ export async function startWorkspaceNotificationService(workspaceId) {
9
10
  if (!workspace || !settings?.enabled)
10
11
  return { started: false };
11
12
  await stopWorkspaceNotificationService(workspaceId);
13
+ const credentials = resolveCredentials(settings);
12
14
  if (settings.provider === 'lark') {
13
- const adapter = new LarkNotificationAdapter(workspace, settings);
15
+ if (!credentials || credentials.type !== 'lark') {
16
+ throw new Error('Lark credentials not found. Link a Robot Account or configure appId/appSecret.');
17
+ }
18
+ const mergedSettings = { ...settings, lark: { ...settings.lark, appId: credentials.appId, appSecret: credentials.appSecret } };
19
+ const adapter = new LarkNotificationAdapter(workspace, mergedSettings);
14
20
  await adapter.start();
15
21
  adapters.set(workspaceId, adapter);
16
22
  persistServiceRunning(workspaceId, true);
17
23
  return { started: true, provider: 'lark' };
18
24
  }
19
25
  if (settings.provider === 'wechat') {
20
- const adapter = new WeChatNotificationAdapter(workspace, settings);
26
+ if (!credentials || credentials.type !== 'wechat') {
27
+ throw new Error('WeChat credentials not found. Link a Robot Account or scan QR code first.');
28
+ }
29
+ const mergedSettings = { ...settings, wechat: { ...settings.wechat, token: credentials.token, baseUrl: credentials.baseUrl, accountId: credentials.accountId, userId: credentials.userId } };
30
+ const adapter = new WeChatNotificationAdapter(workspace, mergedSettings);
21
31
  await adapter.start();
22
32
  adapters.set(workspaceId, adapter);
23
33
  persistServiceRunning(workspaceId, true);
@@ -0,0 +1,34 @@
1
+ import { v4 as uuid } from 'uuid';
2
+ import { listRobotAccounts, getRobotAccount, createRobotAccount, updateRobotAccount, deleteRobotAccount } from '../storage/robot-account-store.js';
3
+ export { listRobotAccounts, getRobotAccount, deleteRobotAccount };
4
+ export function createAccount(input) {
5
+ const now = new Date().toISOString();
6
+ const account = { id: uuid(), name: input.name, type: input.type, lark: input.lark, wechat: input.wechat, createdAt: now, updatedAt: now };
7
+ createRobotAccount(account);
8
+ return account;
9
+ }
10
+ export function updateAccount(id, patch) {
11
+ return updateRobotAccount(id, patch);
12
+ }
13
+ export function resolveCredentials(settings) {
14
+ if (settings.robotAccountId) {
15
+ const account = getRobotAccount(settings.robotAccountId);
16
+ if (account) {
17
+ if (account.type === 'lark' && account.lark) {
18
+ return { type: 'lark', appId: account.lark.appId, appSecret: account.lark.appSecret };
19
+ }
20
+ if (account.type === 'wechat' && account.wechat) {
21
+ return { type: 'wechat', token: account.wechat.token, baseUrl: account.wechat.baseUrl || 'https://qyapi.weixin.qq.com', accountId: account.wechat.accountId, userId: account.wechat.userId };
22
+ }
23
+ }
24
+ }
25
+ // fallback to inline credentials
26
+ if (settings.provider === 'lark' && settings.lark?.appId && settings.lark?.appSecret) {
27
+ return { type: 'lark', appId: settings.lark.appId, appSecret: settings.lark.appSecret };
28
+ }
29
+ if (settings.provider === 'wechat' && settings.wechat?.token && settings.wechat?.accountId) {
30
+ return { type: 'wechat', token: settings.wechat.token, baseUrl: settings.wechat.baseUrl || 'https://qyapi.weixin.qq.com', accountId: settings.wechat.accountId, userId: settings.wechat.userId };
31
+ }
32
+ return null;
33
+ }
34
+ //# sourceMappingURL=robot-account.js.map
@@ -59,6 +59,8 @@ export function update(id, data) {
59
59
  allowed.autoProcessIssues = data.autoProcessIssues;
60
60
  if (Object.hasOwn(data, 'notificationSettings'))
61
61
  allowed.notificationSettings = data.notificationSettings;
62
+ if (Object.hasOwn(data, 'hooksEnabled'))
63
+ allowed.hooksEnabled = data.hooksEnabled;
62
64
  Object.assign(ws, allowed, { updatedAt: new Date().toISOString() });
63
65
  updateWorkspace(ws);
64
66
  return ws;
@@ -0,0 +1,39 @@
1
+ import { join } from 'node:path';
2
+ import { readJsonFile, writeJsonFile, getDataDir } from './json-store.js';
3
+ const FILE = () => join(getDataDir(), 'robot-accounts.json');
4
+ function read() {
5
+ return readJsonFile(FILE()) ?? { accounts: [] };
6
+ }
7
+ function write(data) {
8
+ writeJsonFile(FILE(), data);
9
+ }
10
+ export function listRobotAccounts() {
11
+ return read().accounts;
12
+ }
13
+ export function getRobotAccount(id) {
14
+ return read().accounts.find((a) => a.id === id);
15
+ }
16
+ export function createRobotAccount(account) {
17
+ const data = read();
18
+ data.accounts.push(account);
19
+ write(data);
20
+ }
21
+ export function updateRobotAccount(id, patch) {
22
+ const data = read();
23
+ const idx = data.accounts.findIndex((a) => a.id === id);
24
+ if (idx === -1)
25
+ return null;
26
+ data.accounts[idx] = { ...data.accounts[idx], ...patch, id, updatedAt: new Date().toISOString() };
27
+ write(data);
28
+ return data.accounts[idx];
29
+ }
30
+ export function deleteRobotAccount(id) {
31
+ const data = read();
32
+ const before = data.accounts.length;
33
+ data.accounts = data.accounts.filter((a) => a.id !== id);
34
+ if (data.accounts.length === before)
35
+ return false;
36
+ write(data);
37
+ return true;
38
+ }
39
+ //# sourceMappingURL=robot-account-store.js.map