minimal-agent 0.8.2 → 0.10.0

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 (484) hide show
  1. package/README.md +14 -12
  2. package/package.json +34 -1
  3. package/plugins/cron-runner/.claude-plugin/plugin.json +5 -0
  4. package/plugins/cron-runner/commands/cron.md +26 -0
  5. package/plugins/cron-runner/commands/crons.md +7 -0
  6. package/plugins/cron-runner/plugin.js +34 -0
  7. package/plugins/cron-runner/src/commands.js +348 -0
  8. package/plugins/cron-runner/src/cronTime.js +148 -0
  9. package/plugins/cron-runner/src/lock.js +68 -0
  10. package/plugins/cron-runner/src/runs.js +89 -0
  11. package/plugins/cron-runner/src/scheduleEngine.js +72 -0
  12. package/plugins/cron-runner/src/scheduler.js +355 -0
  13. package/plugins/cron-runner/src/schema.js +140 -0
  14. package/plugins/cron-runner/src/service.js +167 -0
  15. package/plugins/cron-runner/src/store.js +298 -0
  16. package/plugins/cron-runner/src/types.js +1 -0
  17. package/plugins/workflow-runner/commands/workflow.md +4 -4
  18. package/plugins/workflow-runner/commands/workflows.md +1 -1
  19. package/plugins/workflow-runner/plugin.js +3 -1
  20. package/plugins/workflow-runner/src/agentCommon.js +249 -51
  21. package/plugins/workflow-runner/src/dslManifest.js +72 -0
  22. package/plugins/workflow-runner/src/expressions.js +198 -15
  23. package/plugins/workflow-runner/src/index.js +40 -8
  24. package/plugins/workflow-runner/src/jsonPayload.js +1 -1
  25. package/plugins/workflow-runner/src/loader.js +286 -72
  26. package/plugins/workflow-runner/src/multimodal.js +37 -0
  27. package/plugins/workflow-runner/src/recovery.js +49 -39
  28. package/plugins/workflow-runner/src/runner.js +802 -287
  29. package/plugins/workflow-runner/src/stepExecutors/agent.js +157 -39
  30. package/plugins/workflow-runner/src/stepExecutors/assert.js +16 -3
  31. package/plugins/workflow-runner/src/stepExecutors/llm.js +60 -111
  32. package/plugins/workflow-runner/src/stepExecutors/skill.js +47 -47
  33. package/plugins/workflow-runner/src/stepExecutors/tool.js +76 -10
  34. package/plugins/workflow-runner/src/stepExecutors/verify.js +27 -10
  35. package/plugins/workflow-runner/src/taskIdentity.js +296 -0
  36. package/plugins/workflow-runner/src/types.js +19 -9
  37. package/plugins/workflow-runner/src/workflowState.js +516 -33
  38. package/skills/adversarial-review/SKILL.md +0 -1
  39. package/skills/ai-image/SKILL.md +89 -0
  40. package/skills/ai-image/scripts/generate.mjs +497 -0
  41. package/skills/ai-image/scripts/generate_from_plan.mjs +469 -0
  42. package/skills/ai-image/scripts/generate_from_plan_test.mjs +223 -0
  43. package/skills/ai-image/scripts/generate_test.mjs +74 -0
  44. package/skills/browser-automation/SKILL.md +47 -0
  45. package/skills/docx/SKILL.md +25 -3
  46. package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
  47. package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
  48. package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
  49. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  50. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
  51. package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
  52. package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
  53. package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
  54. package/skills/docx/scripts/opc_check.py +253 -0
  55. package/skills/image-gen-openrouter/SKILL.md +10 -2
  56. package/skills/image-stats/SKILL.md +65 -0
  57. package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
  58. package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
  59. package/skills/image-stats/scripts/image_stats.py +368 -0
  60. package/skills/image-stats/scripts/image_stats_test.py +181 -0
  61. package/skills/pptx/SKILL.md +123 -95
  62. package/skills/pptx/design.md +82 -0
  63. package/skills/pptx/examples/README.md +29 -0
  64. package/skills/pptx/examples/hybrid_example.mjs +107 -0
  65. package/skills/pptx/examples/hybrid_example.pptx +0 -0
  66. package/skills/pptx/pptxgenjs.md +41 -0
  67. package/skills/pptx/scripts/deck_helpers.mjs +224 -0
  68. package/skills/pptx/scripts/layout_lint.py +293 -0
  69. package/skills/pptx/scripts/layout_lint_test.py +160 -0
  70. package/skills/pptx/scripts/opc_check.py +339 -0
  71. package/skills/pptx/scripts/svg_fallback.mjs +145 -0
  72. package/skills/pptx/svg.md +67 -0
  73. package/skills/tts-zh/SKILL.md +19 -2
  74. package/skills/ui-design/SKILL.md +158 -0
  75. package/skills/ui-design/claude_design.md +309 -0
  76. package/skills/ui-design/evals/evals.json +254 -0
  77. package/skills/ui-design/references/aesthetic-direction.md +83 -0
  78. package/skills/ui-design/references/claude-design-protocol.md +36 -0
  79. package/skills/ui-design/references/design-context.md +53 -0
  80. package/skills/ui-design/references/design-execution-playbook.md +82 -0
  81. package/skills/ui-design/references/export-pptx.md +124 -0
  82. package/skills/ui-design/references/layout-composition.md +654 -0
  83. package/skills/ui-design/references/preview-feedback.md +37 -0
  84. package/skills/ui-design/references/source-image-layout.md +189 -0
  85. package/skills/ui-design/references/starter-components.md +78 -0
  86. package/skills/ui-design/references/vi-aegis-vector.md +144 -0
  87. package/skills/ui-design/references/vi-amber-prism.md +133 -0
  88. package/skills/ui-design/references/vi-aqua-glass.md +200 -0
  89. package/skills/ui-design/references/vi-comfort-vision.md +147 -0
  90. package/skills/ui-design/references/vi-ember-prism.md +140 -0
  91. package/skills/ui-design/references/vi-nebula-console.md +151 -0
  92. package/skills/ui-design/references/vi-obsidian-optics.md +139 -0
  93. package/skills/ui-design/references/vi-obsidian-xui.md +143 -0
  94. package/skills/ui-design/references/vi-precision-horizon-dark.md +117 -0
  95. package/skills/ui-design/references/vi-precision-horizon.md +215 -0
  96. package/skills/ui-design/references/vi-solar-relay.md +130 -0
  97. package/skills/ui-design/references/vi-template-workflow.md +48 -0
  98. package/skills/ui-design/references/vi-verdant-grid.md +141 -0
  99. package/skills/ui-design/references/vi-violet-meridian.md +172 -0
  100. package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
  101. package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
  102. package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
  103. package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
  104. package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
  105. package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
  106. package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
  107. package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
  108. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
  109. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
  110. package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
  111. package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
  112. package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
  113. package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
  114. package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
  115. package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
  116. package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
  117. package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
  118. package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
  119. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
  120. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
  121. package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
  122. package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
  123. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
  124. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
  125. package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
  126. package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
  127. package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
  128. package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
  129. package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
  130. package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
  131. package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
  132. package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
  133. package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
  134. package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
  135. package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
  136. package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
  137. package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
  138. package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
  139. package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
  140. package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
  141. package/skills/ui-design/scripts/assemble_material.py +217 -0
  142. package/skills/ui-design/scripts/assemble_material_test.py +149 -0
  143. package/skills/ui-design/scripts/browser_check_ui_artifact.mjs +221 -0
  144. package/skills/ui-design/scripts/chart_sanity_check.py +261 -0
  145. package/skills/ui-design/scripts/chart_sanity_check_test.py +165 -0
  146. package/skills/ui-design/scripts/check_language_policy.py +351 -0
  147. package/skills/ui-design/scripts/check_ui_artifact.py +435 -0
  148. package/skills/ui-design/scripts/check_ui_artifact_pptx_test.py +251 -0
  149. package/skills/ui-design/scripts/deck_pages_check.py +158 -0
  150. package/skills/ui-design/scripts/deck_pages_check_test.py +134 -0
  151. package/skills/ui-design/scripts/deck_visual_style_check.py +767 -0
  152. package/skills/ui-design/scripts/deck_visual_style_check_test.py +713 -0
  153. package/skills/ui-design/scripts/design_budget_check.py +271 -0
  154. package/skills/ui-design/scripts/design_budget_check_test.py +183 -0
  155. package/skills/ui-design/scripts/design_repair_hints.py +162 -0
  156. package/skills/ui-design/scripts/design_repair_hints_test.py +54 -0
  157. package/skills/ui-design/scripts/design_style_check.py +397 -0
  158. package/skills/ui-design/scripts/design_style_check_test.py +233 -0
  159. package/skills/ui-design/scripts/draft_coverage_check.py +184 -0
  160. package/skills/ui-design/scripts/draft_coverage_check_test.py +131 -0
  161. package/skills/ui-design/scripts/export_pptx_from_html.mjs +2367 -0
  162. package/skills/ui-design/scripts/extract_source_assets.py +607 -0
  163. package/skills/ui-design/scripts/extract_source_assets_test.py +256 -0
  164. package/skills/ui-design/scripts/generated_image_review_collect.py +206 -0
  165. package/skills/ui-design/scripts/generated_image_review_collect_test.py +91 -0
  166. package/skills/ui-design/scripts/image_plan_lint.py +571 -0
  167. package/skills/ui-design/scripts/image_plan_lint_test.py +515 -0
  168. package/skills/ui-design/scripts/image_prompt_rewrite.py +713 -0
  169. package/skills/ui-design/scripts/image_prompt_rewrite_test.py +966 -0
  170. package/skills/ui-design/scripts/layout_balance.py +881 -0
  171. package/skills/ui-design/scripts/layout_balance_test.py +927 -0
  172. package/skills/ui-design/scripts/normalize_design_json.py +272 -0
  173. package/skills/ui-design/scripts/normalize_design_json_test.py +119 -0
  174. package/skills/ui-design/scripts/plan_source_layout.py +1450 -0
  175. package/skills/ui-design/scripts/plan_source_layout_test.py +1136 -0
  176. package/skills/ui-design/scripts/resolve_source_input.py +270 -0
  177. package/skills/ui-design/scripts/resolve_source_input_test.py +98 -0
  178. package/skills/ui-design/scripts/scaffold_ui_artifact.py +377 -0
  179. package/skills/ui-design/scripts/source_usage_check.py +581 -0
  180. package/skills/ui-design/scripts/source_usage_check_test.py +427 -0
  181. package/skills/ui-design/scripts/text_readability_pixel.py +340 -0
  182. package/skills/ui-design/scripts/text_readability_pixel_test.py +219 -0
  183. package/skills/ui-design/scripts/visual_quality_gate.py +301 -0
  184. package/skills/ui-design/scripts/visual_quality_gate_test.py +233 -0
  185. package/skills/ui-design/tests/e2e/prompts/deck.md +19 -0
  186. package/skills/ui-design/tests/e2e/prompts/webpage.md +17 -0
  187. package/skills/ui-design/tests/e2e/render_check.mjs +101 -0
  188. package/skills/ui-design/tests/e2e/rubric.json +54 -0
  189. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/browser-check.json +49 -0
  190. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/language-check.txt +12 -0
  191. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/static-check.txt +15 -0
  192. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/outputs/index.html +837 -0
  193. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/prompt.md +12 -0
  194. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/review.md +21 -0
  195. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/desktop.png +0 -0
  196. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/mobile.png +0 -0
  197. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/homepage.html +373 -0
  198. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/platform.html +378 -0
  199. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/prompt.md +13 -0
  200. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/review.md +26 -0
  201. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  202. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  203. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  204. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  205. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  206. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  207. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/checks/static-check.txt +2 -0
  208. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/outputs/index.html +656 -0
  209. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/prompt.md +12 -0
  210. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/render_check.mjs +67 -0
  211. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/checks/static-check.txt +2 -0
  212. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/outputs/index.html +587 -0
  213. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/prompt.md +12 -0
  214. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/review.md +36 -0
  215. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/browser-check.json +49 -0
  216. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/language-check.txt +8 -0
  217. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/static-check.txt +16 -0
  218. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/outputs/index.html +739 -0
  219. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/prompt.md +11 -0
  220. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/review.md +22 -0
  221. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/desktop.png +0 -0
  222. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/mobile.png +0 -0
  223. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/checks/browser-check.json +52 -0
  224. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/outputs/index.html +641 -0
  225. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/desktop.png +0 -0
  226. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/mobile.png +0 -0
  227. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/outputs/index.html +467 -0
  228. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/browser-check.json +49 -0
  229. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/desktop.png +0 -0
  230. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/mobile.png +0 -0
  231. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/homepage.html +373 -0
  232. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/platform.html +378 -0
  233. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/prompt.md +13 -0
  234. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/review.md +26 -0
  235. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  236. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  237. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  238. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  239. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  240. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  241. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/browser-check.json +49 -0
  242. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/language-check.txt +4 -0
  243. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/static-check.txt +2 -0
  244. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/execution_notes.md +10 -0
  245. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/outputs/index.html +587 -0
  246. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/prompt.md +14 -0
  247. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/review.md +13 -0
  248. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/desktop.png +0 -0
  249. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/mobile.png +0 -0
  250. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/browser-check.json +24 -0
  251. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/static-check.txt +2 -0
  252. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/execution_notes.md +21 -0
  253. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/outputs/deck.html +451 -0
  254. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-01.png +0 -0
  255. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-02.png +0 -0
  256. package/skills/ui-design/tests/e2e/runs/iteration-1/review.md +79 -0
  257. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/browser-check.json +26 -0
  258. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/static-check.txt +2 -0
  259. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/execution_notes.md +20 -0
  260. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/outputs/index.html +534 -0
  261. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/desktop.png +0 -0
  262. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/mobile.png +0 -0
  263. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/browser-check.json +64 -0
  264. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/language-check.txt +7 -0
  265. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/quick-validate.txt +1 -0
  266. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-console.txt +2 -0
  267. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-homepage.txt +2 -0
  268. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/console.html +600 -0
  269. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/homepage.html +556 -0
  270. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/prompt.md +29 -0
  271. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/review.md +57 -0
  272. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-desktop.png +0 -0
  273. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-mobile.png +0 -0
  274. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-desktop.png +0 -0
  275. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-mobile.png +0 -0
  276. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/browser-check.json +33 -0
  277. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/language-check.txt +6 -0
  278. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/quick-validate.txt +1 -0
  279. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/static-check.txt +2 -0
  280. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/outputs/index.html +682 -0
  281. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/prompt.md +13 -0
  282. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/review.md +59 -0
  283. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/desktop.png +0 -0
  284. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/mobile.png +0 -0
  285. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-01.html +466 -0
  286. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-02.html +433 -0
  287. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/prompt.md +13 -0
  288. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/browser-check.json +49 -0
  289. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/desktop.png +0 -0
  290. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/mobile.png +0 -0
  291. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/browser-check.json +49 -0
  292. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/desktop.png +0 -0
  293. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/mobile.png +0 -0
  294. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/browser-check.json +49 -0
  295. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/language-check.txt +3 -0
  296. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/static-check.txt +2 -0
  297. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/execution_notes.md +24 -0
  298. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/outputs/index.html +723 -0
  299. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/prompt.md +15 -0
  300. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/review.md +26 -0
  301. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/desktop.png +0 -0
  302. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/mobile.png +0 -0
  303. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-dark.json +49 -0
  304. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-light.json +49 -0
  305. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/language-check.txt +11 -0
  306. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-dark.txt +2 -0
  307. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-light.txt +3 -0
  308. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/execution_notes.md +23 -0
  309. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/dark.html +392 -0
  310. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/light.html +476 -0
  311. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/prompt.md +23 -0
  312. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/review.md +41 -0
  313. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/desktop.png +0 -0
  314. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/mobile.png +0 -0
  315. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/desktop.png +0 -0
  316. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/mobile.png +0 -0
  317. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/browser-check.json +54 -0
  318. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/language-check.txt +5 -0
  319. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/quick-validate.txt +1 -0
  320. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/static-check.txt +2 -0
  321. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/outputs/index.html +765 -0
  322. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/prompt.md +15 -0
  323. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/review.md +59 -0
  324. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/desktop.png +0 -0
  325. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/mobile.png +0 -0
  326. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/outputs/index.html +508 -0
  327. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/browser-check.json +49 -0
  328. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/desktop.png +0 -0
  329. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/mobile.png +0 -0
  330. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/browser-check.json +54 -0
  331. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/language-check.txt +6 -0
  332. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/quick-validate.txt +1 -0
  333. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/static-check.txt +2 -0
  334. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/outputs/index.html +788 -0
  335. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/prompt.md +14 -0
  336. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/review.md +60 -0
  337. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/desktop.png +0 -0
  338. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/mobile.png +0 -0
  339. package/skills/video-compose/SKILL.md +8 -1
  340. package/skills/web-crawler/SKILL.md +70 -0
  341. package/skills/web-crawler/scripts/crawl.mjs +538 -0
  342. package/skills/web-crawler/scripts/crawl.test.mjs +204 -0
  343. package/skills/xlsx/SKILL.md +12 -0
  344. package/skills/xlsx/scripts/opc_check.py +253 -0
  345. package/src/advisorState.js +22 -0
  346. package/src/askBridge.js +97 -3
  347. package/src/bootstrap/workingDir.js +12 -1
  348. package/src/cli/args.js +17 -1
  349. package/src/cli/configMenu.js +236 -0
  350. package/src/cli/configWizard.js +7 -5
  351. package/src/cli/print.js +37 -4
  352. package/src/cli/streamJson.js +77 -24
  353. package/src/cli/wizards/advisorWizard.js +147 -0
  354. package/src/cli/wizards/fieldForm.js +149 -0
  355. package/src/cli/wizards/imageWizard.js +60 -0
  356. package/src/cli/wizards/openRouterWizard.js +23 -0
  357. package/src/cli/wizards/providerPresets.js +95 -0
  358. package/src/cli/wizards/providerWizard.js +219 -0
  359. package/src/cli/wizards/searchWizard.js +23 -0
  360. package/src/cli/wizards/ttsWizard.js +34 -0
  361. package/src/config/configFile.js +441 -83
  362. package/src/config/configService.js +446 -0
  363. package/src/config.js +24 -64
  364. package/src/context/agentHome.js +9 -0
  365. package/src/context/archive.js +7 -2
  366. package/src/context/compact.js +8 -5
  367. package/src/context/contextLock.js +81 -0
  368. package/src/context/persistContext.js +30 -11
  369. package/src/context/reactiveCompact.js +4 -2
  370. package/src/context/recentDirs.js +21 -61
  371. package/src/context/sessionPath.js +97 -23
  372. package/src/context/sessionRegistry.js +303 -24
  373. package/src/context/tokenCounter.js +18 -0
  374. package/src/context/workspaceIndex.js +731 -0
  375. package/src/infra/verification.js +28 -15
  376. package/src/llm/client.js +394 -159
  377. package/src/llm/providers/anthropicFormat.js +241 -0
  378. package/src/llm/providers/openaiFormat.js +190 -0
  379. package/src/loop.js +219 -32
  380. package/src/main.js +61 -11
  381. package/src/messageQueue.js +97 -0
  382. package/src/multimodal.js +203 -0
  383. package/src/plugin-sdk.js +15 -0
  384. package/src/plugins/commandRouter.js +3 -0
  385. package/src/plugins/hookEngine.js +5 -1
  386. package/src/plugins/pluginLifecycle.js +28 -0
  387. package/src/plugins/pluginLoader.js +2 -1
  388. package/src/plugins/pluginRunner.js +49 -21
  389. package/src/plugins/transcript.js +4 -3
  390. package/src/prompts/pluginList.js +5 -5
  391. package/src/prompts/projectInstructions.js +3 -3
  392. package/src/prompts/promptAssembly.js +133 -144
  393. package/src/prompts/skillList.js +12 -22
  394. package/src/prompts/system.js +0 -1
  395. package/src/session/runAgentTurn.js +70 -0
  396. package/src/session/runTurn.js +41 -6
  397. package/src/toolGate.js +17 -0
  398. package/src/tools/advisor/advisor.js +379 -0
  399. package/src/tools/agent/agent.js +555 -0
  400. package/src/tools/agent/agentTypes.js +59 -0
  401. package/src/tools/agent/coordinator.js +93 -0
  402. package/src/tools/agent/worktree.js +270 -0
  403. package/src/tools/ask/ask.js +7 -2
  404. package/src/tools/bash/bash.js +112 -18
  405. package/src/tools/download/download.js +144 -0
  406. package/src/tools/edit/edit.js +1 -0
  407. package/src/tools/edit/multi-edit.js +1 -0
  408. package/src/tools/genimage/genimage.js +242 -0
  409. package/src/tools/genspeech/genspeech.js +67 -0
  410. package/src/tools/glob/glob.js +1 -0
  411. package/src/tools/grep/grep.js +3 -0
  412. package/src/tools/grep/rgPath.js +2 -0
  413. package/src/tools/index.js +25 -2
  414. package/src/tools/read/read.js +1 -0
  415. package/src/tools/shared/killTree.js +52 -0
  416. package/src/tools/toolProgressBridge.js +48 -0
  417. package/src/tools/webbrowser/agentBrowserPath.js +258 -0
  418. package/src/tools/webbrowser/browser.js +59 -42
  419. package/src/tools/webbrowser/browserControl.js +118 -0
  420. package/src/tools/webbrowser/browserDaemon.js +97 -0
  421. package/src/tools/webbrowser/browserService.js +27 -0
  422. package/src/tools/webbrowser/browserStream.js +119 -0
  423. package/src/tools/webbrowser/webbrowser.js +699 -130
  424. package/src/tools/webfetch/webfetch.js +1 -0
  425. package/src/tools/websearch/websearch.js +134 -51
  426. package/src/tools/write/write.js +1 -0
  427. package/src/ui/App.js +249 -22
  428. package/src/ui/AskQuestion.js +29 -10
  429. package/src/ui/InputBox.js +104 -12
  430. package/src/ui/LiveArea.js +112 -19
  431. package/src/ui/MessageList.js +100 -14
  432. package/src/ui/ModelPicker.js +56 -0
  433. package/src/ui/QueuedMessagesBanner.js +14 -0
  434. package/src/ui/Root.js +18 -10
  435. package/src/ui/SessionPicker.js +102 -0
  436. package/src/ui/StatusLine.js +13 -4
  437. package/src/ui/WorkspacePicker.js +55 -0
  438. package/src/ui/figures.js +44 -0
  439. package/src/ui/hooks/askSlot.js +82 -0
  440. package/src/ui/hooks/useChat.js +358 -64
  441. package/src/ui/hooks/usePasteHandler.js +11 -1
  442. package/src/ui/hooks/useTextBuffer.js +1 -3
  443. package/src/ui/liveViewport.js +5 -1
  444. package/src/ui/markdown.js +206 -0
  445. package/src/ui/textBuffer.js +0 -7
  446. package/src/ui/theme.js +32 -0
  447. package/src/utils/debugLog.js +0 -4
  448. package/src/utils/resourcePaths.js +2 -2
  449. package/src/utils/stripThink.js +50 -0
  450. package/src/web/browserBridge.js +447 -0
  451. package/src/web/protocol.js +19 -0
  452. package/workflows/adaptive-research.yaml +13 -5
  453. package/workflows/book-review-short.yaml +36 -10
  454. package/workflows/e2e-write-greet.yaml +6 -2
  455. package/workflows/illustrate.yaml +227 -0
  456. package/workflows/plan.yaml +460 -179
  457. package/workflows/ppt.yaml +1659 -0
  458. package/workflows/ralph-loop.yaml +199 -0
  459. package/workflows/schema.json +81 -23
  460. package/workflows/webppt.yaml +496 -0
  461. package/workflows/word.yaml +710 -0
  462. package/workflows/youtube-shorts.yaml +46 -15
  463. package/MINIMAL-TRACE.md +0 -77
  464. package/plugins/HOW-TO-WRITE-A-PLUGIN.md +0 -186
  465. package/plugins/ralph-wiggum/.claude-plugin/plugin.json +0 -9
  466. package/plugins/ralph-wiggum/README.md +0 -197
  467. package/plugins/ralph-wiggum/commands/cancel-ralph.md +0 -18
  468. package/plugins/ralph-wiggum/commands/help.md +0 -137
  469. package/plugins/ralph-wiggum/commands/ralph-loop.md +0 -85
  470. package/plugins/ralph-wiggum/hooks/hooks.json +0 -15
  471. package/plugins/ralph-wiggum/hooks/stop-hook.sh +0 -191
  472. package/plugins/ralph-wiggum/plugin.js +0 -280
  473. package/plugins/ralph-wiggum/src/goalState.js +0 -258
  474. package/plugins/ralph-wiggum/src/sentinels.js +0 -76
  475. package/plugins/ralph-wiggum/src/stopHookRunner.js +0 -104
  476. package/plugins/ralph-wiggum/src/verificationGate.js +0 -202
  477. package/plugins/workflow-runner/src/miniReAct.js +0 -223
  478. package/plugins/workflow-runner/src/voteHelpers.js +0 -110
  479. package/skills/config/SKILL.md +0 -123
  480. package/skills/frontend-design/LICENSE.txt +0 -177
  481. package/skills/frontend-design/SKILL.md +0 -42
  482. package/src/ui/ProgressPanel.js +0 -54
  483. package/src/ui/StreamingBlock.js +0 -11
  484. package/src/ui/ToolStatus.js +0 -11
@@ -0,0 +1,48 @@
1
+ /**
2
+ * ============================================================
3
+ * src/tools/toolProgressBridge.ts —— 长时工具的「步内进度」宿主桥
4
+ * ------------------------------------------------------------
5
+ * 仿 askBridge 的宿主交互桥模式(全局 handler 注册表):
6
+ * 长时工具(GenerateImage 批量生图,单步可达 10-30 分钟)在执行**中途**把
7
+ * 逐项进度行发布出来,有 UI 的宿主(TUI useChat)订阅后实时上屏——
8
+ * 否则整步在 LiveArea 上只有一行静态描述,十几分钟毫无变化,
9
+ * 用户必然误判死机(2026-07-02 真实事故:31 张生图 ~13 分钟被当成卡死,
10
+ * 差点手杀健康运行)。
11
+ *
12
+ * 两种宿主:
13
+ * - TUI:useChat mount 时 setToolProgressHandler() 注册,更新 runningTools
14
+ * 对应条目的 progress 字段 → LiveArea tool_executing 行实时显示。
15
+ * - 非交互(`-p` / gateway / 子进程):没有 handler → publish 是 no-op,
16
+ * 零副作用(进度对结构化结局契约不可见,不污染 stdout/stderr)。
17
+ *
18
+ * 工具侧只依赖本模块,不 import ui/(与 askBridge 同样的中立位置理由:
19
+ * 它是工具与宿主之间的中介,避免 tools → ui 反向耦合)。
20
+ * ============================================================
21
+ */
22
+ let handler = null;
23
+ /**
24
+ * 注册 / 注销宿主处理器。
25
+ * TUI 的 useChat 在 mount 时注册、unmount 时传 null 注销。
26
+ * 单进程单 UI,模块级单例即可(与 askBridge 一致)。
27
+ */
28
+ export function setToolProgressHandler(h) {
29
+ handler = h;
30
+ }
31
+ /** 是否有宿主在场(测试 / 诊断用)。 */
32
+ export function hasToolProgressHandler() {
33
+ return handler !== null;
34
+ }
35
+ /**
36
+ * 发布一条进度行。无 handler 时 no-op;handler 抛异常也吞掉——
37
+ * 进度是锦上添花,绝不能让 UI 侧的 bug 反噬工具执行。
38
+ */
39
+ export function publishToolProgress(toolName, line) {
40
+ if (!handler)
41
+ return;
42
+ try {
43
+ handler(toolName, line);
44
+ }
45
+ catch {
46
+ /* 进度上屏失败不阻断工具 */
47
+ }
48
+ }
@@ -0,0 +1,258 @@
1
+ /**
2
+ * ============================================================
3
+ * src/tools/webbrowser/agentBrowserPath.ts —— 解析 agent-browser 可执行文件 + 内置浏览器
4
+ * ------------------------------------------------------------
5
+ * 内置 agent 浏览器底座(Step1):WebBrowser 工具 / skill 脚本 / 工作流 bash 步
6
+ * 共用同一份「二进制定位 + 浏览器降级」逻辑 —— 单一事实源,完全对标
7
+ * ripgrep 的 `src/tools/grep/rgPath.ts`。
8
+ *
9
+ * 引擎 = vercel-labs/agent-browser(原生 Rust CLI + CDP,已是 kakadeai 子模块,
10
+ * 只读引用)。它的原生二进制按 `agent-browser-<os>-<arch>[.exe]` 命名、从 GitHub
11
+ * releases 下载;我们像 vendor/ripgrep 一样把它放进 vendor/agent-browser/<arch>-<platform>/。
12
+ *
13
+ * ── agent-browser 二进制定位(按优先级,全程 existsSync 过滤)──
14
+ * ① MINIMAL_AGENT_BROWSER_PATH 环境变量(手动指定,调试最高优先)
15
+ * ② 项目自带 vendor/agent-browser/<arch>-<platform>/agent-browser(.exe)(默认,开箱即用)
16
+ * ③ 绿色版(bun --compile):exe 同级 vendor/agent-browser/...(compile 后 import.meta.url
17
+ * 是虚拟路径、② 命不中,绿色目录把 vendor/ 摆 exe 旁边从这捞回)
18
+ * ④ 系统 PATH 上的 agent-browser(用户自己 npm i -g / brew / cargo 装的)
19
+ * ⑤ 都没有 → null(调用方降级:返回可操作错误并提示回退 WebFetch)
20
+ *
21
+ * ── 浏览器(Chrome)分层降级(落实「opt2+opt3 融合」)──
22
+ * agent-browser 自己会:读 AGENT_BROWSER_EXECUTABLE_PATH → 自动探测系统
23
+ * Chrome/Brave/Edge/Playwright/Puppeteer → 缺则可 `agent-browser install` 下完整
24
+ * Chrome for Testing。我们只补**离线兜底**:当无系统浏览器且无法联网安装时,
25
+ * 用绿色版内置的轻量 chromium(resolveBundledChromium)保证「插上永远有浏览器」。
26
+ * dev/npm 下 vendor/chromium 不存在 → 返回 null → 不影响 agent-browser 原生探测。
27
+ *
28
+ * dev/npm 模式零副作用:execPath 指向 node/bun 二进制,其同级没有 vendor/;
29
+ * findPackageRoot 在源码/安装态下指向真实包根,vendor/agent-browser 不存在时
30
+ * existsSync 过滤掉 —— 与 rgPath/greenRoot 同一套「加性、命不中即丢弃」铁律。
31
+ *
32
+ * 可测性:核心解析 detectAgentBrowser / resolveBundledChromium 都接受可注入依赖
33
+ * (env/exists/siblingDir/packageRoot/trySpawn),默认走真实实现;单测注入假依赖
34
+ * 确定性覆盖每条优先级分支,不碰真实 fs、不用 mock.module(见项目测试约定)。
35
+ * ============================================================
36
+ */
37
+ import { spawn } from 'node:child_process';
38
+ import { chmodSync, existsSync } from 'node:fs';
39
+ import { dirname, isAbsolute, resolve, win32 } from 'node:path';
40
+ import { execSiblingDir } from '../../utils/greenRoot.js';
41
+ import { findPackageRoot } from '../../utils/packageRoot.js';
42
+ import { sanitizeCredentialEnv } from '../../config/configService.js';
43
+ function realDeps() {
44
+ return {
45
+ env: process.env,
46
+ exists: existsSync,
47
+ siblingDir: execSiblingDir,
48
+ packageRoot: () => {
49
+ try {
50
+ return findPackageRoot(import.meta.url);
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ },
56
+ trySpawn: defaultTrySpawn,
57
+ platform: process.platform,
58
+ arch: process.arch,
59
+ };
60
+ }
61
+ let cachedBin;
62
+ /**
63
+ * 解析一次 agent-browser 二进制路径,结果缓存(同进程复用)。
64
+ * 返回 null 表示找不到(调用方需降级)。
65
+ */
66
+ export async function resolveAgentBrowserPath() {
67
+ if (cachedBin !== undefined)
68
+ return cachedBin;
69
+ cachedBin = await detectAgentBrowser(realDeps());
70
+ return cachedBin;
71
+ }
72
+ /** 仅供测试重置缓存。 */
73
+ export function _resetAgentBrowserCache() {
74
+ cachedBin = undefined;
75
+ }
76
+ /**
77
+ * 把已解析到的 agent-browser 二进制所在目录前置进 PATH —— 让子进程(Bash 工具 /
78
+ * skill 脚本 / 工作流 bash 步)也能直接 `agent-browser ...`,实现 tools/skills/工作流
79
+ * **共用同一内置浏览器底座**。仅当解析到**绝对路径**(vendored/sibling/env)时生效;
80
+ * 解析到裸 'agent-browser'(已在 PATH)或 null(没装)时 no-op —— dev/npm 无 vendored
81
+ * 二进制时天然 no-op,与 greenRoot 同一套加性铁律。可纯函数化测试:注入 path/env。
82
+ *
83
+ * 注意:离线兜底的轻量 chromium 仅 WebBrowser 工具自动补 --executable-path 用;直接
84
+ * 经 CLI 调 agent-browser 的 skill/bash 走 agent-browser 自身的自动探测/在线安装。
85
+ */
86
+ export async function ensureAgentBrowserOnPath() {
87
+ applyBinToPath(await resolveAgentBrowserPath(), process.env, process.platform);
88
+ }
89
+ /** 纯函数:把 bin 所在目录前置进 env.PATH(已含则跳过;非绝对路径/null 不动)。 */
90
+ export function applyBinToPath(bin, env, platform) {
91
+ if (!bin || bin === 'agent-browser')
92
+ return;
93
+ const pathApi = platform === 'win32' ? win32 : { dirname, isAbsolute };
94
+ if (!pathApi.isAbsolute(bin))
95
+ return;
96
+ const dir = pathApi.dirname(bin);
97
+ const sep = platform === 'win32' ? ';' : ':';
98
+ const cur = env.PATH ?? '';
99
+ if (cur.split(sep).includes(dir))
100
+ return;
101
+ env.PATH = cur ? `${dir}${sep}${cur}` : dir;
102
+ }
103
+ /** 纯解析(可注入依赖):按 ①→⑤ 优先级返回 agent-browser 二进制路径或 null。 */
104
+ export async function detectAgentBrowser(deps) {
105
+ const sub = subdir(deps);
106
+ const bin = binName(deps);
107
+ // ① 显式 env 覆盖
108
+ const fromEnv = deps.env.MINIMAL_AGENT_BROWSER_PATH;
109
+ if (fromEnv && deps.exists(fromEnv))
110
+ return fromEnv;
111
+ // ② 项目自带 vendored(默认路径,开箱即用)
112
+ const root = deps.packageRoot();
113
+ if (root) {
114
+ const vendored = resolve(root, 'vendor', 'agent-browser', sub, bin);
115
+ if (deps.exists(vendored)) {
116
+ ensureExecutable(vendored, deps.platform);
117
+ return vendored;
118
+ }
119
+ }
120
+ // ③ 绿色版 exe 同级
121
+ const sibling = resolve(deps.siblingDir(), 'vendor', 'agent-browser', sub, bin);
122
+ if (deps.exists(sibling)) {
123
+ ensureExecutable(sibling, deps.platform);
124
+ return sibling;
125
+ }
126
+ // ④ PATH 上的 agent-browser
127
+ if (await deps.trySpawn('agent-browser'))
128
+ return 'agent-browser';
129
+ // ⑤ 找不到
130
+ return null;
131
+ }
132
+ /**
133
+ * 绿色版离线兜底:内置轻量 chromium 的可执行路径。返回 null 表示没带
134
+ * (dev/npm 常态,或绿色版未打包 chromium)—— 此时交回 agent-browser 自动探测/在线安装。
135
+ *
136
+ * 约定布局:vendor/chromium/<arch>-<platform>/ 下放该平台 chromium,可执行名按平台
137
+ * 探测一组常见相对路径(不同 chromium 构建解包名不一,宁可多探几个)。
138
+ */
139
+ export function resolveBundledChromium(deps = realDeps()) {
140
+ const sub = subdir(deps);
141
+ const roots = [resolve(deps.siblingDir(), 'vendor', 'chromium', sub)];
142
+ const root = deps.packageRoot();
143
+ if (root)
144
+ roots.push(resolve(root, 'vendor', 'chromium', sub));
145
+ for (const r of roots) {
146
+ for (const rel of chromiumExeCandidates(deps.platform)) {
147
+ const p = resolve(r, rel);
148
+ if (deps.exists(p)) {
149
+ ensureExecutable(p, deps.platform);
150
+ return p;
151
+ }
152
+ }
153
+ }
154
+ return null;
155
+ }
156
+ /**
157
+ * Prefer a user-installed stable browser executable while keeping KakadeAI's
158
+ * own profile directory. This improves site compatibility without touching
159
+ * the user's normal Chrome profile or attempting fingerprint evasion.
160
+ */
161
+ export function resolveSystemBrowser(deps = realDeps()) {
162
+ const explicit = deps.env.MINIMAL_AGENT_BROWSER_EXECUTABLE_PATH
163
+ ?? deps.env.AGENT_BROWSER_EXECUTABLE_PATH;
164
+ if (explicit && deps.exists(explicit))
165
+ return explicit;
166
+ const home = deps.env.HOME ?? deps.env.USERPROFILE ?? '';
167
+ const candidates = deps.platform === 'darwin'
168
+ ? [
169
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
170
+ '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
171
+ '/Applications/Chromium.app/Contents/MacOS/Chromium',
172
+ home ? resolve(home, 'Applications/Google Chrome.app/Contents/MacOS/Google Chrome') : '',
173
+ ]
174
+ : deps.platform === 'win32'
175
+ ? [
176
+ deps.env.PROGRAMFILES ? resolve(deps.env.PROGRAMFILES, 'Google/Chrome/Application/chrome.exe') : '',
177
+ deps.env['PROGRAMFILES(X86)'] ? resolve(deps.env['PROGRAMFILES(X86)'], 'Microsoft/Edge/Application/msedge.exe') : '',
178
+ deps.env.LOCALAPPDATA ? resolve(deps.env.LOCALAPPDATA, 'Google/Chrome/Application/chrome.exe') : '',
179
+ ]
180
+ : [
181
+ '/usr/bin/google-chrome-stable',
182
+ '/usr/bin/google-chrome',
183
+ '/usr/bin/microsoft-edge-stable',
184
+ '/usr/bin/chromium',
185
+ '/usr/bin/chromium-browser',
186
+ ];
187
+ return candidates.find((candidate) => candidate && deps.exists(candidate)) ?? null;
188
+ }
189
+ /** vendor 子目录名 = <arch>-<platform>(对齐 rgPath.ts / build-green.ts 的 vendor 约定)。 */
190
+ function subdir(deps) {
191
+ return `${deps.arch}-${deps.platform}`;
192
+ }
193
+ function binName(deps) {
194
+ return deps.platform === 'win32' ? 'agent-browser.exe' : 'agent-browser';
195
+ }
196
+ /** 各平台 chromium 可执行的候选相对路径(解包布局因构建而异,逐个探)。 */
197
+ function chromiumExeCandidates(platform) {
198
+ switch (platform) {
199
+ case 'win32':
200
+ return ['chrome.exe', 'chrome-headless-shell.exe'];
201
+ case 'darwin':
202
+ return [
203
+ 'Chromium.app/Contents/MacOS/Chromium',
204
+ 'Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',
205
+ 'chrome',
206
+ 'chrome-headless-shell',
207
+ ];
208
+ default: // linux
209
+ return ['chrome', 'chrome-headless-shell', 'chromium'];
210
+ }
211
+ }
212
+ /**
213
+ * Unix 上 git/tarball/cp 偶尔丢可执行位;Windows 不需要。失败忽略(后续 spawn 会响亮报错)。
214
+ */
215
+ function ensureExecutable(path, platform) {
216
+ if (platform === 'win32')
217
+ return;
218
+ try {
219
+ chmodSync(path, 0o755);
220
+ }
221
+ catch {
222
+ /* ignore */
223
+ }
224
+ }
225
+ /**
226
+ * 试 spawn `<cmd> --version`,退出码 0 即认为可用。
227
+ * 传命令名(非绝对路径)让 OS 解析 PATH;windowsHide 防弹窗;5s 兜底超时。
228
+ */
229
+ function defaultTrySpawn(cmd) {
230
+ return new Promise((resolveP) => {
231
+ let settled = false;
232
+ const done = (ok) => {
233
+ if (settled)
234
+ return;
235
+ settled = true;
236
+ resolveP(ok);
237
+ };
238
+ try {
239
+ const child = spawn(cmd, ['--version'], {
240
+ stdio: ['ignore', 'ignore', 'ignore'],
241
+ env: sanitizeCredentialEnv(),
242
+ windowsHide: true,
243
+ });
244
+ child.on('error', () => done(false));
245
+ child.on('close', (code) => done(code === 0));
246
+ setTimeout(() => {
247
+ try {
248
+ child.kill();
249
+ }
250
+ catch { }
251
+ done(false);
252
+ }, 5_000);
253
+ }
254
+ catch {
255
+ done(false);
256
+ }
257
+ });
258
+ }
@@ -1,55 +1,72 @@
1
1
  /**
2
2
  * ============================================================
3
- * src/tools/browser.ts —— Playwright Browser 单例
3
+ * src/tools/webbrowser/browser.ts —— agent-browser 子进程运行器
4
4
  * ------------------------------------------------------------
5
- * 整个 session 复用同一个 headless Chromium page。
6
- * 动态 import playwright-core,按需加载,避免未安装时拖累主程序。
5
+ * 内置 agent 浏览器底座的执行层:把命令发给 agent-browser CLI(原生 Rust + CDP,
6
+ * daemon 模型——多次调用复用同一后台浏览器进程)。取代原 playwright 单例。
7
+ *
8
+ * 二进制定位、Chrome 分层降级见 `agentBrowserPath.ts`。本文件只负责 spawn + 收集输出。
9
+ * 全局 flag(--session / --executable-path)必须排在 subcommand 之前(agent-browser 约定)。
7
10
  * ============================================================
8
11
  */
12
+ import { spawn } from 'node:child_process';
13
+ import { chmod, mkdir } from 'node:fs/promises';
9
14
  import os from 'node:os';
10
15
  import path from 'node:path';
11
- let browserInstance = null;
12
- let pageInstance = null;
16
+ import { sanitizeCredentialEnv } from '../../config/configService.js';
13
17
  /**
14
- * 获取或创建 browser page 单例。
15
- * 每次导航后手动更新 pageInstance.url()。
18
+ * minimal-agent 进程级固定 session 名:隔离机器上其它 agent-browser 用法,
19
+ * 同时让本进程多次调用复用同一浏览器(与原 playwright「一 session 一 page」语义一致)。
16
20
  */
17
- export async function getBrowserPage() {
18
- if (!browserInstance) {
19
- // 动态 import,按需加载
20
- const { chromium } = await import('playwright-core');
21
- try {
22
- browserInstance = await chromium.launch({
23
- headless: true,
24
- args: ['--no-sandbox', '--disable-setuid-sandbox'],
25
- });
26
- }
27
- catch (e) {
28
- const message = e instanceof Error ? e.message : String(e);
29
- throw new Error(`Failed to launch browser: ${message}\n` +
30
- `请先安装 playwright 及浏览器:\n` +
31
- ` npm install playwright-core\n` +
32
- ` npx playwright install chromium`);
33
- }
34
- }
35
- if (!pageInstance) {
36
- pageInstance = await browserInstance.newPage();
37
- await pageInstance.setViewportSize({ width: 1280, height: 720 });
38
- }
39
- return pageInstance;
21
+ export const BROWSER_SESSION = 'minimal-agent';
22
+ /** 全局 session flag(排在 subcommand 之前)。 */
23
+ export function sessionArgs(sessionName = BROWSER_SESSION) {
24
+ return ['--session', sessionName];
25
+ }
26
+ /** KakadeAI-owned persistent browser profile. It is never the user's system Chrome profile. */
27
+ export function browserProfilePath(profileId = 'default') {
28
+ const safe = /^[a-zA-Z0-9._-]+$/.test(profileId) ? profileId : 'default';
29
+ return path.join(process.env.MINIMAL_AGENT_HOME ?? os.homedir(), '.minimal-agent', 'browser', 'profiles', safe);
40
30
  }
41
- /** 关闭浏览器(session 结束时调用) */
42
- export async function closeBrowser() {
43
- if (pageInstance) {
44
- await pageInstance.close();
45
- pageInstance = null;
31
+ export async function ensureBrowserProfile(profileId = 'default') {
32
+ const profile = browserProfilePath(profileId);
33
+ await mkdir(profile, { recursive: true, mode: 0o700 });
34
+ if (process.platform !== 'win32')
35
+ await chmod(profile, 0o700).catch(() => undefined);
36
+ return profile;
37
+ }
38
+ export async function browserGlobalArgs(sessionName = BROWSER_SESSION, profileId = 'default') {
39
+ return [...sessionArgs(sessionName), '--profile', await ensureBrowserProfile(profileId)];
40
+ }
41
+ /** 默认 spawn 运行器:收集 stdout/stderr,close 时 resolve;spawn 抛错也 resolve 成失败。 */
42
+ export const spawnRunner = (bin, args, signal) => new Promise((resolveP) => {
43
+ let stdout = '';
44
+ let stderr = '';
45
+ let child;
46
+ try {
47
+ child = spawn(bin, args, {
48
+ stdio: ['ignore', 'pipe', 'pipe'],
49
+ env: sanitizeCredentialEnv(),
50
+ windowsHide: true,
51
+ signal,
52
+ });
46
53
  }
47
- if (browserInstance) {
48
- await browserInstance.close();
49
- browserInstance = null;
54
+ catch (e) {
55
+ resolveP({ code: -1, stdout: '', stderr: e instanceof Error ? e.message : String(e) });
56
+ return;
50
57
  }
51
- }
52
- /** 生成截图路径 */
53
- export function screenshotPath(prefix = 'browser') {
54
- return path.join(os.tmpdir(), `${prefix}-${Date.now()}.png`);
58
+ child.stdout?.setEncoding('utf8');
59
+ child.stdout?.on('data', (c) => (stdout += c));
60
+ child.stderr?.setEncoding('utf8');
61
+ child.stderr?.on('data', (c) => (stderr += c));
62
+ child.on('error', (e) => resolveP({
63
+ code: -1,
64
+ stdout,
65
+ stderr: stderr || (e instanceof Error ? e.message : String(e)),
66
+ }));
67
+ child.on('close', (code) => resolveP({ code: code ?? -1, stdout, stderr }));
68
+ });
69
+ /** 生成截图/PDF 的临时输出路径。 */
70
+ export function artifactPath(prefix = 'browser', ext = 'png') {
71
+ return path.join(os.tmpdir(), `${prefix}-${Date.now()}.${ext}`);
55
72
  }
@@ -0,0 +1,118 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ const CAPTCHA_PATTERNS = [
3
+ /captcha/i,
4
+ /验证码|安全验证|人机验证|完成.{0,8}验证|拖动.{0,12}(滑块|拼图)|滑动.{0,12}(滑块|拼图)/i,
5
+ /verify\s+(?:you are|that you are)\s+(?:a\s+)?human/i,
6
+ ];
7
+ const OTP_PATTERNS = [/一次性密码|短信验证码|邮件验证码|动态口令|\botp\b|one[- ]time (?:password|code)/i];
8
+ const LOGIN_PATTERNS = [/请先登录|登录后继续|扫码登录|sign in to continue|log in to continue/i];
9
+ const BLOCK_PATTERNS = [/操作存在异常|访问请求.{0,8}(暂停|拦截)|请求过于频繁|访问受限|access denied|unusual traffic|temporarily blocked/i];
10
+ const PERMISSION_PATTERNS = [/需要授权|请求权限|permission required|allow access/i];
11
+ /** Classifies only explicit page signals. It never attempts to solve or bypass a challenge. */
12
+ export function detectBrowserChallenge(text) {
13
+ const normalized = text.replace(/\s+/g, ' ').slice(0, 200_000);
14
+ if (OTP_PATTERNS.some((pattern) => pattern.test(normalized)))
15
+ return { reason: 'otp', summary: '页面需要人工输入一次性验证码' };
16
+ if (CAPTCHA_PATTERNS.some((pattern) => pattern.test(normalized)))
17
+ return { reason: 'captcha', summary: '页面需要人工完成安全验证' };
18
+ if (LOGIN_PATTERNS.some((pattern) => pattern.test(normalized)))
19
+ return { reason: 'login', summary: '页面需要人工登录' };
20
+ if (BLOCK_PATTERNS.some((pattern) => pattern.test(normalized)))
21
+ return { reason: 'blocked', summary: '网站已暂停或拦截当前访问' };
22
+ if (PERMISSION_PATTERNS.some((pattern) => pattern.test(normalized)))
23
+ return { reason: 'permission', summary: '页面需要人工确认权限' };
24
+ return null;
25
+ }
26
+ export class BrowserControlCoordinator {
27
+ state = { owner: 'none', status: 'idle' };
28
+ listeners = new Set();
29
+ waiter = null;
30
+ getState() { return this.state; }
31
+ subscribe(listener) {
32
+ this.listeners.add(listener);
33
+ listener(this.state);
34
+ return () => this.listeners.delete(listener);
35
+ }
36
+ beginAgent(input) {
37
+ if (this.state.status === 'waiting_human')
38
+ return;
39
+ this.setState({ owner: 'agent', status: 'running', ...input });
40
+ }
41
+ finishAgent(runId) {
42
+ if (this.state.status === 'running' && this.state.runId === runId)
43
+ this.setState({ owner: 'none', status: 'idle' });
44
+ }
45
+ requestHuman(reason, input) {
46
+ if (this.state.status === 'waiting_human')
47
+ return this.state.resumeToken;
48
+ const running = this.state.status === 'running' ? this.state : null;
49
+ const resumeToken = randomUUID();
50
+ this.setState({
51
+ owner: 'user',
52
+ status: 'waiting_human',
53
+ runId: input?.runId ?? running?.runId ?? randomUUID(),
54
+ conversationKey: input?.conversationKey ?? running?.conversationKey,
55
+ reason,
56
+ resumeToken,
57
+ });
58
+ return resumeToken;
59
+ }
60
+ takeInteractive(input) {
61
+ this.resolveWaiter('resumed');
62
+ this.setState({ owner: 'user', status: 'interactive', runId: input?.runId, conversationKey: input?.conversationKey });
63
+ }
64
+ resume(token) {
65
+ if (this.state.status !== 'waiting_human' || (token && token !== this.state.resumeToken))
66
+ return false;
67
+ const previous = this.state;
68
+ this.resolveWaiter('resumed');
69
+ this.setState({ owner: 'agent', status: 'running', runId: previous.runId, conversationKey: previous.conversationKey, action: '正在检查人工操作结果' });
70
+ return true;
71
+ }
72
+ stop(token) {
73
+ if (this.state.status === 'waiting_human' && token && token !== this.state.resumeToken)
74
+ return false;
75
+ this.resolveWaiter('stopped');
76
+ this.setState({ owner: 'user', status: 'interactive' });
77
+ return true;
78
+ }
79
+ waitForHuman(signal) {
80
+ if (this.state.status !== 'waiting_human')
81
+ return Promise.resolve('resumed');
82
+ if (this.waiter)
83
+ return new Promise((resolve) => {
84
+ const unsubscribe = this.subscribe((state) => {
85
+ if (state.status !== 'waiting_human') {
86
+ unsubscribe();
87
+ resolve(state.status === 'running' ? 'resumed' : 'stopped');
88
+ }
89
+ });
90
+ });
91
+ return new Promise((resolve, reject) => {
92
+ const token = this.state.status === 'waiting_human' ? this.state.resumeToken : '';
93
+ const onAbort = () => {
94
+ cleanup();
95
+ this.waiter = null;
96
+ reject(new DOMException('aborted', 'AbortError'));
97
+ };
98
+ const cleanup = () => signal?.removeEventListener('abort', onAbort);
99
+ this.waiter = { token, resolve, cleanup };
100
+ signal?.addEventListener('abort', onAbort, { once: true });
101
+ if (signal?.aborted)
102
+ onAbort();
103
+ });
104
+ }
105
+ resolveWaiter(result) {
106
+ const waiter = this.waiter;
107
+ if (!waiter)
108
+ return;
109
+ this.waiter = null;
110
+ waiter.cleanup();
111
+ waiter.resolve(result);
112
+ }
113
+ setState(state) {
114
+ this.state = state;
115
+ for (const listener of this.listeners)
116
+ listener(state);
117
+ }
118
+ }
@@ -0,0 +1,97 @@
1
+ import { resolveAgentBrowserPath, resolveBundledChromium, resolveSystemBrowser } from './agentBrowserPath.js';
2
+ import { browserGlobalArgs, sessionArgs, spawnRunner } from './browser.js';
3
+ const ports = new Map();
4
+ const starts = new Map();
5
+ function parsePort(text) {
6
+ const match = text.match(/ws:\/\/(?:127\.0\.0\.1|localhost):([0-9]+)/i);
7
+ if (!match)
8
+ return null;
9
+ const port = Number(match[1]);
10
+ return Number.isInteger(port) && port > 0 && port < 65_536 ? port : null;
11
+ }
12
+ function browserRuntimeMissing(text) {
13
+ return /browser\s+(?:not found|was not found)|chrom(?:e|ium).*(?:not found|missing)|executable.*(?:not found|does not exist)|run ['\"]?agent-browser install/i.test(text);
14
+ }
15
+ export { browserRuntimeMissing as _browserRuntimeMissing };
16
+ async function readStatus(bin, sessionName) {
17
+ // Profile/executable flags are launch-only. Repeating them against a running
18
+ // daemon makes agent-browser ignore the profile and emit misleading warnings.
19
+ const result = await spawnRunner(bin, [...sessionArgs(sessionName), 'stream', 'status']);
20
+ if (result.code !== 0)
21
+ return { port: null, connected: false };
22
+ const text = `${result.stdout}\n${result.stderr}`;
23
+ return { port: parsePort(text), connected: /Connected:\s*true/i.test(text) };
24
+ }
25
+ export async function ensureDaemon(sessionName = 'minimal-agent') {
26
+ const bin = await resolveAgentBrowserPath();
27
+ if (!bin)
28
+ throw new Error('未找到 agent-browser 可执行文件');
29
+ const status = await readStatus(bin, sessionName);
30
+ if (status.port && status.connected) {
31
+ ports.set(sessionName, status.port);
32
+ return { sessionName, streamPort: status.port, alreadyRunning: true };
33
+ }
34
+ const pending = starts.get(sessionName);
35
+ if (pending)
36
+ return pending;
37
+ const start = (async () => {
38
+ const globals = await browserGlobalArgs(sessionName);
39
+ // Prefer a stable system browser executable, but always use KakadeAI's
40
+ // isolated profile. Bundled Chromium remains an offline fallback.
41
+ const systemBrowser = resolveSystemBrowser();
42
+ let opened = await spawnRunner(bin, [
43
+ ...globals,
44
+ ...(systemBrowser ? ['--executable-path', systemBrowser] : []),
45
+ 'open',
46
+ 'about:blank',
47
+ ]);
48
+ if (opened.code !== 0 && browserRuntimeMissing(`${opened.stderr}\n${opened.stdout}`)) {
49
+ const chromium = resolveBundledChromium();
50
+ if (chromium) {
51
+ opened = await spawnRunner(bin, [...globals, '--executable-path', chromium, 'open', 'about:blank']);
52
+ }
53
+ else {
54
+ const installed = await spawnRunner(bin, ['install']);
55
+ if (installed.code === 0)
56
+ opened = await spawnRunner(bin, [...globals, 'open', 'about:blank']);
57
+ }
58
+ }
59
+ if (opened.code !== 0) {
60
+ throw new Error((opened.stderr || opened.stdout || '浏览器启动失败').trim());
61
+ }
62
+ const streamPort = await waitForStreamPort(sessionName, 10_000);
63
+ ports.set(sessionName, streamPort);
64
+ return { sessionName, streamPort, alreadyRunning: false };
65
+ })().finally(() => starts.delete(sessionName));
66
+ starts.set(sessionName, start);
67
+ return start;
68
+ }
69
+ export async function stopDaemon(sessionName = 'minimal-agent') {
70
+ const bin = await resolveAgentBrowserPath();
71
+ ports.delete(sessionName);
72
+ if (!bin)
73
+ return;
74
+ await spawnRunner(bin, [...sessionArgs(sessionName), 'close']);
75
+ }
76
+ export async function getStreamPort(sessionName = 'minimal-agent') {
77
+ const cached = ports.get(sessionName);
78
+ if (cached)
79
+ return cached;
80
+ const bin = await resolveAgentBrowserPath();
81
+ if (!bin)
82
+ return null;
83
+ const port = (await readStatus(bin, sessionName)).port;
84
+ if (port)
85
+ ports.set(sessionName, port);
86
+ return port;
87
+ }
88
+ export async function waitForStreamPort(sessionName = 'minimal-agent', timeoutMs = 10_000) {
89
+ const deadline = Date.now() + timeoutMs;
90
+ while (Date.now() < deadline) {
91
+ const port = await getStreamPort(sessionName);
92
+ if (port)
93
+ return port;
94
+ await new Promise((resolve) => setTimeout(resolve, 120));
95
+ }
96
+ throw new Error('浏览器已启动,但实时画面端口未就绪');
97
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Process-wide action coordinator for the supervised browser. Desktop input,
3
+ * Agent tools, TUI and workflows share the same daemon, so CDP mutations must
4
+ * be serialized instead of racing through separate command adapters.
5
+ */
6
+ export class BrowserService {
7
+ tails = new Map();
8
+ async run(input) {
9
+ const previous = this.tails.get(input.sessionName) ?? Promise.resolve();
10
+ let release;
11
+ const current = new Promise((resolve) => { release = resolve; });
12
+ const tail = previous.then(() => current);
13
+ this.tails.set(input.sessionName, tail);
14
+ await previous;
15
+ try {
16
+ if (input.signal?.aborted)
17
+ return { code: -1, stdout: '', stderr: 'Browser action cancelled' };
18
+ return await input.runner(input.bin, input.args, input.signal);
19
+ }
20
+ finally {
21
+ release();
22
+ if (this.tails.get(input.sessionName) === tail)
23
+ this.tails.delete(input.sessionName);
24
+ }
25
+ }
26
+ }
27
+ export const browserService = new BrowserService();