@burdenoff/microfe-vibecontrols 2026.601.12 → 2026.601.13

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 (286) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +5 -2
  2. package/dist/components/actions/ActionForm.js +270 -255
  3. package/dist/components/actions/ActionForm.js.map +1 -1
  4. package/dist/components/actions/RemoteExecutionEnableNotice.js +85 -0
  5. package/dist/components/actions/RemoteExecutionEnableNotice.js.map +1 -0
  6. package/dist/components/agents/AgentConnectionRecovery.js +123 -0
  7. package/dist/components/agents/AgentConnectionRecovery.js.map +1 -0
  8. package/dist/components/agents/RemoteTaskPermissionsCard.js +37 -36
  9. package/dist/components/agents/RemoteTaskPermissionsCard.js.map +1 -1
  10. package/dist/components/graph/MeshAgentNode.js +74 -0
  11. package/dist/components/graph/MeshAgentNode.js.map +1 -0
  12. package/dist/components/graph/MeshDeployWizard.js +393 -0
  13. package/dist/components/graph/MeshDeployWizard.js.map +1 -0
  14. package/dist/components/graph/MeshGraphView.js +190 -0
  15. package/dist/components/graph/MeshGraphView.js.map +1 -0
  16. package/dist/components/graph/MeshNodeActions.js +70 -0
  17. package/dist/components/graph/MeshNodeActions.js.map +1 -0
  18. package/dist/components/graph/MeshRequestPanel.js +240 -0
  19. package/dist/components/graph/MeshRequestPanel.js.map +1 -0
  20. package/dist/components/graph/meshTypes.js +11 -0
  21. package/dist/components/graph/meshTypes.js.map +1 -0
  22. package/dist/components/graph/nodeIcons.js +31 -0
  23. package/dist/components/graph/nodeIcons.js.map +1 -0
  24. package/dist/components/shared/EnableRemoteTaskDialog.js +48 -0
  25. package/dist/components/shared/EnableRemoteTaskDialog.js.map +1 -0
  26. package/dist/components/shared/index.js +1 -0
  27. package/dist/generated/wspace-operations.js +1896 -1649
  28. package/dist/generated/wspace-operations.js.map +1 -1
  29. package/dist/generated/wspace-types.js +14 -12
  30. package/dist/generated/wspace-types.js.map +1 -1
  31. package/dist/index.js +10 -10
  32. package/dist/node_modules/dagre/index.js +24 -0
  33. package/dist/node_modules/dagre/index.js.map +1 -0
  34. package/dist/node_modules/dagre/lib/acyclic.js +47 -0
  35. package/dist/node_modules/dagre/lib/acyclic.js.map +1 -0
  36. package/dist/node_modules/dagre/lib/add-border-segments.js +32 -0
  37. package/dist/node_modules/dagre/lib/add-border-segments.js.map +1 -0
  38. package/dist/node_modules/dagre/lib/coordinate-system.js +57 -0
  39. package/dist/node_modules/dagre/lib/coordinate-system.js.map +1 -0
  40. package/dist/node_modules/dagre/lib/data/list.js +30 -0
  41. package/dist/node_modules/dagre/lib/data/list.js.map +1 -0
  42. package/dist/node_modules/dagre/lib/debug.js +31 -0
  43. package/dist/node_modules/dagre/lib/debug.js.map +1 -0
  44. package/dist/node_modules/dagre/lib/graphlib.js +15 -0
  45. package/dist/node_modules/dagre/lib/graphlib.js.map +1 -0
  46. package/dist/node_modules/dagre/lib/greedy-fas.js +74 -0
  47. package/dist/node_modules/dagre/lib/greedy-fas.js.map +1 -0
  48. package/dist/node_modules/dagre/lib/layout.js +305 -0
  49. package/dist/node_modules/dagre/lib/layout.js.map +1 -0
  50. package/dist/node_modules/dagre/lib/lodash.js +67 -0
  51. package/dist/node_modules/dagre/lib/lodash.js.map +1 -0
  52. package/dist/node_modules/dagre/lib/nesting-graph.js +76 -0
  53. package/dist/node_modules/dagre/lib/nesting-graph.js.map +1 -0
  54. package/dist/node_modules/dagre/lib/normalize.js +45 -0
  55. package/dist/node_modules/dagre/lib/normalize.js.map +1 -0
  56. package/dist/node_modules/dagre/lib/order/add-subgraph-constraints.js +24 -0
  57. package/dist/node_modules/dagre/lib/order/add-subgraph-constraints.js.map +1 -0
  58. package/dist/node_modules/dagre/lib/order/barycenter.js +34 -0
  59. package/dist/node_modules/dagre/lib/order/barycenter.js.map +1 -0
  60. package/dist/node_modules/dagre/lib/order/build-layer-graph.js +32 -0
  61. package/dist/node_modules/dagre/lib/order/build-layer-graph.js.map +1 -0
  62. package/dist/node_modules/dagre/lib/order/cross-count.js +39 -0
  63. package/dist/node_modules/dagre/lib/order/cross-count.js.map +1 -0
  64. package/dist/node_modules/dagre/lib/order/index.js +50 -0
  65. package/dist/node_modules/dagre/lib/order/index.js.map +1 -0
  66. package/dist/node_modules/dagre/lib/order/init-order.js +28 -0
  67. package/dist/node_modules/dagre/lib/order/init-order.js.map +1 -0
  68. package/dist/node_modules/dagre/lib/order/resolve-conflicts.js +61 -0
  69. package/dist/node_modules/dagre/lib/order/resolve-conflicts.js.map +1 -0
  70. package/dist/node_modules/dagre/lib/order/sort-subgraph.js +50 -0
  71. package/dist/node_modules/dagre/lib/order/sort-subgraph.js.map +1 -0
  72. package/dist/node_modules/dagre/lib/order/sort.js +34 -0
  73. package/dist/node_modules/dagre/lib/order/sort.js.map +1 -0
  74. package/dist/node_modules/dagre/lib/parent-dummy-chains.js +50 -0
  75. package/dist/node_modules/dagre/lib/parent-dummy-chains.js.map +1 -0
  76. package/dist/node_modules/dagre/lib/position/bk.js +212 -0
  77. package/dist/node_modules/dagre/lib/position/bk.js.map +1 -0
  78. package/dist/node_modules/dagre/lib/position/index.js +30 -0
  79. package/dist/node_modules/dagre/lib/position/index.js.map +1 -0
  80. package/dist/node_modules/dagre/lib/rank/feasible-tree.js +39 -0
  81. package/dist/node_modules/dagre/lib/rank/feasible-tree.js.map +1 -0
  82. package/dist/node_modules/dagre/lib/rank/index.js +35 -0
  83. package/dist/node_modules/dagre/lib/rank/index.js.map +1 -0
  84. package/dist/node_modules/dagre/lib/rank/network-simplex.js +90 -0
  85. package/dist/node_modules/dagre/lib/rank/network-simplex.js.map +1 -0
  86. package/dist/node_modules/dagre/lib/rank/util.js +31 -0
  87. package/dist/node_modules/dagre/lib/rank/util.js.map +1 -0
  88. package/dist/node_modules/dagre/lib/util.js +151 -0
  89. package/dist/node_modules/dagre/lib/util.js.map +1 -0
  90. package/dist/node_modules/dagre/lib/version.js +10 -0
  91. package/dist/node_modules/dagre/lib/version.js.map +1 -0
  92. package/dist/node_modules/graphlib/index.js +19 -0
  93. package/dist/node_modules/graphlib/index.js.map +1 -0
  94. package/dist/node_modules/graphlib/lib/alg/components.js +21 -0
  95. package/dist/node_modules/graphlib/lib/alg/components.js.map +1 -0
  96. package/dist/node_modules/graphlib/lib/alg/dfs.js +25 -0
  97. package/dist/node_modules/graphlib/lib/alg/dfs.js.map +1 -0
  98. package/dist/node_modules/graphlib/lib/alg/dijkstra-all.js +18 -0
  99. package/dist/node_modules/graphlib/lib/alg/dijkstra-all.js.map +1 -0
  100. package/dist/node_modules/graphlib/lib/alg/dijkstra.js +31 -0
  101. package/dist/node_modules/graphlib/lib/alg/dijkstra.js.map +1 -0
  102. package/dist/node_modules/graphlib/lib/alg/find-cycles.js +18 -0
  103. package/dist/node_modules/graphlib/lib/alg/find-cycles.js.map +1 -0
  104. package/dist/node_modules/graphlib/lib/alg/floyd-warshall.js +41 -0
  105. package/dist/node_modules/graphlib/lib/alg/floyd-warshall.js.map +1 -0
  106. package/dist/node_modules/graphlib/lib/alg/index.js +33 -0
  107. package/dist/node_modules/graphlib/lib/alg/index.js.map +1 -0
  108. package/dist/node_modules/graphlib/lib/alg/is-acyclic.js +21 -0
  109. package/dist/node_modules/graphlib/lib/alg/is-acyclic.js.map +1 -0
  110. package/dist/node_modules/graphlib/lib/alg/postorder.js +15 -0
  111. package/dist/node_modules/graphlib/lib/alg/postorder.js.map +1 -0
  112. package/dist/node_modules/graphlib/lib/alg/preorder.js +15 -0
  113. package/dist/node_modules/graphlib/lib/alg/preorder.js.map +1 -0
  114. package/dist/node_modules/graphlib/lib/alg/prim.js +35 -0
  115. package/dist/node_modules/graphlib/lib/alg/prim.js.map +1 -0
  116. package/dist/node_modules/graphlib/lib/alg/tarjan.js +34 -0
  117. package/dist/node_modules/graphlib/lib/alg/tarjan.js.map +1 -0
  118. package/dist/node_modules/graphlib/lib/alg/topsort.js +23 -0
  119. package/dist/node_modules/graphlib/lib/alg/topsort.js.map +1 -0
  120. package/dist/node_modules/graphlib/lib/data/priority-queue.js +56 -0
  121. package/dist/node_modules/graphlib/lib/data/priority-queue.js.map +1 -0
  122. package/dist/node_modules/graphlib/lib/graph.js +196 -0
  123. package/dist/node_modules/graphlib/lib/graph.js.map +1 -0
  124. package/dist/node_modules/graphlib/lib/index.js +15 -0
  125. package/dist/node_modules/graphlib/lib/index.js.map +1 -0
  126. package/dist/node_modules/graphlib/lib/json.js +55 -0
  127. package/dist/node_modules/graphlib/lib/json.js.map +1 -0
  128. package/dist/node_modules/graphlib/lib/lodash.js +47 -0
  129. package/dist/node_modules/graphlib/lib/lodash.js.map +1 -0
  130. package/dist/node_modules/graphlib/lib/version.js +10 -0
  131. package/dist/node_modules/graphlib/lib/version.js.map +1 -0
  132. package/dist/node_modules/lodash/_arrayEach.js +14 -0
  133. package/dist/node_modules/lodash/_arrayEach.js.map +1 -0
  134. package/dist/node_modules/lodash/_arrayReduce.js +15 -0
  135. package/dist/node_modules/lodash/_arrayReduce.js.map +1 -0
  136. package/dist/node_modules/lodash/_asciiSize.js +11 -0
  137. package/dist/node_modules/lodash/_asciiSize.js.map +1 -0
  138. package/dist/node_modules/lodash/_assignMergeValue.js +16 -0
  139. package/dist/node_modules/lodash/_assignMergeValue.js.map +1 -0
  140. package/dist/node_modules/lodash/_assignValue.js +17 -0
  141. package/dist/node_modules/lodash/_assignValue.js.map +1 -0
  142. package/dist/node_modules/lodash/_baseAssign.js +16 -0
  143. package/dist/node_modules/lodash/_baseAssign.js.map +1 -0
  144. package/dist/node_modules/lodash/_baseAssignIn.js +16 -0
  145. package/dist/node_modules/lodash/_baseAssignIn.js.map +1 -0
  146. package/dist/node_modules/lodash/_baseClone.js +64 -0
  147. package/dist/node_modules/lodash/_baseClone.js.map +1 -0
  148. package/dist/node_modules/lodash/_baseCreate.js +21 -0
  149. package/dist/node_modules/lodash/_baseCreate.js.map +1 -0
  150. package/dist/node_modules/lodash/_baseFilter.js +18 -0
  151. package/dist/node_modules/lodash/_baseFilter.js.map +1 -0
  152. package/dist/node_modules/lodash/_baseHas.js +14 -0
  153. package/dist/node_modules/lodash/_baseHas.js.map +1 -0
  154. package/dist/node_modules/lodash/_baseIsMap.js +16 -0
  155. package/dist/node_modules/lodash/_baseIsMap.js.map +1 -0
  156. package/dist/node_modules/lodash/_baseIsSet.js +16 -0
  157. package/dist/node_modules/lodash/_baseIsSet.js.map +1 -0
  158. package/dist/node_modules/lodash/_baseKeysIn.js +20 -0
  159. package/dist/node_modules/lodash/_baseKeysIn.js.map +1 -0
  160. package/dist/node_modules/lodash/_baseMerge.js +27 -0
  161. package/dist/node_modules/lodash/_baseMerge.js.map +1 -0
  162. package/dist/node_modules/lodash/_baseMergeDeep.js +39 -0
  163. package/dist/node_modules/lodash/_baseMergeDeep.js.map +1 -0
  164. package/dist/node_modules/lodash/_basePick.js +18 -0
  165. package/dist/node_modules/lodash/_basePick.js.map +1 -0
  166. package/dist/node_modules/lodash/_basePickBy.js +21 -0
  167. package/dist/node_modules/lodash/_basePickBy.js.map +1 -0
  168. package/dist/node_modules/lodash/_baseReduce.js +15 -0
  169. package/dist/node_modules/lodash/_baseReduce.js.map +1 -0
  170. package/dist/node_modules/lodash/_baseSet.js +30 -0
  171. package/dist/node_modules/lodash/_baseSet.js.map +1 -0
  172. package/dist/node_modules/lodash/_baseValues.js +17 -0
  173. package/dist/node_modules/lodash/_baseValues.js.map +1 -0
  174. package/dist/node_modules/lodash/_baseZipObject.js +17 -0
  175. package/dist/node_modules/lodash/_baseZipObject.js.map +1 -0
  176. package/dist/node_modules/lodash/_castFunction.js +15 -0
  177. package/dist/node_modules/lodash/_castFunction.js.map +1 -0
  178. package/dist/node_modules/lodash/_cloneArrayBuffer.js +16 -0
  179. package/dist/node_modules/lodash/_cloneArrayBuffer.js.map +1 -0
  180. package/dist/node_modules/lodash/_cloneBuffer.js +17 -0
  181. package/dist/node_modules/lodash/_cloneBuffer.js.map +1 -0
  182. package/dist/node_modules/lodash/_cloneDataView.js +16 -0
  183. package/dist/node_modules/lodash/_cloneDataView.js.map +1 -0
  184. package/dist/node_modules/lodash/_cloneRegExp.js +15 -0
  185. package/dist/node_modules/lodash/_cloneRegExp.js.map +1 -0
  186. package/dist/node_modules/lodash/_cloneSymbol.js +15 -0
  187. package/dist/node_modules/lodash/_cloneSymbol.js.map +1 -0
  188. package/dist/node_modules/lodash/_cloneTypedArray.js +16 -0
  189. package/dist/node_modules/lodash/_cloneTypedArray.js.map +1 -0
  190. package/dist/node_modules/lodash/_copyArray.js +15 -0
  191. package/dist/node_modules/lodash/_copyArray.js.map +1 -0
  192. package/dist/node_modules/lodash/_copyObject.js +22 -0
  193. package/dist/node_modules/lodash/_copyObject.js.map +1 -0
  194. package/dist/node_modules/lodash/_copySymbols.js +16 -0
  195. package/dist/node_modules/lodash/_copySymbols.js.map +1 -0
  196. package/dist/node_modules/lodash/_copySymbolsIn.js +16 -0
  197. package/dist/node_modules/lodash/_copySymbolsIn.js.map +1 -0
  198. package/dist/node_modules/lodash/_createAssigner.js +23 -0
  199. package/dist/node_modules/lodash/_createAssigner.js.map +1 -0
  200. package/dist/node_modules/lodash/_flatRest.js +17 -0
  201. package/dist/node_modules/lodash/_flatRest.js.map +1 -0
  202. package/dist/node_modules/lodash/_getAllKeysIn.js +17 -0
  203. package/dist/node_modules/lodash/_getAllKeysIn.js.map +1 -0
  204. package/dist/node_modules/lodash/_getSymbolsIn.js +18 -0
  205. package/dist/node_modules/lodash/_getSymbolsIn.js.map +1 -0
  206. package/dist/node_modules/lodash/_initCloneArray.js +15 -0
  207. package/dist/node_modules/lodash/_initCloneArray.js.map +1 -0
  208. package/dist/node_modules/lodash/_initCloneByTag.js +40 -0
  209. package/dist/node_modules/lodash/_initCloneByTag.js.map +1 -0
  210. package/dist/node_modules/lodash/_initCloneObject.js +17 -0
  211. package/dist/node_modules/lodash/_initCloneObject.js.map +1 -0
  212. package/dist/node_modules/lodash/_nativeKeysIn.js +15 -0
  213. package/dist/node_modules/lodash/_nativeKeysIn.js.map +1 -0
  214. package/dist/node_modules/lodash/_safeGet.js +13 -0
  215. package/dist/node_modules/lodash/_safeGet.js.map +1 -0
  216. package/dist/node_modules/lodash/_stringSize.js +17 -0
  217. package/dist/node_modules/lodash/_stringSize.js.map +1 -0
  218. package/dist/node_modules/lodash/_unicodeSize.js +25 -0
  219. package/dist/node_modules/lodash/_unicodeSize.js.map +1 -0
  220. package/dist/node_modules/lodash/clone.js +15 -0
  221. package/dist/node_modules/lodash/clone.js.map +1 -0
  222. package/dist/node_modules/lodash/cloneDeep.js +15 -0
  223. package/dist/node_modules/lodash/cloneDeep.js.map +1 -0
  224. package/dist/node_modules/lodash/defaults.js +23 -0
  225. package/dist/node_modules/lodash/defaults.js.map +1 -0
  226. package/dist/node_modules/lodash/each.js +11 -0
  227. package/dist/node_modules/lodash/each.js.map +1 -0
  228. package/dist/node_modules/lodash/filter.js +18 -0
  229. package/dist/node_modules/lodash/filter.js.map +1 -0
  230. package/dist/node_modules/lodash/flatten.js +15 -0
  231. package/dist/node_modules/lodash/flatten.js.map +1 -0
  232. package/dist/node_modules/lodash/forEach.js +18 -0
  233. package/dist/node_modules/lodash/forEach.js.map +1 -0
  234. package/dist/node_modules/lodash/forIn.js +17 -0
  235. package/dist/node_modules/lodash/forIn.js.map +1 -0
  236. package/dist/node_modules/lodash/has.js +16 -0
  237. package/dist/node_modules/lodash/has.js.map +1 -0
  238. package/dist/node_modules/lodash/isArrayLikeObject.js +16 -0
  239. package/dist/node_modules/lodash/isArrayLikeObject.js.map +1 -0
  240. package/dist/node_modules/lodash/isEmpty.js +28 -0
  241. package/dist/node_modules/lodash/isEmpty.js.map +1 -0
  242. package/dist/node_modules/lodash/isMap.js +14 -0
  243. package/dist/node_modules/lodash/isMap.js.map +1 -0
  244. package/dist/node_modules/lodash/isSet.js +14 -0
  245. package/dist/node_modules/lodash/isSet.js.map +1 -0
  246. package/dist/node_modules/lodash/isUndefined.js +13 -0
  247. package/dist/node_modules/lodash/isUndefined.js.map +1 -0
  248. package/dist/node_modules/lodash/keysIn.js +17 -0
  249. package/dist/node_modules/lodash/keysIn.js.map +1 -0
  250. package/dist/node_modules/lodash/merge.js +15 -0
  251. package/dist/node_modules/lodash/merge.js.map +1 -0
  252. package/dist/node_modules/lodash/pick.js +15 -0
  253. package/dist/node_modules/lodash/pick.js.map +1 -0
  254. package/dist/node_modules/lodash/reduce.js +20 -0
  255. package/dist/node_modules/lodash/reduce.js.map +1 -0
  256. package/dist/node_modules/lodash/size.js +22 -0
  257. package/dist/node_modules/lodash/size.js.map +1 -0
  258. package/dist/node_modules/lodash/toPlainObject.js +16 -0
  259. package/dist/node_modules/lodash/toPlainObject.js.map +1 -0
  260. package/dist/node_modules/lodash/transform.js +31 -0
  261. package/dist/node_modules/lodash/transform.js.map +1 -0
  262. package/dist/node_modules/lodash/union.js +17 -0
  263. package/dist/node_modules/lodash/union.js.map +1 -0
  264. package/dist/node_modules/lodash/uniqueId.js +16 -0
  265. package/dist/node_modules/lodash/uniqueId.js.map +1 -0
  266. package/dist/node_modules/lodash/values.js +16 -0
  267. package/dist/node_modules/lodash/values.js.map +1 -0
  268. package/dist/node_modules/lodash/zipObject.js +16 -0
  269. package/dist/node_modules/lodash/zipObject.js.map +1 -0
  270. package/dist/pages/ActionsPage.js +269 -230
  271. package/dist/pages/ActionsPage.js.map +1 -1
  272. package/dist/pages/AgentDetailsPage.js +442 -436
  273. package/dist/pages/AgentDetailsPage.js.map +1 -1
  274. package/dist/pages/AgentGraphPage.js +251 -118
  275. package/dist/pages/AgentGraphPage.js.map +1 -1
  276. package/dist/pages/VibeDeckPage.js +268 -224
  277. package/dist/pages/VibeDeckPage.js.map +1 -1
  278. package/dist/services/agentRemoteTaskGates.js +29 -0
  279. package/dist/services/agentRemoteTaskGates.js.map +1 -0
  280. package/package.json +3 -1
  281. package/dist/components/graph/AgentGraphView.js +0 -237
  282. package/dist/components/graph/AgentGraphView.js.map +0 -1
  283. package/dist/components/graph/AgentNode.js +0 -54
  284. package/dist/components/graph/AgentNode.js.map +0 -1
  285. package/dist/components/graph/TargetNode.js +0 -76
  286. package/dist/components/graph/TargetNode.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ActionForm.js","names":[],"sources":["../../../src/components/actions/ActionForm.tsx"],"sourcesContent":["import { useState, useEffect, useRef, type ReactNode } from 'react';\nimport { X } from 'lucide-react';\nimport { motion, AnimatePresence, useReducedMotion } from 'framer-motion';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport interface ActionFormData {\n name: string;\n displayName: string;\n description: string;\n actionType: string;\n triggerType: string;\n schedule: string;\n category: string;\n tags: string[];\n command: string;\n workingDirectory: string;\n targetAgentId: string;\n environmentVariables: Record<string, string>;\n script: string;\n scriptInterpreter: string;\n webhookUrl: string;\n webhookMethod: string;\n webhookHeaders: Record<string, string>;\n webhookBody: string;\n webhookAuthType: string;\n timeoutMs: number;\n retryCount: number;\n confirmationRequired: boolean;\n}\n\nconst DEFAULT_FORM: ActionFormData = {\n name: '',\n displayName: '',\n description: '',\n actionType: 'COMMAND',\n triggerType: 'MANUAL',\n schedule: '',\n category: '',\n tags: [],\n command: '',\n workingDirectory: '',\n targetAgentId: '',\n environmentVariables: {},\n script: '',\n scriptInterpreter: 'bash',\n webhookUrl: '',\n webhookMethod: 'POST',\n webhookHeaders: {},\n webhookBody: '',\n webhookAuthType: '',\n timeoutMs: 30000,\n retryCount: 0,\n confirmationRequired: false,\n};\n\ninterface ActionFormProps {\n title: string;\n initialData?: Partial<ActionFormData>;\n agents: Array<{ id: string; name: string; isActive: boolean }>;\n onSubmit: (data: ActionFormData) => void;\n onCancel: () => void;\n isLoading?: boolean;\n /** Optional extra content rendered below the form fields (e.g. tag picker in edit mode) */\n extraSection?: ReactNode;\n}\n\nexport function ActionForm({\n title,\n initialData,\n agents,\n onSubmit,\n onCancel,\n isLoading = false,\n extraSection,\n}: ActionFormProps) {\n const [form, setForm] = useState<ActionFormData>({ ...DEFAULT_FORM, ...initialData });\n const [tagsInput, setTagsInput] = useState(initialData?.tags?.join(', ') ?? '');\n const dialogRef = useRef<HTMLDialogElement>(null);\n const shouldReduceMotion = useReducedMotion();\n const tr = useTr();\n\n useEffect(() => {\n dialogRef.current?.showModal();\n }, []);\n\n const update = <K extends keyof ActionFormData>(key: K, value: ActionFormData[K]) =>\n setForm((prev) => ({ ...prev, [key]: value }));\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n const tags = tagsInput\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n onSubmit({ ...form, tags });\n };\n\n const inputClass =\n 'w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg focus:border-transparent transition-all';\n const labelClass = 'block text-sm font-medium text-text-secondary mb-1';\n\n return (\n <dialog\n ref={dialogRef}\n className=\"fixed inset-0 z-50 m-auto p-0 bg-transparent backdrop:bg-transparent\"\n onClose={onCancel}\n >\n <AnimatePresence>\n <motion.div\n className=\"fixed inset-0 bg-bg-overlay\"\n initial={shouldReduceMotion ? undefined : { opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n onClick={onCancel}\n />\n <motion.div\n className=\"relative w-full max-w-2xl max-h-[85vh] overflow-y-auto bg-bg-surface rounded-lg shadow-xl\"\n initial={shouldReduceMotion ? undefined : { opacity: 0, scale: 0.95, y: 8 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: 8 }}\n transition={{ duration: 0.2, ease: [0.2, 0, 0, 1] }}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 border-b border-border-default sticky top-0 bg-bg-surface z-10\">\n <h2 className=\"text-lg font-semibold text-text-primary\">{title}</h2>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-5 text-text-secondary\" />\n </button>\n </div>\n\n <form onSubmit={handleSubmit} className=\"p-6 space-y-4\">\n {/* Basic fields */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.nameSlug', 'Name (slug)')}\n </label>\n <input\n aria-label=\"my-action\"\n className={inputClass}\n value={form.name}\n onChange={(e) => update('name', e.target.value)}\n placeholder=\"my-action\"\n required\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.displayName', 'Display Name')}\n </label>\n <input\n aria-label=\"My Action\"\n className={inputClass}\n value={form.displayName}\n onChange={(e) => update('displayName', e.target.value)}\n placeholder=\"My Action\"\n required\n />\n </div>\n </div>\n\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.description', 'Description')}\n </label>\n <textarea\n aria-label=\"What does this action do?\"\n className={`${inputClass} resize-none`}\n rows={2}\n value={form.description}\n onChange={(e) => update('description', e.target.value)}\n placeholder=\"What does this action do?\"\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.actionType', 'Action Type')}\n </label>\n <select\n className={inputClass}\n value={form.actionType}\n onChange={(e) => update('actionType', e.target.value)}\n >\n <option value=\"COMMAND\">Command</option>\n <option value=\"SCRIPT\">Script</option>\n <option value=\"WEBHOOK\">Webhook</option>\n <option value=\"WORKFLOW\">Workflow</option>\n </select>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.triggerType', 'Trigger Type')}\n </label>\n <select\n className={inputClass}\n value={form.triggerType}\n onChange={(e) => update('triggerType', e.target.value)}\n >\n <option value=\"MANUAL\">Manual</option>\n <option value=\"SCHEDULED\">Scheduled</option>\n <option value=\"EVENT\">Event</option>\n <option value=\"ON_DEMAND\">On Demand</option>\n </select>\n </div>\n </div>\n\n {form.triggerType === 'SCHEDULED' && (\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.schedule', 'Schedule (cron expression)')}\n </label>\n <input\n aria-label=\"*/5 * * * *\"\n className={inputClass}\n value={form.schedule}\n onChange={(e) => update('schedule', e.target.value)}\n placeholder=\"*/5 * * * *\"\n />\n </div>\n )}\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.category', 'Category')}\n </label>\n <input\n aria-label=\"deployment, testing, etc.\"\n className={inputClass}\n value={form.category}\n onChange={(e) => update('category', e.target.value)}\n placeholder=\"deployment, testing, etc.\"\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.tags', 'Tags (comma separated)')}\n </label>\n <input\n aria-label=\"ci, deploy, infra\"\n className={inputClass}\n value={tagsInput}\n onChange={(e) => setTagsInput(e.target.value)}\n placeholder=\"ci, deploy, infra\"\n />\n </div>\n </div>\n\n {/* Type-specific fields */}\n {form.actionType === 'COMMAND' && (\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50 border border-border-default\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.actions.form.commandSettings', 'Command Settings')}\n </h3>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.command', 'Command')}\n </label>\n <textarea\n aria-label=\"echo 'Hello World'\"\n className={`${inputClass} font-mono resize-none`}\n rows={3}\n value={form.command}\n onChange={(e) => update('command', e.target.value)}\n placeholder=\"echo 'Hello World'\"\n required\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.workingDirectory', 'Working Directory')}\n </label>\n <input\n aria-label=\"/home/user/project\"\n className={inputClass}\n value={form.workingDirectory}\n onChange={(e) => update('workingDirectory', e.target.value)}\n placeholder=\"/home/user/project\"\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.targetAgent', 'Target Agent')}\n </label>\n <select\n className={inputClass}\n value={form.targetAgentId}\n onChange={(e) => update('targetAgentId', e.target.value)}\n >\n <option value=\"\">\n {tr('vibecontrols.actions.form.anyAvailableAgent', 'Any available agent')}\n </option>\n {agents.map((a) => (\n <option key={a.id} value={a.id} disabled={!a.isActive}>\n {a.name}{' '}\n {!a.isActive ? tr('vibecontrols.actions.form.offline', '(offline)') : ''}\n </option>\n ))}\n </select>\n </div>\n </div>\n </div>\n )}\n\n {form.actionType === 'SCRIPT' && (\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50 border border-border-default\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.actions.form.scriptSettings', 'Script Settings')}\n </h3>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.script', 'Script')}\n </label>\n <textarea\n aria-label=\"#!/bin/bash&#10;echo 'Hello'\"\n className={`${inputClass} font-mono resize-none`}\n rows={6}\n value={form.script}\n onChange={(e) => update('script', e.target.value)}\n placeholder=\"#!/bin/bash&#10;echo 'Hello'\"\n required\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.interpreter', 'Interpreter')}\n </label>\n <select\n className={inputClass}\n value={form.scriptInterpreter}\n onChange={(e) => update('scriptInterpreter', e.target.value)}\n >\n <option value=\"bash\">Bash</option>\n <option value=\"python\">Python</option>\n <option value=\"node\">Node.js</option>\n </select>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.targetAgent', 'Target Agent')}\n </label>\n <select\n className={inputClass}\n value={form.targetAgentId}\n onChange={(e) => update('targetAgentId', e.target.value)}\n >\n <option value=\"\">\n {tr('vibecontrols.actions.form.anyAvailableAgent', 'Any available agent')}\n </option>\n {agents.map((a) => (\n <option key={a.id} value={a.id} disabled={!a.isActive}>\n {a.name}{' '}\n {!a.isActive ? tr('vibecontrols.actions.form.offline', '(offline)') : ''}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.workingDirectory', 'Working Directory')}\n </label>\n <input\n aria-label=\"/home/user/project\"\n className={inputClass}\n value={form.workingDirectory}\n onChange={(e) => update('workingDirectory', e.target.value)}\n placeholder=\"/home/user/project\"\n />\n </div>\n </div>\n )}\n\n {form.actionType === 'WEBHOOK' && (\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50 border border-border-default\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.actions.form.webhookSettings', 'Webhook Settings')}\n </h3>\n <div className=\"grid grid-cols-[1fr_auto] gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.url', 'URL')}\n </label>\n <input\n aria-label=\"https://api.example.com/hook\"\n className={inputClass}\n value={form.webhookUrl}\n onChange={(e) => update('webhookUrl', e.target.value)}\n placeholder=\"https://api.example.com/hook\"\n required\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.method', 'Method')}\n </label>\n <select\n className={inputClass}\n value={form.webhookMethod}\n onChange={(e) => update('webhookMethod', e.target.value)}\n >\n <option value=\"GET\">GET</option>\n <option value=\"POST\">POST</option>\n <option value=\"PUT\">PUT</option>\n <option value=\"PATCH\">PATCH</option>\n <option value=\"DELETE\">DELETE</option>\n </select>\n </div>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.bodyJson', 'Body (JSON)')}\n </label>\n <textarea\n aria-label=\"control\"\n className={`${inputClass} font-mono resize-none`}\n rows={4}\n value={form.webhookBody}\n onChange={(e) => update('webhookBody', e.target.value)}\n placeholder='{\"key\": \"value\"}'\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.authType', 'Auth Type')}\n </label>\n <select\n className={inputClass}\n value={form.webhookAuthType}\n onChange={(e) => update('webhookAuthType', e.target.value)}\n >\n <option value=\"\">None</option>\n <option value=\"BEARER\">Bearer Token</option>\n <option value=\"BASIC\">Basic Auth</option>\n <option value=\"API_KEY\">API Key</option>\n </select>\n </div>\n </div>\n )}\n\n {/* Options */}\n <div className=\"grid grid-cols-3 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.timeout', 'Timeout (ms)')}\n </label>\n <input\n aria-label=\"control\"\n className={inputClass}\n type=\"number\"\n min={1000}\n max={300000}\n value={form.timeoutMs}\n onChange={(e) => update('timeoutMs', parseInt(e.target.value) || 30000)}\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.retryCount', 'Retry Count')}\n </label>\n <input\n aria-label=\"control\"\n className={inputClass}\n type=\"number\"\n min={0}\n max={10}\n value={form.retryCount}\n onChange={(e) => update('retryCount', parseInt(e.target.value) || 0)}\n />\n </div>\n <div className=\"flex items-end pb-2\">\n <label className=\"flex items-center gap-2 text-sm text-text-secondary cursor-pointer\">\n <input\n aria-label=\"control\"\n type=\"checkbox\"\n checked={form.confirmationRequired}\n onChange={(e) => update('confirmationRequired', e.target.checked)}\n className=\"rounded border-border-default\"\n />\n {tr('vibecontrols.actions.form.requireConfirmation', 'Require confirmation')}\n </label>\n </div>\n </div>\n\n {extraSection && (\n <div className=\"pt-4 border-t border-border-default\">{extraSection}</div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 pt-4 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={isLoading}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors disabled:opacity-50\"\n >\n {tr('vibecontrols.actions.form.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n disabled={isLoading}\n className=\"px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {isLoading\n ? tr('vibecontrols.actions.form.saving', 'Saving...')\n : initialData\n ? tr('vibecontrols.actions.form.updateAction', 'Update Action')\n : tr('vibecontrols.actions.form.createAction', 'Create Action')}\n </button>\n </div>\n </form>\n </motion.div>\n </AnimatePresence>\n </dialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ActionForm.js","names":[],"sources":["../../../src/components/actions/ActionForm.tsx"],"sourcesContent":["import { useState, useEffect, useRef, type ReactNode } from 'react';\nimport { X } from 'lucide-react';\nimport { motion, AnimatePresence, useReducedMotion } from 'framer-motion';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { RemoteExecutionEnableNotice } from './RemoteExecutionEnableNotice';\n\nexport interface ActionFormData {\n name: string;\n displayName: string;\n description: string;\n actionType: string;\n triggerType: string;\n schedule: string;\n category: string;\n tags: string[];\n command: string;\n workingDirectory: string;\n targetAgentId: string;\n environmentVariables: Record<string, string>;\n script: string;\n scriptInterpreter: string;\n webhookUrl: string;\n webhookMethod: string;\n webhookHeaders: Record<string, string>;\n webhookBody: string;\n webhookAuthType: string;\n timeoutMs: number;\n retryCount: number;\n confirmationRequired: boolean;\n}\n\nconst DEFAULT_FORM: ActionFormData = {\n name: '',\n displayName: '',\n description: '',\n actionType: 'COMMAND',\n triggerType: 'MANUAL',\n schedule: '',\n category: '',\n tags: [],\n command: '',\n workingDirectory: '',\n targetAgentId: '',\n environmentVariables: {},\n script: '',\n scriptInterpreter: 'bash',\n webhookUrl: '',\n webhookMethod: 'POST',\n webhookHeaders: {},\n webhookBody: '',\n webhookAuthType: '',\n timeoutMs: 30000,\n retryCount: 0,\n confirmationRequired: false,\n};\n\ninterface ActionFormProps {\n title: string;\n initialData?: Partial<ActionFormData>;\n agents: Array<{ id: string; name: string; isActive: boolean }>;\n onSubmit: (data: ActionFormData) => void;\n onCancel: () => void;\n isLoading?: boolean;\n /** Optional extra content rendered below the form fields (e.g. tag picker in edit mode) */\n extraSection?: ReactNode;\n}\n\nexport function ActionForm({\n title,\n initialData,\n agents,\n onSubmit,\n onCancel,\n isLoading = false,\n extraSection,\n}: ActionFormProps) {\n const [form, setForm] = useState<ActionFormData>({ ...DEFAULT_FORM, ...initialData });\n const [tagsInput, setTagsInput] = useState(initialData?.tags?.join(', ') ?? '');\n const dialogRef = useRef<HTMLDialogElement>(null);\n const shouldReduceMotion = useReducedMotion();\n const tr = useTr();\n\n useEffect(() => {\n dialogRef.current?.showModal();\n }, []);\n\n const update = <K extends keyof ActionFormData>(key: K, value: ActionFormData[K]) =>\n setForm((prev) => ({ ...prev, [key]: value }));\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n const tags = tagsInput\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n onSubmit({ ...form, tags });\n };\n\n const inputClass =\n 'w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg focus:border-transparent transition-all';\n const labelClass = 'block text-sm font-medium text-text-secondary mb-1';\n\n return (\n <dialog\n ref={dialogRef}\n className=\"fixed inset-0 z-50 m-auto p-0 bg-transparent backdrop:bg-transparent\"\n onClose={onCancel}\n >\n <AnimatePresence>\n <motion.div\n className=\"fixed inset-0 bg-bg-overlay\"\n initial={shouldReduceMotion ? undefined : { opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n onClick={onCancel}\n />\n <motion.div\n className=\"relative w-full max-w-2xl max-h-[85vh] overflow-y-auto bg-bg-surface rounded-lg shadow-xl\"\n initial={shouldReduceMotion ? undefined : { opacity: 0, scale: 0.95, y: 8 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: 8 }}\n transition={{ duration: 0.2, ease: [0.2, 0, 0, 1] }}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 border-b border-border-default sticky top-0 bg-bg-surface z-10\">\n <h2 className=\"text-lg font-semibold text-text-primary\">{title}</h2>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-5 text-text-secondary\" />\n </button>\n </div>\n\n <form onSubmit={handleSubmit} className=\"p-6 space-y-4\">\n {/* Basic fields */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.nameSlug', 'Name (slug)')}\n </label>\n <input\n aria-label=\"my-action\"\n className={inputClass}\n value={form.name}\n onChange={(e) => update('name', e.target.value)}\n placeholder=\"my-action\"\n required\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.displayName', 'Display Name')}\n </label>\n <input\n aria-label=\"My Action\"\n className={inputClass}\n value={form.displayName}\n onChange={(e) => update('displayName', e.target.value)}\n placeholder=\"My Action\"\n required\n />\n </div>\n </div>\n\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.description', 'Description')}\n </label>\n <textarea\n aria-label=\"What does this action do?\"\n className={`${inputClass} resize-none`}\n rows={2}\n value={form.description}\n onChange={(e) => update('description', e.target.value)}\n placeholder=\"What does this action do?\"\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.actionType', 'Action Type')}\n </label>\n <select\n className={inputClass}\n value={form.actionType}\n onChange={(e) => update('actionType', e.target.value)}\n >\n <option value=\"COMMAND\">Command</option>\n <option value=\"SCRIPT\">Script</option>\n <option value=\"WEBHOOK\">Webhook</option>\n <option value=\"WORKFLOW\">Workflow</option>\n </select>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.triggerType', 'Trigger Type')}\n </label>\n <select\n className={inputClass}\n value={form.triggerType}\n onChange={(e) => update('triggerType', e.target.value)}\n >\n <option value=\"MANUAL\">Manual</option>\n <option value=\"SCHEDULED\">Scheduled</option>\n <option value=\"EVENT\">Event</option>\n <option value=\"ON_DEMAND\">On Demand</option>\n </select>\n </div>\n </div>\n\n {form.triggerType === 'SCHEDULED' && (\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.schedule', 'Schedule (cron expression)')}\n </label>\n <input\n aria-label=\"*/5 * * * *\"\n className={inputClass}\n value={form.schedule}\n onChange={(e) => update('schedule', e.target.value)}\n placeholder=\"*/5 * * * *\"\n />\n </div>\n )}\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.category', 'Category')}\n </label>\n <input\n aria-label=\"deployment, testing, etc.\"\n className={inputClass}\n value={form.category}\n onChange={(e) => update('category', e.target.value)}\n placeholder=\"deployment, testing, etc.\"\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.tags', 'Tags (comma separated)')}\n </label>\n <input\n aria-label=\"ci, deploy, infra\"\n className={inputClass}\n value={tagsInput}\n onChange={(e) => setTagsInput(e.target.value)}\n placeholder=\"ci, deploy, infra\"\n />\n </div>\n </div>\n\n {/* Type-specific fields */}\n {form.actionType === 'COMMAND' && (\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50 border border-border-default\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.actions.form.commandSettings', 'Command Settings')}\n </h3>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.command', 'Command')}\n </label>\n <textarea\n aria-label=\"echo 'Hello World'\"\n className={`${inputClass} font-mono resize-none`}\n rows={3}\n value={form.command}\n onChange={(e) => update('command', e.target.value)}\n placeholder=\"echo 'Hello World'\"\n required\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.workingDirectory', 'Working Directory')}\n </label>\n <input\n aria-label=\"/home/user/project\"\n className={inputClass}\n value={form.workingDirectory}\n onChange={(e) => update('workingDirectory', e.target.value)}\n placeholder=\"/home/user/project\"\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.targetAgent', 'Target Agent')}\n </label>\n <select\n className={inputClass}\n value={form.targetAgentId}\n onChange={(e) => update('targetAgentId', e.target.value)}\n >\n <option value=\"\">\n {tr('vibecontrols.actions.form.anyAvailableAgent', 'Any available agent')}\n </option>\n {agents.map((a) => (\n <option key={a.id} value={a.id} disabled={!a.isActive}>\n {a.name}{' '}\n {!a.isActive ? tr('vibecontrols.actions.form.offline', '(offline)') : ''}\n </option>\n ))}\n </select>\n {form.targetAgentId ? (\n <RemoteExecutionEnableNotice\n agentId={form.targetAgentId}\n agentName={agents.find((a) => a.id === form.targetAgentId)?.name}\n />\n ) : null}\n </div>\n </div>\n </div>\n )}\n\n {form.actionType === 'SCRIPT' && (\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50 border border-border-default\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.actions.form.scriptSettings', 'Script Settings')}\n </h3>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.script', 'Script')}\n </label>\n <textarea\n aria-label=\"#!/bin/bash&#10;echo 'Hello'\"\n className={`${inputClass} font-mono resize-none`}\n rows={6}\n value={form.script}\n onChange={(e) => update('script', e.target.value)}\n placeholder=\"#!/bin/bash&#10;echo 'Hello'\"\n required\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.interpreter', 'Interpreter')}\n </label>\n <select\n className={inputClass}\n value={form.scriptInterpreter}\n onChange={(e) => update('scriptInterpreter', e.target.value)}\n >\n <option value=\"bash\">Bash</option>\n <option value=\"python\">Python</option>\n <option value=\"node\">Node.js</option>\n </select>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.targetAgent', 'Target Agent')}\n </label>\n <select\n className={inputClass}\n value={form.targetAgentId}\n onChange={(e) => update('targetAgentId', e.target.value)}\n >\n <option value=\"\">\n {tr('vibecontrols.actions.form.anyAvailableAgent', 'Any available agent')}\n </option>\n {agents.map((a) => (\n <option key={a.id} value={a.id} disabled={!a.isActive}>\n {a.name}{' '}\n {!a.isActive ? tr('vibecontrols.actions.form.offline', '(offline)') : ''}\n </option>\n ))}\n </select>\n {form.targetAgentId ? (\n <RemoteExecutionEnableNotice\n agentId={form.targetAgentId}\n agentName={agents.find((a) => a.id === form.targetAgentId)?.name}\n />\n ) : null}\n </div>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.workingDirectory', 'Working Directory')}\n </label>\n <input\n aria-label=\"/home/user/project\"\n className={inputClass}\n value={form.workingDirectory}\n onChange={(e) => update('workingDirectory', e.target.value)}\n placeholder=\"/home/user/project\"\n />\n </div>\n </div>\n )}\n\n {form.actionType === 'WEBHOOK' && (\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50 border border-border-default\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.actions.form.webhookSettings', 'Webhook Settings')}\n </h3>\n <div className=\"grid grid-cols-[1fr_auto] gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.url', 'URL')}\n </label>\n <input\n aria-label=\"https://api.example.com/hook\"\n className={inputClass}\n value={form.webhookUrl}\n onChange={(e) => update('webhookUrl', e.target.value)}\n placeholder=\"https://api.example.com/hook\"\n required\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.method', 'Method')}\n </label>\n <select\n className={inputClass}\n value={form.webhookMethod}\n onChange={(e) => update('webhookMethod', e.target.value)}\n >\n <option value=\"GET\">GET</option>\n <option value=\"POST\">POST</option>\n <option value=\"PUT\">PUT</option>\n <option value=\"PATCH\">PATCH</option>\n <option value=\"DELETE\">DELETE</option>\n </select>\n </div>\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.bodyJson', 'Body (JSON)')}\n </label>\n <textarea\n aria-label=\"control\"\n className={`${inputClass} font-mono resize-none`}\n rows={4}\n value={form.webhookBody}\n onChange={(e) => update('webhookBody', e.target.value)}\n placeholder='{\"key\": \"value\"}'\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.authType', 'Auth Type')}\n </label>\n <select\n className={inputClass}\n value={form.webhookAuthType}\n onChange={(e) => update('webhookAuthType', e.target.value)}\n >\n <option value=\"\">None</option>\n <option value=\"BEARER\">Bearer Token</option>\n <option value=\"BASIC\">Basic Auth</option>\n <option value=\"API_KEY\">API Key</option>\n </select>\n </div>\n </div>\n )}\n\n {/* Options */}\n <div className=\"grid grid-cols-3 gap-4\">\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.timeout', 'Timeout (ms)')}\n </label>\n <input\n aria-label=\"control\"\n className={inputClass}\n type=\"number\"\n min={1000}\n max={300000}\n value={form.timeoutMs}\n onChange={(e) => update('timeoutMs', parseInt(e.target.value) || 30000)}\n />\n </div>\n <div>\n <label className={labelClass}>\n {tr('vibecontrols.actions.form.retryCount', 'Retry Count')}\n </label>\n <input\n aria-label=\"control\"\n className={inputClass}\n type=\"number\"\n min={0}\n max={10}\n value={form.retryCount}\n onChange={(e) => update('retryCount', parseInt(e.target.value) || 0)}\n />\n </div>\n <div className=\"flex items-end pb-2\">\n <label className=\"flex items-center gap-2 text-sm text-text-secondary cursor-pointer\">\n <input\n aria-label=\"control\"\n type=\"checkbox\"\n checked={form.confirmationRequired}\n onChange={(e) => update('confirmationRequired', e.target.checked)}\n className=\"rounded border-border-default\"\n />\n {tr('vibecontrols.actions.form.requireConfirmation', 'Require confirmation')}\n </label>\n </div>\n </div>\n\n {extraSection && (\n <div className=\"pt-4 border-t border-border-default\">{extraSection}</div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 pt-4 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={isLoading}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors disabled:opacity-50\"\n >\n {tr('vibecontrols.actions.form.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n disabled={isLoading}\n className=\"px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {isLoading\n ? tr('vibecontrols.actions.form.saving', 'Saving...')\n : initialData\n ? tr('vibecontrols.actions.form.updateAction', 'Update Action')\n : tr('vibecontrols.actions.form.createAction', 'Create Action')}\n </button>\n </div>\n </form>\n </motion.div>\n </AnimatePresence>\n </dialog>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ import { useTr as e } from "../../shared/hooks/useTr.js";
2
+ import { EnableRemoteTaskDialog as t } from "../shared/EnableRemoteTaskDialog.js";
3
+ import { useActiveAgentProfile as n } from "../../hooks/useActiveAgentProfile.js";
4
+ import { getAgentConfigValue as r } from "../../services/aiApi.js";
5
+ import { REMOTE_SHELL_TASK_GATE_KEY as i, enableRemoteTaskExecution as a } from "../../services/agentRemoteTaskGates.js";
6
+ import { useCallback as o, useEffect as s, useMemo as c, useState as l } from "react";
7
+ import { Loader2 as u, ShieldAlert as d, ShieldCheck as f } from "lucide-react";
8
+ import { jsx as p, jsxs as m } from "react/jsx-runtime";
9
+ //#region src/components/actions/RemoteExecutionEnableNotice.tsx
10
+ function h(e) {
11
+ if (!e) return !1;
12
+ let t = e.trim().toLowerCase();
13
+ return t === "1" || t === "true" || t === "yes";
14
+ }
15
+ function g({ agentId: g, agentName: _ }) {
16
+ let v = e(), { profiles: y } = n(g), b = y.find((e) => e.isDefault)?.name ?? "default", [x, S] = l(null), [C, w] = l(!1), [T, E] = l(!1), [D, O] = l(null), k = c(() => ({
17
+ id: g,
18
+ profile: b
19
+ }), [g, b]);
20
+ s(() => {
21
+ let e = !1;
22
+ return S(null), O(null), (async () => {
23
+ try {
24
+ let t = await r(k, i);
25
+ e || S(h(t));
26
+ } catch {
27
+ e || S(!1);
28
+ }
29
+ })(), () => {
30
+ e = !0;
31
+ };
32
+ }, [k]);
33
+ let A = o(async ({ shell: e, file: t }) => {
34
+ E(!0), O(null);
35
+ try {
36
+ await a(k, {
37
+ shell: e,
38
+ file: t
39
+ }), S(!0), w(!1);
40
+ } catch (e) {
41
+ O(e instanceof Error ? e.message : v("vibecontrols.enableRemoteTask.failed", "Failed to enable remote execution"));
42
+ } finally {
43
+ E(!1);
44
+ }
45
+ }, [k, v]);
46
+ return x === !0 ? /* @__PURE__ */ m("div", {
47
+ className: "mt-2 flex items-center gap-1.5 text-xs text-status-success-text",
48
+ children: [/* @__PURE__ */ p(f, { className: "size-3.5 flex-shrink-0" }), v("vibecontrols.enableRemoteTask.alreadyEnabled", "Remote execution is enabled on this agent.")]
49
+ }) : /* @__PURE__ */ m("div", {
50
+ className: "mt-2 rounded-md border border-status-warning-border bg-status-warning-bg-subtle px-3 py-2",
51
+ children: [/* @__PURE__ */ m("div", {
52
+ className: "flex items-start gap-2",
53
+ children: [/* @__PURE__ */ p(d, { className: "size-4 mt-0.5 flex-shrink-0 text-status-warning-text" }), /* @__PURE__ */ m("div", {
54
+ className: "min-w-0 flex-1 space-y-1",
55
+ children: [
56
+ /* @__PURE__ */ p("p", {
57
+ className: "text-xs text-text-secondary",
58
+ children: v("vibecontrols.enableRemoteTask.notice", "This agent blocks remote command/script execution by default. Enable it so decks, schedules, and the Actions page can run this action.")
59
+ }),
60
+ D ? /* @__PURE__ */ p("p", {
61
+ className: "text-xs text-status-error-text",
62
+ children: D
63
+ }) : null,
64
+ /* @__PURE__ */ m("button", {
65
+ type: "button",
66
+ onClick: () => w(!0),
67
+ disabled: x === null,
68
+ className: "inline-flex items-center gap-1.5 text-xs font-medium text-action-primary-text hover:underline disabled:opacity-50",
69
+ children: [x === null ? /* @__PURE__ */ p(u, { className: "size-3.5 animate-spin" }) : null, v("vibecontrols.enableRemoteTask.enableButton", "Enable remote execution")]
70
+ })
71
+ ]
72
+ })]
73
+ }), /* @__PURE__ */ p(t, {
74
+ isOpen: C,
75
+ agentName: _,
76
+ isLoading: T,
77
+ onClose: () => w(!1),
78
+ onConfirm: A
79
+ })]
80
+ });
81
+ }
82
+ //#endregion
83
+ export { g as RemoteExecutionEnableNotice };
84
+
85
+ //# sourceMappingURL=RemoteExecutionEnableNotice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RemoteExecutionEnableNotice.js","names":[],"sources":["../../../src/components/actions/RemoteExecutionEnableNotice.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react';\nimport { Loader2, ShieldAlert, ShieldCheck } from 'lucide-react';\n\nimport { useTr } from '../../shared/hooks/useTr';\nimport { EnableRemoteTaskDialog } from '../shared/EnableRemoteTaskDialog';\nimport { useActiveAgentProfile } from '@/hooks/useActiveAgentProfile';\nimport { getAgentConfigValue } from '@/services/aiApi';\nimport {\n REMOTE_SHELL_TASK_GATE_KEY,\n enableRemoteTaskExecution,\n} from '@/services/agentRemoteTaskGates';\n\ninterface RemoteExecutionEnableNoticeProps {\n agentId: string;\n agentName?: string | null;\n}\n\nfunction isTruthy(raw: string | null | undefined): boolean {\n if (!raw) return false;\n const v = raw.trim().toLowerCase();\n return v === '1' || v === 'true' || v === 'yes';\n}\n\n/**\n * Proactive, in-context affordance shown beneath the Target Agent selector when\n * a COMMAND/SCRIPT action is mapped to an agent: it reports whether that agent\n * allows remote execution and, if not, lets the user enable it right here —\n * instead of having to leave the form for the agent's Security tab. Reuses the\n * same gate write as the Security card.\n */\nexport function RemoteExecutionEnableNotice({\n agentId,\n agentName,\n}: RemoteExecutionEnableNoticeProps) {\n const tr = useTr();\n // Probe + enable the gate on the agent's DEFAULT profile — that is the profile\n // the action runner targets (getDefaultProfileName), not necessarily the UI's\n // \"active\" profile.\n const { profiles } = useActiveAgentProfile(agentId);\n const profile = profiles.find((p) => p.isDefault)?.name ?? 'default';\n // null = unknown / still probing the agent.\n const [enabled, setEnabled] = useState<boolean | null>(null);\n const [dialogOpen, setDialogOpen] = useState(false);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const agentRef = useMemo(() => ({ id: agentId, profile }), [agentId, profile]);\n\n // Best-effort read of the current gate. If the agent is unreachable we simply\n // offer the enable affordance rather than block the form.\n useEffect(() => {\n let cancelled = false;\n setEnabled(null);\n setError(null);\n void (async () => {\n try {\n const value = await getAgentConfigValue(agentRef, REMOTE_SHELL_TASK_GATE_KEY);\n if (!cancelled) setEnabled(isTruthy(value));\n } catch {\n if (!cancelled) setEnabled(false);\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [agentRef]);\n\n const handleConfirm = useCallback(\n async ({ shell, file }: { shell: boolean; file: boolean }) => {\n setSaving(true);\n setError(null);\n try {\n await enableRemoteTaskExecution(agentRef, { shell, file });\n setEnabled(true);\n setDialogOpen(false);\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr('vibecontrols.enableRemoteTask.failed', 'Failed to enable remote execution')\n );\n } finally {\n setSaving(false);\n }\n },\n [agentRef, tr]\n );\n\n if (enabled === true) {\n return (\n <div className=\"mt-2 flex items-center gap-1.5 text-xs text-status-success-text\">\n <ShieldCheck className=\"size-3.5 flex-shrink-0\" />\n {tr(\n 'vibecontrols.enableRemoteTask.alreadyEnabled',\n 'Remote execution is enabled on this agent.'\n )}\n </div>\n );\n }\n\n return (\n <div className=\"mt-2 rounded-md border border-status-warning-border bg-status-warning-bg-subtle px-3 py-2\">\n <div className=\"flex items-start gap-2\">\n <ShieldAlert className=\"size-4 mt-0.5 flex-shrink-0 text-status-warning-text\" />\n <div className=\"min-w-0 flex-1 space-y-1\">\n <p className=\"text-xs text-text-secondary\">\n {tr(\n 'vibecontrols.enableRemoteTask.notice',\n 'This agent blocks remote command/script execution by default. Enable it so decks, schedules, and the Actions page can run this action.'\n )}\n </p>\n {error ? <p className=\"text-xs text-status-error-text\">{error}</p> : null}\n <button\n type=\"button\"\n onClick={() => setDialogOpen(true)}\n disabled={enabled === null}\n className=\"inline-flex items-center gap-1.5 text-xs font-medium text-action-primary-text hover:underline disabled:opacity-50\"\n >\n {enabled === null ? <Loader2 className=\"size-3.5 animate-spin\" /> : null}\n {tr('vibecontrols.enableRemoteTask.enableButton', 'Enable remote execution')}\n </button>\n </div>\n </div>\n <EnableRemoteTaskDialog\n isOpen={dialogOpen}\n agentName={agentName}\n isLoading={saving}\n onClose={() => setDialogOpen(false)}\n onConfirm={handleConfirm}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAiBA,SAAS,EAAS,GAAyC;AACzD,KAAI,CAAC,EAAK,QAAO;CACjB,IAAM,IAAI,EAAI,MAAM,CAAC,aAAa;AAClC,QAAO,MAAM,OAAO,MAAM,UAAU,MAAM;;AAU5C,SAAgB,EAA4B,EAC1C,YACA,gBACmC;CACnC,IAAM,IAAK,GAAO,EAIZ,EAAE,gBAAa,EAAsB,EAAQ,EAC7C,IAAU,EAAS,MAAM,MAAM,EAAE,UAAU,EAAE,QAAQ,WAErD,CAAC,GAAS,KAAc,EAAyB,KAAK,EACtD,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,CAAC,GAAO,KAAY,EAAwB,KAAK,EAEjD,IAAW,SAAe;EAAE,IAAI;EAAS;EAAS,GAAG,CAAC,GAAS,EAAQ,CAAC;AAI9E,SAAgB;EACd,IAAI,IAAY;AAWhB,SAVA,EAAW,KAAK,EAChB,EAAS,KAAK,GACR,YAAY;AAChB,OAAI;IACF,IAAM,IAAQ,MAAM,EAAoB,GAAU,EAA2B;AAC7E,IAAK,KAAW,EAAW,EAAS,EAAM,CAAC;WACrC;AACN,IAAK,KAAW,EAAW,GAAM;;MAEjC,QACS;AACX,OAAY;;IAEb,CAAC,EAAS,CAAC;CAEd,IAAM,IAAgB,EACpB,OAAO,EAAE,UAAO,cAA8C;AAE5D,EADA,EAAU,GAAK,EACf,EAAS,KAAK;AACd,MAAI;AAGF,GAFA,MAAM,EAA0B,GAAU;IAAE;IAAO;IAAM,CAAC,EAC1D,EAAW,GAAK,EAChB,EAAc,GAAM;WACb,GAAK;AACZ,KACE,aAAe,QACX,EAAI,UACJ,EAAG,wCAAwC,oCAAoC,CACpF;YACO;AACR,KAAU,GAAM;;IAGpB,CAAC,GAAU,EAAG,CACf;AAcD,QAZI,MAAY,KAEZ,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD,EAAa,WAAU,0BAA2B,CAAA,EACjD,EACC,gDACA,6CACD,CACG;MAKR,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD,EAAa,WAAU,wDAAyD,CAAA,EAChF,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EACC,wCACA,yIACD;MACC,CAAA;KACH,IAAQ,kBAAC,KAAD;MAAG,WAAU;gBAAkC;MAAU,CAAA,GAAG;KACrE,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAc,GAAK;MAClC,UAAU,MAAY;MACtB,WAAU;gBAJZ,CAMG,MAAY,OAAO,kBAAC,GAAD,EAAS,WAAU,yBAA0B,CAAA,GAAG,MACnE,EAAG,8CAA8C,0BAA0B,CACrE;;KACL;MACF;MACN,kBAAC,GAAD;GACE,QAAQ;GACG;GACX,WAAW;GACX,eAAe,EAAc,GAAM;GACnC,WAAW;GACX,CAAA,CACE"}
@@ -0,0 +1,123 @@
1
+ import { useSetAgentApiKeyMutation as e, useUpdateAgentTunnelUrlMutation as t } from "../../generated/wspace-operations.js";
2
+ import { useTr as n } from "../../shared/hooks/useTr.js";
3
+ import { useState as r } from "react";
4
+ import { AlertTriangle as i, KeyRound as a, Link2 as o, Loader2 as s, ShieldCheck as c } from "lucide-react";
5
+ import { jsx as l, jsxs as u } from "react/jsx-runtime";
6
+ //#region src/components/agents/AgentConnectionRecovery.tsx
7
+ var d = "rounded-lg border border-border-default bg-bg-surface p-4 sm:p-5", f = "inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50", p = "w-full px-3 py-2 text-sm rounded-md bg-bg-sunken border border-border-default text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg/40 font-mono";
8
+ function m(e) {
9
+ return e instanceof Error ? e.message : String(e);
10
+ }
11
+ function h({ agentId: h, currentTunnelUrl: g, onUpdated: _ }) {
12
+ let v = n(), [y, b] = r(""), [x, { loading: S }] = e(), [C, w] = r(null), T = async () => {
13
+ w(null);
14
+ let e = y.trim();
15
+ if (e) try {
16
+ await x({ variables: {
17
+ agentId: h,
18
+ apiKey: e
19
+ } }), w({
20
+ ok: !0,
21
+ msg: v("agentDetails.recovery.keySaved", "API key validated and saved. The agent is reachable again.")
22
+ }), b(""), _?.();
23
+ } catch (e) {
24
+ w({
25
+ ok: !1,
26
+ msg: m(e)
27
+ });
28
+ }
29
+ }, [E, D] = r(g ?? ""), [O, { loading: k }] = t(), [A, j] = r(null), M = async () => {
30
+ j(null);
31
+ let e = E.trim();
32
+ if (e) try {
33
+ await O({ variables: {
34
+ agentId: h,
35
+ tunnelUrl: e
36
+ } }), j({
37
+ ok: !0,
38
+ msg: v("agentDetails.recovery.urlSaved", "Tunnel URL verified and updated. The agent is back online.")
39
+ }), _?.();
40
+ } catch (e) {
41
+ j({
42
+ ok: !1,
43
+ msg: m(e)
44
+ });
45
+ }
46
+ }, N = (e) => e ? /* @__PURE__ */ u("p", {
47
+ className: `mt-2 flex items-start gap-2 text-xs ${e.ok ? "text-status-success-text" : "text-status-error-text"}`,
48
+ children: [e.ok ? /* @__PURE__ */ l(c, { className: "size-4 flex-shrink-0 mt-0.5" }) : /* @__PURE__ */ l(i, { className: "size-4 flex-shrink-0 mt-0.5" }), /* @__PURE__ */ l("span", { children: e.msg })]
49
+ }) : null;
50
+ return /* @__PURE__ */ u("div", {
51
+ className: "space-y-4",
52
+ children: [/* @__PURE__ */ u("section", {
53
+ className: d,
54
+ children: [
55
+ /* @__PURE__ */ u("div", {
56
+ className: "flex items-center gap-2 mb-1",
57
+ children: [/* @__PURE__ */ l(a, { className: "size-5 text-text-secondary" }), /* @__PURE__ */ l("h3", {
58
+ className: "text-base font-semibold text-text-primary",
59
+ children: v("agentDetails.recovery.keyTitle", "Update API key (no re-add)")
60
+ })]
61
+ }),
62
+ /* @__PURE__ */ l("p", {
63
+ className: "text-xs text-text-muted mb-3",
64
+ children: v("agentDetails.recovery.keyHint", "If the agent’s API key changed and it can’t auto-report (gateway auth down), paste the value from `vibe key` on the host. It’s validated against the agent before saving — your sessions and data stay intact.")
65
+ }),
66
+ /* @__PURE__ */ u("div", {
67
+ className: "flex flex-col sm:flex-row gap-2",
68
+ children: [/* @__PURE__ */ l("input", {
69
+ type: "password",
70
+ autoComplete: "off",
71
+ value: y,
72
+ onChange: (e) => b(e.target.value),
73
+ placeholder: "vcak_…",
74
+ className: p
75
+ }), /* @__PURE__ */ u("button", {
76
+ type: "button",
77
+ onClick: T,
78
+ disabled: S || y.trim().length === 0,
79
+ className: f,
80
+ children: [S ? /* @__PURE__ */ l(s, { className: "size-4 animate-spin" }) : /* @__PURE__ */ l(c, { className: "size-4" }), v("agentDetails.recovery.validateSave", "Validate & save")]
81
+ })]
82
+ }),
83
+ N(C)
84
+ ]
85
+ }), /* @__PURE__ */ u("section", {
86
+ className: d,
87
+ children: [
88
+ /* @__PURE__ */ u("div", {
89
+ className: "flex items-center gap-2 mb-1",
90
+ children: [/* @__PURE__ */ l(o, { className: "size-5 text-text-secondary" }), /* @__PURE__ */ l("h3", {
91
+ className: "text-base font-semibold text-text-primary",
92
+ children: v("agentDetails.recovery.urlTitle", "Update tunnel URL (re-pair)")
93
+ })]
94
+ }),
95
+ /* @__PURE__ */ l("p", {
96
+ className: "text-xs text-text-muted mb-3",
97
+ children: v("agentDetails.recovery.urlHint", "After a quick-tunnel rotation, paste the new URL here. The backend verifies it points at the same machine before saving — no re-registration, no data loss.")
98
+ }),
99
+ /* @__PURE__ */ u("div", {
100
+ className: "flex flex-col sm:flex-row gap-2",
101
+ children: [/* @__PURE__ */ l("input", {
102
+ type: "url",
103
+ value: E,
104
+ onChange: (e) => D(e.target.value),
105
+ placeholder: "https://….trycloudflare.com",
106
+ className: p
107
+ }), /* @__PURE__ */ u("button", {
108
+ type: "button",
109
+ onClick: M,
110
+ disabled: k || E.trim().length === 0,
111
+ className: f,
112
+ children: [k ? /* @__PURE__ */ l(s, { className: "size-4 animate-spin" }) : /* @__PURE__ */ l(o, { className: "size-4" }), v("agentDetails.recovery.verifyUpdate", "Verify & update")]
113
+ })]
114
+ }),
115
+ N(A)
116
+ ]
117
+ })]
118
+ });
119
+ }
120
+ //#endregion
121
+ export { h as AgentConnectionRecovery };
122
+
123
+ //# sourceMappingURL=AgentConnectionRecovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentConnectionRecovery.js","names":[],"sources":["../../../src/components/agents/AgentConnectionRecovery.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { KeyRound, Link2, Loader2, ShieldCheck, AlertTriangle } from 'lucide-react';\n\nimport {\n useSetAgentApiKeyMutation,\n useUpdateAgentTunnelUrlMutation,\n} from '@/generated/wspace-operations';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AgentConnectionRecoveryProps {\n agentId: string;\n currentTunnelUrl?: string | null;\n /** Refetch the agent after a successful in-place update. */\n onUpdated?: () => void;\n}\n\nconst CARD = 'rounded-lg border border-border-default bg-bg-surface p-4 sm:p-5';\nconst BTN =\n 'inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50';\nconst INPUT =\n 'w-full px-3 py-2 text-sm rounded-md bg-bg-sunken border border-border-default text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg/40 font-mono';\n\nfunction errMsg(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n\n/**\n * Emergency connection recovery for an existing agent — used when gateway\n * auto-report is broken (so the agent can't refresh its own key/tunnel) and\n * the operator must fix it in place WITHOUT delete + re-add (which loses\n * sessions / notes / bookmarks).\n * - \"Update API key\": paste the value from `vibe key`; the backend probes the\n * agent with it and only saves if the agent accepts it.\n * - \"Update tunnel URL\": paste the new quick-tunnel URL; the backend verifies\n * it's the same machine (hostname) before saving.\n */\nexport function AgentConnectionRecovery({\n agentId,\n currentTunnelUrl,\n onUpdated,\n}: AgentConnectionRecoveryProps) {\n const tr = useTr();\n\n // ── API key ─────────────────────────────────────────────────────────────\n const [keyInput, setKeyInput] = useState('');\n const [setKey, { loading: settingKey }] = useSetAgentApiKeyMutation();\n const [keyResult, setKeyResult] = useState<{ ok: boolean; msg: string } | null>(null);\n\n const handleSetKey = async () => {\n setKeyResult(null);\n const key = keyInput.trim();\n if (!key) return;\n try {\n await setKey({ variables: { agentId, apiKey: key } });\n setKeyResult({\n ok: true,\n msg: tr('agentDetails.recovery.keySaved', 'API key validated and saved. The agent is reachable again.'),\n });\n setKeyInput('');\n onUpdated?.();\n } catch (e) {\n setKeyResult({ ok: false, msg: errMsg(e) });\n }\n };\n\n // ── Tunnel URL ──────────────────────────────────────────────────────────\n const [urlInput, setUrlInput] = useState(currentTunnelUrl ?? '');\n const [updateUrl, { loading: updatingUrl }] = useUpdateAgentTunnelUrlMutation();\n const [urlResult, setUrlResult] = useState<{ ok: boolean; msg: string } | null>(null);\n\n const handleUpdateUrl = async () => {\n setUrlResult(null);\n const url = urlInput.trim();\n if (!url) return;\n try {\n await updateUrl({ variables: { agentId, tunnelUrl: url } });\n setUrlResult({\n ok: true,\n msg: tr('agentDetails.recovery.urlSaved', 'Tunnel URL verified and updated. The agent is back online.'),\n });\n onUpdated?.();\n } catch (e) {\n setUrlResult({ ok: false, msg: errMsg(e) });\n }\n };\n\n const result = (r: { ok: boolean; msg: string } | null) =>\n r ? (\n <p\n className={`mt-2 flex items-start gap-2 text-xs ${\n r.ok ? 'text-status-success-text' : 'text-status-error-text'\n }`}\n >\n {r.ok ? (\n <ShieldCheck className=\"size-4 flex-shrink-0 mt-0.5\" />\n ) : (\n <AlertTriangle className=\"size-4 flex-shrink-0 mt-0.5\" />\n )}\n <span>{r.msg}</span>\n </p>\n ) : null;\n\n return (\n <div className=\"space-y-4\">\n <section className={CARD}>\n <div className=\"flex items-center gap-2 mb-1\">\n <KeyRound className=\"size-5 text-text-secondary\" />\n <h3 className=\"text-base font-semibold text-text-primary\">\n {tr('agentDetails.recovery.keyTitle', 'Update API key (no re-add)')}\n </h3>\n </div>\n <p className=\"text-xs text-text-muted mb-3\">\n {tr(\n 'agentDetails.recovery.keyHint',\n 'If the agent’s API key changed and it can’t auto-report (gateway auth down), paste the value from `vibe key` on the host. It’s validated against the agent before saving — your sessions and data stay intact.'\n )}\n </p>\n <div className=\"flex flex-col sm:flex-row gap-2\">\n <input\n type=\"password\"\n autoComplete=\"off\"\n value={keyInput}\n onChange={(e) => setKeyInput(e.target.value)}\n placeholder=\"vcak_…\"\n className={INPUT}\n />\n <button\n type=\"button\"\n onClick={handleSetKey}\n disabled={settingKey || keyInput.trim().length === 0}\n className={BTN}\n >\n {settingKey ? <Loader2 className=\"size-4 animate-spin\" /> : <ShieldCheck className=\"size-4\" />}\n {tr('agentDetails.recovery.validateSave', 'Validate & save')}\n </button>\n </div>\n {result(keyResult)}\n </section>\n\n <section className={CARD}>\n <div className=\"flex items-center gap-2 mb-1\">\n <Link2 className=\"size-5 text-text-secondary\" />\n <h3 className=\"text-base font-semibold text-text-primary\">\n {tr('agentDetails.recovery.urlTitle', 'Update tunnel URL (re-pair)')}\n </h3>\n </div>\n <p className=\"text-xs text-text-muted mb-3\">\n {tr(\n 'agentDetails.recovery.urlHint',\n 'After a quick-tunnel rotation, paste the new URL here. The backend verifies it points at the same machine before saving — no re-registration, no data loss.'\n )}\n </p>\n <div className=\"flex flex-col sm:flex-row gap-2\">\n <input\n type=\"url\"\n value={urlInput}\n onChange={(e) => setUrlInput(e.target.value)}\n placeholder=\"https://….trycloudflare.com\"\n className={INPUT}\n />\n <button\n type=\"button\"\n onClick={handleUpdateUrl}\n disabled={updatingUrl || urlInput.trim().length === 0}\n className={BTN}\n >\n {updatingUrl ? <Loader2 className=\"size-4 animate-spin\" /> : <Link2 className=\"size-4\" />}\n {tr('agentDetails.recovery.verifyUpdate', 'Verify & update')}\n </button>\n </div>\n {result(urlResult)}\n </section>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,100 +1,101 @@
1
1
  import { useTr as e } from "../../shared/hooks/useTr.js";
2
2
  import { deleteAgentConfigValue as t, getAgentConfigValue as n, setAgentConfigValue as r } from "../../services/aiApi.js";
3
- import { useCallback as i, useEffect as a, useState as o } from "react";
4
- import { Loader2 as s, ShieldAlert as c } from "lucide-react";
5
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
6
- import { Card as d, Switch as f } from "@burdenoff/fe-libs/ui";
3
+ import { REMOTE_FILE_TASK_GATE_KEY as i, REMOTE_SHELL_TASK_GATE_KEY as a } from "../../services/agentRemoteTaskGates.js";
4
+ import { useCallback as o, useEffect as s, useState as c } from "react";
5
+ import { Loader2 as l, ShieldAlert as u } from "lucide-react";
6
+ import { jsx as d, jsxs as f } from "react/jsx-runtime";
7
+ import { Card as p, Switch as m } from "@burdenoff/fe-libs/ui";
7
8
  //#region src/components/agents/RemoteTaskPermissionsCard.tsx
8
- function p(e) {
9
+ function h(e) {
9
10
  return [{
10
- key: "security:vibecontrols_allow_remote_shell_task",
11
+ key: a,
11
12
  title: e("agentDetails.remoteShellTask.title", "Allow remote shell + script tasks"),
12
13
  description: e("agentDetails.remoteShellTask.description", "Enable VibeDeck buttons, scheduled jobs, and the UI to run shell commands and scripts on this agent over its tunnel. Required for any UI-triggered \"execute action\" that runs a command."),
13
14
  warning: e("agentDetails.remoteShellTask.warning", "Anyone with workspace access can execute commands on the host machine. Audit log records every invocation; reviewers can revert this flag at any time.")
14
15
  }, {
15
- key: "security:vibecontrols_allow_remote_file_task",
16
+ key: i,
16
17
  title: e("agentDetails.remoteFileTask.title", "Allow remote file writes + deletes"),
17
18
  description: e("agentDetails.remoteFileTask.description", "Enable UI-triggered file_operation write/delete tasks on this agent. Reads are always allowed; this gate covers mutations only."),
18
19
  warning: e("agentDetails.remoteFileTask.warning", "Writes and deletes are confined to the cwd / homedir / tmpdir allowlist via resolveSafePath + isSensitivePath, but a malicious caller could still corrupt files in those scopes.")
19
20
  }];
20
21
  }
21
- function m(e) {
22
+ function g(e) {
22
23
  if (!e) return !1;
23
24
  let t = e.trim().toLowerCase();
24
25
  return t === "1" || t === "true" || t === "yes";
25
26
  }
26
- function h({ agent: h }) {
27
- let g = e(), _ = p(g), [v, y] = o(!0), [b, x] = o(null), [S, C] = o({}), [w, T] = o(null), E = i(async () => {
27
+ function _({ agent: i }) {
28
+ let a = e(), _ = h(a), [v, y] = c(!0), [b, x] = c(null), [S, C] = c({}), [w, T] = c(null), E = o(async () => {
28
29
  y(!0), T(null);
29
30
  try {
30
- let e = await Promise.all(_.map(async (e) => [e.key, m(await n(h, e.key))]));
31
+ let e = await Promise.all(_.map(async (e) => [e.key, g(await n(i, e.key))]));
31
32
  C(Object.fromEntries(e));
32
33
  } catch (e) {
33
- T(e instanceof Error ? e.message : g("agentDetails.remoteTaskPermissions.loadFailed", "Failed to load permissions"));
34
+ T(e instanceof Error ? e.message : a("agentDetails.remoteTaskPermissions.loadFailed", "Failed to load permissions"));
34
35
  } finally {
35
36
  y(!1);
36
37
  }
37
- }, [h, g]);
38
- a(() => {
38
+ }, [i, a]);
39
+ s(() => {
39
40
  E();
40
41
  }, [E]);
41
- let D = i(async (e, n) => {
42
+ let D = o(async (e, n) => {
42
43
  x(e), T(null);
43
44
  try {
44
- n ? await r(h, e, "1") : await t(h, e), C((t) => ({
45
+ n ? await r(i, e, "1") : await t(i, e), C((t) => ({
45
46
  ...t,
46
47
  [e]: n
47
48
  }));
48
49
  } catch (e) {
49
- T(e instanceof Error ? e.message : g("agentDetails.remoteTaskPermissions.saveFailed", "Failed to save permission"));
50
+ T(e instanceof Error ? e.message : a("agentDetails.remoteTaskPermissions.saveFailed", "Failed to save permission"));
50
51
  } finally {
51
52
  x(null);
52
53
  }
53
- }, [h, g]);
54
- return /* @__PURE__ */ u(d, { children: [/* @__PURE__ */ u("div", {
54
+ }, [i, a]);
55
+ return /* @__PURE__ */ f(p, { children: [/* @__PURE__ */ f("div", {
55
56
  className: "p-4 border-b border-border-default",
56
- children: [/* @__PURE__ */ u("h3", {
57
+ children: [/* @__PURE__ */ f("h3", {
57
58
  className: "font-semibold text-text-primary flex items-center gap-2",
58
- children: [/* @__PURE__ */ l(c, { className: "size-5" }), g("agentDetails.remoteTaskPermissions.title", "Remote Task Permissions")]
59
- }), /* @__PURE__ */ l("p", {
59
+ children: [/* @__PURE__ */ d(u, { className: "size-5" }), a("agentDetails.remoteTaskPermissions.title", "Remote Task Permissions")]
60
+ }), /* @__PURE__ */ d("p", {
60
61
  className: "mt-1 text-xs text-text-secondary",
61
- children: g("agentDetails.remoteTaskPermissions.subtitle", "These gates default to deny so an agent on a public tunnel can’t be coerced into shell execution. Toggle them on per-agent if you want VibeDeck buttons / scheduled jobs to run shell or file-mutation tasks here.")
62
+ children: a("agentDetails.remoteTaskPermissions.subtitle", "These gates default to deny so an agent on a public tunnel can’t be coerced into shell execution. Toggle them on per-agent if you want VibeDeck buttons / scheduled jobs to run shell or file-mutation tasks here.")
62
63
  })]
63
- }), /* @__PURE__ */ u("div", {
64
+ }), /* @__PURE__ */ f("div", {
64
65
  className: "p-4 space-y-4",
65
- children: [w ? /* @__PURE__ */ l("div", {
66
+ children: [w ? /* @__PURE__ */ d("div", {
66
67
  className: "rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-sm text-status-error-text",
67
68
  children: w
68
- }) : null, v ? /* @__PURE__ */ u("div", {
69
+ }) : null, v ? /* @__PURE__ */ f("div", {
69
70
  className: "flex items-center gap-2 text-sm text-text-secondary",
70
- children: [/* @__PURE__ */ l(s, { className: "size-4 animate-spin" }), g("agentDetails.remoteTaskPermissions.loading", "Loading…")]
71
+ children: [/* @__PURE__ */ d(l, { className: "size-4 animate-spin" }), a("agentDetails.remoteTaskPermissions.loading", "Loading…")]
71
72
  }) : _.map((e) => {
72
73
  let t = S[e.key] ?? !1, n = b === e.key;
73
- return /* @__PURE__ */ u("div", {
74
+ return /* @__PURE__ */ f("div", {
74
75
  className: "flex items-start gap-3 rounded-lg border border-border-default bg-bg-primary p-3",
75
- children: [/* @__PURE__ */ u("div", {
76
+ children: [/* @__PURE__ */ f("div", {
76
77
  className: "flex-1 min-w-0 space-y-1",
77
78
  children: [
78
- /* @__PURE__ */ l("p", {
79
+ /* @__PURE__ */ d("p", {
79
80
  className: "text-sm font-medium text-text-primary",
80
81
  children: e.title
81
82
  }),
82
- /* @__PURE__ */ l("p", {
83
+ /* @__PURE__ */ d("p", {
83
84
  className: "text-xs text-text-secondary",
84
85
  children: e.description
85
86
  }),
86
- e.warning ? /* @__PURE__ */ l("p", {
87
+ e.warning ? /* @__PURE__ */ d("p", {
87
88
  className: "text-xs text-status-warning-text",
88
89
  children: e.warning
89
90
  }) : null,
90
- /* @__PURE__ */ l("p", {
91
+ /* @__PURE__ */ d("p", {
91
92
  className: "text-[11px] font-mono text-text-tertiary",
92
93
  children: e.key
93
94
  })
94
95
  ]
95
- }), /* @__PURE__ */ u("div", {
96
+ }), /* @__PURE__ */ f("div", {
96
97
  className: "flex items-center gap-2 flex-shrink-0",
97
- children: [n ? /* @__PURE__ */ l(s, { className: "size-4 animate-spin text-text-secondary" }) : null, /* @__PURE__ */ l(f, {
98
+ children: [n ? /* @__PURE__ */ d(l, { className: "size-4 animate-spin text-text-secondary" }) : null, /* @__PURE__ */ d(m, {
98
99
  checked: t,
99
100
  onCheckedChange: (t) => void D(e.key, t),
100
101
  disabled: n,
@@ -106,6 +107,6 @@ function h({ agent: h }) {
106
107
  })] });
107
108
  }
108
109
  //#endregion
109
- export { h as RemoteTaskPermissionsCard };
110
+ export { _ as RemoteTaskPermissionsCard };
110
111
 
111
112
  //# sourceMappingURL=RemoteTaskPermissionsCard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RemoteTaskPermissionsCard.js","names":[],"sources":["../../../src/components/agents/RemoteTaskPermissionsCard.tsx"],"sourcesContent":["/**\n * RemoteTaskPermissionsCard\n *\n * Toggles the per-agent \"allow remote shell tasks\" and \"allow remote\n * file-mutation tasks\" gates that the agent enforces in\n * `core/request-security.ts:denyNonLocalMutation`. The agent's task\n * plugin (POST /api/profiles/<n>/tasks/) refuses non-loopback callers by\n * default because remote shell execution + file writes are high-blast\n * radius; this card lets operators opt in per-agent without having to\n * SSH in and restart the daemon with the matching env var\n * (VIBECONTROLS_ALLOW_REMOTE_SHELL_TASK / _FILE_TASK).\n *\n * Storage: writes to the agent's encrypted config bag under\n * `security:vibecontrols_allow_remote_shell_task` and\n * `security:vibecontrols_allow_remote_file_task`. The same keys are\n * read by the agent's gate via `db.getConfig(...)`. Persisted across\n * agent restarts.\n */\nimport { useCallback, useEffect, useState } from 'react';\nimport { ShieldAlert, Loader2 } from 'lucide-react';\n\nimport { Card } from '@burdenoff/fe-libs/ui';\nimport { Switch } from '@burdenoff/fe-libs/ui';\n\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n deleteAgentConfigValue,\n getAgentConfigValue,\n setAgentConfigValue,\n type AgentRef,\n} from '@/services/aiApi';\n\ninterface RemoteTaskPermissionsCardProps {\n agent: AgentRef;\n}\n\ninterface GateDef {\n key: string;\n title: string;\n description: string;\n warning?: string;\n}\n\nfunction buildGates(tr: ReturnType<typeof useTr>): GateDef[] {\n return [\n {\n key: 'security:vibecontrols_allow_remote_shell_task',\n title: tr('agentDetails.remoteShellTask.title', 'Allow remote shell + script tasks'),\n description: tr(\n 'agentDetails.remoteShellTask.description',\n 'Enable VibeDeck buttons, scheduled jobs, and the UI to run shell commands and scripts on this agent over its tunnel. Required for any UI-triggered \"execute action\" that runs a command.'\n ),\n warning: tr(\n 'agentDetails.remoteShellTask.warning',\n 'Anyone with workspace access can execute commands on the host machine. Audit log records every invocation; reviewers can revert this flag at any time.'\n ),\n },\n {\n key: 'security:vibecontrols_allow_remote_file_task',\n title: tr('agentDetails.remoteFileTask.title', 'Allow remote file writes + deletes'),\n description: tr(\n 'agentDetails.remoteFileTask.description',\n 'Enable UI-triggered file_operation write/delete tasks on this agent. Reads are always allowed; this gate covers mutations only.'\n ),\n warning: tr(\n 'agentDetails.remoteFileTask.warning',\n 'Writes and deletes are confined to the cwd / homedir / tmpdir allowlist via resolveSafePath + isSensitivePath, but a malicious caller could still corrupt files in those scopes.'\n ),\n },\n ];\n}\n\nfunction isTruthy(raw: string | null | undefined): boolean {\n if (!raw) return false;\n const v = raw.trim().toLowerCase();\n return v === '1' || v === 'true' || v === 'yes';\n}\n\nexport function RemoteTaskPermissionsCard({ agent }: RemoteTaskPermissionsCardProps) {\n const tr = useTr();\n const gates = buildGates(tr);\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState<string | null>(null);\n const [values, setValues] = useState<Record<string, boolean>>({});\n const [error, setError] = useState<string | null>(null);\n\n const load = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const entries = await Promise.all(\n gates.map(async (g) => [g.key, isTruthy(await getAgentConfigValue(agent, g.key))] as const)\n );\n setValues(Object.fromEntries(entries));\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr('agentDetails.remoteTaskPermissions.loadFailed', 'Failed to load permissions')\n );\n } finally {\n setLoading(false);\n }\n // gates is stable across renders (derived from tr); intentionally not in deps.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [agent, tr]);\n\n useEffect(() => {\n void load();\n }, [load]);\n\n const toggle = useCallback(\n async (key: string, next: boolean) => {\n setSaving(key);\n setError(null);\n try {\n if (next) {\n await setAgentConfigValue(agent, key, '1');\n } else {\n await deleteAgentConfigValue(agent, key);\n }\n setValues((prev) => ({ ...prev, [key]: next }));\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr('agentDetails.remoteTaskPermissions.saveFailed', 'Failed to save permission')\n );\n } finally {\n setSaving(null);\n }\n },\n [agent, tr]\n );\n\n return (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <ShieldAlert className=\"size-5\" />\n {tr('agentDetails.remoteTaskPermissions.title', 'Remote Task Permissions')}\n </h3>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {tr(\n 'agentDetails.remoteTaskPermissions.subtitle',\n 'These gates default to deny so an agent on a public tunnel can’t be coerced into shell execution. Toggle them on per-agent if you want VibeDeck buttons / scheduled jobs to run shell or file-mutation tasks here.'\n )}\n </p>\n </div>\n <div className=\"p-4 space-y-4\">\n {error ? (\n <div className=\"rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n ) : null}\n {loading ? (\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('agentDetails.remoteTaskPermissions.loading', 'Loading…')}\n </div>\n ) : (\n gates.map((g) => {\n const current = values[g.key] ?? false;\n const busy = saving === g.key;\n return (\n <div\n key={g.key}\n className=\"flex items-start gap-3 rounded-lg border border-border-default bg-bg-primary p-3\"\n >\n <div className=\"flex-1 min-w-0 space-y-1\">\n <p className=\"text-sm font-medium text-text-primary\">{g.title}</p>\n <p className=\"text-xs text-text-secondary\">{g.description}</p>\n {g.warning ? (\n <p className=\"text-xs text-status-warning-text\">{g.warning}</p>\n ) : null}\n <p className=\"text-[11px] font-mono text-text-tertiary\">{g.key}</p>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n {busy ? <Loader2 className=\"size-4 animate-spin text-text-secondary\" /> : null}\n <Switch\n checked={current}\n onCheckedChange={(checked) => void toggle(g.key, checked)}\n disabled={busy}\n aria-label={g.title}\n />\n </div>\n </div>\n );\n })\n )}\n </div>\n </Card>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RemoteTaskPermissionsCard.js","names":[],"sources":["../../../src/components/agents/RemoteTaskPermissionsCard.tsx"],"sourcesContent":["/**\n * RemoteTaskPermissionsCard\n *\n * Toggles the per-agent \"allow remote shell tasks\" and \"allow remote\n * file-mutation tasks\" gates that the agent enforces in\n * `core/request-security.ts:denyNonLocalMutation`. The agent's task\n * plugin (POST /api/profiles/<n>/tasks/) refuses non-loopback callers by\n * default because remote shell execution + file writes are high-blast\n * radius; this card lets operators opt in per-agent without having to\n * SSH in and restart the daemon with the matching env var\n * (VIBECONTROLS_ALLOW_REMOTE_SHELL_TASK / _FILE_TASK).\n *\n * Storage: writes to the agent's encrypted config bag under\n * `security:vibecontrols_allow_remote_shell_task` and\n * `security:vibecontrols_allow_remote_file_task`. The same keys are\n * read by the agent's gate via `db.getConfig(...)`. Persisted across\n * agent restarts.\n */\nimport { useCallback, useEffect, useState } from 'react';\nimport { ShieldAlert, Loader2 } from 'lucide-react';\n\nimport { Card } from '@burdenoff/fe-libs/ui';\nimport { Switch } from '@burdenoff/fe-libs/ui';\n\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n deleteAgentConfigValue,\n getAgentConfigValue,\n setAgentConfigValue,\n type AgentRef,\n} from '@/services/aiApi';\nimport {\n REMOTE_FILE_TASK_GATE_KEY,\n REMOTE_SHELL_TASK_GATE_KEY,\n} from '@/services/agentRemoteTaskGates';\n\ninterface RemoteTaskPermissionsCardProps {\n agent: AgentRef;\n}\n\ninterface GateDef {\n key: string;\n title: string;\n description: string;\n warning?: string;\n}\n\nfunction buildGates(tr: ReturnType<typeof useTr>): GateDef[] {\n return [\n {\n key: REMOTE_SHELL_TASK_GATE_KEY,\n title: tr('agentDetails.remoteShellTask.title', 'Allow remote shell + script tasks'),\n description: tr(\n 'agentDetails.remoteShellTask.description',\n 'Enable VibeDeck buttons, scheduled jobs, and the UI to run shell commands and scripts on this agent over its tunnel. Required for any UI-triggered \"execute action\" that runs a command.'\n ),\n warning: tr(\n 'agentDetails.remoteShellTask.warning',\n 'Anyone with workspace access can execute commands on the host machine. Audit log records every invocation; reviewers can revert this flag at any time.'\n ),\n },\n {\n key: REMOTE_FILE_TASK_GATE_KEY,\n title: tr('agentDetails.remoteFileTask.title', 'Allow remote file writes + deletes'),\n description: tr(\n 'agentDetails.remoteFileTask.description',\n 'Enable UI-triggered file_operation write/delete tasks on this agent. Reads are always allowed; this gate covers mutations only.'\n ),\n warning: tr(\n 'agentDetails.remoteFileTask.warning',\n 'Writes and deletes are confined to the cwd / homedir / tmpdir allowlist via resolveSafePath + isSensitivePath, but a malicious caller could still corrupt files in those scopes.'\n ),\n },\n ];\n}\n\nfunction isTruthy(raw: string | null | undefined): boolean {\n if (!raw) return false;\n const v = raw.trim().toLowerCase();\n return v === '1' || v === 'true' || v === 'yes';\n}\n\nexport function RemoteTaskPermissionsCard({ agent }: RemoteTaskPermissionsCardProps) {\n const tr = useTr();\n const gates = buildGates(tr);\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState<string | null>(null);\n const [values, setValues] = useState<Record<string, boolean>>({});\n const [error, setError] = useState<string | null>(null);\n\n const load = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const entries = await Promise.all(\n gates.map(async (g) => [g.key, isTruthy(await getAgentConfigValue(agent, g.key))] as const)\n );\n setValues(Object.fromEntries(entries));\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr('agentDetails.remoteTaskPermissions.loadFailed', 'Failed to load permissions')\n );\n } finally {\n setLoading(false);\n }\n // gates is stable across renders (derived from tr); intentionally not in deps.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [agent, tr]);\n\n useEffect(() => {\n void load();\n }, [load]);\n\n const toggle = useCallback(\n async (key: string, next: boolean) => {\n setSaving(key);\n setError(null);\n try {\n if (next) {\n await setAgentConfigValue(agent, key, '1');\n } else {\n await deleteAgentConfigValue(agent, key);\n }\n setValues((prev) => ({ ...prev, [key]: next }));\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr('agentDetails.remoteTaskPermissions.saveFailed', 'Failed to save permission')\n );\n } finally {\n setSaving(null);\n }\n },\n [agent, tr]\n );\n\n return (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <ShieldAlert className=\"size-5\" />\n {tr('agentDetails.remoteTaskPermissions.title', 'Remote Task Permissions')}\n </h3>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {tr(\n 'agentDetails.remoteTaskPermissions.subtitle',\n 'These gates default to deny so an agent on a public tunnel can’t be coerced into shell execution. Toggle them on per-agent if you want VibeDeck buttons / scheduled jobs to run shell or file-mutation tasks here.'\n )}\n </p>\n </div>\n <div className=\"p-4 space-y-4\">\n {error ? (\n <div className=\"rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n ) : null}\n {loading ? (\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('agentDetails.remoteTaskPermissions.loading', 'Loading…')}\n </div>\n ) : (\n gates.map((g) => {\n const current = values[g.key] ?? false;\n const busy = saving === g.key;\n return (\n <div\n key={g.key}\n className=\"flex items-start gap-3 rounded-lg border border-border-default bg-bg-primary p-3\"\n >\n <div className=\"flex-1 min-w-0 space-y-1\">\n <p className=\"text-sm font-medium text-text-primary\">{g.title}</p>\n <p className=\"text-xs text-text-secondary\">{g.description}</p>\n {g.warning ? (\n <p className=\"text-xs text-status-warning-text\">{g.warning}</p>\n ) : null}\n <p className=\"text-[11px] font-mono text-text-tertiary\">{g.key}</p>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n {busy ? <Loader2 className=\"size-4 animate-spin text-text-secondary\" /> : null}\n <Switch\n checked={current}\n onCheckedChange={(checked) => void toggle(g.key, checked)}\n disabled={busy}\n aria-label={g.title}\n />\n </div>\n </div>\n );\n })\n )}\n </div>\n </Card>\n );\n}\n"],"mappings":"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"}