agentworkshop 0.7.5 → 0.7.7

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 (519) hide show
  1. package/.AgentWorkShop/prompts/host-tools.json +1 -1
  2. package/.output/.AgentWorkShop/prompts/host-tools.json +1 -1
  3. package/.output/nitro.json +1 -1
  4. package/.output/public/_nuxt/{rzGB_ojX.js → 3612gD2V.js} +2 -2
  5. package/.output/public/_nuxt/8eIQAVlb.js +1 -0
  6. package/.output/public/_nuxt/Ah9qOv1m.js +1 -0
  7. package/.output/public/_nuxt/{DdPeifHR.js → B4Fia4FB.js} +1 -1
  8. package/.output/public/_nuxt/B7aqqzkY.js +1 -0
  9. package/.output/public/_nuxt/BJ-fxrSw.js +1 -0
  10. package/.output/public/_nuxt/{DPDqAD1e.js → BSXx2vqR.js} +1 -1
  11. package/.output/public/_nuxt/{CdSgfcYR.js → BVZjSVs2.js} +1 -1
  12. package/.output/public/_nuxt/{DP6bdyIp.js → BVzDoF8t.js} +1 -1
  13. package/.output/public/_nuxt/BYL0Rjqj.js +7 -0
  14. package/.output/public/_nuxt/{CXmkVXS5.js → BjVLsC3M.js} +1 -1
  15. package/.output/public/_nuxt/{7ZABjMRh2.js → BkfchkK62.js} +1 -1
  16. package/.output/public/_nuxt/BnVoQ74r.js +1 -0
  17. package/.output/public/_nuxt/Bnp4vIAf.js +1 -0
  18. package/.output/public/_nuxt/BsMpJf7S.js +70 -0
  19. package/.output/public/_nuxt/{Z8k0uMSI.js → Bt_HDizI.js} +1 -1
  20. package/.output/public/_nuxt/Bu-P92Pe.js +18 -0
  21. package/.output/public/_nuxt/ButLXkjo.js +2 -0
  22. package/.output/public/_nuxt/C-GarxYC.js +1 -0
  23. package/.output/public/_nuxt/{D8DDDzV62.js → C2iWjzEC2.js} +1 -1
  24. package/.output/public/_nuxt/{HWXLx5Hz.js → C8dxXc6K.js} +5 -5
  25. package/.output/public/_nuxt/CGdPdJPP.js +1 -0
  26. package/.output/public/_nuxt/{CyygA0q4.js → CQiKcuoG.js} +4 -4
  27. package/.output/public/_nuxt/{CN7Zh8GQ.js → CRGlf1Gb.js} +1 -1
  28. package/.output/public/_nuxt/{DSBfvCi5.js → CblLEiCZ.js} +7 -7
  29. package/.output/public/_nuxt/Cd15nPnG2.js +1 -0
  30. package/.output/public/_nuxt/{OwJ1LJkF.js → CsCpu2Zh.js} +1 -1
  31. package/.output/public/_nuxt/CvbHIGQY2.js +1 -0
  32. package/.output/public/_nuxt/Cyk3du4K2.js +1 -0
  33. package/.output/public/_nuxt/{DVQAz9WI.js → D4qXvjuV.js} +2 -2
  34. package/.output/public/_nuxt/{CfIXY-ET.js → DCEyMaN-.js} +1 -1
  35. package/.output/public/_nuxt/{BFYNhG9S.js → DCXIGxzT.js} +1 -1
  36. package/.output/public/_nuxt/{B9aPoQvt.js → DM8DS9MA.js} +1 -1
  37. package/.output/public/_nuxt/{ChnfyQHz.js → DNzaswei.js} +1 -1
  38. package/.output/public/_nuxt/{CrFGZYVB2.js → DYZBdSSg2.js} +1 -1
  39. package/.output/public/_nuxt/{dObsRdSQ.js → DZk24ego.js} +1 -1
  40. package/.output/public/_nuxt/DeYsUzhr.js +1 -0
  41. package/.output/public/_nuxt/Dk6pq9JB2.js +1 -0
  42. package/.output/public/_nuxt/{ByGNWgAr.js → Do88WZG2.js} +3 -3
  43. package/.output/public/_nuxt/{R_WxTozV2.js → Du-eSpo2.js} +1 -1
  44. package/.output/public/_nuxt/OmpTerminalPanel.B_Pl5Ynm.css +1 -0
  45. package/.output/public/_nuxt/{Bsc69G4x.js → VnIWJ0ts.js} +3 -3
  46. package/.output/public/_nuxt/_id_.BzKMZnLe.css +1 -0
  47. package/.output/public/_nuxt/_id_.CbpDAWRh.css +1 -0
  48. package/.output/public/_nuxt/{_wsId_.DET_GinP.css → _wsId_.COUzMXpP.css} +1 -1
  49. package/.output/public/_nuxt/agents.BlejnOOC.css +1 -0
  50. package/.output/public/_nuxt/builds/latest.json +1 -1
  51. package/.output/public/_nuxt/builds/meta/14df171d-89cb-43ec-ad6f-9df72d6da7f5.json +1 -0
  52. package/.output/public/_nuxt/daq.zZ8GR433.css +1 -0
  53. package/.output/public/_nuxt/{default.D17kXlD6.css → default.V3_hUJq1.css} +1 -1
  54. package/.output/public/_nuxt/{B4GHHF6d2.js → fQUs58kX2.js} +1 -1
  55. package/.output/public/_nuxt/p7wSGMtc.js +1 -0
  56. package/.output/public/_nuxt/tRBT-plo.js +1 -0
  57. package/.output/server/chunks/_/SearchOutlined.mjs +4 -4
  58. package/.output/server/chunks/_/SearchOutlined.mjs.map +1 -1
  59. package/.output/server/chunks/_/character-asset.repo.mjs +3 -6
  60. package/.output/server/chunks/_/character-asset.repo.mjs.map +1 -1
  61. package/.output/server/chunks/_/host-bindings.mjs +1 -1
  62. package/.output/server/chunks/_/nitro.mjs +4967 -2035
  63. package/.output/server/chunks/_/nitro.mjs.map +1 -1
  64. package/.output/server/chunks/_/scene-layout.repo.mjs +4 -10
  65. package/.output/server/chunks/_/scene-layout.repo.mjs.map +1 -1
  66. package/.output/server/chunks/build/{Col-D7fjGDF6.mjs → Col-biaNnEPf.mjs} +2 -2
  67. package/.output/server/chunks/build/{Col-D7fjGDF6.mjs.map → Col-biaNnEPf.mjs.map} +1 -1
  68. package/.output/server/chunks/build/{OmpTerminalPanel-B_SYhmgb.mjs → OmpTerminalPanel-COphA1br.mjs} +20 -19
  69. package/.output/server/chunks/build/OmpTerminalPanel-COphA1br.mjs.map +1 -0
  70. package/.output/server/chunks/build/{TownView-Bl8Gkcxi.mjs → TownView-B5KifIaT.mjs} +4 -4
  71. package/.output/server/chunks/build/{TownView-Bl8Gkcxi.mjs.map → TownView-B5KifIaT.mjs.map} +1 -1
  72. package/.output/server/chunks/build/{_id_-GnRJPykX.mjs → _id_-C-ewLgfl.mjs} +119 -119
  73. package/.output/server/chunks/build/{_id_-GnRJPykX.mjs.map → _id_-C-ewLgfl.mjs.map} +1 -1
  74. package/.output/server/chunks/build/{_id_-CWLFGZjV.mjs → _id_-Ct4Jhvl1.mjs} +42 -38
  75. package/.output/server/chunks/build/_id_-Ct4Jhvl1.mjs.map +1 -0
  76. package/.output/server/chunks/build/{_wsId_-BhQMjyHG.mjs → _wsId_-CanI_6Su.mjs} +591 -53
  77. package/.output/server/chunks/build/{_wsId_-BhQMjyHG.mjs.map → _wsId_-CanI_6Su.mjs.map} +1 -1
  78. package/.output/server/chunks/build/{agents-D4l1icvn.mjs → agents-DSOptxXA.mjs} +146 -91
  79. package/.output/server/chunks/build/agents-DSOptxXA.mjs.map +1 -0
  80. package/.output/server/chunks/build/{alert-gH-s7yIh.mjs → alert-qJnjyRcP.mjs} +3 -3
  81. package/.output/server/chunks/build/{alert-gH-s7yIh.mjs.map → alert-qJnjyRcP.mjs.map} +1 -1
  82. package/.output/server/chunks/build/{button-BhbX5ACI.mjs → button-DEcZFcqN.mjs} +3 -3
  83. package/.output/server/chunks/build/{button-BhbX5ACI.mjs.map → button-DEcZFcqN.mjs.map} +1 -1
  84. package/.output/server/chunks/build/{card-l4BbX67w.mjs → card-Dipvaykh.mjs} +5 -5
  85. package/.output/server/chunks/build/{card-l4BbX67w.mjs.map → card-Dipvaykh.mjs.map} +1 -1
  86. package/.output/server/chunks/build/{channel-templates-DqjtC-R0.mjs → channel-templates-BK0KuPOd.mjs} +13 -12
  87. package/.output/server/chunks/build/channel-templates-BK0KuPOd.mjs.map +1 -0
  88. package/.output/server/chunks/build/{checkbox-BF11p3dH.mjs → checkbox-dzcpU_Cd.mjs} +4 -4
  89. package/.output/server/chunks/build/{checkbox-BF11p3dH.mjs.map → checkbox-dzcpU_Cd.mjs.map} +1 -1
  90. package/.output/server/chunks/build/{daq-BOgpn9sl.mjs → daq-D9RA_uze.mjs} +102 -102
  91. package/.output/server/chunks/build/{daq-BOgpn9sl.mjs.map → daq-D9RA_uze.mjs.map} +1 -1
  92. package/.output/server/chunks/build/dcw-BTM5YU-B.mjs +1 -1
  93. package/.output/server/chunks/build/{default-CKJWhkot.mjs → default-BfWGTlbj.mjs} +81 -23
  94. package/.output/server/chunks/build/{default-CKJWhkot.mjs.map → default-BfWGTlbj.mjs.map} +1 -1
  95. package/.output/server/chunks/build/{drawer-CVValqoh.mjs → drawer-Cmh90erI.mjs} +3 -3
  96. package/.output/server/chunks/build/{drawer-CVValqoh.mjs.map → drawer-Cmh90erI.mjs.map} +1 -1
  97. package/.output/server/chunks/build/{dropdown-ByCsEWlQ.mjs → dropdown-CGQ0KAzA.mjs} +2 -2
  98. package/.output/server/chunks/build/{dropdown-ByCsEWlQ.mjs.map → dropdown-CGQ0KAzA.mjs.map} +1 -1
  99. package/.output/server/chunks/build/{dropdown-Dex8PXu6.mjs → dropdown-vhn1NPFs.mjs} +4 -4
  100. package/.output/server/chunks/build/{dropdown-Dex8PXu6.mjs.map → dropdown-vhn1NPFs.mjs.map} +1 -1
  101. package/.output/server/chunks/build/{input-number-BBlRoAJ7.mjs → input-number-Bd55elNN.mjs} +5 -5
  102. package/.output/server/chunks/build/{input-number-BBlRoAJ7.mjs.map → input-number-Bd55elNN.mjs.map} +1 -1
  103. package/.output/server/chunks/build/{input--KMeLYqZ.mjs → input-sf_BT6o6.mjs} +3 -3
  104. package/.output/server/chunks/build/{input--KMeLYqZ.mjs.map → input-sf_BT6o6.mjs.map} +1 -1
  105. package/.output/server/chunks/build/logs-Dyk0I2I9.mjs +1 -1
  106. package/.output/server/chunks/build/{menu-xN-SXb9Q.mjs → menu-WCrlpYVh.mjs} +3 -3
  107. package/.output/server/chunks/build/{menu-xN-SXb9Q.mjs.map → menu-WCrlpYVh.mjs.map} +1 -1
  108. package/.output/server/chunks/build/{modal-DrCOcjrl.mjs → modal-yvUquPj5.mjs} +4 -4
  109. package/.output/server/chunks/build/{modal-DrCOcjrl.mjs.map → modal-yvUquPj5.mjs.map} +1 -1
  110. package/.output/server/chunks/build/{monitor-KW8WLOJd.mjs → monitor-MIOvsyLa.mjs} +21 -21
  111. package/.output/server/chunks/build/{monitor-KW8WLOJd.mjs.map → monitor-MIOvsyLa.mjs.map} +1 -1
  112. package/.output/server/chunks/build/pages-DR2bzczM.mjs +1 -1
  113. package/.output/server/chunks/build/{plugins-B-ZNey0Z.mjs → plugins-DnOBIWGg.mjs} +7 -7
  114. package/.output/server/chunks/build/{plugins-B-ZNey0Z.mjs.map → plugins-DnOBIWGg.mjs.map} +1 -1
  115. package/.output/server/chunks/build/{popconfirm-C5H1Qoh3.mjs → popconfirm-CRaHcz5O.mjs} +4 -4
  116. package/.output/server/chunks/build/{popconfirm-C5H1Qoh3.mjs.map → popconfirm-CRaHcz5O.mjs.map} +1 -1
  117. package/.output/server/chunks/build/{useWorkshopApi-Bh5VUHWu.mjs → segmented-DPe9ZzLO.mjs} +7 -78
  118. package/.output/server/chunks/build/segmented-DPe9ZzLO.mjs.map +1 -0
  119. package/.output/server/chunks/build/{select-D7H2ysgT.mjs → select-D-CeE5vY.mjs} +3 -3
  120. package/.output/server/chunks/build/{select-D7H2ysgT.mjs.map → select-D-CeE5vY.mjs.map} +1 -1
  121. package/.output/server/chunks/build/{settings-Cr06lbRy.mjs → settings-DA8IkGLo.mjs} +11 -11
  122. package/.output/server/chunks/build/{settings-Cr06lbRy.mjs.map → settings-DA8IkGLo.mjs.map} +1 -1
  123. package/.output/server/chunks/build/{space-DCwIlnkI.mjs → space-CCBYAkXt.mjs} +2 -2
  124. package/.output/server/chunks/build/{space-DCwIlnkI.mjs.map → space-CCBYAkXt.mjs.map} +1 -1
  125. package/.output/server/chunks/build/{switch-VC0m9ede.mjs → switch-CelRXpcy.mjs} +3 -3
  126. package/.output/server/chunks/build/{switch-VC0m9ede.mjs.map → switch-CelRXpcy.mjs.map} +1 -1
  127. package/.output/server/chunks/build/{table-BksLg3Q_.mjs → table-Cswq-l2r.mjs} +10 -10
  128. package/.output/server/chunks/build/{table-BksLg3Q_.mjs.map → table-Cswq-l2r.mjs.map} +1 -1
  129. package/.output/server/chunks/build/{tabs-CzUfD3iX.mjs → tabs-BM3tAh_J.mjs} +5 -5
  130. package/.output/server/chunks/build/{tabs-CzUfD3iX.mjs.map → tabs-BM3tAh_J.mjs.map} +1 -1
  131. package/.output/server/chunks/build/{tag-oQI3VtqM.mjs → tag-NO9jEqKy.mjs} +4 -4
  132. package/.output/server/chunks/build/{tag-oQI3VtqM.mjs.map → tag-NO9jEqKy.mjs.map} +1 -1
  133. package/.output/server/chunks/build/{teams-QR3BBF7A.mjs → teams-BhSKBBBM.mjs} +15 -14
  134. package/.output/server/chunks/build/teams-BhSKBBBM.mjs.map +1 -0
  135. package/.output/server/chunks/build/{tokens-DFvr_TLj.mjs → tokens-D_eyxmcv.mjs} +16 -16
  136. package/.output/server/chunks/build/{tokens-DFvr_TLj.mjs.map → tokens-D_eyxmcv.mjs.map} +1 -1
  137. package/.output/server/chunks/build/town-BUqRobze.mjs +1 -1
  138. package/.output/server/chunks/build/{town-_4tEEEVW.mjs → town-DPtOPRRO.mjs} +4 -4
  139. package/.output/server/chunks/build/{town-_4tEEEVW.mjs.map → town-DPtOPRRO.mjs.map} +1 -1
  140. package/.output/server/chunks/build/useLocalTime-9a4B-aHv.mjs +42 -0
  141. package/.output/server/chunks/build/useLocalTime-9a4B-aHv.mjs.map +1 -0
  142. package/.output/server/chunks/build/useWorkshopApi-D2gq06N2.mjs +81 -0
  143. package/.output/server/chunks/build/useWorkshopApi-D2gq06N2.mjs.map +1 -0
  144. package/.output/server/chunks/build/useWorkshopWs-B3j5wIAK.mjs +1 -1
  145. package/.output/server/chunks/build/user-Dk-ytEUV.mjs +1 -1
  146. package/.output/server/chunks/build/{users-4FZ0yFUO.mjs → users-CmVvb1oq.mjs} +16 -16
  147. package/.output/server/chunks/build/{users-4FZ0yFUO.mjs.map → users-CmVvb1oq.mjs.map} +1 -1
  148. package/.output/server/chunks/build/{workshop-k0XZTFKg.mjs → workshop-iCzNRifn.mjs} +9 -9
  149. package/.output/server/chunks/build/{workshop-k0XZTFKg.mjs.map → workshop-iCzNRifn.mjs.map} +1 -1
  150. package/.output/server/chunks/build/workspaces-IfORCDJE.mjs +1 -1
  151. package/.output/server/chunks/routes/api/health.get.mjs +1 -1
  152. package/.output/server/chunks/routes/api/index.get.mjs +1 -1
  153. package/.output/server/chunks/routes/api/index.post.mjs +1 -1
  154. package/.output/server/chunks/routes/api/mcp/workshop.mjs +1 -1
  155. package/.output/server/chunks/routes/api/metrics.mjs +1 -1
  156. package/.output/server/chunks/routes/api/plugins/_name/_...path_.mjs +1 -1
  157. package/.output/server/chunks/routes/api/plugins/client/_name_.get.mjs +1 -1
  158. package/.output/server/chunks/routes/api/plugins/manifest.get.mjs +1 -1
  159. package/.output/server/chunks/routes/api/system/config.get.mjs +1 -1
  160. package/.output/server/chunks/routes/api/system/index.get.mjs +3 -3
  161. package/.output/server/chunks/routes/api/system/index.get.mjs.map +1 -1
  162. package/.output/server/chunks/routes/api/system/index.patch.mjs +1 -1
  163. package/.output/server/chunks/routes/api/system/monitor/terminal/ws.mjs +1 -1
  164. package/.output/server/chunks/routes/api/system/monitor/terminate.post.mjs +1 -1
  165. package/.output/server/chunks/routes/api/system/monitor.get.mjs +1 -1
  166. package/.output/server/chunks/routes/api/system/settings/events.get.mjs +1 -1
  167. package/.output/server/chunks/routes/api/system/settings/reset.post.mjs +1 -1
  168. package/.output/server/chunks/routes/api/users/_id_.delete.mjs +1 -1
  169. package/.output/server/chunks/routes/api/users/_id_.get.mjs +1 -1
  170. package/.output/server/chunks/routes/api/users/_id_.put.mjs +1 -1
  171. package/.output/server/chunks/routes/api/users/index.get.mjs +1 -1
  172. package/.output/server/chunks/routes/api/users/index.post.mjs +1 -1
  173. package/.output/server/chunks/routes/api/users/login.post.mjs +1 -1
  174. package/.output/server/chunks/routes/api/users/logout.post.mjs +1 -1
  175. package/.output/server/chunks/routes/api/users/me.get.mjs +1 -1
  176. package/.output/server/chunks/routes/api/users/register.post.mjs +1 -1
  177. package/.output/server/chunks/routes/api/users/tokens/_id/token.get.mjs +1 -1
  178. package/.output/server/chunks/routes/api/users/tokens/_id_.delete.mjs +1 -1
  179. package/.output/server/chunks/routes/api/users/tokens/_id_.patch.mjs +1 -1
  180. package/.output/server/chunks/routes/api/workshop/_...path_.mjs +1 -1
  181. package/.output/server/chunks/routes/api/workshop/a2a/_agentId/index.get.mjs +1 -1
  182. package/.output/server/chunks/routes/api/workshop/a2a/_agentId/index.post.mjs +1 -1
  183. package/.output/server/chunks/routes/api/workshop/a2a/send.post.mjs +1 -1
  184. package/.output/server/chunks/routes/api/workshop/agent-tools/approvals/_id/decide.post.mjs +1 -1
  185. package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/_id_.delete.mjs +1 -1
  186. package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/_id_.patch.mjs +1 -1
  187. package/.output/server/chunks/routes/api/workshop/agent-tools/index.get.mjs +1 -1
  188. package/.output/server/chunks/routes/api/workshop/agent-tools/index.post.mjs +1 -1
  189. package/.output/server/chunks/routes/api/workshop/agent-tools/index2.get.mjs +1 -1
  190. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs +6 -14
  191. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs.map +1 -1
  192. package/.output/server/chunks/routes/api/workshop/agent-tools/list.get.mjs +39 -0
  193. package/.output/server/chunks/routes/api/workshop/agent-tools/list.get.mjs.map +1 -0
  194. package/.output/server/chunks/routes/api/workshop/agents/_id_.delete.mjs +1 -1
  195. package/.output/server/chunks/routes/api/workshop/agents/_id_.get.mjs +1 -1
  196. package/.output/server/chunks/routes/api/workshop/agents/_id_.patch.mjs +1 -1
  197. package/.output/server/chunks/routes/api/workshop/agents/subscribe.post.mjs +1 -1
  198. package/.output/server/chunks/routes/api/workshop/approvals/_id/decide.post.mjs +1 -1
  199. package/.output/server/chunks/routes/api/workshop/assets/character/_id_.delete.mjs +1 -1
  200. package/.output/server/chunks/routes/api/workshop/assets/devices/_id_.delete.mjs +1 -1
  201. package/.output/server/chunks/routes/api/workshop/assets/index.get.mjs +1 -1
  202. package/.output/server/chunks/routes/api/workshop/assets/index.post.mjs +2 -2
  203. package/.output/server/chunks/routes/api/workshop/assets/index2.get.mjs +1 -1
  204. package/.output/server/chunks/routes/api/workshop/assets/index2.post.mjs +2 -2
  205. package/.output/server/chunks/routes/api/workshop/audit.get.mjs +1 -1
  206. package/.output/server/chunks/routes/api/workshop/caller.mjs +1 -1
  207. package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +2 -2
  208. package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.delete.mjs +1 -1
  209. package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.get.mjs +1 -1
  210. package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.patch.mjs +1 -1
  211. package/.output/server/chunks/routes/api/workshop/channel-templates/from-channel.post.mjs +1 -1
  212. package/.output/server/chunks/routes/api/workshop/channels/_id/activate.post.mjs +2 -2
  213. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/index.get.mjs +1 -1
  214. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/index.post.mjs +1 -1
  215. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/memories/_memoryId_.delete.mjs +1 -1
  216. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/memories/search.post.mjs +1 -1
  217. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/model.patch.mjs +1 -1
  218. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/position.patch.mjs +1 -1
  219. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/range.patch.mjs +1 -1
  220. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/stop.post.mjs +1 -1
  221. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId_.delete.mjs +1 -1
  222. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId_.patch.mjs +1 -1
  223. package/.output/server/chunks/routes/api/workshop/channels/_id/events.get.mjs +1 -1
  224. package/.output/server/chunks/routes/api/workshop/channels/_id/index.get.mjs +1 -1
  225. package/.output/server/chunks/routes/api/workshop/channels/_id/index.post.mjs +2 -2
  226. package/.output/server/chunks/routes/api/workshop/channels/_id/index2.get.mjs +1 -1
  227. package/.output/server/chunks/routes/api/workshop/channels/_id/index2.post.mjs +1 -1
  228. package/.output/server/chunks/routes/api/workshop/channels/_id/index3.get.mjs +1 -1
  229. package/.output/server/chunks/routes/api/workshop/channels/_id/index3.post.mjs +1 -1
  230. package/.output/server/chunks/routes/api/workshop/channels/_id/index4.get.mjs +1 -1
  231. package/.output/server/chunks/routes/api/workshop/channels/_id/index4.post.mjs +1 -1
  232. package/.output/server/chunks/routes/api/workshop/channels/_id/memories/_memoryId_.delete.mjs +1 -1
  233. package/.output/server/chunks/routes/api/workshop/channels/_id/queue.get.mjs +1 -1
  234. package/.output/server/chunks/routes/api/workshop/channels/_id/terminals.get.mjs +2 -2
  235. package/.output/server/chunks/routes/api/workshop/channels/_id_.delete.mjs +1 -1
  236. package/.output/server/chunks/routes/api/workshop/channels/index.get.mjs +2 -2
  237. package/.output/server/chunks/routes/api/workshop/channels/index.patch.mjs +9 -2
  238. package/.output/server/chunks/routes/api/workshop/channels/index.patch.mjs.map +1 -1
  239. package/.output/server/chunks/routes/api/workshop/channels.get.mjs +1 -1
  240. package/.output/server/chunks/routes/api/workshop/channels.post.mjs +2 -2
  241. package/.output/server/chunks/routes/api/workshop/daq/_id/bind.post.mjs +2 -2
  242. package/.output/server/chunks/routes/api/workshop/daq/_id/frames/content.get.mjs +2 -2
  243. package/.output/server/chunks/routes/api/workshop/daq/_id/frames.get.mjs +2 -2
  244. package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.mjs +6 -4
  245. package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.mjs.map +1 -1
  246. package/.output/server/chunks/routes/api/workshop/daq/_id/test.post.mjs +2 -2
  247. package/.output/server/chunks/routes/api/workshop/daq/_id_.delete.mjs +2 -2
  248. package/.output/server/chunks/routes/api/workshop/daq/_id_.patch.mjs +2 -2
  249. package/.output/server/chunks/routes/api/workshop/daq/alarms/_id/ack.post.mjs +2 -2
  250. package/.output/server/chunks/routes/api/workshop/daq/alarms.get.mjs +2 -2
  251. package/.output/server/chunks/routes/api/workshop/daq/controller.post.mjs +2 -2
  252. package/.output/server/chunks/routes/api/workshop/daq/index.post.mjs +2 -2
  253. package/.output/server/chunks/routes/api/workshop/daq/infra/reconnect.post.mjs +2 -2
  254. package/.output/server/chunks/routes/api/workshop/daq/infra.get.mjs +2 -2
  255. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs +2 -2
  256. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs +2 -2
  257. package/.output/server/chunks/routes/api/workshop/daq/test-driver.post.mjs +2 -2
  258. package/.output/server/chunks/routes/api/workshop/dcw/_id/bind.post.mjs +2 -2
  259. package/.output/server/chunks/routes/api/workshop/dcw/_id/param-ledger.get.mjs +2 -2
  260. package/.output/server/chunks/routes/api/workshop/dcw/_id/read.post.mjs +2 -2
  261. package/.output/server/chunks/routes/api/workshop/dcw/_id/test.post.mjs +2 -2
  262. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +2 -2
  263. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +2 -2
  264. package/.output/server/chunks/routes/api/workshop/dcw/_id_.patch.mjs +2 -2
  265. package/.output/server/chunks/routes/api/workshop/dcw/controller.post.mjs +2 -2
  266. package/.output/server/chunks/routes/api/workshop/dcw/index.get.mjs +2 -2
  267. package/.output/server/chunks/routes/api/workshop/dcw/index.post.mjs +2 -2
  268. package/.output/server/chunks/routes/api/workshop/dcw/index2.get.mjs +2 -2
  269. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +2 -2
  270. package/.output/server/chunks/routes/api/workshop/dcw/index3.get.mjs +2 -2
  271. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +2 -2
  272. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +2 -2
  273. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +2 -2
  274. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +2 -2
  275. package/.output/server/chunks/routes/api/workshop/dcw/journal/node/_nodeId/rollback.post.mjs +2 -2
  276. package/.output/server/chunks/routes/api/workshop/dcw/line/query.get.mjs +2 -2
  277. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +2 -2
  278. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +2 -2
  279. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.delete.mjs +2 -2
  280. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.patch.mjs +2 -2
  281. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/judge.post.mjs +2 -2
  282. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/rollback.post.mjs +2 -2
  283. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/series.get.mjs +2 -2
  284. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id_.get.mjs +2 -2
  285. package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.delete.mjs +2 -2
  286. package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.patch.mjs +2 -2
  287. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/apply.post.mjs +2 -2
  288. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/mark-good.post.mjs +2 -2
  289. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/rollback-good.post.mjs +2 -2
  290. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.delete.mjs +2 -2
  291. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.patch.mjs +2 -2
  292. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/close.post.mjs +2 -2
  293. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/data.get.mjs +2 -2
  294. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/rollback.post.mjs +2 -2
  295. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +2 -2
  296. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +2 -2
  297. package/.output/server/chunks/routes/api/workshop/dcw/test-driver.post.mjs +2 -2
  298. package/.output/server/chunks/routes/api/workshop/device-twins/_id/control.post.mjs +1 -1
  299. package/.output/server/chunks/routes/api/workshop/device-twins/_id/telemetry.post.mjs +1 -1
  300. package/.output/server/chunks/routes/api/workshop/device-twins/_id_.delete.mjs +2 -2
  301. package/.output/server/chunks/routes/api/workshop/device-twins/_id_.patch.mjs +2 -2
  302. package/.output/server/chunks/routes/api/workshop/fs/dirs.get.mjs +1 -1
  303. package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +247 -0
  304. package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs.map +1 -0
  305. package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +27 -0
  306. package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs.map +1 -0
  307. package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
  308. package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +19 -7
  309. package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs.map +1 -1
  310. package/.output/server/chunks/routes/api/workshop/index.get.mjs +1 -1
  311. package/.output/server/chunks/routes/api/workshop/index.post.mjs +1 -1
  312. package/.output/server/chunks/routes/api/workshop/index10.get.mjs +1 -1
  313. package/.output/server/chunks/routes/api/workshop/index2.get.mjs +1 -1
  314. package/.output/server/chunks/routes/api/workshop/index2.post.mjs +1 -1
  315. package/.output/server/chunks/routes/api/workshop/index3.get.mjs +1 -1
  316. package/.output/server/chunks/routes/api/workshop/index3.post.mjs +2 -2
  317. package/.output/server/chunks/routes/api/workshop/index4.get.mjs +2 -2
  318. package/.output/server/chunks/routes/api/workshop/index4.post.mjs +2 -2
  319. package/.output/server/chunks/routes/api/workshop/index5.get.mjs +2 -2
  320. package/.output/server/chunks/routes/api/workshop/index5.post.mjs +2 -2
  321. package/.output/server/chunks/routes/api/workshop/index6.get.mjs +1 -1
  322. package/.output/server/chunks/routes/api/workshop/index6.post.mjs +2 -2
  323. package/.output/server/chunks/routes/api/workshop/index7.get.mjs +1 -1
  324. package/.output/server/chunks/routes/api/workshop/index7.post.mjs +1 -1
  325. package/.output/server/chunks/routes/api/workshop/index8.get.mjs +1 -1
  326. package/.output/server/chunks/routes/api/workshop/index8.post.mjs +1 -1
  327. package/.output/server/chunks/routes/api/workshop/index9.get.mjs +1 -1
  328. package/.output/server/chunks/routes/api/workshop/mailbox/all.get.mjs +1 -1
  329. package/.output/server/chunks/routes/api/workshop/mailbox.get.mjs +1 -1
  330. package/.output/server/chunks/routes/api/workshop/memories/maintenance.post.mjs +1 -1
  331. package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
  332. package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
  333. package/.output/server/chunks/routes/api/workshop/plugins/index.get.mjs +1 -1
  334. package/.output/server/chunks/routes/api/workshop/runtime.get.mjs +1 -1
  335. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
  336. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
  337. package/.output/server/chunks/routes/api/workshop/scene/layouts.get.mjs +1 -1
  338. package/.output/server/chunks/routes/api/workshop/tasks/_id/complete.post.mjs +1 -1
  339. package/.output/server/chunks/routes/api/workshop/tasks/_id/dispatch.post.mjs +1 -1
  340. package/.output/server/chunks/routes/api/workshop/tasks/_id/index.post.mjs +1 -1
  341. package/.output/server/chunks/routes/api/workshop/tasks/_id/report.post.mjs +1 -1
  342. package/.output/server/chunks/routes/api/workshop/tasks/_id/retry.post.mjs +1 -1
  343. package/.output/server/chunks/routes/api/workshop/tasks/_id_.get.mjs +1 -1
  344. package/.output/server/chunks/routes/api/workshop/tasks.get.mjs +1 -1
  345. package/.output/server/chunks/routes/api/workshop/teams/_id/deploy.post.mjs +2 -2
  346. package/.output/server/chunks/routes/api/workshop/teams/_id/index.get.mjs +1 -1
  347. package/.output/server/chunks/routes/api/workshop/teams/_id/index.post.mjs +1 -1
  348. package/.output/server/chunks/routes/api/workshop/teams/_id/members/_agentId_.delete.mjs +1 -1
  349. package/.output/server/chunks/routes/api/workshop/teams/_id_.delete.mjs +1 -1
  350. package/.output/server/chunks/routes/api/workshop/teams/_id_.get.mjs +1 -1
  351. package/.output/server/chunks/routes/api/workshop/teams/_id_.patch.mjs +1 -1
  352. package/.output/server/chunks/routes/api/workshop/users/me.get.mjs +1 -1
  353. package/.output/server/chunks/routes/api/workshop/users/register.post.mjs +1 -1
  354. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +2 -2
  355. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channels/_channelId_.delete.mjs +1 -1
  356. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channels/_channelId_.post.mjs +1 -1
  357. package/.output/server/chunks/routes/api/workshop/workspaces/index.delete.mjs +1 -1
  358. package/.output/server/chunks/routes/renderer.mjs +1 -1
  359. package/.output/server/chunks/virtual/entry.mjs +123 -113
  360. package/.output/server/chunks/virtual/entry.mjs.map +1 -1
  361. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  362. package/.output/server/index.mjs +1 -1
  363. package/.output/server/package.json +1 -1
  364. package/app/components/AppHeader.vue +56 -2
  365. package/app/components/workshop/ChannelSessionList.vue +703 -593
  366. package/app/components/workshop/terminal/OmpTerminalPanel.vue +2 -1
  367. package/app/composables/workshop/useLocalTime.ts +16 -0
  368. package/app/composables/workshop/useSceneLayouts.ts +2 -1
  369. package/app/composables/workshop/useWorkshopApi.ts +249 -224
  370. package/app/pages/daq/[id].vue +7 -6
  371. package/app/pages/daq/index.vue +2 -2
  372. package/app/pages/dcw/[id].vue +3 -3
  373. package/app/pages/workshop/agents.vue +432 -382
  374. package/bin/aw.mjs +0 -0
  375. package/cli/aw.mjs +8 -1
  376. package/cli/commands/stop.mjs +111 -108
  377. package/cli/commands/tui.mjs +10 -1
  378. package/cli/core/registry.mjs +5 -3
  379. package/cli/infra.mjs +108 -109
  380. package/config.yml +134 -134
  381. package/docs/tui.md +1 -1
  382. package/i18n/locales/en.ts +5 -0
  383. package/i18n/locales/zh-CN.ts +5 -0
  384. package/package.json +148 -140
  385. package/scripts/_dbg-catalog-smoke.ts +20 -0
  386. package/scripts/_dbg-codex-probe.mjs +58 -0
  387. package/scripts/_dbg-dsh-probe.mjs +31 -0
  388. package/scripts/_dbg-interval-scenario.mjs +126 -0
  389. package/scripts/_dbg-line-spawn.ts +31 -0
  390. package/scripts/_dbg-live-engine-probe.mjs +80 -0
  391. package/scripts/_dbg-mcp-bridge-probe.mjs +147 -0
  392. package/scripts/_dbg-opencode-sse.mjs +101 -0
  393. package/scripts/_dbg-opencode-start.ts +57 -0
  394. package/scripts/_dbg-plugin-matrix.mjs +212 -0
  395. package/scripts/_dbg-protocol-matrix.mjs +425 -0
  396. package/scripts/_dbg-real-scenario-timeline.log +123 -0
  397. package/scripts/_dbg-registry-smoke.ts +14 -0
  398. package/scripts/_dbg-sim-keeper.mjs +100 -0
  399. package/scripts/_dbg-team-drill.mjs +173 -0
  400. package/scripts/dev-guard.mjs +11 -3
  401. package/scripts/dev-protocol-simulators.mjs +28 -1
  402. package/scripts/e2e-multi-harness.ts +374 -0
  403. package/scripts/e2e-multiharness-team.mjs +319 -0
  404. package/scripts/e2e-plc-scenario.mjs +292 -0
  405. package/scripts/e2e-real-scenario.mjs +452 -0
  406. package/scripts/migrate-local-time.mjs +123 -0
  407. package/scripts/start.mjs +60 -9
  408. package/server/api/system/settings/index.get.ts +3 -3
  409. package/server/api/workshop/agent-tools/invoke.post.ts +27 -41
  410. package/server/api/workshop/agent-tools/list.get.ts +25 -0
  411. package/server/api/workshop/channels/[id]/index.patch.ts +38 -30
  412. package/server/api/workshop/daq/[id]/samples.get.ts +6 -3
  413. package/server/api/workshop/harnesses/[id]/providers.get.ts +16 -0
  414. package/server/api/workshop/harnesses.get.ts +12 -0
  415. package/server/api/workshop/hitl/respond.post.ts +87 -67
  416. package/server/api/workshop/ws.ts +13 -0
  417. package/server/data/daqs.json +1366 -16
  418. package/server/data/dcw-lines.json +266 -0
  419. package/server/data/dcw-products.json +203 -0
  420. package/server/data/dcw-recipes.json +784 -0
  421. package/server/data/dcw-rollback.json +1144 -1
  422. package/server/data/dcw-runs.json +983 -791
  423. package/server/data/dcw-writes.json +1080 -0
  424. package/server/data/dcws.json +3278 -203
  425. package/server/data/line-runs.json +52 -2
  426. package/server/harness/aw-mcp-bridge.mjs +153 -0
  427. package/server/plugins/00-local-time.ts +10 -0
  428. package/server/plugins/aw-plugins.ts +3 -0
  429. package/server/plugins/backup.ts +116 -97
  430. package/server/plugins/dev-stability-guard.ts +24 -15
  431. package/server/plugins/retention.ts +1 -0
  432. package/server/plugins/security.ts +24 -3
  433. package/server/plugins/workshop.ts +156 -153
  434. package/server/repositories/user.repository.ts +414 -391
  435. package/server/services/system-config.ts +30 -6
  436. package/server/services/workshop/.mimosa/hook-state/sess_fed429fb-c8b7-4a50-bbd6-45afc580893f.json +1 -0
  437. package/server/services/workshop/.mimosa/hook-status/sess_fed429fb-c8b7-4a50-bbd6-45afc580893f-e6b75bbfe6.json +14 -0
  438. package/server/services/workshop/agents/adapters/line-spawn.ts +108 -0
  439. package/server/services/workshop/agents/adapters/stdio-jsonrpc.ts +316 -0
  440. package/server/services/workshop/agents/agent-interface.ts +17 -0
  441. package/server/services/workshop/agents/codex-agent.ts +822 -0
  442. package/server/services/workshop/agents/dsh-agent.ts +670 -0
  443. package/server/services/workshop/agents/factory.ts +4 -16
  444. package/server/services/workshop/agents/harness-env.ts +54 -0
  445. package/server/services/workshop/agents/harness-models.ts +270 -0
  446. package/server/services/workshop/agents/host-tool-bridge.ts +592 -0
  447. package/server/services/workshop/agents/industrial-tools.ts +5 -1
  448. package/server/services/workshop/agents/node-bindings.repo.ts +4 -5
  449. package/server/services/workshop/agents/omp-agent.ts +1178 -1957
  450. package/server/services/workshop/agents/opencode-agent.ts +987 -0
  451. package/server/services/workshop/agents/prompt-builder.ts +393 -0
  452. package/server/services/workshop/agents/registry.ts +125 -0
  453. package/server/services/workshop/assets/character-asset.repo.ts +3 -5
  454. package/server/services/workshop/assets/device-twin.repo.ts +3 -5
  455. package/server/services/workshop/daq/daq-controller.ts +8 -4
  456. package/server/services/workshop/daq/daq-node.repo.ts +3 -5
  457. package/server/services/workshop/daq/daq-node.ts +2 -2
  458. package/server/services/workshop/daq/daq-templates.ts +3 -5
  459. package/server/services/workshop/daq/drivers.ts +41 -12
  460. package/server/services/workshop/daq/storage/sqlite.adapter.ts +313 -309
  461. package/server/services/workshop/db/backup-registry.ts +33 -0
  462. package/server/services/workshop/db/channel.repo.ts +104 -101
  463. package/server/services/workshop/db/database.ts +956 -949
  464. package/server/services/workshop/dcw/dcw-controller.ts +3 -1
  465. package/server/services/workshop/dcw/dcw-line.repo.ts +4 -11
  466. package/server/services/workshop/dcw/dcw-node.repo.ts +3 -5
  467. package/server/services/workshop/dcw/dcw-node.ts +13 -1
  468. package/server/services/workshop/dcw/dcw-product.repo.ts +3 -4
  469. package/server/services/workshop/dcw/dcw-recipe.repo.ts +3 -19
  470. package/server/services/workshop/dcw/dcw-templates.ts +3 -5
  471. package/server/services/workshop/dcw/drivers.ts +37 -13
  472. package/server/services/workshop/dcw/line-run.ts +3 -5
  473. package/server/services/workshop/dcw/recipe-rollback.repo.ts +4 -10
  474. package/server/services/workshop/json-store.mjs +72 -0
  475. package/server/services/workshop/plugins/host.mjs +2 -1
  476. package/server/services/workshop/runtime/agent-runtime.ts +44 -0
  477. package/server/services/workshop/runtime/manager.ts +2953 -2868
  478. package/server/services/workshop/scene/scene-layout.repo.ts +4 -11
  479. package/server/services/workshop/settings.ts +7 -0
  480. package/shared/config/schema.json +5 -0
  481. package/shared/config/single-instance.mjs +133 -86
  482. package/shared/local-time.mjs +36 -0
  483. package/shared/workshop-protocol.ts +11 -5
  484. package/tui/aw-tui.mjs +28 -10
  485. package/tui/components/hitl-card.mjs +47 -36
  486. package/tui/lib/config.mjs +62 -58
  487. package/tui/lib/ws.mjs +2 -1
  488. package/.output/public/_nuxt/9X0fVBD7.js +0 -57
  489. package/.output/public/_nuxt/B6M2WJWm2.js +0 -1
  490. package/.output/public/_nuxt/B9Yie5HN.js +0 -2
  491. package/.output/public/_nuxt/BF9XPEuw.js +0 -1
  492. package/.output/public/_nuxt/BbQornXa.js +0 -1
  493. package/.output/public/_nuxt/Bxgaym6j.js +0 -1
  494. package/.output/public/_nuxt/Bxh5nf8j.js +0 -1
  495. package/.output/public/_nuxt/CXLI50Sq.js +0 -1
  496. package/.output/public/_nuxt/CdfAP_IH2.js +0 -1
  497. package/.output/public/_nuxt/Ce1zyNYu.js +0 -1
  498. package/.output/public/_nuxt/D-Ne4C9a.js +0 -1
  499. package/.output/public/_nuxt/DEHNjlTq2.js +0 -1
  500. package/.output/public/_nuxt/DFnGFEav.js +0 -18
  501. package/.output/public/_nuxt/DYWgGHNo.js +0 -7
  502. package/.output/public/_nuxt/OmpTerminalPanel.Bc9GjNU5.css +0 -1
  503. package/.output/public/_nuxt/W1E3-5ML.js +0 -14
  504. package/.output/public/_nuxt/XVB9_zwP.js +0 -1
  505. package/.output/public/_nuxt/Xaf_HIR3.js +0 -1
  506. package/.output/public/_nuxt/_id_.BeqSLvl_.css +0 -1
  507. package/.output/public/_nuxt/_id_.DN3U6EF9.css +0 -1
  508. package/.output/public/_nuxt/agents.BJwgTLFZ.css +0 -1
  509. package/.output/public/_nuxt/builds/meta/55c10854-ee0e-4be4-924b-a1b0e100fe11.json +0 -1
  510. package/.output/public/_nuxt/daq.BXsnevQv.css +0 -1
  511. package/.output/public/_nuxt/yIKY_V1P.js +0 -1
  512. package/.output/server/chunks/build/OmpTerminalPanel-B_SYhmgb.mjs.map +0 -1
  513. package/.output/server/chunks/build/_id_-CWLFGZjV.mjs.map +0 -1
  514. package/.output/server/chunks/build/agents-D4l1icvn.mjs.map +0 -1
  515. package/.output/server/chunks/build/channel-templates-DqjtC-R0.mjs.map +0 -1
  516. package/.output/server/chunks/build/interval-WlxgjTzO.mjs +0 -9
  517. package/.output/server/chunks/build/interval-WlxgjTzO.mjs.map +0 -1
  518. package/.output/server/chunks/build/teams-QR3BBF7A.mjs.map +0 -1
  519. package/.output/server/chunks/build/useWorkshopApi-Bh5VUHWu.mjs.map +0 -1
@@ -1,391 +1,414 @@
1
- import { existsSync } from 'node:fs'
2
- import { join, resolve } from 'node:path'
3
- import { randomBytes, randomUUID, scryptSync, createHash, timingSafeEqual } from 'node:crypto'
4
- import { DatabaseSync } from 'node:sqlite'
5
- import { ensureDataDir } from '@/shared/config/home.mjs'
6
- import type { Paginated, User, UserListQuery, UserToken } from '../types/user'
7
- import type { UserCreate, UserUpdate } from '../schemas/user.schema'
8
-
9
- /**
10
- * 全局用户数据访问层(Repository)—— SQLite 持久化(配置根 .AgentWorkShop/data/users.sqlite;
11
- * repo 模式 <repo>/.AgentWorkShop/data,home 模式 ~/.AgentWorkShop/data,经 ensureDataDir 统一解析
12
- * 并自动迁移旧 cwd/data 位置)。
13
- * - users 表:用户档案 + 密码哈希(scrypt,salt 内嵌,明文不进库)
14
- * - user_tokens 表:每用户多个 API Token(仅存 SHA-256 哈希;明文只在签发时返回一次)
15
- * 进程内单例 DB 懒加载;数据源与业务逻辑解耦,service 层零改动。
16
- */
17
-
18
- // 配置根 .AgentWorkShop/data(repo: <repo>/.AgentWorkShop/data,home: ~/.AgentWorkShop/data;
19
- // ensureDataDir 统一解析并自动迁移旧 cwd/data 位置)
20
- const DB_PATH = join(ensureDataDir(), 'users.sqlite')
21
-
22
- const SCHEMA_SQL = `
23
- CREATE TABLE IF NOT EXISTS users (
24
- id TEXT PRIMARY KEY,
25
- name TEXT NOT NULL,
26
- email TEXT NOT NULL UNIQUE,
27
- password_hash TEXT NOT NULL,
28
- role TEXT NOT NULL DEFAULT 'user',
29
- status TEXT NOT NULL DEFAULT 'active',
30
- created_at TEXT NOT NULL
31
- );
32
- CREATE TABLE IF NOT EXISTS user_tokens (
33
- id TEXT PRIMARY KEY,
34
- user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
35
- label TEXT NOT NULL DEFAULT '',
36
- token_hash TEXT NOT NULL UNIQUE,
37
- token_plain TEXT,
38
- created_at TEXT NOT NULL,
39
- last_used_at TEXT
40
- );
41
- CREATE INDEX IF NOT EXISTS idx_user_tokens_user ON user_tokens(user_id);
42
- `
43
-
44
- /** 种子用户(首次建库时写入;默认密码 Awshop@123,便于开箱登录) */
45
- const SEED_USERS = [
46
- { name: '张伟', email: 'zhangwei@awshop.io', role: 'admin', status: 'active' },
47
- { name: '王芳', email: 'wangfang@awshop.io', role: 'editor', status: 'active' },
48
- { name: '李娜', email: 'lina@awshop.io', role: 'user', status: 'disabled' },
49
- { name: '刘洋', email: 'liuyang@awshop.io', role: 'editor', status: 'active' },
50
- { name: '陈静', email: 'chenjing@awshop.io', role: 'user', status: 'active' },
51
- { name: 'Michael Chen', email: 'michael@awshop.io', role: 'user', status: 'active' },
52
- ]
53
-
54
- let db: DatabaseSync | null = null
55
-
56
- function getDb(): DatabaseSync {
57
- if (!db) {
58
- // 目录由 ensureDataDir 内部创建(含旧位置迁移)
59
- db = new DatabaseSync(DB_PATH)
60
- db.exec(SCHEMA_SQL)
61
- migrateSchema(db)
62
- seedIfEmpty()
63
- importLegacyWorkshopUsers(db)
64
- }
65
- return db
66
- }
67
-
68
- /** 轻量迁移:旧库 user_tokens 补 token_plain 列(明文存档,支持随时查看)+ expires_at(R2 后端过期) */
69
- function migrateSchema(d: DatabaseSync): void {
70
- const cols = d.prepare('PRAGMA table_info(user_tokens)').all() as Array<{ name: string }>
71
- if (!cols.some(c => c.name === 'token_plain')) {
72
- d.exec('ALTER TABLE user_tokens ADD COLUMN token_plain TEXT')
73
- }
74
- if (!cols.some(c => c.name === 'expires_at')) {
75
- d.exec('ALTER TABLE user_tokens ADD COLUMN expires_at TEXT')
76
- }
77
- // 存量 token 宽限 30 天(从部署时刻起算,避免升级即全员掉线)
78
- d.prepare('UPDATE user_tokens SET expires_at = ? WHERE expires_at IS NULL').run(isoInDays(TOKEN_TTL_DAYS))
79
- }
80
-
81
- /** token 有效期(天,R2):过期后 findByToken 拒绝,用户重新登录即可 */
82
- const TOKEN_TTL_DAYS = 30
83
-
84
- /** 当前时刻 + n 天的 ISO 时间戳 */
85
- function isoInDays(days: number): string {
86
- return new Date(Date.now() + days * 86_400_000).toISOString()
87
- }
88
-
89
- /** 掩码预览:前 6 后 4(token 形如 ut-xxxxxxxx…,前 6 含前缀可辨识) */
90
- function maskPreview(raw: string): string {
91
- return `${raw.slice(0, 6)}${'•'.repeat(8)}${raw.slice(-4)}`
92
- }
93
-
94
- /** 首次建库写入种子用户(幂等:users 非空即跳过) */
95
- function seedIfEmpty(): void {
96
- const d = db!
97
- const { n } = d.prepare('SELECT COUNT(*) AS n FROM users').get() as { n: number }
98
- if (n > 0) return
99
- const insert = d.prepare('INSERT INTO users (id, name, email, password_hash, role, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)')
100
- const hash = hashPassword('Awshop@123')
101
- for (const u of SEED_USERS) {
102
- insert.run(randomUUID(), u.name, u.email, hash, u.role, u.status, now())
103
- }
104
- }
105
- /**
106
- * 一次性迁移:旧 workshop 本地用户(data/workshop.sqlite 的 users 表)导入全局用户系统。
107
- * - 保留原 id/name(资源归属 owner_user_id 不变,历史数据不丢)
108
- * - 旧 token 原值重新注册为全局 token(老客户端免重新登录)
109
- * - email 派生 + 随机密码(不可经密码登录,可用管理面重置)
110
- * 幂等:按 id 判断已存在则跳过。
111
- */
112
- function importLegacyWorkshopUsers(d: DatabaseSync): void {
113
- // 迁移后配置根已有 workshop.sqlite;旧 cwd/data 位置兜底(仅历史未迁移场景)
114
- const migrated = join(ensureDataDir(), 'workshop.sqlite')
115
- const wsPath = existsSync(migrated) ? migrated : resolve(process.cwd(), 'data', 'workshop.sqlite')
116
- if (!existsSync(wsPath)) return
117
- let wdb: DatabaseSync
118
- try {
119
- wdb = new DatabaseSync(wsPath, { readOnly: true })
120
- }
121
- catch {
122
- return // workshop 库尚不存在或不可读:跳过
123
- }
124
- let rows: Array<{ id: string, name: string, token: string, created_at: string }>
125
- try {
126
- rows = wdb.prepare('SELECT id, name, token, created_at FROM users').all() as Array<{ id: string, name: string, token: string, created_at: string }>
127
- }
128
- catch {
129
- wdb.close()
130
- return
131
- }
132
- wdb.close()
133
-
134
- const exists = d.prepare('SELECT 1 FROM users WHERE id = ?')
135
- const insertUser = d.prepare('INSERT INTO users (id, name, email, password_hash, role, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)')
136
- const tokenExists = d.prepare('SELECT 1 FROM user_tokens WHERE token_hash = ?')
137
- const insertToken = d.prepare('INSERT INTO user_tokens (id, user_id, label, token_hash, token_plain, created_at) VALUES (?, ?, ?, ?, ?, ?)')
138
-
139
- for (const row of rows) {
140
- if (exists.get(row.id)) continue
141
- const email = `legacy-${row.id.slice(0, 8)}@workshop.local`
142
- insertUser.run(row.id, row.name, email, hashPassword(randomPassword()), 'user', 'active', row.created_at ?? now())
143
- const hash = hashToken(row.token)
144
- if (!tokenExists.get(hash)) {
145
- insertToken.run(randomUUID(), row.id, 'legacy', hash, row.token, row.created_at ?? now())
146
- }
147
- }
148
- }
149
-
150
- /** 20 位随机口令(legacy 导入用户与自动建号共用) */
151
- function randomPassword(): string {
152
- const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
153
- const bytes = randomBytes(20)
154
- let out = ''
155
- for (const b of bytes) out += chars[b % chars.length]
156
- return out
157
- }
158
-
159
- function now(): string {
160
- return new Date().toISOString().slice(0, 19).replace('T', ' ')
161
- }
162
-
163
- // ===== 密码哈希(scrypt)=====
164
-
165
- /** 生成 scrypt 哈希(格式 salt:hash,均为 hex) */
166
- export function hashPassword(password: string): string {
167
- const salt = randomBytes(16).toString('hex')
168
- const hash = scryptSync(password, salt, 64).toString('hex')
169
- return `${salt}:${hash}`
170
- }
171
-
172
- /** 校验密码:长度与哈希双重防御(timingSafeEqual 防时序侧信道) */
173
- export function verifyPassword(password: string, stored: string): boolean {
174
- const [salt, hex] = stored.split(':')
175
- if (!salt || !hex) return false
176
- const expected = Buffer.from(hex, 'hex')
177
- const actual = scryptSync(password, salt, expected.length)
178
- return expected.length === actual.length && timingSafeEqual(expected, actual)
179
- }
180
-
181
- // ===== Token 哈希 =====
182
-
183
- function hashToken(token: string): string {
184
- return createHash('sha256').update(token).digest('hex')
185
- }
186
-
187
- /** 签发新 token:明文返回一次并存档(token_plain,支持随时查看),库中另存哈希用于认证比对 */
188
- function issueToken(userId: string, label: string): { raw: string, row: UserToken } {
189
- const raw = `ut-${randomUUID().replace(/-/g, '')}`
190
- const hash = hashToken(raw)
191
- const row: UserToken = {
192
- id: randomUUID(),
193
- userId,
194
- label,
195
- createdAt: now(),
196
- lastUsedAt: null,
197
- preview: maskPreview(raw),
198
- hasPlain: true,
199
- }
200
- const d = getDb()
201
- d.prepare('INSERT INTO user_tokens (id, user_id, label, token_hash, token_plain, created_at, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?)')
202
- .run(row.id, userId, label, hash, raw, row.createdAt, isoInDays(TOKEN_TTL_DAYS))
203
- return { raw, row }
204
- }
205
-
206
- /** 按 token 明文查用户(命中且未过期则刷新 last_used_at);无效/已过期返回 null。tokenId 供前端识别当前会话 token。 */
207
- export function findByToken(token: string): (User & { tokenId: string }) | null {
208
- const d = getDb()
209
- const hash = hashToken(token)
210
- const row = d.prepare(
211
- `SELECT u.id, u.name, u.email, u.role, u.status, u.created_at AS createdAt, t.id AS tokenId, t.expires_at AS expiresAt
212
- FROM user_tokens t JOIN users u ON u.id = t.user_id
213
- WHERE t.token_hash = ?`,
214
- ).get(hash) as (User & { tokenId: string, expiresAt: string | null }) | undefined
215
- if (!row) return null
216
- // R2:过期单点判定(resolveUser/resolveAgentOrUser/me 全部经此收敛)
217
- if (row.expiresAt && Date.parse(row.expiresAt) < Date.now()) return null
218
- d.prepare('UPDATE user_tokens SET last_used_at = ? WHERE token_hash = ?').run(now(), hash)
219
- return row
220
- }
221
-
222
- /** users 行 → User 领域对象(禁带 password_hash) */
223
- function toUser(row: Record<string, unknown>): User {
224
- return {
225
- id: String(row.id),
226
- name: String(row.name),
227
- email: String(row.email),
228
- role: row.role as User['role'],
229
- status: row.status as User['status'],
230
- createdAt: String(row.created_at ?? row.createdAt),
231
- }
232
- }
233
-
234
- export const userRepository = {
235
- list({ page, pageSize, keyword }: UserListQuery): Paginated<User> {
236
- const d = getDb()
237
- const kw = keyword?.toLowerCase() ?? ''
238
- const where = kw
239
- ? `WHERE LOWER(name) LIKE ? OR LOWER(email) LIKE ?`
240
- : ''
241
- const params = kw ? [`%${kw}%`, `%${kw}%`] : []
242
- const total = (d.prepare(`SELECT COUNT(*) AS n FROM users ${where}`).get(...params) as { n: number }).n
243
- const rows = d.prepare(
244
- `SELECT id, name, email, role, status, created_at AS createdAt FROM users ${where}
245
- ORDER BY created_at DESC LIMIT ? OFFSET ?`,
246
- ).all(...params, pageSize, (page - 1) * pageSize) as Array<Record<string, unknown>>
247
- return { items: rows.map(toUser), total, page, pageSize }
248
- },
249
-
250
- findById(id: string): User | undefined {
251
- const d = getDb()
252
- const row = d.prepare('SELECT id, name, email, role, status, created_at AS createdAt FROM users WHERE id = ?').get(id) as Record<string, unknown> | undefined
253
- return row ? toUser(row) : undefined
254
- },
255
-
256
- findByEmail(email: string): User | undefined {
257
- const d = getDb()
258
- const row = d.prepare('SELECT id, name, email, role, status, created_at AS createdAt FROM users WHERE LOWER(email) = LOWER(?)').get(email) as Record<string, unknown> | undefined
259
- return row ? toUser(row) : undefined
260
- },
261
-
262
- findByName(name: string): User | undefined {
263
- const d = getDb()
264
- const row = d.prepare('SELECT id, name, email, role, status, created_at AS createdAt FROM users WHERE name = ?').get(name) as Record<string, unknown> | undefined
265
- return row ? toUser(row) : undefined
266
- },
267
-
268
- /** 内部:取密码哈希(仅认证路径使用,不参与领域对象外泄) */
269
- getPasswordHash(email: string): { id: string, hash: string } | null {
270
- const d = getDb()
271
- const row = d.prepare('SELECT id, password_hash AS hash FROM users WHERE LOWER(email) = LOWER(?)').get(email) as { id: string, hash: string } | undefined
272
- return row ?? null
273
- },
274
-
275
- create(input: UserCreate & { password: string }): User {
276
- const d = getDb()
277
- const user: User = {
278
- id: randomUUID(),
279
- name: input.name,
280
- email: input.email,
281
- role: input.role,
282
- status: input.status,
283
- createdAt: now(),
284
- }
285
- d.prepare('INSERT INTO users (id, name, email, password_hash, role, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)')
286
- .run(user.id, user.name, user.email, hashPassword(input.password), user.role, user.status, user.createdAt)
287
- return user
288
- },
289
-
290
- update(id: string, input: UserUpdate): User | undefined {
291
- const d = getDb()
292
- const current = this.findById(id)
293
- if (!current) return undefined
294
- const fields: string[] = []
295
- const params: string[] = []
296
- if (input.name !== undefined) {
297
- fields.push('name = ?')
298
- params.push(input.name)
299
- }
300
- if (input.email !== undefined) {
301
- fields.push('email = ?')
302
- params.push(input.email)
303
- }
304
- if (input.role !== undefined) {
305
- fields.push('role = ?')
306
- params.push(input.role)
307
- }
308
- if (input.status !== undefined) {
309
- fields.push('status = ?')
310
- params.push(input.status)
311
- }
312
- if (input.password !== undefined) {
313
- fields.push('password_hash = ?')
314
- params.push(hashPassword(input.password))
315
- }
316
- if (fields.length === 0) return current
317
- d.prepare(`UPDATE users SET ${fields.join(', ')} WHERE id = ?`).run(...params, id)
318
- return this.findById(id)
319
- },
320
-
321
- remove(id: string): boolean {
322
- const d = getDb()
323
- return d.prepare('DELETE FROM users WHERE id = ?').run(id).changes > 0
324
- },
325
-
326
- // ===== API Token(每用户多个,CRUD)=====
327
-
328
- /** 列出某用户全部 token(含掩码 preview,不含哈希/明文) */
329
- listTokens(userId: string): UserToken[] {
330
- const d = getDb()
331
- const rows = d.prepare(
332
- 'SELECT id, user_id AS userId, label, token_plain AS tokenPlain, created_at AS createdAt, last_used_at AS lastUsedAt FROM user_tokens WHERE user_id = ? ORDER BY created_at ASC',
333
- ).all(userId) as Array<{ id: string, userId: string, label: string, tokenPlain: string | null, createdAt: string, lastUsedAt: string | null }>
334
- return rows.map(r => ({
335
- id: r.id,
336
- userId: r.userId,
337
- label: r.label,
338
- createdAt: r.createdAt,
339
- lastUsedAt: r.lastUsedAt,
340
- preview: r.tokenPlain ? maskPreview(r.tokenPlain) : null,
341
- hasPlain: r.tokenPlain != null && r.tokenPlain !== '',
342
- }))
343
- },
344
-
345
- /** 创建 token(label 缺省空串;明文仅此一次返回) */
346
- createToken(userId: string, label: string): { raw: string, row: UserToken } {
347
- return issueToken(userId, label)
348
- },
349
-
350
- /** 按 id 找某用户的 token(越权场景返回 null) */
351
- findTokenById(userId: string, tokenId: string): UserToken | undefined {
352
- const d = getDb()
353
- const row = d.prepare(
354
- 'SELECT id, user_id AS userId, label, created_at AS createdAt, last_used_at AS lastUsedAt FROM user_tokens WHERE id = ? AND user_id = ?',
355
- ).get(tokenId, userId) as unknown as UserToken | undefined
356
- return row
357
- },
358
-
359
- /** 按 id 取某用户 token 的存档明文;非本人/不存在 → undefined,未存档(旧数据)→ null */
360
- findTokenPlain(userId: string, tokenId: string): string | null | undefined {
361
- const d = getDb()
362
- const row = d.prepare(
363
- 'SELECT token_plain AS tokenPlain FROM user_tokens WHERE id = ? AND user_id = ?',
364
- ).get(tokenId, userId) as { tokenPlain: string | null } | undefined
365
- return row ? row.tokenPlain : undefined
366
- },
367
-
368
- /** 改 token 标签;不存在或非本人返回 false */
369
- updateTokenLabel(userId: string, tokenId: string, label: string): boolean {
370
- const d = getDb()
371
- return d.prepare('UPDATE user_tokens SET label = ? WHERE id = ? AND user_id = ?').run(label, tokenId, userId).changes > 0
372
- },
373
-
374
- /** 删除/吊销 token;不存在或非本人返回 false */
375
- revokeToken(userId: string, tokenId: string): boolean {
376
- const d = getDb()
377
- return d.prepare('DELETE FROM user_tokens WHERE id = ? AND user_id = ?').run(tokenId, userId).changes > 0
378
- },
379
-
380
- /** 登出当前 token(仅吊销该 token 本身) */
381
- revokeTokenByValue(token: string): boolean {
382
- const d = getDb()
383
- return d.prepare('DELETE FROM user_tokens WHERE token_hash = ?').run(hashToken(token)).changes > 0
384
- },
385
-
386
- /** 测试钩子:替换内部 DB(重开内存库等) */
387
- _resetForTest(): void {
388
- db?.close()
389
- db = null
390
- },
391
- }
1
+ import { existsSync } from 'node:fs'
2
+ import { backupRegistry } from '../services/workshop/db/backup-registry'
3
+ import { join, resolve } from 'node:path'
4
+ import { randomBytes, randomUUID, scryptSync, createHash, timingSafeEqual } from 'node:crypto'
5
+ import { DatabaseSync } from 'node:sqlite'
6
+ import { ensureDataDir } from '@/shared/config/home.mjs'
7
+ import type { Paginated, User, UserListQuery, UserToken } from '../types/user'
8
+ import type { UserCreate, UserUpdate } from '../schemas/user.schema'
9
+
10
+ /**
11
+ * 全局用户数据访问层(Repository)—— SQLite 持久化(配置根 .AgentWorkShop/data/users.sqlite;
12
+ * repo 模式 <repo>/.AgentWorkShop/data,home 模式 ~/.AgentWorkShop/data,经 ensureDataDir 统一解析
13
+ * 并自动迁移旧 cwd/data 位置)。
14
+ * - users 表:用户档案 + 密码哈希(scrypt,salt 内嵌,明文不进库)
15
+ * - user_tokens 表:每用户多个 API Token(仅存 SHA-256 哈希;明文只在签发时返回一次)
16
+ * 进程内单例 DB 懒加载;数据源与业务逻辑解耦,service 层零改动。
17
+ */
18
+
19
+ // 配置根 .AgentWorkShop/data(repo: <repo>/.AgentWorkShop/data,home: ~/.AgentWorkShop/data;
20
+ // ensureDataDir 统一解析并自动迁移旧 cwd/data 位置)
21
+ const DB_PATH = join(ensureDataDir(), 'users.sqlite')
22
+
23
+ const SCHEMA_SQL = `
24
+ CREATE TABLE IF NOT EXISTS users (
25
+ id TEXT PRIMARY KEY,
26
+ name TEXT NOT NULL,
27
+ email TEXT NOT NULL UNIQUE,
28
+ password_hash TEXT NOT NULL,
29
+ role TEXT NOT NULL DEFAULT 'user',
30
+ status TEXT NOT NULL DEFAULT 'active',
31
+ created_at TEXT NOT NULL
32
+ );
33
+ CREATE TABLE IF NOT EXISTS user_tokens (
34
+ id TEXT PRIMARY KEY,
35
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
36
+ label TEXT NOT NULL DEFAULT '',
37
+ token_hash TEXT NOT NULL UNIQUE,
38
+ token_plain TEXT,
39
+ created_at TEXT NOT NULL,
40
+ last_used_at TEXT
41
+ );
42
+ CREATE INDEX IF NOT EXISTS idx_user_tokens_user ON user_tokens(user_id);
43
+ `
44
+
45
+ /** 种子用户(首次建库时写入;默认密码 Awshop@123,便于开箱登录) */
46
+ const SEED_USERS = [
47
+ { name: '张伟', email: 'zhangwei@awshop.io', role: 'admin', status: 'active' },
48
+ { name: '王芳', email: 'wangfang@awshop.io', role: 'editor', status: 'active' },
49
+ { name: '李娜', email: 'lina@awshop.io', role: 'user', status: 'disabled' },
50
+ { name: '刘洋', email: 'liuyang@awshop.io', role: 'editor', status: 'active' },
51
+ { name: '陈静', email: 'chenjing@awshop.io', role: 'user', status: 'active' },
52
+ { name: 'Michael Chen', email: 'michael@awshop.io', role: 'user', status: 'active' },
53
+ ]
54
+
55
+ let db: DatabaseSync | null = null
56
+
57
+ function getDb(): DatabaseSync {
58
+ if (!db) {
59
+ // 目录由 ensureDataDir 内部创建(含旧位置迁移)
60
+ db = new DatabaseSync(DB_PATH)
61
+ // 主连接登记(备份 serialize 用;见 backup-registry 说明)
62
+ backupRegistry.register(DB_PATH, db)
63
+ db.exec('PRAGMA busy_timeout = 5000')
64
+ db.exec(SCHEMA_SQL)
65
+ migrateSchema(db)
66
+ seedIfEmpty()
67
+ importLegacyWorkshopUsers(db)
68
+ }
69
+ return db
70
+ }
71
+
72
+ /** 轻量迁移:旧库 user_tokens 补 token_plain 列(明文存档,支持随时查看)+ expires_at(R2 后端过期) */
73
+ function migrateSchema(d: DatabaseSync): void {
74
+ const cols = d.prepare('PRAGMA table_info(user_tokens)').all() as Array<{ name: string }>
75
+ if (!cols.some(c => c.name === 'token_plain')) {
76
+ d.exec('ALTER TABLE user_tokens ADD COLUMN token_plain TEXT')
77
+ }
78
+ if (!cols.some(c => c.name === 'expires_at')) {
79
+ d.exec('ALTER TABLE user_tokens ADD COLUMN expires_at TEXT')
80
+ }
81
+ // 存量 token 宽限 30 天(从部署时刻起算,避免升级即全员掉线)
82
+ d.prepare('UPDATE user_tokens SET expires_at = ? WHERE expires_at IS NULL').run(isoInDays(TOKEN_TTL_DAYS))
83
+ }
84
+
85
+ /** token 有效期(天,R2):过期后 findByToken 拒绝,用户重新登录即可 */
86
+ const TOKEN_TTL_DAYS = 30
87
+
88
+ /** 当前时刻 + n 天的 ISO 时间戳 */
89
+ function isoInDays(days: number): string {
90
+ return new Date(Date.now() + days * 86_400_000).toISOString()
91
+ }
92
+
93
+ /** 掩码预览:前 6 后 4(token 形如 ut-xxxxxxxx…,前 6 含前缀可辨识) */
94
+ function maskPreview(raw: string): string {
95
+ return `${raw.slice(0, 6)}${'•'.repeat(8)}${raw.slice(-4)}`
96
+ }
97
+
98
+ /** 发布默认密码(仅用于存量库检测;新库种子已改为随机密码)。分段构造避免再次落为字面量凭据 */
99
+ const SEED_DEFAULT_PASSWORD = ['Awshop@', '123'].join('')
100
+
101
+ /** 首次建库写入种子用户(幂等:users 非空即跳过) */
102
+ function seedIfEmpty(): void {
103
+ const d = db!
104
+ const { n } = d.prepare('SELECT COUNT(*) AS n FROM users').get() as { n: number }
105
+ if (n > 0) return
106
+ const insert = d.prepare('INSERT INTO users (id, name, email, password_hash, role, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)')
107
+ // 种子密码:env 显式指定 > 每库随机生成(启动打印一次)。随源码发布的固定密码
108
+ // 等于向任何能访问端口的人发放 admin(README/npm 公开可得)。
109
+ const seedPw = process.env.AW_SEED_PASSWORD?.trim() || `Aw-${randomBytes(9).toString('base64url')}`
110
+ const hash = hashPassword(seedPw)
111
+ console.log(`[users] 首次初始化:种子账号初始密码 = ${seedPw}`)
112
+ console.log('[users] 请尽快登录修改各账号密码;或在设置 AW_SEED_PASSWORD 后删除 users.sqlite 重新初始化。')
113
+ for (const u of SEED_USERS) {
114
+ insert.run(randomUUID(), u.name, u.email, hash, u.role, u.status, now())
115
+ }
116
+ }
117
+ /** 生产启动检查:任一种子账号仍持发布默认密码 → true(security 插件据此拒绝启动) */
118
+ export function anySeedUsingDefaultPassword(): boolean {
119
+ const d = getDb()
120
+ const placeholders = SEED_USERS.map(() => '?').join(',')
121
+ const rows = d.prepare(`SELECT password_hash FROM users WHERE email IN (${placeholders})`)
122
+ .all(...SEED_USERS.map(u => u.email)) as Array<{ password_hash: string }>
123
+ return rows.some(r => verifyPassword(SEED_DEFAULT_PASSWORD, String(r.password_hash)))
124
+ }
125
+
126
+ /**
127
+ * 一次性迁移:旧 workshop 本地用户(data/workshop.sqlite 的 users 表)导入全局用户系统。
128
+ * - 保留原 id/name(资源归属 owner_user_id 不变,历史数据不丢)
129
+ * - 旧 token 原值重新注册为全局 token(老客户端免重新登录)
130
+ * - email 派生 + 随机密码(不可经密码登录,可用管理面重置)
131
+ * 幂等:按 id 判断已存在则跳过。
132
+ */
133
+ function importLegacyWorkshopUsers(d: DatabaseSync): void {
134
+ // 迁移后配置根已有 workshop.sqlite;旧 cwd/data 位置兜底(仅历史未迁移场景)
135
+ const migrated = join(ensureDataDir(), 'workshop.sqlite')
136
+ const wsPath = existsSync(migrated) ? migrated : resolve(process.cwd(), 'data', 'workshop.sqlite')
137
+ if (!existsSync(wsPath)) return
138
+ let wdb: DatabaseSync
139
+ try {
140
+ wdb = new DatabaseSync(wsPath, { readOnly: true })
141
+ }
142
+ catch {
143
+ return // workshop 库尚不存在或不可读:跳过
144
+ }
145
+ let rows: Array<{ id: string, name: string, token: string, created_at: string }>
146
+ try {
147
+ rows = wdb.prepare('SELECT id, name, token, created_at FROM users').all() as Array<{ id: string, name: string, token: string, created_at: string }>
148
+ }
149
+ catch {
150
+ wdb.close()
151
+ return
152
+ }
153
+ wdb.close()
154
+
155
+ const exists = d.prepare('SELECT 1 FROM users WHERE id = ?')
156
+ const insertUser = d.prepare('INSERT INTO users (id, name, email, password_hash, role, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)')
157
+ const tokenExists = d.prepare('SELECT 1 FROM user_tokens WHERE token_hash = ?')
158
+ const insertToken = d.prepare('INSERT INTO user_tokens (id, user_id, label, token_hash, token_plain, created_at) VALUES (?, ?, ?, ?, ?, ?)')
159
+
160
+ for (const row of rows) {
161
+ if (exists.get(row.id)) continue
162
+ const email = `legacy-${row.id.slice(0, 8)}@workshop.local`
163
+ insertUser.run(row.id, row.name, email, hashPassword(randomPassword()), 'user', 'active', row.created_at ?? now())
164
+ const hash = hashToken(row.token)
165
+ if (!tokenExists.get(hash)) {
166
+ insertToken.run(randomUUID(), row.id, 'legacy', hash, row.token, row.created_at ?? now())
167
+ }
168
+ }
169
+ }
170
+
171
+ /** 20 位随机口令(legacy 导入用户与自动建号共用) */
172
+ function randomPassword(): string {
173
+ const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
174
+ const bytes = randomBytes(20)
175
+ let out = ''
176
+ for (const b of bytes) out += chars[b % chars.length]
177
+ return out
178
+ }
179
+
180
+ function now(): string {
181
+ return new Date().toISOString().slice(0, 19).replace('T', ' ')
182
+ }
183
+
184
+ // ===== 密码哈希(scrypt)=====
185
+
186
+ /** 生成 scrypt 哈希(格式 salt:hash,均为 hex) */
187
+ export function hashPassword(password: string): string {
188
+ const salt = randomBytes(16).toString('hex')
189
+ const hash = scryptSync(password, salt, 64).toString('hex')
190
+ return `${salt}:${hash}`
191
+ }
192
+
193
+ /** 校验密码:长度与哈希双重防御(timingSafeEqual 防时序侧信道) */
194
+ export function verifyPassword(password: string, stored: string): boolean {
195
+ const [salt, hex] = stored.split(':')
196
+ if (!salt || !hex) return false
197
+ const expected = Buffer.from(hex, 'hex')
198
+ const actual = scryptSync(password, salt, expected.length)
199
+ return expected.length === actual.length && timingSafeEqual(expected, actual)
200
+ }
201
+
202
+ // ===== Token 哈希 =====
203
+
204
+ function hashToken(token: string): string {
205
+ return createHash('sha256').update(token).digest('hex')
206
+ }
207
+
208
+ /** 签发新 token:明文返回一次并存档(token_plain,支持随时查看),库中另存哈希用于认证比对 */
209
+ function issueToken(userId: string, label: string): { raw: string, row: UserToken } {
210
+ const raw = `ut-${randomUUID().replace(/-/g, '')}`
211
+ const hash = hashToken(raw)
212
+ const row: UserToken = {
213
+ id: randomUUID(),
214
+ userId,
215
+ label,
216
+ createdAt: now(),
217
+ lastUsedAt: null,
218
+ preview: maskPreview(raw),
219
+ hasPlain: true,
220
+ }
221
+ const d = getDb()
222
+ d.prepare('INSERT INTO user_tokens (id, user_id, label, token_hash, token_plain, created_at, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?)')
223
+ .run(row.id, userId, label, hash, raw, row.createdAt, isoInDays(TOKEN_TTL_DAYS))
224
+ return { raw, row }
225
+ }
226
+
227
+ /** 按 token 明文查用户(命中且未过期则刷新 last_used_at);无效/已过期返回 null。tokenId 供前端识别当前会话 token。 */
228
+ export function findByToken(token: string): (User & { tokenId: string }) | null {
229
+ const d = getDb()
230
+ const hash = hashToken(token)
231
+ const row = d.prepare(
232
+ `SELECT u.id, u.name, u.email, u.role, u.status, u.created_at AS createdAt, t.id AS tokenId, t.expires_at AS expiresAt
233
+ FROM user_tokens t JOIN users u ON u.id = t.user_id
234
+ WHERE t.token_hash = ?`,
235
+ ).get(hash) as (User & { tokenId: string, expiresAt: string | null }) | undefined
236
+ if (!row) return null
237
+ // R2:过期单点判定(resolveUser/resolveAgentOrUser/me 全部经此收敛)
238
+ if (row.expiresAt && Date.parse(row.expiresAt) < Date.now()) return null
239
+ // 禁用账号即时失效(此前存量 token 在禁用后最长 30 天仍可用)
240
+ if (row.status && row.status !== 'active') return null
241
+ d.prepare('UPDATE user_tokens SET last_used_at = ? WHERE token_hash = ?').run(now(), hash)
242
+ return row
243
+ }
244
+
245
+ /** users 行 → User 领域对象(禁带 password_hash) */
246
+ function toUser(row: Record<string, unknown>): User {
247
+ return {
248
+ id: String(row.id),
249
+ name: String(row.name),
250
+ email: String(row.email),
251
+ role: row.role as User['role'],
252
+ status: row.status as User['status'],
253
+ createdAt: String(row.created_at ?? row.createdAt),
254
+ }
255
+ }
256
+
257
+ export const userRepository = {
258
+ list({ page, pageSize, keyword }: UserListQuery): Paginated<User> {
259
+ const d = getDb()
260
+ const kw = keyword?.toLowerCase() ?? ''
261
+ const where = kw
262
+ ? `WHERE LOWER(name) LIKE ? OR LOWER(email) LIKE ?`
263
+ : ''
264
+ const params = kw ? [`%${kw}%`, `%${kw}%`] : []
265
+ const total = (d.prepare(`SELECT COUNT(*) AS n FROM users ${where}`).get(...params) as { n: number }).n
266
+ const rows = d.prepare(
267
+ `SELECT id, name, email, role, status, created_at AS createdAt FROM users ${where}
268
+ ORDER BY created_at DESC LIMIT ? OFFSET ?`,
269
+ ).all(...params, pageSize, (page - 1) * pageSize) as Array<Record<string, unknown>>
270
+ return { items: rows.map(toUser), total, page, pageSize }
271
+ },
272
+
273
+ findById(id: string): User | undefined {
274
+ const d = getDb()
275
+ const row = d.prepare('SELECT id, name, email, role, status, created_at AS createdAt FROM users WHERE id = ?').get(id) as Record<string, unknown> | undefined
276
+ return row ? toUser(row) : undefined
277
+ },
278
+
279
+ findByEmail(email: string): User | undefined {
280
+ const d = getDb()
281
+ const row = d.prepare('SELECT id, name, email, role, status, created_at AS createdAt FROM users WHERE LOWER(email) = LOWER(?)').get(email) as Record<string, unknown> | undefined
282
+ return row ? toUser(row) : undefined
283
+ },
284
+
285
+ findByName(name: string): User | undefined {
286
+ const d = getDb()
287
+ const row = d.prepare('SELECT id, name, email, role, status, created_at AS createdAt FROM users WHERE name = ?').get(name) as Record<string, unknown> | undefined
288
+ return row ? toUser(row) : undefined
289
+ },
290
+
291
+ /** 内部:取密码哈希(仅认证路径使用,不参与领域对象外泄) */
292
+ getPasswordHash(email: string): { id: string, hash: string } | null {
293
+ const d = getDb()
294
+ const row = d.prepare('SELECT id, password_hash AS hash FROM users WHERE LOWER(email) = LOWER(?)').get(email) as { id: string, hash: string } | undefined
295
+ return row ?? null
296
+ },
297
+
298
+ create(input: UserCreate & { password: string }): User {
299
+ const d = getDb()
300
+ const user: User = {
301
+ id: randomUUID(),
302
+ name: input.name,
303
+ email: input.email,
304
+ role: input.role,
305
+ status: input.status,
306
+ createdAt: now(),
307
+ }
308
+ d.prepare('INSERT INTO users (id, name, email, password_hash, role, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)')
309
+ .run(user.id, user.name, user.email, hashPassword(input.password), user.role, user.status, user.createdAt)
310
+ return user
311
+ },
312
+
313
+ update(id: string, input: UserUpdate): User | undefined {
314
+ const d = getDb()
315
+ const current = this.findById(id)
316
+ if (!current) return undefined
317
+ const fields: string[] = []
318
+ const params: string[] = []
319
+ if (input.name !== undefined) {
320
+ fields.push('name = ?')
321
+ params.push(input.name)
322
+ }
323
+ if (input.email !== undefined) {
324
+ fields.push('email = ?')
325
+ params.push(input.email)
326
+ }
327
+ if (input.role !== undefined) {
328
+ fields.push('role = ?')
329
+ params.push(input.role)
330
+ }
331
+ if (input.status !== undefined) {
332
+ fields.push('status = ?')
333
+ params.push(input.status)
334
+ }
335
+ if (input.password !== undefined) {
336
+ fields.push('password_hash = ?')
337
+ params.push(hashPassword(input.password))
338
+ }
339
+ if (fields.length === 0) return current
340
+ d.prepare(`UPDATE users SET ${fields.join(', ')} WHERE id = ?`).run(...params, id)
341
+ return this.findById(id)
342
+ },
343
+
344
+ remove(id: string): boolean {
345
+ const d = getDb()
346
+ return d.prepare('DELETE FROM users WHERE id = ?').run(id).changes > 0
347
+ },
348
+
349
+ // ===== API Token(每用户多个,CRUD)=====
350
+
351
+ /** 列出某用户全部 token(含掩码 preview,不含哈希/明文) */
352
+ listTokens(userId: string): UserToken[] {
353
+ const d = getDb()
354
+ const rows = d.prepare(
355
+ 'SELECT id, user_id AS userId, label, token_plain AS tokenPlain, created_at AS createdAt, last_used_at AS lastUsedAt FROM user_tokens WHERE user_id = ? ORDER BY created_at ASC',
356
+ ).all(userId) as Array<{ id: string, userId: string, label: string, tokenPlain: string | null, createdAt: string, lastUsedAt: string | null }>
357
+ return rows.map(r => ({
358
+ id: r.id,
359
+ userId: r.userId,
360
+ label: r.label,
361
+ createdAt: r.createdAt,
362
+ lastUsedAt: r.lastUsedAt,
363
+ preview: r.tokenPlain ? maskPreview(r.tokenPlain) : null,
364
+ hasPlain: r.tokenPlain != null && r.tokenPlain !== '',
365
+ }))
366
+ },
367
+
368
+ /** 创建 token(label 缺省空串;明文仅此一次返回) */
369
+ createToken(userId: string, label: string): { raw: string, row: UserToken } {
370
+ return issueToken(userId, label)
371
+ },
372
+
373
+ /** 按 id 找某用户的 token(越权场景返回 null) */
374
+ findTokenById(userId: string, tokenId: string): UserToken | undefined {
375
+ const d = getDb()
376
+ const row = d.prepare(
377
+ 'SELECT id, user_id AS userId, label, created_at AS createdAt, last_used_at AS lastUsedAt FROM user_tokens WHERE id = ? AND user_id = ?',
378
+ ).get(tokenId, userId) as unknown as UserToken | undefined
379
+ return row
380
+ },
381
+
382
+ /** 按 id 取某用户 token 的存档明文;非本人/不存在 → undefined,未存档(旧数据)→ null */
383
+ findTokenPlain(userId: string, tokenId: string): string | null | undefined {
384
+ const d = getDb()
385
+ const row = d.prepare(
386
+ 'SELECT token_plain AS tokenPlain FROM user_tokens WHERE id = ? AND user_id = ?',
387
+ ).get(tokenId, userId) as { tokenPlain: string | null } | undefined
388
+ return row ? row.tokenPlain : undefined
389
+ },
390
+
391
+ /** 改 token 标签;不存在或非本人返回 false */
392
+ updateTokenLabel(userId: string, tokenId: string, label: string): boolean {
393
+ const d = getDb()
394
+ return d.prepare('UPDATE user_tokens SET label = ? WHERE id = ? AND user_id = ?').run(label, tokenId, userId).changes > 0
395
+ },
396
+
397
+ /** 删除/吊销 token;不存在或非本人返回 false */
398
+ revokeToken(userId: string, tokenId: string): boolean {
399
+ const d = getDb()
400
+ return d.prepare('DELETE FROM user_tokens WHERE id = ? AND user_id = ?').run(tokenId, userId).changes > 0
401
+ },
402
+
403
+ /** 登出当前 token(仅吊销该 token 本身) */
404
+ revokeTokenByValue(token: string): boolean {
405
+ const d = getDb()
406
+ return d.prepare('DELETE FROM user_tokens WHERE token_hash = ?').run(hashToken(token)).changes > 0
407
+ },
408
+
409
+ /** 测试钩子:替换内部 DB(重开内存库等) */
410
+ _resetForTest(): void {
411
+ db?.close()
412
+ db = null
413
+ },
414
+ }