@xopcai/xopc 0.0.161 → 0.0.162

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 (233) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{activity-store-o7GRljfD.js → activity-store-hpcNv9MZ.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-9R3szLMc.js → agent-avatar-dicebear-Zf9UQEmQ.js} +1 -1
  5. package/dist/gateway/static/root/assets/agents-DfVkZlHc.js +2 -0
  6. package/dist/gateway/static/root/assets/{api-jSTqFMq9.js → api-Dnatri09.js} +1 -1
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-D81cyE76.js → app-management-settings-panel-BBTR9wJP.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-Ct6bZQCV.js → appearance-settings-C2zpLCHD.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-BMtAa4Ra.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-CfD7AZKn.js → archive-plugin-9eR_ODY1.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-CP_Gaxt3.js → attachment-load-DPuWYUeX.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automation-suggestion-card-BGKM_NDZ.js → automation-suggestion-card-Dw49OJRl.js} +1 -1
  13. package/dist/gateway/static/root/assets/automations-page-BiSAMYTr.js +6 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-IPr2EVnS.js → binary-plugins-BdkJpDFm.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-B6aHEGA6.js → block-editor-CAi7SXYU.js} +47 -47
  16. package/dist/gateway/static/root/assets/browser-settings-page-DHmaEJS2.js +8 -0
  17. package/dist/gateway/static/root/assets/browser-workflow-inputs-Dx6wkGz1.js +1 -0
  18. package/dist/gateway/static/root/assets/browser-workflows-page-Skertc-A.js +3 -0
  19. package/dist/gateway/static/root/assets/capabilities-settings-panel-B5wSgf4f.js +2 -0
  20. package/dist/gateway/static/root/assets/{capability-presets-api-CCX9-LjU.js → capability-presets-api-CNFl7dM3.js} +1 -1
  21. package/dist/gateway/static/root/assets/capability-presets-settings-panel-DyvFZwBj.js +2 -0
  22. package/dist/gateway/static/root/assets/{channel-recipient-api-MmLMLDWf.js → channel-recipient-api-DYQyGDw4.js} +1 -1
  23. package/dist/gateway/static/root/assets/channels-settings-BBKEqDZn.js +1 -0
  24. package/dist/gateway/static/root/assets/channels-status-swr-Drk110ve.js +1 -0
  25. package/dist/gateway/static/root/assets/{connector-logo-BbjzIs87.js → connector-logo-Bng6kxYF.js} +1 -1
  26. package/dist/gateway/static/root/assets/connectors-page-HpgaUDQ1.js +2 -0
  27. package/dist/gateway/static/root/assets/desktop-pet-Bpf6oegd.js +1 -0
  28. package/dist/gateway/static/root/assets/{desktop-pet-settings-DQLqGLc0.js → desktop-pet-settings-BVUyefSd.js} +1 -1
  29. package/dist/gateway/static/root/assets/{directory-picker-path-field-ClsiXhmz.js → directory-picker-path-field-DjSZAPz_.js} +1 -1
  30. package/dist/gateway/static/root/assets/{dreaming-settings-CjMPPdzg.js → dreaming-settings-CAZk2AM4.js} +3 -3
  31. package/dist/gateway/static/root/assets/{extension-debug-page-57eizjhB.js → extension-debug-page-C2F6vTTd.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-page-CsbSb3u1.js → extension-page-AnIt4bn4.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-settings-page-DKA9-lyP.js → extension-settings-page-CLSc6682.js} +1 -1
  34. package/dist/gateway/static/root/assets/{gateway-config-api-DIPJuzBZ.js → gateway-config-api-DLFyZv33.js} +1 -1
  35. package/dist/gateway/static/root/assets/{gateway-settings-CDLqAQY7.js → gateway-settings-DnQAA-qq.js} +1 -1
  36. package/dist/gateway/static/root/assets/{gateway-startup-retry-DrfSJtvT.js → gateway-startup-retry-DbzLyJ3R.js} +1 -1
  37. package/dist/gateway/static/root/assets/goal-create-dialog-DrnKFC6M.js +1 -0
  38. package/dist/gateway/static/root/assets/goal-detail-page-Cw1hpJkZ.js +4 -0
  39. package/dist/gateway/static/root/assets/{goals-config-api-lGC10UkU.js → goals-config-api-REmr3QCr.js} +1 -1
  40. package/dist/gateway/static/root/assets/goals-page-BDGkTVma.js +2 -0
  41. package/dist/gateway/static/root/assets/{goals-settings-BWq0pdrA.js → goals-settings-Dz0F3uRO.js} +1 -1
  42. package/dist/gateway/static/root/assets/{heartbeat-settings-D4o8BJVv.js → heartbeat-settings-CQFY5gYs.js} +1 -1
  43. package/dist/gateway/static/root/assets/index-C8JkTS6d.js +199 -0
  44. package/dist/gateway/static/root/assets/index-hmrG55qg.css +1 -0
  45. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-6T2QYehS.js → keyboard-shortcuts-settings-BnOQXU5k.js} +1 -1
  46. package/dist/gateway/static/root/assets/local-app-workbench-page-Bdef1b7F.js +2 -0
  47. package/dist/gateway/static/root/assets/{local-apps-page-DCjg2K9f.js → local-apps-page-KsOtWOyM.js} +1 -1
  48. package/dist/gateway/static/root/assets/{locale-store-Dar-xqQr.js → locale-store-0lpYdNDr.js} +1 -1
  49. package/dist/gateway/static/root/assets/{logs-page-CyYCBqym.js → logs-page-CwLPgrWh.js} +3 -3
  50. package/dist/gateway/static/root/assets/{markdown-split-rt1XO43g.js → markdown-split-D5GRg10r.js} +1 -1
  51. package/dist/gateway/static/root/assets/{media-plugins-Bwo8CJuh.js → media-plugins-C1ryULGs.js} +1 -1
  52. package/dist/gateway/static/root/assets/messages-BwQ3WssU.js +5 -0
  53. package/dist/gateway/static/root/assets/{notes-page-Dp-pkX7d.js → notes-page-CIeZ1_DT.js} +1 -1
  54. package/dist/gateway/static/root/assets/notes-workbench-Cf06q19x.js +4 -0
  55. package/dist/gateway/static/root/assets/{page-tabs-CzZ4iKOb.js → page-tabs-Caj1-E_X.js} +1 -1
  56. package/dist/gateway/static/root/assets/{preview-open-alternatives-3E_Qa-58.js → preview-open-alternatives-DKwEce-9.js} +1 -1
  57. package/dist/gateway/static/root/assets/{product-open-page-B2T48Sbt.js → product-open-page-B90ZskkN.js} +1 -1
  58. package/dist/gateway/static/root/assets/project-detail-page-SHTurM4t.js +1 -0
  59. package/dist/gateway/static/root/assets/projects-page-BcC2qUAw.js +1 -0
  60. package/dist/gateway/static/root/assets/remote-access-hub-6vGefBHv.js +1 -0
  61. package/dist/gateway/static/root/assets/save-bar-controls-BDYt53uQ.js +1 -0
  62. package/dist/gateway/static/root/assets/{schedule-field-TkqUL3wm.js → schedule-field-Bcdni4Pj.js} +1 -1
  63. package/dist/gateway/static/root/assets/{schema-form-DC-iU5IG.js → schema-form-D7oCARF7.js} +1 -1
  64. package/dist/gateway/static/root/assets/sessions-page-C5014a53.js +1 -0
  65. package/dist/gateway/static/root/assets/settings-advanced-gate-CDY3ZI1K.js +1 -0
  66. package/dist/gateway/static/root/assets/{settings-collapsible-section-Dpqth3oA.js → settings-collapsible-section-6eNF8Jey.js} +1 -1
  67. package/dist/gateway/static/root/assets/{settings-form-section-CNqF6IwY.js → settings-form-section-CARTHCxH.js} +1 -1
  68. package/dist/gateway/static/root/assets/{settings-loading-skeleton-DkvwitK_.js → settings-loading-skeleton-DEwYqESe.js} +1 -1
  69. package/dist/gateway/static/root/assets/settings-page-B6vVdHeQ.js +1 -0
  70. package/dist/gateway/static/root/assets/{settings-page-layout-DWvNgISx.js → settings-page-layout-D3srt5Bi.js} +1 -1
  71. package/dist/gateway/static/root/assets/setup-status-panel-z-rF0Hdg.js +1 -0
  72. package/dist/gateway/static/root/assets/{share-preview-page-DGpSPW-n.js → share-preview-page-Ck9SxiXw.js} +1 -1
  73. package/dist/gateway/static/root/assets/shares-settings-CHZFXE_d.js +1 -0
  74. package/dist/gateway/static/root/assets/skill-reload-api-B7p2QNXt.js +1 -0
  75. package/dist/gateway/static/root/assets/skills-page-ncw3tX9P.js +2 -0
  76. package/dist/gateway/static/root/assets/{system-settings-panel-8TxxSbUw.js → system-settings-panel-DLBUQSk2.js} +1 -1
  77. package/dist/gateway/static/root/assets/{theme-store-B5FzMWOx.js → theme-store-C1oaqeD2.js} +1 -1
  78. package/dist/gateway/static/root/assets/{typed-models-lib-DEGtSA5h.js → typed-models-lib-Divbg_C4.js} +1 -1
  79. package/dist/gateway/static/root/assets/{use-channel-catalog-BDU6FneG.js → use-channel-catalog-CP9a_rAf.js} +1 -1
  80. package/dist/gateway/static/root/assets/user-context-page-iYfeSLKI.js +1 -0
  81. package/dist/gateway/static/root/assets/voice-api-key-field-CrwkicvT.js +1 -0
  82. package/dist/gateway/static/root/assets/work-discovery-page-BQWxyPg8.js +1 -0
  83. package/dist/gateway/static/root/assets/work-item-detail-page-50ZzGay5.js +1 -0
  84. package/dist/gateway/static/root/assets/work-page-DPqEahx7.js +1 -0
  85. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Cwi8Pz7n.js → workflow-run-setup-panel-DbDPYQwB.js} +2 -2
  86. package/dist/gateway/static/root/assets/workflows-page-C3eIC5HZ.js +11 -0
  87. package/dist/gateway/static/root/index.html +7 -7
  88. package/dist/package.js +1 -1
  89. package/dist/src/agent/agent-manager.d.ts +1 -0
  90. package/dist/src/agent/agent-manager.js +1 -0
  91. package/dist/src/agent/client-error-format.js +3 -3
  92. package/dist/src/agent/outcomes/task-evaluation.d.ts +20 -0
  93. package/dist/src/agent/outcomes/task-evaluation.js +48 -0
  94. package/dist/src/agent/outcomes/task-verifier.d.ts +29 -0
  95. package/dist/src/agent/outcomes/task-verifier.js +71 -0
  96. package/dist/src/agent/prompt/memory/index.js +1 -1
  97. package/dist/src/agent/prompt/service-prompt-builder.js +32 -1
  98. package/dist/src/agent/service.js +1 -0
  99. package/dist/src/agent/service.types.d.ts +1 -0
  100. package/dist/src/agent/tools/browser/tool/browser-use-tool.d.ts +1 -7
  101. package/dist/src/agent/tools/browser/tool/browser-use-tool.js +2 -73
  102. package/dist/src/agent/tools/browser/tool/schemas.d.ts +1 -13
  103. package/dist/src/agent/tools/browser/tool/schemas.js +1 -9
  104. package/dist/src/agent/tools/browser-recipe-tool.d.ts +12 -0
  105. package/dist/src/agent/tools/browser-recipe-tool.js +113 -0
  106. package/dist/src/agent/tools/factory.d.ts +2 -0
  107. package/dist/src/agent/tools/factory.js +2 -0
  108. package/dist/src/agent/tools/index.d.ts +1 -0
  109. package/dist/src/agent/tools/index.js +2 -1
  110. package/dist/src/automations/domain/types.d.ts +13 -0
  111. package/dist/src/automations/domain/validation.d.ts +44 -24
  112. package/dist/src/automations/domain/validation.js +27 -18
  113. package/dist/src/automations/draft/automation-draft-validator.d.ts +6 -1
  114. package/dist/src/automations/draft/automation-draft-validator.js +1 -0
  115. package/dist/src/automations/service/action-executor.d.ts +1 -0
  116. package/dist/src/automations/service/action-executor.js +22 -0
  117. package/dist/src/browser/index.d.ts +0 -1
  118. package/dist/src/browser/index.js +1 -2
  119. package/dist/src/browser/pipeline/runner.d.ts +9 -2
  120. package/dist/src/browser/pipeline/runner.js +151 -105
  121. package/dist/src/browser/pipeline/schema.d.ts +21 -15
  122. package/dist/src/browser/pipeline/schema.js +93 -99
  123. package/dist/src/browser/recipes/index.d.ts +3 -0
  124. package/dist/src/browser/recipes/index.js +3 -0
  125. package/dist/src/browser/recipes/repository.d.ts +13 -0
  126. package/dist/src/browser/recipes/repository.js +81 -0
  127. package/dist/src/browser/recipes/runtime-service.d.ts +6 -0
  128. package/dist/src/browser/recipes/runtime-service.js +30 -0
  129. package/dist/src/browser/recipes/service.d.ts +36 -0
  130. package/dist/src/browser/recipes/service.js +186 -0
  131. package/dist/src/browser/recipes/types.d.ts +36 -0
  132. package/dist/src/browser/recipes/types.js +1 -0
  133. package/dist/src/cli/commands/browser-cli-helpers.js +6 -12
  134. package/dist/src/cli/commands/browser.js +4 -3
  135. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  136. package/dist/src/config/agent-profile.js +1 -1
  137. package/dist/src/config/schema.d.ts +3 -3
  138. package/dist/src/config/schema.js +21 -21
  139. package/dist/src/gateway/agents-admin.js +1 -1
  140. package/dist/src/gateway/hono/lib/config-payload.d.ts +1 -1
  141. package/dist/src/gateway/hono/routes/browser-workflows.d.ts +3 -0
  142. package/dist/src/gateway/hono/routes/browser-workflows.js +78 -0
  143. package/dist/src/gateway/hono/routes/home.js +1 -0
  144. package/dist/src/gateway/hono/routes/index.js +4 -0
  145. package/dist/src/gateway/hono/routes/interaction-state.d.ts +3 -0
  146. package/dist/src/gateway/hono/routes/interaction-state.js +45 -0
  147. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  148. package/dist/src/gateway/hono/routes/memory.js +33 -4
  149. package/dist/src/gateway/hono/routes/task-outcomes.d.ts +3 -0
  150. package/dist/src/gateway/hono/routes/task-outcomes.js +109 -0
  151. package/dist/src/gateway/hono/routes/you.d.ts +10 -2
  152. package/dist/src/gateway/hono/routes/you.js +184 -28
  153. package/dist/src/gateway/service/run-gateway-agent.js +84 -0
  154. package/dist/src/gateway/service.d.ts +3 -0
  155. package/dist/src/gateway/service.js +14 -0
  156. package/dist/src/proactive/insight-repository.d.ts +2 -0
  157. package/dist/src/proactive/insight-repository.js +5 -2
  158. package/dist/src/proactive/run-processor.js +22 -1
  159. package/dist/src/proactive/types.d.ts +2 -0
  160. package/dist/src/proactive/value-score.d.ts +13 -0
  161. package/dist/src/proactive/value-score.js +30 -0
  162. package/dist/src/share/share-types.js +5 -5
  163. package/dist/src/share/site-share-types.d.ts +1 -1
  164. package/dist/src/share/site-share-types.js +5 -5
  165. package/dist/src/storage/sqlite/index.d.ts +3 -0
  166. package/dist/src/storage/sqlite/index.js +4 -1
  167. package/dist/src/storage/sqlite/interaction-state-repository.d.ts +17 -0
  168. package/dist/src/storage/sqlite/interaction-state-repository.js +60 -0
  169. package/dist/src/storage/sqlite/memory-records-repository.js +1 -1
  170. package/dist/src/storage/sqlite/migrations/053_browser_recipes.sql +45 -0
  171. package/dist/src/storage/sqlite/migrations/054_task_outcomes.sql +24 -0
  172. package/dist/src/storage/sqlite/migrations/055_relationship_settings.sql +14 -0
  173. package/dist/src/storage/sqlite/migrations/056_task_verification.sql +6 -0
  174. package/dist/src/storage/sqlite/migrations/057_interaction_states.sql +11 -0
  175. package/dist/src/storage/sqlite/migrations/058_proactive_value.sql +3 -0
  176. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  177. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  178. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  179. package/dist/src/storage/sqlite/relationship-settings-repository.d.ts +17 -0
  180. package/dist/src/storage/sqlite/relationship-settings-repository.js +64 -0
  181. package/dist/src/storage/sqlite/schema.js +1 -1
  182. package/dist/src/storage/sqlite/schema.sql +46 -0
  183. package/dist/src/storage/sqlite/task-outcome-repository.d.ts +90 -0
  184. package/dist/src/storage/sqlite/task-outcome-repository.js +138 -0
  185. package/dist/src/user-context/interaction-state.d.ts +13 -0
  186. package/dist/src/user-context/interaction-state.js +69 -0
  187. package/dist/src/user-context/memory-use-audit.d.ts +10 -0
  188. package/dist/src/user-context/memory-use-audit.js +25 -0
  189. package/dist/src/user-context/personal-playbook.d.ts +11 -0
  190. package/dist/src/user-context/personal-playbook.js +41 -0
  191. package/dist/src/user-context/projection.d.ts +4 -0
  192. package/dist/src/user-context/projection.js +5 -1
  193. package/dist/src/user-context/relationship-continuity.d.ts +14 -0
  194. package/dist/src/user-context/relationship-continuity.js +51 -0
  195. package/package.json +1 -1
  196. package/dist/gateway/static/root/assets/action-boundary-settings-panel-BIVBUiNT.js +0 -1
  197. package/dist/gateway/static/root/assets/agents-_qbUaYbs.js +0 -2
  198. package/dist/gateway/static/root/assets/apps-page-0sPqD0FK.js +0 -1
  199. package/dist/gateway/static/root/assets/automations-page-BxWBuBsE.js +0 -6
  200. package/dist/gateway/static/root/assets/browser-settings-page-C499b9AH.js +0 -8
  201. package/dist/gateway/static/root/assets/capability-presets-settings-panel-Dva9SiPK.js +0 -2
  202. package/dist/gateway/static/root/assets/channels-settings-DNAU7u9o.js +0 -1
  203. package/dist/gateway/static/root/assets/channels-status-swr-CIOH7tgn.js +0 -1
  204. package/dist/gateway/static/root/assets/connectors-page-TwiBp8vY.js +0 -2
  205. package/dist/gateway/static/root/assets/desktop-pet-CYH5ZSXy.js +0 -1
  206. package/dist/gateway/static/root/assets/goal-create-dialog-Q3B0E-0s.js +0 -1
  207. package/dist/gateway/static/root/assets/goal-detail-page-C5VAK04G.js +0 -4
  208. package/dist/gateway/static/root/assets/goals-page-BJb-5IYw.js +0 -2
  209. package/dist/gateway/static/root/assets/index-CkUHH170.css +0 -1
  210. package/dist/gateway/static/root/assets/index-vq_cJZJC.js +0 -199
  211. package/dist/gateway/static/root/assets/local-app-workbench-page-euH17Kvb.js +0 -2
  212. package/dist/gateway/static/root/assets/messages-CxySDn9d.js +0 -5
  213. package/dist/gateway/static/root/assets/models-hub-panel-DObxxjc9.js +0 -2
  214. package/dist/gateway/static/root/assets/notes-workbench-CCb4RXcZ.js +0 -4
  215. package/dist/gateway/static/root/assets/project-detail-page-BvEcriRC.js +0 -1
  216. package/dist/gateway/static/root/assets/projects-page-B_e79MRJ.js +0 -1
  217. package/dist/gateway/static/root/assets/remote-access-hub-DGcqnYfR.js +0 -1
  218. package/dist/gateway/static/root/assets/save-bar-controls-CkeKasGm.js +0 -1
  219. package/dist/gateway/static/root/assets/sessions-page-DQZhrDJ8.js +0 -1
  220. package/dist/gateway/static/root/assets/settings-advanced-gate-BkdeN99Y.js +0 -1
  221. package/dist/gateway/static/root/assets/settings-page-qPG7I2zd.js +0 -1
  222. package/dist/gateway/static/root/assets/setup-status-panel-lwRAh9aw.js +0 -1
  223. package/dist/gateway/static/root/assets/shares-settings-BGj9IVUr.js +0 -1
  224. package/dist/gateway/static/root/assets/skill-reload-api-B2UBAs5U.js +0 -1
  225. package/dist/gateway/static/root/assets/skills-page-DdjzFZ8I.js +0 -2
  226. package/dist/gateway/static/root/assets/user-context-page-BTTZpAGV.js +0 -1
  227. package/dist/gateway/static/root/assets/voice-api-key-field-DuDQd-WU.js +0 -1
  228. package/dist/gateway/static/root/assets/work-discovery-page-BUVkVCsW.js +0 -1
  229. package/dist/gateway/static/root/assets/work-item-detail-page-DeEPPu4n.js +0 -1
  230. package/dist/gateway/static/root/assets/work-page-DQNUyTUq.js +0 -1
  231. package/dist/gateway/static/root/assets/workflows-page-BIy4wrzO.js +0 -11
  232. package/dist/src/browser/pipeline/source.d.ts +0 -9
  233. package/dist/src/browser/pipeline/source.js +0 -77
@@ -2,7 +2,6 @@ import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
3
  import { isTruthyValue, resolveTemplateDeep } from "./template.js";
4
4
  import { parseBrowserPipeline } from "./schema.js";
5
- import { loadBrowserPipelineSource, resolvePipelineIncludeLocation } from "./source.js";
6
5
  import { addStepTrace, createPipelineOutput, formatPipelineResult } from "./output.js";
7
6
  import { setTimeout } from "node:timers/promises";
8
7
  //#region src/browser/pipeline/runner.ts
@@ -17,6 +16,29 @@ const CONTROL_ACTIONS = new Set([
17
16
  "sleep",
18
17
  "set_var"
19
18
  ]);
19
+ const RECIPE_ACTIONS = new Set([
20
+ ...CONTROL_ACTIONS,
21
+ "navigate",
22
+ "snapshot",
23
+ "click",
24
+ "type",
25
+ "scroll",
26
+ "screenshot",
27
+ "press",
28
+ "evaluate",
29
+ "wait",
30
+ "wait_for_navigation",
31
+ "wait_for_network_idle",
32
+ "element_text",
33
+ "element_attribute",
34
+ "output",
35
+ "assert",
36
+ "select",
37
+ "map",
38
+ "filter",
39
+ "sort",
40
+ "limit"
41
+ ]);
20
42
  function validateBrowserPipeline(yamlSource, registry) {
21
43
  const parseResult = parseBrowserPipeline(yamlSource);
22
44
  if (!parseResult.ok || !parseResult.document) return {
@@ -36,85 +58,56 @@ function validateBrowserPipeline(yamlSource, registry) {
36
58
  errors: []
37
59
  };
38
60
  }
39
- async function validateBrowserPipelineSource(yamlSource, registry, sourceLocation) {
40
- try {
41
- const doc = await expandPipelineDocument(yamlSource, sourceLocation);
42
- const errors = [];
43
- validateSteps(doc.pipeline, "pipeline", registry, errors);
44
- validateSteps(doc.onError ?? [], "on_error", registry, errors);
45
- return errors.length > 0 ? {
46
- ok: false,
47
- document: doc,
48
- errors
49
- } : {
50
- ok: true,
51
- document: doc,
52
- errors: []
53
- };
54
- } catch (e) {
55
- return {
56
- ok: false,
57
- errors: [{
58
- path: "",
59
- message: e instanceof Error ? e.message : String(e)
60
- }]
61
- };
62
- }
63
- }
64
61
  function validateSteps(steps, path, registry, errors) {
65
62
  if (!registry) return;
66
63
  for (let i = 0; i < steps.length; i++) {
67
64
  const step = steps[i];
68
- if (!CONTROL_ACTIONS.has(step.action) && !registry.has(step.action)) errors.push({
65
+ if (!RECIPE_ACTIONS.has(step.action)) errors.push({
66
+ path: `${path}[${i}]`,
67
+ message: `Action is not available in Browser Recipe v1: "${step.action}"`
68
+ });
69
+ else if (!CONTROL_ACTIONS.has(step.action) && !registry.has(step.action)) errors.push({
69
70
  path: `${path}[${i}]`,
70
71
  message: `Unknown action: "${step.action}"`
71
72
  });
73
+ const requiredKey = {
74
+ navigate: "url",
75
+ type: "text",
76
+ press: "key",
77
+ evaluate: "javascript",
78
+ element_text: "selector",
79
+ element_attribute: "selector",
80
+ set_var: "name"
81
+ }[step.action];
82
+ if (requiredKey && !(requiredKey in step.args)) errors.push({
83
+ path: `${path}[${i}].${step.action}.${requiredKey}`,
84
+ message: `Required field is missing: ${requiredKey}`
85
+ });
86
+ if (step.action === "click" && ![
87
+ "selector",
88
+ "text",
89
+ "role"
90
+ ].some((key) => key in step.args)) errors.push({
91
+ path: `${path}[${i}].click`,
92
+ message: "One of selector, text, or role is required"
93
+ });
94
+ if (step.action === "type" && !["selector", "label"].some((key) => key in step.args)) errors.push({
95
+ path: `${path}[${i}].type`,
96
+ message: "One of selector or label is required"
97
+ });
98
+ try {
99
+ if (step.action === "if") {
100
+ validateSteps(normalizeNestedPipeline(step.args.then, `${path}[${i}].if.then`), `${path}[${i}].if.then`, registry, errors);
101
+ validateSteps(normalizeNestedPipeline(step.args.else, `${path}[${i}].if.else`), `${path}[${i}].if.else`, registry, errors);
102
+ } else if (step.action === "retry") validateSteps(normalizeNestedPipeline(step.args.pipeline, `${path}[${i}].retry.pipeline`), `${path}[${i}].retry.pipeline`, registry, errors);
103
+ } catch (error) {
104
+ errors.push({
105
+ path: `${path}[${i}].${step.action}`,
106
+ message: error instanceof Error ? error.message : String(error)
107
+ });
108
+ }
72
109
  }
73
110
  }
74
- async function expandPipelineDocument(yamlSource, sourceLocation, visiting = /* @__PURE__ */ new Set()) {
75
- const parsed = parseBrowserPipeline(yamlSource);
76
- if (!parsed.ok || !parsed.document) {
77
- const message = parsed.errors.map((e) => `${e.path}: ${e.message}`).join("\n");
78
- throw new Error(message);
79
- }
80
- const doc = parsed.document;
81
- const key = sourceLocation ?? `inline:${doc.name}`;
82
- if (visiting.has(key)) throw new Error(`Circular pipeline include detected: ${key}`);
83
- visiting.add(key);
84
- let merged = {
85
- name: doc.name,
86
- description: doc.description,
87
- provider: doc.provider,
88
- include: [],
89
- timeoutSeconds: doc.timeoutSeconds,
90
- args: {},
91
- pipeline: [],
92
- onError: []
93
- };
94
- for (const includePath of doc.include ?? []) {
95
- const includeSource = await loadBrowserPipelineSource(resolvePipelineIncludeLocation(includePath, sourceLocation));
96
- const includeDoc = await expandPipelineDocument(includeSource.source, includeSource.location, visiting);
97
- merged = mergePipelineDocuments(merged, includeDoc);
98
- }
99
- merged = mergePipelineDocuments(merged, doc);
100
- visiting.delete(key);
101
- return merged;
102
- }
103
- function mergePipelineDocuments(target, source) {
104
- return {
105
- name: source.name || target.name,
106
- description: source.description ?? target.description,
107
- provider: source.provider ?? target.provider,
108
- include: [...target.include ?? [], ...source.include ?? []],
109
- timeoutSeconds: source.timeoutSeconds ?? target.timeoutSeconds,
110
- args: {
111
- ...target.args,
112
- ...source.args
113
- },
114
- pipeline: [...target.pipeline, ...source.pipeline],
115
- onError: [...target.onError ?? [], ...source.onError ?? []]
116
- };
117
- }
118
111
  function resolveArgs(doc, overrides) {
119
112
  const resolved = {};
120
113
  for (const key of Object.keys(overrides)) if (!(key in doc.args)) throw new Error(`Unknown pipeline arg: ${key}`);
@@ -125,6 +118,10 @@ function resolveArgs(doc, overrides) {
125
118
  continue;
126
119
  }
127
120
  if (def.choices && !def.choices.some((choice) => Object.is(choice, value))) throw new Error(`Invalid value for arg ${key}: expected one of ${def.choices.map(String).join(", ")}`);
121
+ if (def.type === "string" && typeof value !== "string") throw new Error(`Arg ${key} must be a string`);
122
+ if (def.type === "number" && (typeof value !== "number" || !Number.isFinite(value))) throw new Error(`Arg ${key} must be a number`);
123
+ if (def.type === "integer" && (typeof value !== "number" || !Number.isInteger(value))) throw new Error(`Arg ${key} must be an integer`);
124
+ if (def.type === "boolean" && typeof value !== "boolean") throw new Error(`Arg ${key} must be a boolean`);
128
125
  resolved[key] = value;
129
126
  }
130
127
  return resolved;
@@ -139,24 +136,25 @@ function templateContext(runtime) {
139
136
  };
140
137
  }
141
138
  function actionArgs(step, runtime) {
142
- if (typeof step.args === "string") {
143
- const resolved = resolveTemplateDeep(step.args, templateContext(runtime));
144
- if (step.action === "evaluate" || step.action === "eval" || step.action === "console") return { javascript: String(resolved ?? "") };
145
- return { value: resolved };
146
- }
147
139
  return resolveTemplateDeep(step.args, templateContext(runtime));
148
140
  }
149
- async function executePipeline(steps, scope, runtime, actionCtx, registry, output) {
141
+ async function executePipeline(steps, scope, runtime, actionCtx, registry, output, options) {
150
142
  for (let i = 0; i < steps.length; i++) {
151
143
  if (actionCtx.signal?.aborted) throw actionError("ABORTED", "Pipeline aborted");
152
- await executeStep(steps[i], i, scope, runtime, actionCtx, registry, output);
144
+ await executeStep(steps[i], i, scope, runtime, actionCtx, registry, output, options);
153
145
  }
154
146
  }
155
- async function executeStep(step, index, scope, runtime, actionCtx, registry, output) {
147
+ async function executeStep(step, index, scope, runtime, actionCtx, registry, output, options) {
156
148
  const started = Date.now();
149
+ options.onStep?.({
150
+ index,
151
+ scope,
152
+ action: step.action,
153
+ status: "started"
154
+ });
157
155
  let result;
158
156
  try {
159
- result = await executeStepOnce(step, index, scope, runtime, actionCtx, registry, output);
157
+ result = await executeStepOnce(step, index, scope, runtime, actionCtx, registry, output, options);
160
158
  } catch (e) {
161
159
  const error = e instanceof Error ? e : new Error(String(e));
162
160
  result = {
@@ -178,16 +176,24 @@ async function executeStep(step, index, scope, runtime, actionCtx, registry, out
178
176
  data: result.data,
179
177
  error: result.error
180
178
  }, result);
179
+ options.onStep?.({
180
+ index,
181
+ scope,
182
+ action: step.action,
183
+ status: result.ok ? "completed" : "failed",
184
+ elapsedMs: Date.now() - started,
185
+ error: result.error?.message
186
+ });
181
187
  if (!result.ok) {
182
188
  runtime.error = result.error;
183
189
  throw actionError(result.error?.code ?? "STEP_FAILED", result.error?.message ?? "Step failed");
184
190
  }
185
191
  }
186
- async function executeStepOnce(step, index, scope, runtime, actionCtx, registry, output) {
192
+ async function executeStepOnce(step, index, scope, runtime, actionCtx, registry, output, options) {
187
193
  switch (step.action) {
188
194
  case "sleep": {
189
195
  const args = actionArgs(step, runtime);
190
- const ms = Number(args.ms ?? args.delay_ms ?? args.delayMs ?? 1e3);
196
+ const ms = Number(args.ms ?? 1e3);
191
197
  await setTimeout(Number.isFinite(ms) ? ms : 1e3, void 0, { signal: actionCtx.signal });
192
198
  runtime.last = null;
193
199
  return {
@@ -219,7 +225,7 @@ async function executeStepOnce(step, index, scope, runtime, actionCtx, registry,
219
225
  }
220
226
  case "if": {
221
227
  const args = actionArgs(step, runtime);
222
- await executePipeline(normalizeNestedPipeline(isTruthyValue(args.condition) ? args.then : args.else, `${scope}.${index}.if`), `${scope}.${index}.if`, runtime, actionCtx, registry, output);
228
+ await executePipeline(normalizeNestedPipeline(isTruthyValue(args.condition) ? args.then : args.else, `${scope}.${index}.if`), `${scope}.${index}.if`, runtime, actionCtx, registry, output, options);
223
229
  return {
224
230
  ok: true,
225
231
  action: "if",
@@ -230,11 +236,11 @@ async function executeStepOnce(step, index, scope, runtime, actionCtx, registry,
230
236
  case "retry": {
231
237
  const args = actionArgs(step, runtime);
232
238
  const times = Math.max(1, Number(args.times ?? 3));
233
- const delayMs = Math.max(0, Number(args.delay_ms ?? args.delayMs ?? 250));
234
- const retrySteps = normalizeNestedPipeline(args.pipeline ?? args.step, `${scope}.${index}.retry`);
239
+ const delayMs = Math.max(0, Number(args.delay_ms ?? 250));
240
+ const retrySteps = normalizeNestedPipeline(args.pipeline, `${scope}.${index}.retry`);
235
241
  let lastError;
236
242
  for (let attempt = 1; attempt <= times; attempt++) try {
237
- await executePipeline(retrySteps, `${scope}.${index}.retry.${attempt}`, runtime, actionCtx, registry, output);
243
+ await executePipeline(retrySteps, `${scope}.${index}.retry.${attempt}`, runtime, actionCtx, registry, output, options);
238
244
  output.ok = true;
239
245
  output.error = void 0;
240
246
  return {
@@ -263,6 +269,29 @@ async function executeStepOnce(step, index, scope, runtime, actionCtx, registry,
263
269
  }
264
270
  async function executeBrowserAction(step, runtime, actionCtx, registry) {
265
271
  const args = actionArgs(step, runtime);
272
+ if (step.action === "navigate") {
273
+ let hostname;
274
+ try {
275
+ hostname = new URL(String(args.url)).hostname.toLowerCase();
276
+ } catch {
277
+ return {
278
+ ok: false,
279
+ action: step.action,
280
+ error: {
281
+ code: "INVALID_URL",
282
+ message: "navigate.url must be an absolute URL"
283
+ }
284
+ };
285
+ }
286
+ if (!runtime.doc.domains.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`))) return {
287
+ ok: false,
288
+ action: step.action,
289
+ error: {
290
+ code: "DOMAIN_BLOCKED",
291
+ message: `Domain is not declared by the recipe: ${hostname}`
292
+ }
293
+ };
294
+ }
266
295
  actionCtx.pipeline = {
267
296
  args: runtime.args,
268
297
  last: runtime.last,
@@ -271,6 +300,20 @@ async function executeBrowserAction(step, runtime, actionCtx, registry) {
271
300
  error: runtime.error
272
301
  };
273
302
  const result = await registry.execute(step.action, actionCtx, args);
303
+ if (result.ok && step.action === "navigate") {
304
+ const finalUrl = result.data?.url;
305
+ if (typeof finalUrl === "string") {
306
+ const hostname = new URL(finalUrl).hostname.toLowerCase();
307
+ if (!runtime.doc.domains.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`))) return {
308
+ ok: false,
309
+ action: step.action,
310
+ error: {
311
+ code: "DOMAIN_BLOCKED",
312
+ message: `Redirected to an undeclared domain: ${hostname}`
313
+ }
314
+ };
315
+ }
316
+ }
274
317
  if (result.ok) {
275
318
  if (step.action === "output") runtime.outputs.push(result.data);
276
319
  if (result.data !== void 0) runtime.last = result.data;
@@ -285,22 +328,11 @@ function normalizeNestedPipeline(value, path) {
285
328
  if (keys.length !== 1) throw new Error(`${path}[${index}] must contain exactly one action`);
286
329
  const action = keys[0];
287
330
  const args = obj[action];
288
- if (typeof args === "string") return {
289
- action,
290
- args
291
- };
292
331
  if (args && typeof args === "object" && !Array.isArray(args)) return {
293
332
  action,
294
333
  args
295
334
  };
296
- if (args === void 0 || args === null) return {
297
- action,
298
- args: {}
299
- };
300
- return {
301
- action,
302
- args: { value: args }
303
- };
335
+ throw new Error(`${path}[${index}].${action} must be an object`);
304
336
  });
305
337
  }
306
338
  function actionError(code, message) {
@@ -311,7 +343,9 @@ function actionError(code, message) {
311
343
  async function runBrowserPipeline(yamlSource, argOverrides, ctx, registry, options = {}) {
312
344
  let doc;
313
345
  try {
314
- doc = await expandPipelineDocument(yamlSource, options.sourceLocation);
346
+ const parsed = parseBrowserPipeline(yamlSource);
347
+ if (!parsed.ok || !parsed.document) throw new Error(parsed.errors.map((error) => `${error.path}: ${error.message}`).join("\n"));
348
+ doc = parsed.document;
315
349
  } catch (e) {
316
350
  const msg = e instanceof Error ? e.message : String(e);
317
351
  return {
@@ -377,18 +411,30 @@ async function runBrowserPipeline(yamlSource, argOverrides, ctx, registry, optio
377
411
  steps: doc.pipeline.length
378
412
  }, `Running pipeline: ${doc.name}`);
379
413
  try {
380
- const run = executePipeline(doc.pipeline, "pipeline", runtime, ctx, registry, output);
414
+ const timeoutController = new AbortController();
415
+ const runContext = {
416
+ ...ctx,
417
+ signal: ctx.signal ? AbortSignal.any([ctx.signal, timeoutController.signal]) : timeoutController.signal
418
+ };
419
+ const run = executePipeline(doc.pipeline, "pipeline", runtime, runContext, registry, output, options);
381
420
  if (doc.timeoutSeconds && doc.timeoutSeconds > 0) await Promise.race([run, setTimeout(doc.timeoutSeconds * 1e3, void 0, { signal: ctx.signal }).then(() => {
421
+ timeoutController.abort();
382
422
  throw actionError("PIPELINE_TIMEOUT", `Pipeline timed out after ${doc.timeoutSeconds} seconds`);
383
423
  })]);
384
424
  else await run;
385
- } catch {
425
+ } catch (error) {
426
+ const failure = error instanceof Error ? error : new Error(String(error));
427
+ const code = failure.code ?? "PIPELINE_FAILED";
428
+ runtime.error ??= {
429
+ code,
430
+ message: failure.message
431
+ };
386
432
  output.ok = false;
387
433
  output.error ??= {
388
434
  step: output.trace.length,
389
435
  action: "pipeline",
390
- code: "PIPELINE_FAILED",
391
- message: runtime.error?.message ?? "Pipeline failed"
436
+ code,
437
+ message: failure.message
392
438
  };
393
439
  }
394
440
  output.last = runtime.last;
@@ -396,7 +442,7 @@ async function runBrowserPipeline(yamlSource, argOverrides, ctx, registry, optio
396
442
  if (!output.ok && doc.onError && doc.onError.length > 0) {
397
443
  log.info({ pipeline: doc.name }, "Running on_error diagnostics");
398
444
  try {
399
- await executePipeline(doc.onError, "on_error", runtime, ctx, registry, output);
445
+ await executePipeline(doc.onError, "on_error", runtime, ctx, registry, output, options);
400
446
  } catch {}
401
447
  output.last = runtime.last;
402
448
  output.outputs = runtime.outputs;
@@ -404,4 +450,4 @@ async function runBrowserPipeline(yamlSource, argOverrides, ctx, registry, optio
404
450
  return formatPipelineResult(output);
405
451
  }
406
452
  //#endregion
407
- export { runBrowserPipeline, validateBrowserPipeline, validateBrowserPipelineSource };
453
+ export { runBrowserPipeline, validateBrowserPipeline };
@@ -1,24 +1,29 @@
1
- /**
2
- * Pipeline YAML schema — parse and validate brocli-style browser pipeline documents.
3
- */
4
- export interface PipelineArgDef {
5
- type: string;
6
- required?: boolean;
7
- default?: unknown;
8
- description?: string;
9
- choices?: unknown[];
10
- }
1
+ import { z } from 'zod';
2
+ export declare const BROWSER_RECIPE_API_VERSION: "xopc.ai/browser-recipe/v1";
3
+ declare const PipelineArgSchema: z.ZodObject<{
4
+ type: z.ZodEnum<{
5
+ string: "string";
6
+ number: "number";
7
+ boolean: "boolean";
8
+ integer: "integer";
9
+ }>;
10
+ required: z.ZodOptional<z.ZodBoolean>;
11
+ default: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
12
+ description: z.ZodOptional<z.ZodString>;
13
+ choices: z.ZodOptional<z.ZodArray<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>>;
14
+ }, z.core.$strict>;
15
+ export type PipelineArgDef = z.infer<typeof PipelineArgSchema>;
11
16
  export interface PipelineStep {
12
- /** The single action name (key of the step object). */
13
17
  action: string;
14
- /** Action arguments (value of that key), or raw string for shorthand like `evaluate: |`. */
15
- args: Record<string, unknown> | string;
18
+ args: Record<string, unknown>;
16
19
  }
17
20
  export interface PipelineDocument {
21
+ apiVersion: typeof BROWSER_RECIPE_API_VERSION;
22
+ id: string;
18
23
  name: string;
19
24
  description?: string;
20
- provider?: string;
21
- include?: string[];
25
+ risk: 'read_only' | 'account_write' | 'sensitive';
26
+ domains: string[];
22
27
  timeoutSeconds?: number;
23
28
  args: Record<string, PipelineArgDef>;
24
29
  pipeline: PipelineStep[];
@@ -34,3 +39,4 @@ export interface PipelineParseResult {
34
39
  errors: PipelineValidationError[];
35
40
  }
36
41
  export declare function parseBrowserPipeline(yamlSource: string): PipelineParseResult;
42
+ export {};
@@ -1,122 +1,116 @@
1
+ import { z } from "zod";
1
2
  import yaml from "js-yaml";
2
3
  //#region src/browser/pipeline/schema.ts
3
- /**
4
- * Pipeline YAML schema — parse and validate brocli-style browser pipeline documents.
5
- */
4
+ const BROWSER_RECIPE_API_VERSION = "xopc.ai/browser-recipe/v1";
5
+ const IdSchema = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).max(100);
6
+ const JsonValueSchema = z.lazy(() => z.union([
7
+ z.string(),
8
+ z.number(),
9
+ z.boolean(),
10
+ z.null(),
11
+ z.array(JsonValueSchema),
12
+ z.record(z.string(), JsonValueSchema)
13
+ ]));
14
+ const PipelineArgSchema = z.object({
15
+ type: z.enum([
16
+ "string",
17
+ "number",
18
+ "integer",
19
+ "boolean"
20
+ ]),
21
+ required: z.boolean().optional(),
22
+ default: JsonValueSchema.optional(),
23
+ description: z.string().max(500).optional(),
24
+ choices: z.array(JsonValueSchema).optional()
25
+ }).strict().superRefine((definition, ctx) => {
26
+ const matchesType = (value) => {
27
+ if (definition.type === "string") return typeof value === "string";
28
+ if (definition.type === "boolean") return typeof value === "boolean";
29
+ if (definition.type === "integer") return typeof value === "number" && Number.isInteger(value);
30
+ return typeof value === "number" && Number.isFinite(value);
31
+ };
32
+ if (definition.default !== void 0 && !matchesType(definition.default)) ctx.addIssue({
33
+ code: "custom",
34
+ path: ["default"],
35
+ message: `Default must match arg type ${definition.type}.`
36
+ });
37
+ definition.choices?.forEach((choice, index) => {
38
+ if (!matchesType(choice)) ctx.addIssue({
39
+ code: "custom",
40
+ path: ["choices", index],
41
+ message: `Choice must match arg type ${definition.type}.`
42
+ });
43
+ });
44
+ });
45
+ const DomainSchema = z.string().trim().toLowerCase().regex(/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/, "Domain must be a hostname without scheme, path, port, or wildcard.");
46
+ const StepSchema = z.record(z.string(), z.record(z.string(), z.unknown())).superRefine((step, ctx) => {
47
+ if (Object.keys(step).length !== 1) ctx.addIssue({
48
+ code: "custom",
49
+ message: "Each step must contain exactly one action."
50
+ });
51
+ });
52
+ const RecipeSchema = z.object({
53
+ apiVersion: z.literal(BROWSER_RECIPE_API_VERSION),
54
+ id: IdSchema,
55
+ name: z.string().trim().min(1).max(200),
56
+ description: z.string().max(2e3).optional(),
57
+ risk: z.enum([
58
+ "read_only",
59
+ "account_write",
60
+ "sensitive"
61
+ ]),
62
+ domains: z.array(DomainSchema).min(1),
63
+ timeoutSeconds: z.number().int().min(1).max(3600).optional(),
64
+ args: z.record(z.string(), PipelineArgSchema).default({}),
65
+ pipeline: z.array(StepSchema).min(1),
66
+ on_error: z.array(StepSchema).optional()
67
+ }).strict();
68
+ function stepsFromRaw(steps) {
69
+ return steps.map((step) => {
70
+ const [action, args] = Object.entries(step)[0];
71
+ return {
72
+ action,
73
+ args
74
+ };
75
+ });
76
+ }
6
77
  function parseBrowserPipeline(yamlSource) {
7
- const errors = [];
8
78
  let raw;
9
79
  try {
10
80
  raw = yaml.load(yamlSource);
11
- } catch (e) {
81
+ } catch (error) {
12
82
  return {
13
83
  ok: false,
14
84
  errors: [{
15
85
  path: "",
16
- message: `YAML parse error: ${e instanceof Error ? e.message : String(e)}`
86
+ message: `YAML parse error: ${error instanceof Error ? error.message : String(error)}`
17
87
  }]
18
88
  };
19
89
  }
20
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {
21
- ok: false,
22
- errors: [{
23
- path: "",
24
- message: "Pipeline must be a YAML mapping (object)."
25
- }]
26
- };
27
- const doc = raw;
28
- const name = typeof doc.name === "string" ? doc.name.trim() : "";
29
- if (!name) errors.push({
30
- path: "name",
31
- message: "`name` is required."
32
- });
33
- const description = typeof doc.description === "string" ? doc.description : void 0;
34
- const provider = typeof doc.provider === "string" ? doc.provider : void 0;
35
- const include = Array.isArray(doc.include) ? doc.include.filter((x) => typeof x === "string") : void 0;
36
- const timeoutSeconds = typeof doc.timeoutSeconds === "number" ? doc.timeoutSeconds : void 0;
37
- const argsRaw = doc.args && typeof doc.args === "object" && !Array.isArray(doc.args) ? doc.args : {};
38
- const args = {};
39
- for (const [k, v] of Object.entries(argsRaw)) if (v && typeof v === "object" && !Array.isArray(v)) {
40
- const def = v;
41
- args[k] = {
42
- type: typeof def.type === "string" ? def.type : "string",
43
- required: def.required === true,
44
- default: def.default,
45
- description: typeof def.description === "string" ? def.description : void 0,
46
- choices: Array.isArray(def.choices) ? def.choices : void 0
47
- };
48
- } else args[k] = {
49
- type: "string",
50
- default: v
51
- };
52
- function parseSteps(rawSteps, path, required) {
53
- if (!Array.isArray(rawSteps)) {
54
- if (required) errors.push({
55
- path,
56
- message: `\`${path}\` must be an array of steps.`
57
- });
58
- return [];
59
- }
60
- const pipeline = [];
61
- for (let i = 0; i < rawSteps.length; i++) {
62
- const step = rawSteps[i];
63
- if (!step || typeof step !== "object" || Array.isArray(step)) {
64
- errors.push({
65
- path: `${path}[${i}]`,
66
- message: "Each step must be a YAML mapping."
67
- });
68
- continue;
69
- }
70
- const stepObj = step;
71
- const actionKeys = Object.keys(stepObj);
72
- if (actionKeys.length !== 1) {
73
- errors.push({
74
- path: `${path}[${i}]`,
75
- message: `Each step must have exactly one action key, found ${actionKeys.length}: [${actionKeys.join(", ")}].`
76
- });
77
- continue;
78
- }
79
- const action = actionKeys[0];
80
- const actionArgs = stepObj[action];
81
- if (typeof actionArgs === "string") pipeline.push({
82
- action,
83
- args: actionArgs
84
- });
85
- else if (actionArgs && typeof actionArgs === "object" && !Array.isArray(actionArgs)) pipeline.push({
86
- action,
87
- args: actionArgs
88
- });
89
- else if (actionArgs === null || actionArgs === void 0) pipeline.push({
90
- action,
91
- args: {}
92
- });
93
- else pipeline.push({
94
- action,
95
- args: { value: actionArgs }
96
- });
97
- }
98
- return pipeline;
99
- }
100
- const pipeline = parseSteps(doc.pipeline, "pipeline", true);
101
- const onError = doc.on_error === void 0 ? void 0 : parseSteps(doc.on_error, "on_error", false);
102
- if (errors.length > 0) return {
90
+ const parsed = RecipeSchema.safeParse(raw);
91
+ if (!parsed.success) return {
103
92
  ok: false,
104
- errors
93
+ errors: parsed.error.issues.map((issue) => ({
94
+ path: issue.path.join("."),
95
+ message: issue.message
96
+ }))
105
97
  };
106
98
  return {
107
99
  ok: true,
108
100
  document: {
109
- name,
110
- description,
111
- provider,
112
- include,
113
- timeoutSeconds,
114
- args,
115
- pipeline,
116
- onError
101
+ apiVersion: parsed.data.apiVersion,
102
+ id: parsed.data.id,
103
+ name: parsed.data.name,
104
+ description: parsed.data.description,
105
+ risk: parsed.data.risk,
106
+ domains: parsed.data.domains,
107
+ timeoutSeconds: parsed.data.timeoutSeconds,
108
+ args: parsed.data.args,
109
+ pipeline: stepsFromRaw(parsed.data.pipeline),
110
+ onError: parsed.data.on_error ? stepsFromRaw(parsed.data.on_error) : void 0
117
111
  },
118
112
  errors: []
119
113
  };
120
114
  }
121
115
  //#endregion
122
- export { parseBrowserPipeline };
116
+ export { BROWSER_RECIPE_API_VERSION, parseBrowserPipeline };
@@ -0,0 +1,3 @@
1
+ export * from './types.js';
2
+ export * from './service.js';
3
+ export * from './runtime-service.js';
@@ -0,0 +1,3 @@
1
+ import { BrowserRecipeService } from "./service.js";
2
+ import { createRuntimeBrowserRecipeService } from "./runtime-service.js";
3
+ export { BrowserRecipeService, createRuntimeBrowserRecipeService };
@@ -0,0 +1,13 @@
1
+ import type { BrowserRecipe, BrowserRecipeRun, BrowserRecipeRunEvent } from './types.js';
2
+ export declare function listBrowserRecipes(): BrowserRecipe[];
3
+ export declare function getBrowserRecipe(id: string): BrowserRecipe | null;
4
+ export declare function saveBrowserRecipe(recipe: BrowserRecipe): void;
5
+ export declare function deleteBrowserRecipe(id: string): boolean;
6
+ export declare function saveBrowserRecipeRun(run: BrowserRecipeRun): void;
7
+ export declare function getBrowserRecipeRun(id: string): BrowserRecipeRun | null;
8
+ export declare function listBrowserRecipeRuns(recipeId?: string): BrowserRecipeRun[];
9
+ export declare function hasActiveBrowserRecipeRuns(recipeId: string): boolean;
10
+ export declare function listActiveBrowserRecipeRuns(): BrowserRecipeRun[];
11
+ export declare function nextBrowserRecipeRunEventSeq(runId: string): number;
12
+ export declare function appendBrowserRecipeRunEvent(event: BrowserRecipeRunEvent): void;
13
+ export declare function listBrowserRecipeRunEvents(runId: string): BrowserRecipeRunEvent[];