@runeya/apps-cli 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/dist/agent/index.js +5303 -0
  2. package/dist/agent/index.js.map +1 -0
  3. package/dist/agent-manager-3S4FUNJS-MYT5X6XQ.js +8 -0
  4. package/dist/agent-manager-3S4FUNJS-MYT5X6XQ.js.map +1 -0
  5. package/dist/chunk-32HTB3XS.js +350 -0
  6. package/dist/chunk-32HTB3XS.js.map +1 -0
  7. package/dist/chunk-ERJIU7R4.js +195 -0
  8. package/dist/chunk-ERJIU7R4.js.map +1 -0
  9. package/dist/chunk-XZPCEGWS.js +2452 -0
  10. package/dist/chunk-XZPCEGWS.js.map +1 -0
  11. package/dist/dist-MFM5N25P.js +10985 -0
  12. package/dist/dist-MFM5N25P.js.map +1 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/kanban-commands-VEVMJTFS-OIYWEHUP.js +14 -0
  15. package/dist/kanban-commands-VEVMJTFS-OIYWEHUP.js.map +1 -0
  16. package/dist/package.json +36 -0
  17. package/dist/runeya +4 -0
  18. package/dist/web/assets/AddressBar.vue-dB66q4DS.js +1 -0
  19. package/dist/web/assets/AgentPage-B3wrH3gG.js +2 -0
  20. package/dist/web/assets/AgentPage-BTWkJvqr.css +1 -0
  21. package/dist/web/assets/AgentScalarChatInterface.vue-CJVuZIB9.js +13 -0
  22. package/dist/web/assets/AgentTerminalPage-Cj5fRr-j.js +36 -0
  23. package/dist/web/assets/AgentTerminalPage-EX_n6P3x.css +1 -0
  24. package/dist/web/assets/AiChatPage-Dhsx3B9B.js +1 -0
  25. package/dist/web/assets/ApiDocsPage-BddLJuUF.js +5 -0
  26. package/dist/web/assets/ApiDocsPage-D2Z0wqGZ.css +1 -0
  27. package/dist/web/assets/AppLayout-AfhaeKlp.js +1 -0
  28. package/dist/web/assets/AppLayout-sngalCCj.css +1 -0
  29. package/dist/web/assets/AuditPage-BBV9FW9y.css +1 -0
  30. package/dist/web/assets/AuditPage-DqycuiVb.js +1 -0
  31. package/dist/web/assets/CodeInput.vue-Dyew7ml-.js +1 -0
  32. package/dist/web/assets/Collection.vue-DePdVAHM.js +1 -0
  33. package/dist/web/assets/CollectionAuthentication.vue-DagSEg45.js +1 -0
  34. package/dist/web/assets/CollectionCookies.vue-vMKPCgg1.js +1 -0
  35. package/dist/web/assets/CollectionEnvironment.vue-B0JkZ6GC.js +1 -0
  36. package/dist/web/assets/CollectionOverview.vue-Bm7OsMWM.js +1 -0
  37. package/dist/web/assets/CollectionScripts.vue-CZiBq0rT.js +1 -0
  38. package/dist/web/assets/CollectionServers.vue-bziJbP-5.js +1 -0
  39. package/dist/web/assets/CollectionSettings.vue-D4oBu-fT.js +1 -0
  40. package/dist/web/assets/CollectionSync.vue-BG50-Lo4.js +1 -0
  41. package/dist/web/assets/CommandActionInput.vue-DmZld2w7.js +1 -0
  42. package/dist/web/assets/Cookies.vue-DjxMLL79.js +1 -0
  43. package/dist/web/assets/DashboardPage-QjYCF0Kd.js +1 -0
  44. package/dist/web/assets/DashboardPage-gZJ4_s8T.css +1 -0
  45. package/dist/web/assets/DataTable.vue-BV9IuUNB.js +1 -0
  46. package/dist/web/assets/DataTableCheckbox.vue-Cx0exaNK.js +1 -0
  47. package/dist/web/assets/DataTableHeader.vue-ZtUdyiLV.js +1 -0
  48. package/dist/web/assets/DataTableInput.vue-C4NF8kJn.js +1 -0
  49. package/dist/web/assets/DeleteSidebarListElement.vue-CcAW4Pdn.js +1 -0
  50. package/dist/web/assets/EditSidebarListElement.vue-Crbagha4.js +1 -0
  51. package/dist/web/assets/EditorIcon-BaDORp4k.js +15 -0
  52. package/dist/web/assets/EditorIcon-zS3bHXe1.css +1 -0
  53. package/dist/web/assets/EmptyState.vue-2hJbVxAi.js +22 -0
  54. package/dist/web/assets/Environment.vue-CcERFnQq.js +1 -0
  55. package/dist/web/assets/EnvironmentForm-DSKxdDGW.js +108 -0
  56. package/dist/web/assets/EnvironmentForm-DVMc303P.css +1 -0
  57. package/dist/web/assets/EnvironmentFormPage-BDhR07CG.js +1 -0
  58. package/dist/web/assets/EnvironmentFormPage-OaDJvyrc.css +1 -0
  59. package/dist/web/assets/EnvironmentListPage-BDqcug66.css +1 -0
  60. package/dist/web/assets/EnvironmentListPage-BzdxgZdS.js +1 -0
  61. package/dist/web/assets/EnvironmentModal.vue-DvsPa6V9.js +1 -0
  62. package/dist/web/assets/EnvironmentSelector-B9SZvB3I.css +1 -0
  63. package/dist/web/assets/EnvironmentSelector-BWljvJWP.js +1 -0
  64. package/dist/web/assets/Form.vue-BxhIukSx.js +1 -0
  65. package/dist/web/assets/IconSelector.vue-SxFpYToZ.js +1 -0
  66. package/dist/web/assets/KanbanBoardPage-7nOYpwHn.css +1 -0
  67. package/dist/web/assets/KanbanBoardPage-Bbyn-iLC.js +141 -0
  68. package/dist/web/assets/KanbanListPage-BEX3MKW0.js +1 -0
  69. package/dist/web/assets/KanbanListPage-DbdjvTCx.css +1 -0
  70. package/dist/web/assets/LaunchProjectPage-DB_XBa86.js +1 -0
  71. package/dist/web/assets/LaunchProjectPage-_L_i2xo0.css +1 -0
  72. package/dist/web/assets/LaunchServicesPage-BABQz-ey.css +1 -0
  73. package/dist/web/assets/LaunchServicesPage-fmsU-_p0.js +1 -0
  74. package/dist/web/assets/LogViewer-CJcXcTP0.css +1 -0
  75. package/dist/web/assets/LogViewer-CV9OmAb1.js +3 -0
  76. package/dist/web/assets/ProcessControls-CKmB6sB9.css +1 -0
  77. package/dist/web/assets/ProcessControls-L_hld0kN.js +1 -0
  78. package/dist/web/assets/ProcessDetailPage-B0qGKtvQ.css +1 -0
  79. package/dist/web/assets/ProcessDetailPage-CW0F6wvO.js +1 -0
  80. package/dist/web/assets/ProcessMetrics-BgEFMdnW.css +1 -0
  81. package/dist/web/assets/ProcessMetrics-D2vCK15o.js +1 -0
  82. package/dist/web/assets/ProjectDetailPage-Bo04MsdE.js +1 -0
  83. package/dist/web/assets/ProjectDetailPage-CZBB6F-t.css +1 -0
  84. package/dist/web/assets/ProjectListPage-BGU6SKAr.css +1 -0
  85. package/dist/web/assets/ProjectListPage-CG-r82FQ.js +1 -0
  86. package/dist/web/assets/RandomJobTestPage-C1mgTTSY.css +1 -0
  87. package/dist/web/assets/RandomJobTestPage-COnuE_dj.js +2 -0
  88. package/dist/web/assets/Request.vue-CeAzdpVl.js +1 -0
  89. package/dist/web/assets/RequestAuth.vue-DABBKM9M.js +1 -0
  90. package/dist/web/assets/RequestRoot.vue-CRaiGtgb.js +19 -0
  91. package/dist/web/assets/ScalarAsciiArt.vue-mR3JflbN.js +2 -0
  92. package/dist/web/assets/ScalarIconEyeSlash.vue-JQCpS43o.js +1 -0
  93. package/dist/web/assets/ScalarIconTrash.vue-D7zGeCPC.js +1 -0
  94. package/dist/web/assets/ScenarioEditorPage-BVnDp-fS.js +14 -0
  95. package/dist/web/assets/ScenarioEditorPage-CfBKebp6.css +1 -0
  96. package/dist/web/assets/ServerVariablesForm.vue-DbVG31Dx.js +1 -0
  97. package/dist/web/assets/ServiceControlsBar-Cp2VUifX.js +1 -0
  98. package/dist/web/assets/ServiceControlsBar-D_S_sGQj.css +1 -0
  99. package/dist/web/assets/ServiceDetailPage-BriHTCpT.css +1 -0
  100. package/dist/web/assets/ServiceDetailPage-DMoXJliU.js +2 -0
  101. package/dist/web/assets/ServiceFormPage-BXTTwyWP.js +1 -0
  102. package/dist/web/assets/ServiceFormPage-CyY-14jH.css +1 -0
  103. package/dist/web/assets/Settings.vue-BtjfBYMB.js +1 -0
  104. package/dist/web/assets/SettingsPage-BFqYvm6R.js +508 -0
  105. package/dist/web/assets/SettingsPage-vGhb_tlY.css +1 -0
  106. package/dist/web/assets/SetupKeyPage-CrmikLTx.js +1 -0
  107. package/dist/web/assets/SetupKeyPage-DoY1Ua1s.css +1 -0
  108. package/dist/web/assets/SetupPage-CRuL5LXc.js +1 -0
  109. package/dist/web/assets/SetupPage-_KXpCQSU.css +1 -0
  110. package/dist/web/assets/Sidebar.vue-CLpz7l3s.js +1 -0
  111. package/dist/web/assets/SidebarButton.vue-DAvh-OoW.js +1 -0
  112. package/dist/web/assets/SidebarListElement.vue-CfQ22Epo.js +1 -0
  113. package/dist/web/assets/UnlockPage-Cx3PYgUl.css +1 -0
  114. package/dist/web/assets/UnlockPage-UxunGumn.js +1 -0
  115. package/dist/web/assets/VariablesTable-BtdK5olT.css +1 -0
  116. package/dist/web/assets/VariablesTable-CKe8mApI.js +1 -0
  117. package/dist/web/assets/ViewLayout.vue-D30IFsIP.js +1 -0
  118. package/dist/web/assets/ViewLayoutContent.vue-B4BXKBtQ.js +1 -0
  119. package/dist/web/assets/ViewLayoutSection.vue-BGkXXzBQ.js +1 -0
  120. package/dist/web/assets/_plugin-vue_export-helper-C7ztIXtE.js +1 -0
  121. package/dist/web/assets/abap-C4tHUUCU.js +1 -0
  122. package/dist/web/assets/active-entities-DMSK8Kvo.js +1 -0
  123. package/dist/web/assets/apex-6cG6aG-w.js +1 -0
  124. package/dist/web/assets/api-reference-DUBKG98-.js +1 -0
  125. package/dist/web/assets/autocomplete-Bx5M86A5.js +308 -0
  126. package/dist/web/assets/azcli-51BngrSU.js +1 -0
  127. package/dist/web/assets/base64-uZi92-DX.js +1 -0
  128. package/dist/web/assets/bat-BDcdVSIt.js +1 -0
  129. package/dist/web/assets/bicep-Cg0BCcky.js +2 -0
  130. package/dist/web/assets/cameligo-C3ekVui9.js +1 -0
  131. package/dist/web/assets/card-6r6P_U-q.js +32 -0
  132. package/dist/web/assets/checkbox-CQpllPXd.js +141 -0
  133. package/dist/web/assets/chip-gp-_w98f.js +54 -0
  134. package/dist/web/assets/clojure-Ci5xNgh_.js +1 -0
  135. package/dist/web/assets/codicon-DCmgc-ay.ttf +0 -0
  136. package/dist/web/assets/coffee-xsRz4GKy.js +1 -0
  137. package/dist/web/assets/column-CE6TbiAC.js +763 -0
  138. package/dist/web/assets/core-CLjfGkKM.js +1 -0
  139. package/dist/web/assets/cpp-B2gW4e23.js +1 -0
  140. package/dist/web/assets/csharp-SRujvHqy.js +1 -0
  141. package/dist/web/assets/csp-Cr-ll6gP.js +1 -0
  142. package/dist/web/assets/css-BYTJZCno.js +3 -0
  143. package/dist/web/assets/cssMode-Bl7Y9u0B.js +4 -0
  144. package/dist/web/assets/cypher-AmQ0R_D3.js +1 -0
  145. package/dist/web/assets/dart-Du01a0J5.js +1 -0
  146. package/dist/web/assets/dist-CNJKy0Au.js +192 -0
  147. package/dist/web/assets/dist-DGrDIHmK.js +1 -0
  148. package/dist/web/assets/dist-DkaYBczY.js +1 -0
  149. package/dist/web/assets/dist-E9g7EMme.js +1 -0
  150. package/dist/web/assets/dist-wHtavuXf.js +33 -0
  151. package/dist/web/assets/divider-J1wJyhb6.js +82 -0
  152. package/dist/web/assets/dockerfile-Dtlk9wqf.js +1 -0
  153. package/dist/web/assets/ecl-CpmVnLkK.js +1 -0
  154. package/dist/web/assets/editor.store-CIIP294U.js +1 -0
  155. package/dist/web/assets/editor.worker-Di51weDy.js +12 -0
  156. package/dist/web/assets/elixir-B3MVAC8k.js +1 -0
  157. package/dist/web/assets/fieldset-BRuU3VW2.js +88 -0
  158. package/dist/web/assets/flow9-Chkly0RN.js +1 -0
  159. package/dist/web/assets/freemarker2-C-5i2Fhr.js +3 -0
  160. package/dist/web/assets/fsharp-CZvMkppk.js +1 -0
  161. package/dist/web/assets/go-dT2iT7lt.js +1 -0
  162. package/dist/web/assets/graphql-DmObWyBD.js +1 -0
  163. package/dist/web/assets/handlebars-Bq-uEMhK.js +1 -0
  164. package/dist/web/assets/hcl-D1x5Oyp4.js +1 -0
  165. package/dist/web/assets/html-B-mweV_C.js +1 -0
  166. package/dist/web/assets/htmlMode-BlnuAGDY.js +4 -0
  167. package/dist/web/assets/index-BRDI9Dib.css +1 -0
  168. package/dist/web/assets/index-gfmcW9Sb.js +4220 -0
  169. package/dist/web/assets/ini-Bg2o1S3s.js +1 -0
  170. package/dist/web/assets/java-XviKSSn5.js +1 -0
  171. package/dist/web/assets/javascript-D61EWPTf.js +1 -0
  172. package/dist/web/assets/jsonMode-DmSJWG69.js +10 -0
  173. package/dist/web/assets/julia-DfTIwUGh.js +1 -0
  174. package/dist/web/assets/keyboard-nBda2f6m.js +4 -0
  175. package/dist/web/assets/kotlin-CNaIBh9d.js +1 -0
  176. package/dist/web/assets/less-D7o76tXd.js +2 -0
  177. package/dist/web/assets/lexon-Dd374QjV.js +1 -0
  178. package/dist/web/assets/lib-CegStMAw.js +5 -0
  179. package/dist/web/assets/library-BwIJAfg0.js +1 -0
  180. package/dist/web/assets/liquid-BRXADGhu.js +1 -0
  181. package/dist/web/assets/lua-DZKuiFbY.js +1 -0
  182. package/dist/web/assets/m3-CbwRx9Bb.js +1 -0
  183. package/dist/web/assets/markdown-DwLB5Rib.js +1 -0
  184. package/dist/web/assets/mdx-DXlefD6G.js +1 -0
  185. package/dist/web/assets/mediaTypes-BtSbXDUA.js +2 -0
  186. package/dist/web/assets/message-EfwB0RVQ.js +316 -0
  187. package/dist/web/assets/minus-DCEEn9ly.js +2 -0
  188. package/dist/web/assets/mips-DLbIq1yM.js +1 -0
  189. package/dist/web/assets/msdax-D4IjoZ6m.js +1 -0
  190. package/dist/web/assets/multiselect-BMOAw1qo.js +251 -0
  191. package/dist/web/assets/mysql-DQL_jy6H.js +1 -0
  192. package/dist/web/assets/objective-c-D74wM5bv.js +1 -0
  193. package/dist/web/assets/pascal-5tRlZk4O.js +1 -0
  194. package/dist/web/assets/pascaligo-CAE4rAEt.js +1 -0
  195. package/dist/web/assets/password-B0kJu8RE.js +101 -0
  196. package/dist/web/assets/perl-DREAbuQT.js +1 -0
  197. package/dist/web/assets/pgsql-DASKdyAW.js +1 -0
  198. package/dist/web/assets/php-C37ZWqoO.js +1 -0
  199. package/dist/web/assets/pla-v5u9R8i1.js +1 -0
  200. package/dist/web/assets/plus-CaknAInj.js +197 -0
  201. package/dist/web/assets/postiats-Szt5m-7G.js +1 -0
  202. package/dist/web/assets/powerquery-BzVrUSu3.js +1 -0
  203. package/dist/web/assets/powershell-BdbPbX_X.js +1 -0
  204. package/dist/web/assets/pretty-ms-CG-W6pSW.js +202 -0
  205. package/dist/web/assets/primeicons-C6QP2o4f.woff2 +0 -0
  206. package/dist/web/assets/primeicons-DMOk5skT.eot +0 -0
  207. package/dist/web/assets/primeicons-Dr5RGzOO.svg +345 -0
  208. package/dist/web/assets/primeicons-MpK4pl85.ttf +0 -0
  209. package/dist/web/assets/primeicons-WjwUDZjB.woff +0 -0
  210. package/dist/web/assets/process.store-DxDr1XKZ.js +1 -0
  211. package/dist/web/assets/project.store-CUclo-tf.js +1 -0
  212. package/dist/web/assets/protobuf-qqDa50tu.js +2 -0
  213. package/dist/web/assets/pug-Cu20UQ5w.js +1 -0
  214. package/dist/web/assets/python-BeJzJfgr.js +1 -0
  215. package/dist/web/assets/qsharp-DcbHL5X7.js +1 -0
  216. package/dist/web/assets/r-Bvig6Flk.js +1 -0
  217. package/dist/web/assets/radiobutton-Dpp9kpvR.js +145 -0
  218. package/dist/web/assets/razor-C8GjTcdm.js +1 -0
  219. package/dist/web/assets/redis-BxHDcW3-.js +1 -0
  220. package/dist/web/assets/redshift-B8dvlzjH.js +1 -0
  221. package/dist/web/assets/restructuredtext-npodtAFM.js +1 -0
  222. package/dist/web/assets/router-BAq_YY48.js +1 -0
  223. package/dist/web/assets/ruby-DiofbcJD.js +1 -0
  224. package/dist/web/assets/rust-BjYid3-m.js +1 -0
  225. package/dist/web/assets/sb-CzcPAP71.js +1 -0
  226. package/dist/web/assets/scala-Ci4wFrYi.js +1 -0
  227. package/dist/web/assets/scheme-D3iWhvP7.js +1 -0
  228. package/dist/web/assets/scss-DdlqDgmu.js +3 -0
  229. package/dist/web/assets/settings.store-RtV37pb_.js +1 -0
  230. package/dist/web/assets/shell-Bawb9gbH.js +1 -0
  231. package/dist/web/assets/solidity-Z2Sq8PBC.js +1 -0
  232. package/dist/web/assets/sophia--yD-H1Ow.js +1 -0
  233. package/dist/web/assets/sparql-0vv3-wLi.js +1 -0
  234. package/dist/web/assets/sql-0gdWKhmJ.js +1 -0
  235. package/dist/web/assets/st-rE-x-DTZ.js +1 -0
  236. package/dist/web/assets/store-BSUCffsu.js +1840 -0
  237. package/dist/web/assets/style-4Pk2icOw.css +1 -0
  238. package/dist/web/assets/swift-CSKeG8Ne.js +1 -0
  239. package/dist/web/assets/systemverilog-CblNM3Im.js +1 -0
  240. package/dist/web/assets/tabpanel-CXo9Yhvl.js +150 -0
  241. package/dist/web/assets/tag-BsP7PdQ9.js +54 -0
  242. package/dist/web/assets/tcl-BYnzj-GZ.js +1 -0
  243. package/dist/web/assets/toggleswitch-C0fuTzJt.js +115 -0
  244. package/dist/web/assets/trpc-DgyerAaK.js +1 -0
  245. package/dist/web/assets/ts.worker-DHQ2j8_R.js +51339 -0
  246. package/dist/web/assets/tsMode-CaqfXb40.js +11 -0
  247. package/dist/web/assets/twig-DiGWiEtp.js +1 -0
  248. package/dist/web/assets/typescript-D23_zyMA.js +1 -0
  249. package/dist/web/assets/typespec-a6xMcpXw.js +1 -0
  250. package/dist/web/assets/urls-D4kou9RH.js +12 -0
  251. package/dist/web/assets/use-tree-walker-BebEauYQ.js +1 -0
  252. package/dist/web/assets/useAiChatDrawer-DDBZQU71.js +1 -0
  253. package/dist/web/assets/useClipboard-BgE-EP5h.js +1 -0
  254. package/dist/web/assets/useLayout-BtC8xLAa.js +1 -0
  255. package/dist/web/assets/usePluginManager-MFUURiUn.js +1 -0
  256. package/dist/web/assets/useSidebar-DC34K6Vb.js +1 -0
  257. package/dist/web/assets/uuid-B9GFVXPm.js +1 -0
  258. package/dist/web/assets/vb---51HVMC.js +1 -0
  259. package/dist/web/assets/vue-router-CCwpUNtv.js +1 -0
  260. package/dist/web/assets/w3c-keyname-DDCGDLuI.js +1 -0
  261. package/dist/web/assets/wgsl-2A8z4pT5.js +298 -0
  262. package/dist/web/assets/xml-e0SmIpRt.js +1 -0
  263. package/dist/web/assets/yaml-ChhAbF6s.js +1 -0
  264. package/dist/web/index.html +14 -0
  265. package/package.json +8 -8
  266. package/Dockerfile +0 -54
  267. package/scripts/post-build.mjs +0 -61
  268. package/src/__tests__/cli-port.test.ts +0 -93
  269. package/src/index.ts +0 -298
  270. package/src/port-resolution.ts +0 -19
  271. package/tsconfig.json +0 -9
  272. package/tsup.config.bundled_lh0xis3zdq.mjs +0 -10
  273. package/tsup.config.bundled_qj1zdvku09a.mjs +0 -10
  274. package/tsup.config.ts +0 -6
@@ -0,0 +1,8 @@
1
+ import {
2
+ agentManager
3
+ } from "./chunk-XZPCEGWS.js";
4
+ import "./chunk-ERJIU7R4.js";
5
+ export {
6
+ agentManager
7
+ };
8
+ //# sourceMappingURL=agent-manager-3S4FUNJS-MYT5X6XQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,350 @@
1
+ import {
2
+ decryptValue,
3
+ encryptValue,
4
+ env
5
+ } from "./chunk-ERJIU7R4.js";
6
+
7
+ // ../server/dist/chunk-QJEZQQUG.js
8
+ import { randomUUID as randomUUID2 } from "crypto";
9
+ import { randomUUID } from "crypto";
10
+ import { EventEmitter } from "events";
11
+ import { readFile, writeFile, rename, mkdir, chmod } from "fs/promises";
12
+ import { join } from "path";
13
+ var FILENAME = "kanban.private.json.enc";
14
+ var KanbanStore = class {
15
+ boards = /* @__PURE__ */ new Map();
16
+ loaded = false;
17
+ events = new EventEmitter();
18
+ saveTimer = null;
19
+ getFilePath() {
20
+ return join(env.DATA_DIR, FILENAME);
21
+ }
22
+ async ensureDir() {
23
+ await mkdir(env.DATA_DIR, { recursive: true });
24
+ }
25
+ async load() {
26
+ if (this.loaded) return;
27
+ try {
28
+ const encrypted = await readFile(this.getFilePath(), "utf-8");
29
+ const raw = decryptValue(encrypted);
30
+ const data = JSON.parse(raw);
31
+ for (const b of data) {
32
+ this.boards.set(b.id, b);
33
+ }
34
+ } catch {
35
+ }
36
+ this.loaded = true;
37
+ }
38
+ async saveNow() {
39
+ await this.ensureDir();
40
+ const filePath = this.getFilePath();
41
+ const tmpPath = filePath + ".tmp";
42
+ const json = JSON.stringify(Array.from(this.boards.values()), null, 2);
43
+ const encrypted = encryptValue(json);
44
+ await writeFile(tmpPath, encrypted, "utf-8");
45
+ await rename(tmpPath, filePath);
46
+ await chmod(filePath, 384);
47
+ }
48
+ save() {
49
+ if (this.saveTimer) clearTimeout(this.saveTimer);
50
+ this.saveTimer = setTimeout(() => {
51
+ this.saveNow().catch((err) => {
52
+ console.error("[kanban-store] save failed:", err);
53
+ });
54
+ }, 200);
55
+ }
56
+ async list(includeArchived = false) {
57
+ await this.load();
58
+ const all = Array.from(this.boards.values());
59
+ return includeArchived ? all : all.filter((b) => !b.archived);
60
+ }
61
+ async get(id) {
62
+ await this.load();
63
+ return this.boards.get(id) ?? null;
64
+ }
65
+ async create(input) {
66
+ await this.load();
67
+ const now = (/* @__PURE__ */ new Date()).toISOString();
68
+ const board = {
69
+ id: randomUUID(),
70
+ name: input.name,
71
+ description: input.description ?? "",
72
+ columns: [],
73
+ archived: false,
74
+ createdAt: now,
75
+ updatedAt: now
76
+ };
77
+ this.boards.set(board.id, board);
78
+ this.save();
79
+ this.events.emit("changed", { type: "created", board });
80
+ return board;
81
+ }
82
+ async update(input) {
83
+ await this.load();
84
+ const existing = this.boards.get(input.id);
85
+ if (!existing) return null;
86
+ const updated = {
87
+ ...existing,
88
+ ...input.name !== void 0 && { name: input.name },
89
+ ...input.description !== void 0 && { description: input.description },
90
+ ...input.columns !== void 0 && { columns: input.columns },
91
+ ...input.archived !== void 0 && { archived: input.archived },
92
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
93
+ };
94
+ this.boards.set(input.id, updated);
95
+ this.save();
96
+ this.events.emit("changed", { type: "updated", board: updated });
97
+ return updated;
98
+ }
99
+ async delete(id) {
100
+ await this.load();
101
+ if (!this.boards.has(id)) return false;
102
+ this.boards.delete(id);
103
+ this.save();
104
+ this.events.emit("changed", { type: "deleted", id });
105
+ return true;
106
+ }
107
+ onChanged(handler) {
108
+ this.events.on("changed", handler);
109
+ }
110
+ offChanged(handler) {
111
+ this.events.off("changed", handler);
112
+ }
113
+ };
114
+ var kanbanStore = new KanbanStore();
115
+ async function executeKanbanCommands(boardId, cardId, commands, conversationId) {
116
+ if (commands.length === 0) return;
117
+ let board = await kanbanStore.get(boardId);
118
+ if (!board) {
119
+ console.warn(`[kanban-commands] Board ${boardId} not found, skipping ${commands.length} command(s)`);
120
+ return;
121
+ }
122
+ const cardExistsInBoard = () => board.columns.some((col) => col.cards.some((c) => c.id === cardId));
123
+ if (!cardExistsInBoard()) {
124
+ for (let attempt = 1; attempt <= 5; attempt++) {
125
+ console.warn(`[kanban-commands] Card ${cardId} not found in board, retry ${attempt}/5...`);
126
+ await new Promise((r) => setTimeout(r, 500 * attempt));
127
+ board = await kanbanStore.get(boardId);
128
+ if (!board) return;
129
+ if (cardExistsInBoard()) break;
130
+ }
131
+ if (!cardExistsInBoard()) {
132
+ console.error(`[kanban-commands] Card ${cardId} still not found after retries, skipping ${commands.length} command(s)`);
133
+ return;
134
+ }
135
+ }
136
+ for (const cmd of commands) {
137
+ if (cmd.type === "move_card" && cmd.columnTitle) {
138
+ const moved = moveCard(board, cardId, cmd.columnTitle);
139
+ if (!moved) console.warn(`[kanban-commands] move_card failed: card=${cardId} target="${cmd.columnTitle}"`);
140
+ } else if (cmd.type === "add_comment" && cmd.comment) {
141
+ addComment(board, cardId, cmd.comment, conversationId);
142
+ } else if (cmd.type === "add_card" && cmd.columnTitle && cmd.cardTitle) {
143
+ addCardToColumn(board, cmd.columnTitle, cmd.cardTitle, cmd.cardContent);
144
+ }
145
+ }
146
+ await kanbanStore.update({ id: board.id, columns: board.columns });
147
+ }
148
+ async function executeKanbanBoardCommands(boardId, commands) {
149
+ if (commands.length === 0) return;
150
+ const board = await kanbanStore.get(boardId);
151
+ if (!board) return;
152
+ for (const cmd of commands) {
153
+ if (cmd.type === "add_card" && cmd.columnTitle && cmd.cardTitle) {
154
+ addCardToColumn(board, cmd.columnTitle, cmd.cardTitle, cmd.cardContent);
155
+ }
156
+ }
157
+ await kanbanStore.update({ id: board.id, columns: board.columns });
158
+ }
159
+ function moveCard(board, cardId, targetColTitle) {
160
+ let sourceCol = "";
161
+ let card = null;
162
+ let sourceColIdx = -1;
163
+ let cardIdx = -1;
164
+ for (let ci = 0; ci < board.columns.length; ci++) {
165
+ const col = board.columns[ci];
166
+ const idx = col.cards.findIndex((c) => c.id === cardId);
167
+ if (idx !== -1) {
168
+ sourceCol = col.title;
169
+ card = col.cards[idx];
170
+ sourceColIdx = ci;
171
+ cardIdx = idx;
172
+ break;
173
+ }
174
+ }
175
+ if (!card || sourceColIdx === -1) return false;
176
+ const targetCol = board.columns.find(
177
+ (c) => c.title.toLowerCase() === targetColTitle.toLowerCase()
178
+ );
179
+ if (!targetCol) {
180
+ console.warn(`[kanban-commands] Target column "${targetColTitle}" not found. Available: ${board.columns.map((c) => c.title).join(", ")}`);
181
+ return false;
182
+ }
183
+ if (targetCol.id === board.columns[sourceColIdx].id) return true;
184
+ board.columns[sourceColIdx].cards.splice(cardIdx, 1);
185
+ card.order = targetCol.cards.length;
186
+ card.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
187
+ card.history = [...card.history ?? [], {
188
+ type: "moved",
189
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
190
+ detail: `"${sourceCol}" \u2192 "${targetCol.title}"`,
191
+ author: "ai"
192
+ }];
193
+ targetCol.cards.push(card);
194
+ return true;
195
+ }
196
+ function addCardToColumn(board, columnTitle, cardTitle, cardContent) {
197
+ const col = board.columns.find((c) => c.title.toLowerCase() === columnTitle.toLowerCase());
198
+ if (!col) return;
199
+ const now = (/* @__PURE__ */ new Date()).toISOString();
200
+ const card = {
201
+ id: randomUUID2(),
202
+ title: cardTitle,
203
+ markdown: cardContent ?? "",
204
+ images: [],
205
+ order: col.cards.length,
206
+ history: [{ type: "created", timestamp: now, detail: `AI \u2014 In column "${col.title}"` }],
207
+ createdAt: now,
208
+ updatedAt: now
209
+ };
210
+ col.cards.push(card);
211
+ }
212
+ function addComment(board, cardId, comment, conversationId) {
213
+ for (const col of board.columns) {
214
+ const card = col.cards.find((c) => c.id === cardId);
215
+ if (card) {
216
+ card.history = [...card.history ?? [], {
217
+ type: "comment",
218
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
219
+ detail: comment,
220
+ author: "ai",
221
+ ...conversationId ? { conversationId } : {}
222
+ }];
223
+ card.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
224
+ return;
225
+ }
226
+ }
227
+ }
228
+ function generateKanbanSystemPrompt(board, cardId) {
229
+ let currentColTitle = "";
230
+ let card = null;
231
+ for (const col of board.columns) {
232
+ const found = col.cards.find((c) => c.id === cardId);
233
+ if (found) {
234
+ currentColTitle = col.title;
235
+ card = found;
236
+ break;
237
+ }
238
+ }
239
+ const sortedColumns = [...board.columns].sort((a, b) => a.order - b.order);
240
+ const lines = [];
241
+ lines.push("## Kanban Board Context");
242
+ lines.push("");
243
+ lines.push(`Tu travailles dans le contexte d'une carte Kanban.`);
244
+ lines.push(`- **Board** : ${board.name}`);
245
+ if (card) {
246
+ lines.push(`- **Carte** : ${card.title}`);
247
+ lines.push(`- **Colonne actuelle** : \`${currentColTitle}\``);
248
+ }
249
+ lines.push("");
250
+ lines.push("### Colonnes disponibles (noms exacts \xE0 utiliser) :");
251
+ for (const col of sortedColumns) {
252
+ const cardCount = col.cards.length;
253
+ const isCurrent = card && col.cards.some((c) => c.id === cardId);
254
+ lines.push(`- \`${col.title}\` (${cardCount} carte${cardCount !== 1 ? "s" : ""})${isCurrent ? " \u2190 **colonne actuelle**" : ""}`);
255
+ }
256
+ lines.push("");
257
+ if (card) {
258
+ const comments = (card.history ?? []).filter((e) => e.type === "comment");
259
+ if (comments.length > 0) {
260
+ lines.push("### Commentaires existants sur cette carte :");
261
+ lines.push("");
262
+ for (const c of comments.slice(-20)) {
263
+ const who = c.author === "ai" ? "AI" : "Utilisateur";
264
+ const date = new Date(c.timestamp).toLocaleString("fr-FR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" });
265
+ lines.push(`- **${who}** (${date}) : ${c.detail}`);
266
+ }
267
+ lines.push("");
268
+ }
269
+ }
270
+ lines.push("### Outils Kanban disponibles");
271
+ lines.push("");
272
+ lines.push(`- **boardId** (\xE0 passer dans tous les outils) : \`${board.id}\``);
273
+ if (card) lines.push(`- **cardId** (carte actuelle) : \`${card.id}\``);
274
+ lines.push("");
275
+ lines.push("Tu disposes des outils suivants pour interagir avec le board :");
276
+ lines.push("- `kanban_move_card(boardId, cardId, targetColumn)` \u2014 D\xE9placer la carte vers une autre colonne");
277
+ lines.push("- `kanban_add_comment(boardId, cardId, comment)` \u2014 Ajouter un commentaire sur la carte");
278
+ lines.push("");
279
+ lines.push("### OBLIGATOIRE \u2014 D\xE9cision Kanban");
280
+ lines.push("");
281
+ lines.push("\xC0 chaque r\xE9ponse, tu DOIS obligatoirement faire les deux actions suivantes :");
282
+ lines.push("");
283
+ lines.push("**1. \xC9valuer et d\xE9placer la carte si n\xE9cessaire** via `kanban_move_card` :");
284
+ lines.push(" - **Au d\xE9but** si tu sais d\xE9j\xE0 que tu vas travailler activement (ex. tu passes en `In progress`).");
285
+ lines.push(" - **\xC0 la fin** pour une d\xE9cision forte apr\xE8s analyse compl\xE8te (ex. tu conclus que c'est `Done`).");
286
+ lines.push(" - Si tu ne fais qu'analyser ou poser des questions \u2192 **ne pas d\xE9placer**.");
287
+ lines.push("");
288
+ lines.push(" > \u26A0\uFE0F **CRITIQUE** : En fin de r\xE9ponse, tu DOIS TOUJOURS \xE9valuer si la colonne actuelle est encore correcte.");
289
+ lines.push("");
290
+ lines.push("**2. Appeler `kanban_add_comment`** r\xE9sumant ce que tu as fait ou constat\xE9 (**\xE0 la fin** de ta r\xE9ponse).");
291
+ lines.push("");
292
+ lines.push("> \u{1F6AB} **INTERDIT** : Terminer une r\xE9ponse sans appeler `kanban_add_comment`. C'est non n\xE9gociable.");
293
+ lines.push("");
294
+ return lines.join("\n");
295
+ }
296
+ function generateKanbanPopulatePrompt(board) {
297
+ const lines = [];
298
+ lines.push("## Kanban Board \u2014 Mode Populate");
299
+ lines.push("");
300
+ lines.push(`Tu es en mode **populate** sur le tableau Kanban **"${board.name}"**.`);
301
+ lines.push(`Tu es un outil de gestion de projet. Quand l'utilisateur dit "cr\xE9er une card", "ajouter une t\xE2che", "cr\xE9er une carte", ou toute demande similaire, il parle EXCLUSIVEMENT d'une carte Kanban \xE0 ajouter au tableau \u2014 PAS d'un composant UI, PAS de code HTML ou CSS, PAS d'un composant React/Vue/Angular.`);
302
+ lines.push("");
303
+ lines.push("Ton UNIQUE r\xF4le est de cr\xE9er des cartes Kanban pertinentes en r\xE9ponse \xE0 la demande de l'utilisateur.");
304
+ lines.push("Tu ne dois PAS r\xE9pondre \xE0 la question, ni donner d'explication, ni \xE9crire du code, ni montrer de composant.");
305
+ lines.push("Tu dois UNIQUEMENT produire des tags XML `<kanban_add_card>` pour cr\xE9er des cartes dans le tableau.");
306
+ if (board.description) {
307
+ lines.push("");
308
+ lines.push(`Description du board : ${board.description}`);
309
+ }
310
+ lines.push("");
311
+ const sortedCols = [...board.columns].sort((a, b) => a.order - b.order);
312
+ lines.push("### Colonnes existantes (noms exacts \xE0 utiliser) :");
313
+ for (const col of sortedCols) {
314
+ const cardCount = col.cards.length;
315
+ lines.push(`- \`${col.title}\` (${cardCount} carte${cardCount !== 1 ? "s" : ""})`);
316
+ for (const card of col.cards) {
317
+ lines.push(` - ${card.title}`);
318
+ }
319
+ }
320
+ lines.push("");
321
+ lines.push("### Outil disponible");
322
+ lines.push("");
323
+ lines.push(`- **boardId** (\xE0 passer dans tous les outils) : \`${board.id}\``);
324
+ lines.push("");
325
+ lines.push("Pour chaque carte \xE0 cr\xE9er, appelle l'outil `kanban_add_card(boardId, column, title, content)` :");
326
+ lines.push("- `column` : nom exact de la colonne cible");
327
+ lines.push("- `title` : titre concis de la carte");
328
+ lines.push("- `content` : contenu markdown d\xE9taill\xE9 et actionnable");
329
+ lines.push("");
330
+ lines.push("### R\xE8gles STRICTES");
331
+ lines.push("");
332
+ lines.push("1. **NE R\xC9PONDS PAS** \xE0 la question de l'utilisateur. Ne donne aucune explication, aucun texte libre.");
333
+ lines.push("2. **Appelle uniquement** `kanban_add_card`, rien d'autre.");
334
+ lines.push("3. Tu ne peux PAS cr\xE9er de nouvelles colonnes. Dispatch les cartes dans les colonnes existantes list\xE9es ci-dessus.");
335
+ lines.push("4. Utilise les noms **exacts** des colonnes.");
336
+ lines.push("5. Chaque carte doit avoir un titre concis et un contenu markdown d\xE9taill\xE9 et actionnable.");
337
+ lines.push("6. D\xE9compose la demande de l'utilisateur en t\xE2ches concr\xE8tes, chacune \xE9tant une carte.");
338
+ lines.push("7. Dispatch chaque carte dans la colonne la plus appropri\xE9e selon sa nature.");
339
+ lines.push("");
340
+ return lines.join("\n");
341
+ }
342
+
343
+ export {
344
+ kanbanStore,
345
+ executeKanbanCommands,
346
+ executeKanbanBoardCommands,
347
+ generateKanbanSystemPrompt,
348
+ generateKanbanPopulatePrompt
349
+ };
350
+ //# sourceMappingURL=chunk-32HTB3XS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../server/src/services/kanban-commands.ts","../../server/src/services/kanban-store.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { kanbanStore } from './kanban-store.js';\nimport type { KanbanBoard, KanbanCard } from '@runeya/packages-shared';\n\n// ─── Command parsing ────────────────────────────────────────────────────────\n\nexport interface KanbanCommand {\n type: 'move_card' | 'add_comment' | 'add_card';\n /** Target column title (for move_card, add_card) */\n columnTitle?: string;\n /** Comment text (for add_comment) */\n comment?: string;\n /** Card title (for add_card) */\n cardTitle?: string;\n /** Card content / markdown (for add_card) */\n cardContent?: string;\n}\n\n/**\n * Parse kanban commands from AI output.\n * Commands use XML tags for robustness (support multiline content, no delimiter conflicts).\n *\n * Supported tags:\n * <kanban_move column=\"Column Title\" />\n * <kanban_comment>Comment text (multiline ok)</kanban_comment>\n * <kanban_add_card column=\"Col\" title=\"Card Title\">Markdown content</kanban_add_card>\n */\nfunction parseKanbanCommands(output: string): KanbanCommand[] {\n const commands: KanbanCommand[] = [];\n\n // <kanban_move column=\"...\" />\n const moveMatches = output.matchAll(/<kanban_move\\s+column=\"([^\"]*?)\"\\s*\\/>/gs);\n for (const m of moveMatches) {\n commands.push({ type: 'move_card', columnTitle: m[1].trim() });\n }\n\n // <kanban_comment>...</kanban_comment>\n const commentMatches = output.matchAll(/<kanban_comment>([\\s\\S]*?)<\\/kanban_comment>/g);\n for (const m of commentMatches) {\n commands.push({ type: 'add_comment', comment: m[1].trim() });\n }\n\n // <kanban_add_card column=\"...\" title=\"...\">...</kanban_add_card>\n // Accept attributes in any order (some LLMs swap column= and title=)\n // Use (?:[^>\"]|\"[^\"]*\")* to correctly handle `>` inside quoted attribute values\n // (e.g. title=\"CÔTÉ CLIENT > PANEL\" would break [^>]* which stops at the first >)\n const addCardMatches = output.matchAll(/<kanban_add_card\\s+((?:[^>\"]|\"[^\"]*\")*?)>([\\s\\S]*?)<\\/kanban_add_card>/g);\n for (const m of addCardMatches) {\n const attrs = m[1];\n const colMatch = attrs.match(/column=\"([^\"]*?)\"/);\n const titleMatch = attrs.match(/title=\"([^\"]*?)\"/);\n if (colMatch && titleMatch) {\n commands.push({\n type: 'add_card',\n columnTitle: colMatch[1].trim(),\n cardTitle: titleMatch[1].trim(),\n cardContent: m[2].trim(),\n });\n }\n }\n\n return commands;\n}\n\n// ─── Command execution ──────────────────────────────────────────────────────\n\nexport async function executeKanbanCommands(\n boardId: string,\n cardId: string,\n commands: KanbanCommand[],\n conversationId?: string,\n): Promise<void> {\n if (commands.length === 0) return;\n\n // Retry loading the board a few times — the card may not have been persisted yet\n // when the conversation was launched before the card was fully saved.\n let board = await kanbanStore.get(boardId);\n if (!board) {\n console.warn(`[kanban-commands] Board ${boardId} not found, skipping ${commands.length} command(s)`);\n return;\n }\n\n // Check if card exists in the board; if not, retry a few times with a short delay\n const cardExistsInBoard = () => board!.columns.some(col => col.cards.some(c => c.id === cardId));\n if (!cardExistsInBoard()) {\n for (let attempt = 1; attempt <= 5; attempt++) {\n console.warn(`[kanban-commands] Card ${cardId} not found in board, retry ${attempt}/5...`);\n await new Promise(r => setTimeout(r, 500 * attempt));\n board = await kanbanStore.get(boardId);\n if (!board) return;\n if (cardExistsInBoard()) break;\n }\n if (!cardExistsInBoard()) {\n console.error(`[kanban-commands] Card ${cardId} still not found after retries, skipping ${commands.length} command(s)`);\n return;\n }\n }\n\n for (const cmd of commands) {\n if (cmd.type === 'move_card' && cmd.columnTitle) {\n const moved = moveCard(board, cardId, cmd.columnTitle);\n if (!moved) console.warn(`[kanban-commands] move_card failed: card=${cardId} target=\"${cmd.columnTitle}\"`);\n } else if (cmd.type === 'add_comment' && cmd.comment) {\n addComment(board, cardId, cmd.comment, conversationId);\n } else if (cmd.type === 'add_card' && cmd.columnTitle && cmd.cardTitle) {\n addCardToColumn(board, cmd.columnTitle, cmd.cardTitle, cmd.cardContent);\n }\n }\n\n await kanbanStore.update({ id: board.id, columns: board.columns });\n}\n\n/**\n * Execute board-level commands (no card context needed).\n * Used by the AI populate feature.\n */\nexport async function executeKanbanBoardCommands(\n boardId: string,\n commands: KanbanCommand[],\n): Promise<void> {\n if (commands.length === 0) return;\n\n const board = await kanbanStore.get(boardId);\n if (!board) return;\n\n for (const cmd of commands) {\n if (cmd.type === 'add_card' && cmd.columnTitle && cmd.cardTitle) {\n addCardToColumn(board, cmd.columnTitle, cmd.cardTitle, cmd.cardContent);\n }\n }\n\n await kanbanStore.update({ id: board.id, columns: board.columns });\n}\n\nfunction moveCard(board: KanbanBoard, cardId: string, targetColTitle: string): boolean {\n let sourceCol = '';\n let card = null;\n let sourceColIdx = -1;\n let cardIdx = -1;\n\n for (let ci = 0; ci < board.columns.length; ci++) {\n const col = board.columns[ci];\n const idx = col.cards.findIndex(c => c.id === cardId);\n if (idx !== -1) {\n sourceCol = col.title;\n card = col.cards[idx];\n sourceColIdx = ci;\n cardIdx = idx;\n break;\n }\n }\n if (!card || sourceColIdx === -1) return false;\n\n const targetCol = board.columns.find(\n c => c.title.toLowerCase() === targetColTitle.toLowerCase(),\n );\n if (!targetCol) {\n console.warn(`[kanban-commands] Target column \"${targetColTitle}\" not found. Available: ${board.columns.map(c => c.title).join(', ')}`);\n return false;\n }\n if (targetCol.id === board.columns[sourceColIdx].id) return true; // already there\n\n board.columns[sourceColIdx].cards.splice(cardIdx, 1);\n\n card.order = targetCol.cards.length;\n card.updatedAt = new Date().toISOString();\n card.history = [...(card.history ?? []), {\n type: 'moved',\n timestamp: new Date().toISOString(),\n detail: `\"${sourceCol}\" → \"${targetCol.title}\"`,\n author: 'ai',\n }];\n\n targetCol.cards.push(card);\n return true;\n}\n\nfunction addCardToColumn(board: KanbanBoard, columnTitle: string, cardTitle: string, cardContent?: string): void {\n const col = board.columns.find(c => c.title.toLowerCase() === columnTitle.toLowerCase());\n // Ignore if column doesn't exist — AI must use existing columns\n if (!col) return;\n const now = new Date().toISOString();\n const card: KanbanCard = {\n id: randomUUID(),\n title: cardTitle,\n markdown: cardContent ?? '',\n images: [],\n order: col.cards.length,\n history: [{ type: 'created', timestamp: now, detail: `AI — In column \"${col.title}\"` }],\n createdAt: now,\n updatedAt: now,\n };\n col.cards.push(card);\n}\n\nfunction addComment(board: KanbanBoard, cardId: string, comment: string, conversationId?: string): void {\n for (const col of board.columns) {\n const card = col.cards.find(c => c.id === cardId);\n if (card) {\n card.history = [...(card.history ?? []), {\n type: 'comment',\n timestamp: new Date().toISOString(),\n detail: comment,\n author: 'ai',\n ...(conversationId ? { conversationId } : {}),\n }];\n card.updatedAt = new Date().toISOString();\n return;\n }\n }\n}\n\n// ─── System prompt for kanban context ───────────────────────────────────────\n\nexport function generateKanbanSystemPrompt(board: KanbanBoard, cardId: string): string {\n let currentColTitle = '';\n let card = null;\n for (const col of board.columns) {\n const found = col.cards.find(c => c.id === cardId);\n if (found) {\n currentColTitle = col.title;\n card = found;\n break;\n }\n }\n\n const sortedColumns = [...board.columns].sort((a, b) => a.order - b.order);\n\n const lines: string[] = [];\n lines.push('## Kanban Board Context');\n lines.push('');\n lines.push(`Tu travailles dans le contexte d'une carte Kanban.`);\n lines.push(`- **Board** : ${board.name}`);\n if (card) {\n lines.push(`- **Carte** : ${card.title}`);\n lines.push(`- **Colonne actuelle** : \\`${currentColTitle}\\``);\n }\n lines.push('');\n\n // List columns explicitly so the AI knows the exact names to use\n lines.push('### Colonnes disponibles (noms exacts à utiliser) :');\n for (const col of sortedColumns) {\n const cardCount = col.cards.length;\n const isCurrent = card && col.cards.some(c => c.id === cardId);\n lines.push(`- \\`${col.title}\\` (${cardCount} carte${cardCount !== 1 ? 's' : ''})${isCurrent ? ' ← **colonne actuelle**' : ''}`);\n }\n lines.push('');\n\n // Inject existing comments\n if (card) {\n const comments = (card.history ?? []).filter(e => e.type === 'comment');\n if (comments.length > 0) {\n lines.push('### Commentaires existants sur cette carte :');\n lines.push('');\n for (const c of comments.slice(-20)) { // Last 20 comments max\n const who = c.author === 'ai' ? 'AI' : 'Utilisateur';\n const date = new Date(c.timestamp).toLocaleString('fr-FR', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' });\n lines.push(`- **${who}** (${date}) : ${c.detail}`);\n }\n lines.push('');\n }\n }\n\n lines.push('### Outils Kanban disponibles');\n lines.push('');\n lines.push(`- **boardId** (à passer dans tous les outils) : \\`${board.id}\\``);\n if (card) lines.push(`- **cardId** (carte actuelle) : \\`${card.id}\\``);\n lines.push('');\n lines.push('Tu disposes des outils suivants pour interagir avec le board :');\n lines.push('- `kanban_move_card(boardId, cardId, targetColumn)` — Déplacer la carte vers une autre colonne');\n lines.push('- `kanban_add_comment(boardId, cardId, comment)` — Ajouter un commentaire sur la carte');\n lines.push('');\n lines.push('### OBLIGATOIRE — Décision Kanban');\n lines.push('');\n lines.push('À chaque réponse, tu DOIS obligatoirement faire les deux actions suivantes :');\n lines.push('');\n lines.push('**1. Évaluer et déplacer la carte si nécessaire** via `kanban_move_card` :');\n lines.push(' - **Au début** si tu sais déjà que tu vas travailler activement (ex. tu passes en `In progress`).');\n lines.push(' - **À la fin** pour une décision forte après analyse complète (ex. tu conclus que c\\'est `Done`).');\n lines.push(' - Si tu ne fais qu\\'analyser ou poser des questions → **ne pas déplacer**.');\n lines.push('');\n lines.push(' > ⚠️ **CRITIQUE** : En fin de réponse, tu DOIS TOUJOURS évaluer si la colonne actuelle est encore correcte.');\n lines.push('');\n lines.push('**2. Appeler `kanban_add_comment`** résumant ce que tu as fait ou constaté (**à la fin** de ta réponse).');\n lines.push('');\n lines.push('> 🚫 **INTERDIT** : Terminer une réponse sans appeler `kanban_add_comment`. C\\'est non négociable.');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n// ─── Board-level populate prompt ─────────────────────────────────────────────\n\nexport function generateKanbanPopulatePrompt(board: KanbanBoard): string {\n const lines: string[] = [];\n lines.push('## Kanban Board — Mode Populate');\n lines.push('');\n lines.push(`Tu es en mode **populate** sur le tableau Kanban **\"${board.name}\"**.`);\n lines.push('Tu es un outil de gestion de projet. Quand l\\'utilisateur dit \"créer une card\", \"ajouter une tâche\", \"créer une carte\", ou toute demande similaire, il parle EXCLUSIVEMENT d\\'une carte Kanban à ajouter au tableau — PAS d\\'un composant UI, PAS de code HTML ou CSS, PAS d\\'un composant React/Vue/Angular.');\n lines.push('');\n lines.push('Ton UNIQUE rôle est de créer des cartes Kanban pertinentes en réponse à la demande de l\\'utilisateur.');\n lines.push('Tu ne dois PAS répondre à la question, ni donner d\\'explication, ni écrire du code, ni montrer de composant.');\n lines.push('Tu dois UNIQUEMENT produire des tags XML `<kanban_add_card>` pour créer des cartes dans le tableau.');\n if (board.description) {\n lines.push('');\n lines.push(`Description du board : ${board.description}`);\n }\n lines.push('');\n\n const sortedCols = [...board.columns].sort((a, b) => a.order - b.order);\n\n lines.push('### Colonnes existantes (noms exacts à utiliser) :');\n for (const col of sortedCols) {\n const cardCount = col.cards.length;\n lines.push(`- \\`${col.title}\\` (${cardCount} carte${cardCount !== 1 ? 's' : ''})`);\n for (const card of col.cards) {\n lines.push(` - ${card.title}`);\n }\n }\n lines.push('');\n\n lines.push('### Outil disponible');\n lines.push('');\n lines.push(`- **boardId** (à passer dans tous les outils) : \\`${board.id}\\``);\n lines.push('');\n lines.push('Pour chaque carte à créer, appelle l\\'outil `kanban_add_card(boardId, column, title, content)` :');\n lines.push('- `column` : nom exact de la colonne cible');\n lines.push('- `title` : titre concis de la carte');\n lines.push('- `content` : contenu markdown détaillé et actionnable');\n lines.push('');\n lines.push('### Règles STRICTES');\n lines.push('');\n lines.push('1. **NE RÉPONDS PAS** à la question de l\\'utilisateur. Ne donne aucune explication, aucun texte libre.');\n lines.push('2. **Appelle uniquement** `kanban_add_card`, rien d\\'autre.');\n lines.push('3. Tu ne peux PAS créer de nouvelles colonnes. Dispatch les cartes dans les colonnes existantes listées ci-dessus.');\n lines.push('4. Utilise les noms **exacts** des colonnes.');\n lines.push('5. Chaque carte doit avoir un titre concis et un contenu markdown détaillé et actionnable.');\n lines.push('6. Décompose la demande de l\\'utilisateur en tâches concrètes, chacune étant une carte.');\n lines.push('7. Dispatch chaque carte dans la colonne la plus appropriée selon sa nature.');\n lines.push('');\n\n return lines.join('\\n');\n}\n","import { randomUUID } from 'node:crypto';\nimport { EventEmitter } from 'node:events';\nimport { readFile, writeFile, rename, mkdir, chmod } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { env } from '../config/env.js';\nimport { encryptValue, decryptValue } from './crypto-helpers.js';\nimport type { KanbanBoard, CreateKanbanBoardInput, UpdateKanbanBoardInput } from '@runeya/packages-shared';\n\nexport type KanbanChangeEvent =\n | { type: 'created'; board: KanbanBoard }\n | { type: 'updated'; board: KanbanBoard }\n | { type: 'deleted'; id: string };\n\nconst FILENAME = 'kanban.private.json.enc';\n\nclass KanbanStore {\n private boards: Map<string, KanbanBoard> = new Map();\n private loaded = false;\n private readonly events = new EventEmitter();\n private saveTimer: ReturnType<typeof setTimeout> | null = null;\n\n private getFilePath(): string {\n return join(env.DATA_DIR, FILENAME);\n }\n\n private async ensureDir(): Promise<void> {\n await mkdir(env.DATA_DIR, { recursive: true });\n }\n\n private async load(): Promise<void> {\n if (this.loaded) return;\n try {\n const encrypted = await readFile(this.getFilePath(), 'utf-8');\n const raw = decryptValue(encrypted);\n const data: KanbanBoard[] = JSON.parse(raw);\n for (const b of data) {\n this.boards.set(b.id, b);\n }\n } catch {\n // File doesn't exist yet or decryption failed, start empty\n }\n this.loaded = true;\n }\n\n private async saveNow(): Promise<void> {\n await this.ensureDir();\n const filePath = this.getFilePath();\n const tmpPath = filePath + '.tmp';\n const json = JSON.stringify(Array.from(this.boards.values()), null, 2);\n const encrypted = encryptValue(json);\n await writeFile(tmpPath, encrypted, 'utf-8');\n await rename(tmpPath, filePath);\n await chmod(filePath, 0o600);\n }\n\n private save(): void {\n if (this.saveTimer) clearTimeout(this.saveTimer);\n this.saveTimer = setTimeout(() => {\n this.saveNow().catch((err) => {\n console.error('[kanban-store] save failed:', err);\n });\n }, 200);\n }\n\n async list(includeArchived = false): Promise<KanbanBoard[]> {\n await this.load();\n const all = Array.from(this.boards.values());\n return includeArchived ? all : all.filter(b => !b.archived);\n }\n\n async get(id: string): Promise<KanbanBoard | null> {\n await this.load();\n return this.boards.get(id) ?? null;\n }\n\n async create(input: CreateKanbanBoardInput): Promise<KanbanBoard> {\n await this.load();\n const now = new Date().toISOString();\n const board: KanbanBoard = {\n id: randomUUID(),\n name: input.name,\n description: input.description ?? '',\n columns: [],\n archived: false,\n createdAt: now,\n updatedAt: now,\n };\n this.boards.set(board.id, board);\n this.save();\n this.events.emit('changed', { type: 'created', board } as KanbanChangeEvent);\n return board;\n }\n\n async update(input: UpdateKanbanBoardInput): Promise<KanbanBoard | null> {\n await this.load();\n const existing = this.boards.get(input.id);\n if (!existing) return null;\n const updated: KanbanBoard = {\n ...existing,\n ...(input.name !== undefined && { name: input.name }),\n ...(input.description !== undefined && { description: input.description }),\n ...(input.columns !== undefined && { columns: input.columns }),\n ...(input.archived !== undefined && { archived: input.archived }),\n updatedAt: new Date().toISOString(),\n };\n this.boards.set(input.id, updated);\n this.save();\n this.events.emit('changed', { type: 'updated', board: updated } as KanbanChangeEvent);\n return updated;\n }\n\n async delete(id: string): Promise<boolean> {\n await this.load();\n if (!this.boards.has(id)) return false;\n this.boards.delete(id);\n this.save();\n this.events.emit('changed', { type: 'deleted', id } as KanbanChangeEvent);\n return true;\n }\n\n onChanged(handler: (event: KanbanChangeEvent) => void): void {\n this.events.on('changed', handler);\n }\n\n offChanged(handler: (event: KanbanChangeEvent) => void): void {\n this.events.off('changed', handler);\n }\n}\n\nexport const kanbanStore = new KanbanStore();\n"],"mappings":"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","names":["randomUUID"]}
@@ -0,0 +1,195 @@
1
+ // ../server/dist/chunk-ECCOVHDM.js
2
+ import { z } from "zod";
3
+ import { createHash } from "crypto";
4
+ import { randomBytes, createCipheriv, createDecipheriv } from "crypto";
5
+ var effectivePortStr = process.env.RUNEYA_HTTP_PORT ?? process.env.PORT;
6
+ var serverPort = z.coerce.number().int().min(1).max(65535).default(4e3).parse(effectivePortStr);
7
+ var effectiveAgentPortStr = process.env.RUNEYA_AGENT_PORT ?? process.env.AGENT_PORT;
8
+ var defaultAgentPort = effectiveAgentPortStr ? z.coerce.number().int().min(1).max(65535).parse(effectiveAgentPortStr) : serverPort + 1;
9
+ var EnvSchema = z.object({
10
+ RUNEYA_HTTP_PORT: z.coerce.number().int().min(1).max(65535).optional(),
11
+ PORT: z.coerce.number().int().min(1).max(65535).default(4e3),
12
+ HOST: z.string().default("127.0.0.1"),
13
+ RUNEYA_AGENT_PORT: z.coerce.number().int().min(1).max(65535).default(defaultAgentPort),
14
+ RUNEYA_AGENT_HOST: z.string().default("127.0.0.1"),
15
+ AGENT_BINARY_PATH: z.string().nullable().default(null),
16
+ DATA_DIR: z.preprocess((v) => typeof v === "string" && v.length > 0 ? v : void 0, z.string().default(".runeya")),
17
+ CLOUD_URL: z.string().url().nullable().default(null),
18
+ MODE: z.enum(["local-simple", "cloud"]).default("local-simple"),
19
+ CORS_ORIGIN: z.string().nullable().default(null),
20
+ RUNEYA_LAN_PROXY: z.preprocess((v) => v === "true" || v === "1", z.boolean()).default(false)
21
+ }).transform((data) => ({
22
+ ...data,
23
+ // RUNEYA_HTTP_PORT takes priority over PORT
24
+ PORT: data.RUNEYA_HTTP_PORT ?? data.PORT
25
+ }));
26
+ var env = EnvSchema.parse(process.env);
27
+ var KEY_SIZE_BYTES = 32;
28
+ var cachedKey = null;
29
+ var _ciMode = false;
30
+ var _pendingLegacyKey = null;
31
+ function isCiMode() {
32
+ return _ciMode;
33
+ }
34
+ async function loadFromEnv() {
35
+ const keyHex = process.env["RUNEYA_ENCRYPTION_KEY"];
36
+ if (!keyHex) return false;
37
+ if (!/^[0-9a-f]{64}$/i.test(keyHex)) {
38
+ throw new Error("RUNEYA_ENCRYPTION_KEY invalide : attendu 64 caract\xE8res hex");
39
+ }
40
+ console.log("");
41
+ console.log("\u26A0 ATTENTION : RUNEYA_ENCRYPTION_KEY est d\xE9fini \u2014 l'authentification par master password est d\xE9sactiv\xE9e.");
42
+ console.log(" Cette variable d'environnement est r\xE9serv\xE9e aux environnements CI/CD.");
43
+ console.log(" Ne la d\xE9finissez jamais sur une machine de d\xE9veloppement.");
44
+ console.log("");
45
+ cachedKey = Buffer.from(keyHex, "hex");
46
+ _ciMode = true;
47
+ return true;
48
+ }
49
+ function setPendingLegacyKey(key) {
50
+ if (key.length !== KEY_SIZE_BYTES) throw new Error("Taille de cl\xE9 invalide");
51
+ _pendingLegacyKey = key;
52
+ }
53
+ function getPendingLegacyKey() {
54
+ return _pendingLegacyKey;
55
+ }
56
+ function clearPendingLegacyKey() {
57
+ _pendingLegacyKey = null;
58
+ }
59
+ function hasPendingLegacyKey() {
60
+ return _pendingLegacyKey !== null;
61
+ }
62
+ function setEncryptionKeyInMemory(key) {
63
+ if (key.length !== KEY_SIZE_BYTES) throw new Error("Taille de cl\xE9 invalide");
64
+ cachedKey = key;
65
+ }
66
+ function clearEncryptionKey() {
67
+ cachedKey = null;
68
+ }
69
+ function hasEncryptionKey() {
70
+ return cachedKey !== null;
71
+ }
72
+ function getEncryptionKey() {
73
+ return cachedKey;
74
+ }
75
+ function deriveStableSecret(purpose) {
76
+ const key = getEncryptionKey();
77
+ if (!key) return null;
78
+ return createHash("sha256").update(key).update(purpose).digest("hex");
79
+ }
80
+ async function ensureEncryptionKey() {
81
+ return cachedKey;
82
+ }
83
+ async function setEncryptionKey(keyHex) {
84
+ if (!/^[0-9a-f]{64}$/i.test(keyHex)) throw new Error("Invalid key format");
85
+ cachedKey = Buffer.from(keyHex, "hex");
86
+ }
87
+ var ALGORITHM = "aes-256-gcm";
88
+ function encryptValue(plaintext) {
89
+ const maybeKey = getEncryptionKey();
90
+ if (!maybeKey) {
91
+ throw new Error("Encryption key not loaded \u2014 please provide the key via setup UI");
92
+ }
93
+ const key = maybeKey;
94
+ const iv = randomBytes(16);
95
+ const cipher = createCipheriv(ALGORITHM, key, iv);
96
+ let enc = cipher.update(plaintext, "utf-8", "hex");
97
+ enc += cipher.final("hex");
98
+ const authTag = cipher.getAuthTag().toString("hex");
99
+ return `${iv.toString("hex")}:${authTag}:${enc}`;
100
+ }
101
+ function decryptValue(encrypted) {
102
+ const parts = encrypted.split(":");
103
+ if (parts.length !== 3) throw new Error("Invalid encrypted value format \u2014 expected iv:authTag:ciphertext");
104
+ const [ivHex, authTagHex, ciphertext] = parts;
105
+ if (!/^[0-9a-f]+$/i.test(ivHex) || !/^[0-9a-f]+$/i.test(authTagHex) || ciphertext !== "" && !/^[0-9a-f]+$/i.test(ciphertext)) {
106
+ throw new Error("Invalid hex in encrypted value \u2014 possible data corruption");
107
+ }
108
+ const maybeKey = getEncryptionKey();
109
+ if (!maybeKey) {
110
+ throw new Error("Encryption key not loaded \u2014 please provide the key via setup UI");
111
+ }
112
+ const key = maybeKey;
113
+ const iv = Buffer.from(ivHex, "hex");
114
+ const decipher = createDecipheriv(ALGORITHM, key, iv, { authTagLength: 16 });
115
+ decipher.setAuthTag(Buffer.from(authTagHex, "hex"));
116
+ let dec = decipher.update(ciphertext, "hex", "utf-8");
117
+ dec += decipher.final("utf-8");
118
+ return dec;
119
+ }
120
+ function stableEncryptValue(plaintext, existingEncrypted) {
121
+ if (existingEncrypted) {
122
+ try {
123
+ if (decryptValue(existingEncrypted) === plaintext) return existingEncrypted;
124
+ } catch {
125
+ }
126
+ }
127
+ return encryptValue(plaintext);
128
+ }
129
+ function encryptVariableSlots(variables, existingEncrypted) {
130
+ const result = {};
131
+ for (const [key, variable] of Object.entries(variables)) {
132
+ const slot = variable.value;
133
+ if (slot.isSecret && slot.value) {
134
+ result[key] = { ...variable, value: { ...slot, value: stableEncryptValue(slot.value, existingEncrypted?.[key]) } };
135
+ } else {
136
+ result[key] = { ...variable, value: { ...slot } };
137
+ }
138
+ }
139
+ return result;
140
+ }
141
+ function decryptVariableSlots(variables, contextLabel) {
142
+ const result = {};
143
+ for (const [key, variable] of Object.entries(variables)) {
144
+ const slot = variable.value;
145
+ if (slot.isSecret && slot.value) {
146
+ try {
147
+ result[key] = { ...variable, value: { ...slot, value: decryptValue(slot.value) } };
148
+ } catch (err) {
149
+ console.error(`[crypto-helpers] Failed to decrypt variable "${key}" for ${contextLabel}: ${err.message}`);
150
+ result[key] = { ...variable, value: { ...slot, value: "" } };
151
+ }
152
+ } else {
153
+ result[key] = { ...variable, value: { ...slot } };
154
+ }
155
+ }
156
+ return result;
157
+ }
158
+ function extractEncryptedVariableSlots(variables) {
159
+ const slots = {};
160
+ for (const [key, variable] of Object.entries(variables)) {
161
+ if (variable.value?.isSecret && variable.value?.value) {
162
+ slots[key] = variable.value.value;
163
+ }
164
+ }
165
+ return slots;
166
+ }
167
+ function isEncryptedValue(s) {
168
+ const parts = s.split(":");
169
+ return parts.length === 3 && /^[0-9a-f]{32}$/i.test(parts[0]) && /^[0-9a-f]{32}$/i.test(parts[1]);
170
+ }
171
+
172
+ export {
173
+ env,
174
+ isCiMode,
175
+ loadFromEnv,
176
+ setPendingLegacyKey,
177
+ getPendingLegacyKey,
178
+ clearPendingLegacyKey,
179
+ hasPendingLegacyKey,
180
+ setEncryptionKeyInMemory,
181
+ clearEncryptionKey,
182
+ hasEncryptionKey,
183
+ getEncryptionKey,
184
+ deriveStableSecret,
185
+ ensureEncryptionKey,
186
+ setEncryptionKey,
187
+ encryptValue,
188
+ decryptValue,
189
+ stableEncryptValue,
190
+ encryptVariableSlots,
191
+ decryptVariableSlots,
192
+ extractEncryptedVariableSlots,
193
+ isEncryptedValue
194
+ };
195
+ //# sourceMappingURL=chunk-ERJIU7R4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../server/src/config/env.ts","../../server/src/utils/encryption-key.ts","../../server/src/services/crypto-helpers.ts"],"sourcesContent":["import { z } from 'zod';\n\n// RUNEYA_HTTP_PORT takes priority over PORT for RUNEYA_AGENT_PORT default derivation\nconst effectivePortStr = process.env.RUNEYA_HTTP_PORT ?? process.env.PORT;\nconst serverPort = z.coerce.number().int().min(1).max(65535).default(4000).parse(effectivePortStr);\n\n// RUNEYA_AGENT_PORT takes priority over AGENT_PORT (legacy alias used by e2e test-server)\nconst effectiveAgentPortStr = process.env.RUNEYA_AGENT_PORT ?? process.env.AGENT_PORT;\nconst defaultAgentPort = effectiveAgentPortStr\n ? z.coerce.number().int().min(1).max(65535).parse(effectiveAgentPortStr)\n : serverPort + 1;\n\nconst EnvSchema = z.object({\n RUNEYA_HTTP_PORT: z.coerce.number().int().min(1).max(65535).optional(),\n PORT: z.coerce.number().int().min(1).max(65535).default(4000),\n HOST: z.string().default('127.0.0.1'),\n RUNEYA_AGENT_PORT: z.coerce.number().int().min(1).max(65535).default(defaultAgentPort),\n RUNEYA_AGENT_HOST: z.string().default('127.0.0.1'),\n AGENT_BINARY_PATH: z.string().nullable().default(null),\n DATA_DIR: z.preprocess((v) => (typeof v === 'string' && v.length > 0 ? v : undefined), z.string().default('.runeya')),\n CLOUD_URL: z.string().url().nullable().default(null),\n MODE: z.enum(['local-simple', 'cloud']).default('local-simple'),\n CORS_ORIGIN: z.string().nullable().default(null),\n RUNEYA_LAN_PROXY: z.preprocess((v) => v === 'true' || v === '1', z.boolean()).default(false),\n}).transform((data) => ({\n ...data,\n // RUNEYA_HTTP_PORT takes priority over PORT\n PORT: data.RUNEYA_HTTP_PORT ?? data.PORT,\n}));\n\nexport type Env = z.infer<typeof EnvSchema>;\n\nexport const env = EnvSchema.parse(process.env);\n","import { createHash } from 'node:crypto';\n\nconst KEY_SIZE_BYTES = 32;\n\nlet cachedKey: Buffer | null = null;\nlet _ciMode = false;\n\n// Pending legacy key holder — kept in memory between migration and setup\nlet _pendingLegacyKey: Buffer | null = null;\n\n/** True only when the key was loaded from RUNEYA_ENCRYPTION_KEY (CI/CD — no JWT required). */\nexport function isCiMode(): boolean {\n return _ciMode;\n}\n\n/** Load encryption key from env var (CI mode) */\nexport async function loadFromEnv(): Promise<boolean> {\n const keyHex = process.env['RUNEYA_ENCRYPTION_KEY'];\n if (!keyHex) return false;\n if (!/^[0-9a-f]{64}$/i.test(keyHex)) {\n throw new Error('RUNEYA_ENCRYPTION_KEY invalide : attendu 64 caractères hex');\n }\n console.log('');\n console.log('⚠ ATTENTION : RUNEYA_ENCRYPTION_KEY est défini — l\\'authentification par master password est désactivée.');\n console.log(' Cette variable d\\'environnement est réservée aux environnements CI/CD.');\n console.log(' Ne la définissez jamais sur une machine de développement.');\n console.log('');\n cachedKey = Buffer.from(keyHex, 'hex');\n _ciMode = true;\n return true;\n}\n\n// ── Pending legacy key holder ──────────────────────────────────────────────\n// After legacy migration, the libsodium key is stored here (NOT in the main\n// singleton) so that hasEncryptionKey() returns false → server state = setup.\n// The key is consumed by /api/setup to write .encryption.key.enc.\n\nexport function setPendingLegacyKey(key: Buffer): void {\n if (key.length !== KEY_SIZE_BYTES) throw new Error('Taille de clé invalide');\n _pendingLegacyKey = key;\n}\n\nexport function getPendingLegacyKey(): Buffer | null {\n return _pendingLegacyKey;\n}\n\nexport function clearPendingLegacyKey(): void {\n _pendingLegacyKey = null;\n}\n\nexport function hasPendingLegacyKey(): boolean {\n return _pendingLegacyKey !== null;\n}\n\n/** Set the encryption key in memory (called after successful unlock/setup) */\nexport function setEncryptionKeyInMemory(key: Buffer): void {\n if (key.length !== KEY_SIZE_BYTES) throw new Error('Taille de clé invalide');\n cachedKey = key;\n}\n\n/** Clear the cached key (lock) */\nexport function clearEncryptionKey(): void {\n cachedKey = null;\n}\n\nexport function hasEncryptionKey(): boolean {\n return cachedKey !== null;\n}\n\nexport function getEncryptionKey(): Buffer | null {\n return cachedKey;\n}\n\nexport function deriveStableSecret(purpose: string): string | null {\n const key = getEncryptionKey();\n if (!key) return null;\n return createHash('sha256').update(key).update(purpose).digest('hex');\n}\n\n// Legacy compat: ensureEncryptionKey for code that still calls it\n// Returns null if key not loaded yet (LOCKED/SETUP state)\nexport async function ensureEncryptionKey(): Promise<Buffer | null> {\n return cachedKey;\n}\n\n// Legacy compat: setEncryptionKey (used by old setup router)\nexport async function setEncryptionKey(keyHex: string): Promise<void> {\n if (!/^[0-9a-f]{64}$/i.test(keyHex)) throw new Error('Invalid key format');\n cachedKey = Buffer.from(keyHex, 'hex');\n}\n","import { randomBytes, createCipheriv, createDecipheriv } from 'node:crypto';\nimport { getEncryptionKey } from '../utils/encryption-key.js';\nimport type { VariableSlot } from '@runeya/packages-shared';\n\nconst ALGORITHM = 'aes-256-gcm';\n\nexport function encryptValue(plaintext: string): string {\n const maybeKey = getEncryptionKey();\n if (!maybeKey) {\n throw new Error('Encryption key not loaded — please provide the key via setup UI');\n }\n const key: Buffer = maybeKey;\n const iv = randomBytes(16);\n const cipher = createCipheriv(ALGORITHM, key, iv);\n let enc = cipher.update(plaintext, 'utf-8', 'hex');\n enc += cipher.final('hex');\n const authTag = cipher.getAuthTag().toString('hex');\n return `${iv.toString('hex')}:${authTag}:${enc}`;\n}\n\nexport function decryptValue(encrypted: string): string {\n const parts = encrypted.split(':');\n if (parts.length !== 3) throw new Error('Invalid encrypted value format — expected iv:authTag:ciphertext');\n const [ivHex, authTagHex, ciphertext] = parts;\n if (!/^[0-9a-f]+$/i.test(ivHex) || !/^[0-9a-f]+$/i.test(authTagHex) || (ciphertext !== '' && !/^[0-9a-f]+$/i.test(ciphertext))) {\n throw new Error('Invalid hex in encrypted value — possible data corruption');\n }\n const maybeKey = getEncryptionKey();\n if (!maybeKey) {\n throw new Error('Encryption key not loaded — please provide the key via setup UI');\n }\n const key: Buffer = maybeKey;\n const iv = Buffer.from(ivHex, 'hex');\n const decipher = createDecipheriv(ALGORITHM, key, iv, { authTagLength: 16 });\n decipher.setAuthTag(Buffer.from(authTagHex, 'hex'));\n let dec = decipher.update(ciphertext, 'hex', 'utf-8');\n dec += decipher.final('utf-8');\n return dec;\n}\n\n/** Reuse existing ciphertext if plaintext is unchanged — keeps diffs stable */\nexport function stableEncryptValue(plaintext: string, existingEncrypted?: string): string {\n if (existingEncrypted) {\n try {\n if (decryptValue(existingEncrypted) === plaintext) return existingEncrypted;\n } catch {\n // Existing value is corrupt — fall through to re-encrypt\n }\n }\n return encryptValue(plaintext);\n}\n\n/** Encrypt secret VariableSlot values in a flat slots record (overrides) */\nfunction encryptSlots(\n slots: Record<string, VariableSlot>,\n existingEncrypted?: Record<string, string>,\n): Record<string, VariableSlot> {\n const result: Record<string, VariableSlot> = {};\n for (const [key, slot] of Object.entries(slots)) {\n if (slot.isSecret && slot.value) {\n result[key] = { ...slot, value: stableEncryptValue(slot.value, existingEncrypted?.[key]) };\n } else {\n result[key] = { ...slot };\n }\n }\n return result;\n}\n\n/** Decrypt secret VariableSlot values in a flat slots record (overrides) */\nfunction decryptSlots(slots: Record<string, VariableSlot>, contextLabel: string): Record<string, VariableSlot> {\n const result: Record<string, VariableSlot> = {};\n for (const [key, slot] of Object.entries(slots)) {\n if (slot.isSecret && slot.value) {\n try {\n result[key] = { ...slot, value: decryptValue(slot.value) };\n } catch (err) {\n console.error(`[crypto-helpers] Failed to decrypt \"${key}\" for ${contextLabel}: ${(err as Error).message}`);\n result[key] = { ...slot, value: '' };\n }\n } else {\n result[key] = { ...slot };\n }\n }\n return result;\n}\n\ntype HasValueSlot = { value: VariableSlot; [k: string]: unknown };\n\n/** Encrypt secret Variable.value slots (environment variables — {value: VariableSlot}) */\nexport function encryptVariableSlots(\n variables: Record<string, HasValueSlot>,\n existingEncrypted?: Record<string, string>,\n): Record<string, HasValueSlot> {\n const result: Record<string, HasValueSlot> = {};\n for (const [key, variable] of Object.entries(variables)) {\n const slot = variable.value;\n if (slot.isSecret && slot.value) {\n result[key] = { ...variable, value: { ...slot, value: stableEncryptValue(slot.value, existingEncrypted?.[key]) } };\n } else {\n result[key] = { ...variable, value: { ...slot } };\n }\n }\n return result;\n}\n\n/** Decrypt secret Variable.value slots (environment variables) */\nexport function decryptVariableSlots(\n variables: Record<string, HasValueSlot>,\n contextLabel: string,\n): Record<string, HasValueSlot> {\n const result: Record<string, HasValueSlot> = {};\n for (const [key, variable] of Object.entries(variables)) {\n const slot = variable.value;\n if (slot.isSecret && slot.value) {\n try {\n result[key] = { ...variable, value: { ...slot, value: decryptValue(slot.value) } };\n } catch (err) {\n console.error(`[crypto-helpers] Failed to decrypt variable \"${key}\" for ${contextLabel}: ${(err as Error).message}`);\n result[key] = { ...variable, value: { ...slot, value: '' } };\n }\n } else {\n result[key] = { ...variable, value: { ...slot } };\n }\n }\n return result;\n}\n\n/** Extract encrypted slot values from Variable records (for stable-encrypt cache) */\nexport function extractEncryptedVariableSlots(\n variables: Record<string, HasValueSlot>,\n): Record<string, string> {\n const slots: Record<string, string> = {};\n for (const [key, variable] of Object.entries(variables)) {\n if (variable.value?.isSecret && variable.value?.value) {\n slots[key] = variable.value.value;\n }\n }\n return slots;\n}\n\n/**\n * Returns true if `s` looks like a value encrypted by `encryptValue()`.\n * Format: `<32hex>:<32hex>:<Nhex>` (iv:authTag:ciphertext).\n */\nexport function isEncryptedValue(s: string): boolean {\n const parts = s.split(':');\n return parts.length === 3 && /^[0-9a-f]{32}$/i.test(parts[0]) && /^[0-9a-f]{32}$/i.test(parts[1]);\n}\n\n/** Extract encrypted slot values from VariableSlot records (for stable-encrypt cache) */\nfunction extractEncryptedSlots(\n slots: Record<string, VariableSlot>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [key, slot] of Object.entries(slots)) {\n if (slot.isSecret && slot.value) result[key] = slot.value;\n }\n return result;\n}\n"],"mappings":"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","names":[]}