opendevbrowser 0.0.17 → 0.0.18

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 (529) hide show
  1. package/README.md +171 -73
  2. package/dist/annotate/agent-inbox-store.d.ts +58 -0
  3. package/dist/annotate/agent-inbox-store.d.ts.map +1 -0
  4. package/dist/annotate/agent-inbox.d.ts +25 -0
  5. package/dist/annotate/agent-inbox.d.ts.map +1 -0
  6. package/dist/annotate/direct-annotator.d.ts.map +1 -1
  7. package/dist/annotate/timeout-messages.d.ts +4 -0
  8. package/dist/annotate/timeout-messages.d.ts.map +1 -0
  9. package/dist/automation/coordinator.d.ts +55 -0
  10. package/dist/automation/coordinator.d.ts.map +1 -0
  11. package/dist/browser/annotation-manager.d.ts +4 -1
  12. package/dist/browser/annotation-manager.d.ts.map +1 -1
  13. package/dist/browser/browser-manager.d.ts +147 -47
  14. package/dist/browser/browser-manager.d.ts.map +1 -1
  15. package/dist/browser/canvas-client.d.ts +1 -0
  16. package/dist/browser/canvas-client.d.ts.map +1 -1
  17. package/dist/browser/canvas-code-sync-manager.d.ts +9 -1
  18. package/dist/browser/canvas-code-sync-manager.d.ts.map +1 -1
  19. package/dist/browser/canvas-manager.d.ts +29 -1
  20. package/dist/browser/canvas-manager.d.ts.map +1 -1
  21. package/dist/browser/global-challenge-coordinator.d.ts +27 -0
  22. package/dist/browser/global-challenge-coordinator.d.ts.map +1 -0
  23. package/dist/browser/manager-types.d.ts +167 -1
  24. package/dist/browser/manager-types.d.ts.map +1 -1
  25. package/dist/browser/ops-browser-manager.d.ts +103 -3
  26. package/dist/browser/ops-browser-manager.d.ts.map +1 -1
  27. package/dist/browser/ops-client.d.ts +17 -1
  28. package/dist/browser/ops-client.d.ts.map +1 -1
  29. package/dist/browser/playwright-runtime.d.ts +4 -0
  30. package/dist/browser/playwright-runtime.d.ts.map +1 -0
  31. package/dist/browser/review-surface.d.ts +9 -0
  32. package/dist/browser/review-surface.d.ts.map +1 -0
  33. package/dist/browser/screencast-recorder.d.ts +57 -0
  34. package/dist/browser/screencast-recorder.d.ts.map +1 -0
  35. package/dist/browser/session-inspector.d.ts +71 -0
  36. package/dist/browser/session-inspector.d.ts.map +1 -0
  37. package/dist/browser/session-store.d.ts +5 -1
  38. package/dist/browser/session-store.d.ts.map +1 -1
  39. package/dist/browser/system-chrome-cookies.d.ts +46 -0
  40. package/dist/browser/system-chrome-cookies.d.ts.map +1 -0
  41. package/dist/browser/target-manager.d.ts +1 -0
  42. package/dist/browser/target-manager.d.ts.map +1 -1
  43. package/dist/cache/chrome-locator.d.ts.map +1 -1
  44. package/dist/cache/chrome-user-data.d.ts +17 -0
  45. package/dist/cache/chrome-user-data.d.ts.map +1 -0
  46. package/dist/canvas/adapter-plugins/loader.d.ts +13 -0
  47. package/dist/canvas/adapter-plugins/loader.d.ts.map +1 -0
  48. package/dist/canvas/adapter-plugins/manifest.d.ts +146 -0
  49. package/dist/canvas/adapter-plugins/manifest.d.ts.map +1 -0
  50. package/dist/canvas/adapter-plugins/types.d.ts +83 -0
  51. package/dist/canvas/adapter-plugins/types.d.ts.map +1 -0
  52. package/dist/canvas/adapter-plugins/validator.d.ts +10 -0
  53. package/dist/canvas/adapter-plugins/validator.d.ts.map +1 -0
  54. package/dist/canvas/code-sync/apply-tsx.d.ts +3 -1
  55. package/dist/canvas/code-sync/apply-tsx.d.ts.map +1 -1
  56. package/dist/canvas/code-sync/import.d.ts +1 -0
  57. package/dist/canvas/code-sync/import.d.ts.map +1 -1
  58. package/dist/canvas/code-sync/manifest.d.ts +2 -1
  59. package/dist/canvas/code-sync/manifest.d.ts.map +1 -1
  60. package/dist/canvas/code-sync/tsx-adapter.d.ts.map +1 -1
  61. package/dist/canvas/code-sync/types.d.ts +102 -10
  62. package/dist/canvas/code-sync/types.d.ts.map +1 -1
  63. package/dist/canvas/document-store.d.ts +11 -1
  64. package/dist/canvas/document-store.d.ts.map +1 -1
  65. package/dist/canvas/export.d.ts.map +1 -1
  66. package/dist/canvas/framework-adapters/custom-elements-v1.d.ts +3 -0
  67. package/dist/canvas/framework-adapters/custom-elements-v1.d.ts.map +1 -0
  68. package/dist/canvas/framework-adapters/html-static-v1.d.ts +3 -0
  69. package/dist/canvas/framework-adapters/html-static-v1.d.ts.map +1 -0
  70. package/dist/canvas/framework-adapters/markup.d.ts +9 -0
  71. package/dist/canvas/framework-adapters/markup.d.ts.map +1 -0
  72. package/dist/canvas/framework-adapters/react-tsx-v2.d.ts +3 -0
  73. package/dist/canvas/framework-adapters/react-tsx-v2.d.ts.map +1 -0
  74. package/dist/canvas/framework-adapters/registry.d.ts +12 -0
  75. package/dist/canvas/framework-adapters/registry.d.ts.map +1 -0
  76. package/dist/canvas/framework-adapters/svelte-sfc-v1.d.ts +3 -0
  77. package/dist/canvas/framework-adapters/svelte-sfc-v1.d.ts.map +1 -0
  78. package/dist/canvas/framework-adapters/types.d.ts +57 -0
  79. package/dist/canvas/framework-adapters/types.d.ts.map +1 -0
  80. package/dist/canvas/framework-adapters/vue-sfc-v1.d.ts +3 -0
  81. package/dist/canvas/framework-adapters/vue-sfc-v1.d.ts.map +1 -0
  82. package/dist/canvas/kits/catalog.d.ts +5 -0
  83. package/dist/canvas/kits/catalog.d.ts.map +1 -0
  84. package/dist/canvas/library-adapters/react/index.d.ts +3 -0
  85. package/dist/canvas/library-adapters/react/index.d.ts.map +1 -0
  86. package/dist/canvas/library-adapters/registry.d.ts +11 -0
  87. package/dist/canvas/library-adapters/registry.d.ts.map +1 -0
  88. package/dist/canvas/library-adapters/types.d.ts +43 -0
  89. package/dist/canvas/library-adapters/types.d.ts.map +1 -0
  90. package/dist/canvas/repo-store.d.ts +2 -0
  91. package/dist/canvas/repo-store.d.ts.map +1 -1
  92. package/dist/canvas/starters/catalog.d.ts +34 -0
  93. package/dist/canvas/starters/catalog.d.ts.map +1 -0
  94. package/dist/canvas/token-references.d.ts +22 -0
  95. package/dist/canvas/token-references.d.ts.map +1 -0
  96. package/dist/canvas/types.d.ts +345 -6
  97. package/dist/canvas/types.d.ts.map +1 -1
  98. package/dist/challenges/action-loop.d.ts +13 -0
  99. package/dist/challenges/action-loop.d.ts.map +1 -0
  100. package/dist/challenges/capability-matrix.d.ts +3 -0
  101. package/dist/challenges/capability-matrix.d.ts.map +1 -0
  102. package/dist/challenges/evidence-bundle.d.ts +48 -0
  103. package/dist/challenges/evidence-bundle.d.ts.map +1 -0
  104. package/dist/challenges/governed-adapter-gateway.d.ts +4 -0
  105. package/dist/challenges/governed-adapter-gateway.d.ts.map +1 -0
  106. package/dist/challenges/human-yield-gate.d.ts +20 -0
  107. package/dist/challenges/human-yield-gate.d.ts.map +1 -0
  108. package/dist/challenges/index.d.ts +15 -0
  109. package/dist/challenges/index.d.ts.map +1 -0
  110. package/dist/challenges/interpreter.d.ts +3 -0
  111. package/dist/challenges/interpreter.d.ts.map +1 -0
  112. package/dist/challenges/optional-computer-use-bridge.d.ts +9 -0
  113. package/dist/challenges/optional-computer-use-bridge.d.ts.map +1 -0
  114. package/dist/challenges/orchestrator.d.ts +32 -0
  115. package/dist/challenges/orchestrator.d.ts.map +1 -0
  116. package/dist/challenges/outcome-recorder.d.ts +8 -0
  117. package/dist/challenges/outcome-recorder.d.ts.map +1 -0
  118. package/dist/challenges/owned-environment-lane.d.ts +3 -0
  119. package/dist/challenges/owned-environment-lane.d.ts.map +1 -0
  120. package/dist/challenges/policy-gate.d.ts +9 -0
  121. package/dist/challenges/policy-gate.d.ts.map +1 -0
  122. package/dist/challenges/sanctioned-identity-lane.d.ts +3 -0
  123. package/dist/challenges/sanctioned-identity-lane.d.ts.map +1 -0
  124. package/dist/challenges/service-adapter-lane.d.ts +3 -0
  125. package/dist/challenges/service-adapter-lane.d.ts.map +1 -0
  126. package/dist/challenges/strategy-selector.d.ts +10 -0
  127. package/dist/challenges/strategy-selector.d.ts.map +1 -0
  128. package/dist/challenges/types.d.ts +277 -0
  129. package/dist/challenges/types.d.ts.map +1 -0
  130. package/dist/challenges/verification-gate.d.ts +15 -0
  131. package/dist/challenges/verification-gate.d.ts.map +1 -0
  132. package/dist/chunk-5FZQJRBQ.js +15256 -0
  133. package/dist/chunk-5FZQJRBQ.js.map +1 -0
  134. package/dist/chunk-L57D35TB.js +33513 -0
  135. package/dist/chunk-L57D35TB.js.map +1 -0
  136. package/dist/chunk-YBQECXZX.js +409 -0
  137. package/dist/chunk-YBQECXZX.js.map +1 -0
  138. package/dist/cli/args.d.ts +4 -4
  139. package/dist/cli/args.d.ts.map +1 -1
  140. package/dist/cli/commands/artifacts.d.ts.map +1 -1
  141. package/dist/cli/commands/canvas.d.ts +7 -7
  142. package/dist/cli/commands/canvas.d.ts.map +1 -1
  143. package/dist/cli/commands/daemon.d.ts +7 -0
  144. package/dist/cli/commands/daemon.d.ts.map +1 -1
  145. package/dist/cli/commands/desktop/accessibility-snapshot.d.ts +3 -0
  146. package/dist/cli/commands/desktop/accessibility-snapshot.d.ts.map +1 -0
  147. package/dist/cli/commands/desktop/active-window.d.ts +3 -0
  148. package/dist/cli/commands/desktop/active-window.d.ts.map +1 -0
  149. package/dist/cli/commands/desktop/capture-desktop.d.ts +3 -0
  150. package/dist/cli/commands/desktop/capture-desktop.d.ts.map +1 -0
  151. package/dist/cli/commands/desktop/capture-window.d.ts +3 -0
  152. package/dist/cli/commands/desktop/capture-window.d.ts.map +1 -0
  153. package/dist/cli/commands/desktop/shared.d.ts +19 -0
  154. package/dist/cli/commands/desktop/shared.d.ts.map +1 -0
  155. package/dist/cli/commands/desktop/status.d.ts +3 -0
  156. package/dist/cli/commands/desktop/status.d.ts.map +1 -0
  157. package/dist/cli/commands/desktop/windows.d.ts +3 -0
  158. package/dist/cli/commands/desktop/windows.d.ts.map +1 -0
  159. package/dist/cli/commands/devtools/dialog.d.ts +19 -0
  160. package/dist/cli/commands/devtools/dialog.d.ts.map +1 -0
  161. package/dist/cli/commands/devtools/screencast-start.d.ts +20 -0
  162. package/dist/cli/commands/devtools/screencast-start.d.ts.map +1 -0
  163. package/dist/cli/commands/devtools/screencast-stop.d.ts +17 -0
  164. package/dist/cli/commands/devtools/screencast-stop.d.ts.map +1 -0
  165. package/dist/cli/commands/devtools/screenshot.d.ts +2 -0
  166. package/dist/cli/commands/devtools/screenshot.d.ts.map +1 -1
  167. package/dist/cli/commands/interact/click.d.ts.map +1 -1
  168. package/dist/cli/commands/interact/pointer-down.d.ts +7 -0
  169. package/dist/cli/commands/interact/pointer-down.d.ts.map +1 -0
  170. package/dist/cli/commands/interact/pointer-drag.d.ts +7 -0
  171. package/dist/cli/commands/interact/pointer-drag.d.ts.map +1 -0
  172. package/dist/cli/commands/interact/pointer-move.d.ts +7 -0
  173. package/dist/cli/commands/interact/pointer-move.d.ts.map +1 -0
  174. package/dist/cli/commands/interact/pointer-shared.d.ts +6 -0
  175. package/dist/cli/commands/interact/pointer-shared.d.ts.map +1 -0
  176. package/dist/cli/commands/interact/pointer-up.d.ts +7 -0
  177. package/dist/cli/commands/interact/pointer-up.d.ts.map +1 -0
  178. package/dist/cli/commands/interact/upload.d.ts +18 -0
  179. package/dist/cli/commands/interact/upload.d.ts.map +1 -0
  180. package/dist/cli/commands/macro-resolve.d.ts +2 -0
  181. package/dist/cli/commands/macro-resolve.d.ts.map +1 -1
  182. package/dist/cli/commands/native.d.ts +10 -7
  183. package/dist/cli/commands/native.d.ts.map +1 -1
  184. package/dist/cli/commands/nav/review.d.ts +7 -0
  185. package/dist/cli/commands/nav/review.d.ts.map +1 -0
  186. package/dist/cli/commands/nav/snapshot.d.ts.map +1 -1
  187. package/dist/cli/commands/pages/open.d.ts.map +1 -1
  188. package/dist/cli/commands/product-video.d.ts +2 -0
  189. package/dist/cli/commands/product-video.d.ts.map +1 -1
  190. package/dist/cli/commands/research.d.ts +3 -0
  191. package/dist/cli/commands/research.d.ts.map +1 -1
  192. package/dist/cli/commands/run.d.ts +14 -0
  193. package/dist/cli/commands/run.d.ts.map +1 -1
  194. package/dist/cli/commands/serve.d.ts +1 -26
  195. package/dist/cli/commands/serve.d.ts.map +1 -1
  196. package/dist/cli/commands/session/connect.d.ts.map +1 -1
  197. package/dist/cli/commands/session/disconnect.d.ts.map +1 -1
  198. package/dist/cli/commands/session/inspector.d.ts +21 -0
  199. package/dist/cli/commands/session/inspector.d.ts.map +1 -0
  200. package/dist/cli/commands/session/launch.d.ts.map +1 -1
  201. package/dist/cli/commands/shopping.d.ts +5 -0
  202. package/dist/cli/commands/shopping.d.ts.map +1 -1
  203. package/dist/cli/commands/status.d.ts +2 -14
  204. package/dist/cli/commands/status.d.ts.map +1 -1
  205. package/dist/cli/commands/targets/new.d.ts.map +1 -1
  206. package/dist/cli/daemon-autostart.d.ts +11 -0
  207. package/dist/cli/daemon-autostart.d.ts.map +1 -1
  208. package/dist/cli/daemon-client.d.ts +3 -0
  209. package/dist/cli/daemon-client.d.ts.map +1 -1
  210. package/dist/cli/daemon-commands.d.ts.map +1 -1
  211. package/dist/cli/daemon-state.d.ts +16 -0
  212. package/dist/cli/daemon-state.d.ts.map +1 -1
  213. package/dist/cli/daemon-status.d.ts +7 -2
  214. package/dist/cli/daemon-status.d.ts.map +1 -1
  215. package/dist/cli/daemon.d.ts +1 -0
  216. package/dist/cli/daemon.d.ts.map +1 -1
  217. package/dist/cli/help.d.ts +15 -4
  218. package/dist/cli/help.d.ts.map +1 -1
  219. package/dist/cli/index.js +2467 -1033
  220. package/dist/cli/index.js.map +1 -1
  221. package/dist/cli/install-autostart-output.d.ts +6 -0
  222. package/dist/cli/install-autostart-output.d.ts.map +1 -0
  223. package/dist/cli/install-autostart-reconciliation.d.ts +23 -0
  224. package/dist/cli/install-autostart-reconciliation.d.ts.map +1 -0
  225. package/dist/cli/installers/skills.d.ts +42 -6
  226. package/dist/cli/installers/skills.d.ts.map +1 -1
  227. package/dist/cli/output.d.ts +3 -0
  228. package/dist/cli/output.d.ts.map +1 -1
  229. package/dist/cli/remote-desktop-runtime.d.ts +15 -0
  230. package/dist/cli/remote-desktop-runtime.d.ts.map +1 -0
  231. package/dist/cli/remote-manager.d.ts +24 -2
  232. package/dist/cli/remote-manager.d.ts.map +1 -1
  233. package/dist/cli/transport-timeouts.d.ts +8 -0
  234. package/dist/cli/transport-timeouts.d.ts.map +1 -0
  235. package/dist/cli/utils/http.d.ts +9 -0
  236. package/dist/cli/utils/http.d.ts.map +1 -1
  237. package/dist/cli/utils/parse.d.ts +2 -0
  238. package/dist/cli/utils/parse.d.ts.map +1 -1
  239. package/dist/cli/utils/skills.d.ts +1 -2
  240. package/dist/cli/utils/skills.d.ts.map +1 -1
  241. package/dist/cli/utils/workflow-message.d.ts +2 -0
  242. package/dist/cli/utils/workflow-message.d.ts.map +1 -0
  243. package/dist/config.d.ts +47 -0
  244. package/dist/config.d.ts.map +1 -1
  245. package/dist/core/bootstrap.d.ts.map +1 -1
  246. package/dist/core/index.d.ts +1 -0
  247. package/dist/core/index.d.ts.map +1 -1
  248. package/dist/core/logging.d.ts +3 -1
  249. package/dist/core/logging.d.ts.map +1 -1
  250. package/dist/core/runtime-assemblies.d.ts +22 -0
  251. package/dist/core/runtime-assemblies.d.ts.map +1 -0
  252. package/dist/core/types.d.ts +15 -0
  253. package/dist/core/types.d.ts.map +1 -1
  254. package/dist/desktop/audit.d.ts +37 -0
  255. package/dist/desktop/audit.d.ts.map +1 -0
  256. package/dist/desktop/errors.d.ts +7 -0
  257. package/dist/desktop/errors.d.ts.map +1 -0
  258. package/dist/desktop/index.d.ts +6 -0
  259. package/dist/desktop/index.d.ts.map +1 -0
  260. package/dist/desktop/runtime.d.ts +26 -0
  261. package/dist/desktop/runtime.d.ts.map +1 -0
  262. package/dist/desktop/types.d.ts +76 -0
  263. package/dist/desktop/types.d.ts.map +1 -0
  264. package/dist/extension-extractor.d.ts +6 -0
  265. package/dist/extension-extractor.d.ts.map +1 -1
  266. package/dist/index.d.ts.map +1 -1
  267. package/dist/index.js +1103 -467
  268. package/dist/index.js.map +1 -1
  269. package/dist/integrations/figma/assets.d.ts +13 -0
  270. package/dist/integrations/figma/assets.d.ts.map +1 -0
  271. package/dist/integrations/figma/auth.d.ts +3 -0
  272. package/dist/integrations/figma/auth.d.ts.map +1 -0
  273. package/dist/integrations/figma/client.d.ts +42 -0
  274. package/dist/integrations/figma/client.d.ts.map +1 -0
  275. package/dist/integrations/figma/mappers.d.ts +23 -0
  276. package/dist/integrations/figma/mappers.d.ts.map +1 -0
  277. package/dist/integrations/figma/normalize.d.ts +99 -0
  278. package/dist/integrations/figma/normalize.d.ts.map +1 -0
  279. package/dist/integrations/figma/url.d.ts +17 -0
  280. package/dist/integrations/figma/url.d.ts.map +1 -0
  281. package/dist/integrations/figma/variables.d.ts +21 -0
  282. package/dist/integrations/figma/variables.d.ts.map +1 -0
  283. package/dist/macros/execute-runtime.d.ts +19 -0
  284. package/dist/macros/execute-runtime.d.ts.map +1 -0
  285. package/dist/macros/execute.d.ts +3 -1
  286. package/dist/macros/execute.d.ts.map +1 -1
  287. package/dist/opendevbrowser.d.ts.map +1 -1
  288. package/dist/opendevbrowser.js +1103 -467
  289. package/dist/opendevbrowser.js.map +1 -1
  290. package/dist/providers/blocker.d.ts.map +1 -1
  291. package/dist/providers/browser-fallback.d.ts +30 -0
  292. package/dist/providers/browser-fallback.d.ts.map +1 -0
  293. package/dist/providers/constraint.d.ts +45 -0
  294. package/dist/providers/constraint.d.ts.map +1 -0
  295. package/dist/providers/index.d.ts +11 -2
  296. package/dist/providers/index.d.ts.map +1 -1
  297. package/dist/providers/policy.d.ts.map +1 -1
  298. package/dist/providers/product-video-compiler.d.ts +92 -0
  299. package/dist/providers/product-video-compiler.d.ts.map +1 -0
  300. package/dist/providers/registry.d.ts +37 -1
  301. package/dist/providers/registry.d.ts.map +1 -1
  302. package/dist/providers/renderer.d.ts.map +1 -1
  303. package/dist/providers/research-compiler.d.ts +64 -0
  304. package/dist/providers/research-compiler.d.ts.map +1 -0
  305. package/dist/providers/research-executor.d.ts +27 -0
  306. package/dist/providers/research-executor.d.ts.map +1 -0
  307. package/dist/providers/runtime-bundle.d.ts +26 -0
  308. package/dist/providers/runtime-bundle.d.ts.map +1 -0
  309. package/dist/providers/runtime-factory.d.ts +6 -1
  310. package/dist/providers/runtime-factory.d.ts.map +1 -1
  311. package/dist/providers/runtime-policy.d.ts +24 -0
  312. package/dist/providers/runtime-policy.d.ts.map +1 -0
  313. package/dist/providers/shared/anti-bot-policy.d.ts +3 -2
  314. package/dist/providers/shared/anti-bot-policy.d.ts.map +1 -1
  315. package/dist/providers/shopping/index.d.ts +11 -1
  316. package/dist/providers/shopping/index.d.ts.map +1 -1
  317. package/dist/providers/shopping-compiler.d.ts +51 -0
  318. package/dist/providers/shopping-compiler.d.ts.map +1 -0
  319. package/dist/providers/shopping-executor.d.ts +18 -0
  320. package/dist/providers/shopping-executor.d.ts.map +1 -0
  321. package/dist/providers/shopping-postprocess.d.ts +46 -0
  322. package/dist/providers/shopping-postprocess.d.ts.map +1 -0
  323. package/dist/providers/shopping-workflow.d.ts +33 -0
  324. package/dist/providers/shopping-workflow.d.ts.map +1 -0
  325. package/dist/providers/social/platform.d.ts +2 -1
  326. package/dist/providers/social/platform.d.ts.map +1 -1
  327. package/dist/providers/social/search-quality.d.ts +16 -0
  328. package/dist/providers/social/search-quality.d.ts.map +1 -0
  329. package/dist/providers/social/youtube-resolver.d.ts +2 -1
  330. package/dist/providers/social/youtube-resolver.d.ts.map +1 -1
  331. package/dist/providers/social/youtube.d.ts.map +1 -1
  332. package/dist/providers/types.d.ts +116 -4
  333. package/dist/providers/types.d.ts.map +1 -1
  334. package/dist/providers/web/crawl-worker.d.ts.map +1 -1
  335. package/dist/providers/web/extract.d.ts +16 -0
  336. package/dist/providers/web/extract.d.ts.map +1 -1
  337. package/dist/providers/web/index.d.ts.map +1 -1
  338. package/dist/providers/workflow-contracts.d.ts +53 -0
  339. package/dist/providers/workflow-contracts.d.ts.map +1 -0
  340. package/dist/providers/workflows.d.ts +30 -6
  341. package/dist/providers/workflows.d.ts.map +1 -1
  342. package/dist/{providers-G3LRHQXX.js → providers-G36AM3Z2.js} +2 -2
  343. package/dist/public-surface/generated-manifest.d.ts +1168 -0
  344. package/dist/public-surface/generated-manifest.d.ts.map +1 -0
  345. package/dist/public-surface/source.d.ts +437 -0
  346. package/dist/public-surface/source.d.ts.map +1 -0
  347. package/dist/relay/protocol.d.ts +25 -3
  348. package/dist/relay/protocol.d.ts.map +1 -1
  349. package/dist/relay/relay-endpoints.d.ts +21 -0
  350. package/dist/relay/relay-endpoints.d.ts.map +1 -1
  351. package/dist/relay/relay-server.d.ts +18 -0
  352. package/dist/relay/relay-server.d.ts.map +1 -1
  353. package/dist/skills/bundled-skill-directories.d.ts +8 -0
  354. package/dist/skills/bundled-skill-directories.d.ts.map +1 -0
  355. package/dist/skills/skill-loader.d.ts +9 -1
  356. package/dist/skills/skill-loader.d.ts.map +1 -1
  357. package/dist/skills/skill-loader.js +7 -0
  358. package/dist/skills/skill-nudge.d.ts.map +1 -1
  359. package/dist/skills/types.d.ts +31 -0
  360. package/dist/skills/types.d.ts.map +1 -1
  361. package/dist/snapshot/ops-snapshot.d.ts +1 -1
  362. package/dist/snapshot/ops-snapshot.d.ts.map +1 -1
  363. package/dist/snapshot/refs.d.ts +6 -1
  364. package/dist/snapshot/refs.d.ts.map +1 -1
  365. package/dist/snapshot/snapshotter.d.ts.map +1 -1
  366. package/dist/tools/connect.d.ts.map +1 -1
  367. package/dist/tools/deps.d.ts +4 -0
  368. package/dist/tools/deps.d.ts.map +1 -1
  369. package/dist/tools/desktop-shared.d.ts +6 -0
  370. package/dist/tools/desktop-shared.d.ts.map +1 -0
  371. package/dist/tools/desktop_accessibility_snapshot.d.ts +4 -0
  372. package/dist/tools/desktop_accessibility_snapshot.d.ts.map +1 -0
  373. package/dist/tools/desktop_active_window.d.ts +4 -0
  374. package/dist/tools/desktop_active_window.d.ts.map +1 -0
  375. package/dist/tools/desktop_capture_desktop.d.ts +4 -0
  376. package/dist/tools/desktop_capture_desktop.d.ts.map +1 -0
  377. package/dist/tools/desktop_capture_window.d.ts +4 -0
  378. package/dist/tools/desktop_capture_window.d.ts.map +1 -0
  379. package/dist/tools/desktop_status.d.ts +4 -0
  380. package/dist/tools/desktop_status.d.ts.map +1 -0
  381. package/dist/tools/desktop_windows.d.ts +4 -0
  382. package/dist/tools/desktop_windows.d.ts.map +1 -0
  383. package/dist/tools/dialog.d.ts +4 -0
  384. package/dist/tools/dialog.d.ts.map +1 -0
  385. package/dist/tools/index.d.ts +3 -0
  386. package/dist/tools/index.d.ts.map +1 -1
  387. package/dist/tools/launch.d.ts.map +1 -1
  388. package/dist/tools/macro_resolve.d.ts.map +1 -1
  389. package/dist/tools/pointer_down.d.ts +4 -0
  390. package/dist/tools/pointer_down.d.ts.map +1 -0
  391. package/dist/tools/pointer_drag.d.ts +4 -0
  392. package/dist/tools/pointer_drag.d.ts.map +1 -0
  393. package/dist/tools/pointer_move.d.ts +4 -0
  394. package/dist/tools/pointer_move.d.ts.map +1 -0
  395. package/dist/tools/pointer_up.d.ts +4 -0
  396. package/dist/tools/pointer_up.d.ts.map +1 -0
  397. package/dist/tools/product_video_run.d.ts.map +1 -1
  398. package/dist/tools/prompting_guide.d.ts.map +1 -1
  399. package/dist/tools/research_run.d.ts.map +1 -1
  400. package/dist/tools/review.d.ts +4 -0
  401. package/dist/tools/review.d.ts.map +1 -0
  402. package/dist/tools/screencast_start.d.ts +4 -0
  403. package/dist/tools/screencast_start.d.ts.map +1 -0
  404. package/dist/tools/screencast_stop.d.ts +4 -0
  405. package/dist/tools/screencast_stop.d.ts.map +1 -0
  406. package/dist/tools/screenshot.d.ts.map +1 -1
  407. package/dist/tools/session_inspector.d.ts +4 -0
  408. package/dist/tools/session_inspector.d.ts.map +1 -0
  409. package/dist/tools/shopping_run.d.ts.map +1 -1
  410. package/dist/tools/skill_list.d.ts.map +1 -1
  411. package/dist/tools/skill_load.d.ts.map +1 -1
  412. package/dist/tools/upload.d.ts +4 -0
  413. package/dist/tools/upload.d.ts.map +1 -0
  414. package/dist/tools/workflow-runtime.d.ts +4 -1
  415. package/dist/tools/workflow-runtime.d.ts.map +1 -1
  416. package/dist/utils/package-assets.d.ts +4 -0
  417. package/dist/utils/package-assets.d.ts.map +1 -0
  418. package/extension/canvas.html +379 -9
  419. package/extension/dist/annotate-content.js +62 -32
  420. package/extension/dist/annotation-payload.js +57 -21
  421. package/extension/dist/background.js +406 -61
  422. package/extension/dist/canvas/canvas-runtime.js +481 -52
  423. package/extension/dist/canvas/model.js +129 -1
  424. package/extension/dist/canvas-page.js +1882 -74
  425. package/extension/dist/ops/dom-bridge.js +139 -0
  426. package/extension/dist/ops/ops-runtime.js +2854 -295
  427. package/extension/dist/ops/ops-session-store.js +83 -5
  428. package/extension/dist/ops/snapshot-builder.js +2 -2
  429. package/extension/dist/ops/snapshot-shared.js +2 -2
  430. package/extension/dist/ops/target-session-coordinator.js +5 -3
  431. package/extension/dist/popup.js +50 -15
  432. package/extension/dist/services/CDPRouter.js +1567 -63
  433. package/extension/dist/services/ConnectionManager.js +436 -78
  434. package/extension/dist/services/RelayClient.js +70 -30
  435. package/extension/dist/services/TabManager.js +83 -10
  436. package/extension/dist/services/TargetSessionMap.js +127 -3
  437. package/extension/dist/services/attach-errors.js +20 -0
  438. package/extension/dist/services/cdp-router-commands.js +135 -8
  439. package/extension/dist/services/url-restrictions.js +9 -13
  440. package/extension/manifest.json +2 -2
  441. package/extension/popup.html +7 -6
  442. package/package.json +15 -7
  443. package/skills/AGENTS.md +9 -8
  444. package/skills/opendevbrowser-best-practices/SKILL.md +118 -9
  445. package/skills/opendevbrowser-best-practices/artifacts/browser-agent-known-issues-matrix.md +1 -0
  446. package/skills/opendevbrowser-best-practices/artifacts/command-channel-reference.md +26 -12
  447. package/skills/opendevbrowser-best-practices/artifacts/parity-gates.md +9 -2
  448. package/skills/opendevbrowser-best-practices/artifacts/provider-workflows.md +6 -0
  449. package/skills/opendevbrowser-best-practices/artifacts/skill-runtime-surface-matrix.md +58 -0
  450. package/skills/opendevbrowser-best-practices/assets/templates/skill-runtime-pack-matrix.json +674 -0
  451. package/skills/opendevbrowser-best-practices/assets/templates/surface-audit-checklist.json +9 -4
  452. package/skills/opendevbrowser-best-practices/scripts/odb-workflow.sh +89 -20
  453. package/skills/opendevbrowser-best-practices/scripts/resolve-odb-cli.sh +100 -0
  454. package/skills/opendevbrowser-best-practices/scripts/run-robustness-audit.sh +1 -0
  455. package/skills/opendevbrowser-best-practices/scripts/validate-skill-assets.sh +256 -116
  456. package/skills/opendevbrowser-best-practices/scripts/validator-fixture-cli.sh +208 -0
  457. package/skills/opendevbrowser-continuity-ledger/SKILL.md +14 -1
  458. package/skills/opendevbrowser-continuity-ledger/scripts/validate-skill-assets.sh +61 -0
  459. package/skills/opendevbrowser-data-extraction/SKILL.md +6 -0
  460. package/skills/opendevbrowser-data-extraction/scripts/validate-skill-assets.sh +112 -0
  461. package/skills/opendevbrowser-design-agent/SKILL.md +275 -0
  462. package/skills/opendevbrowser-design-agent/artifacts/app-shell-and-state-wiring.md +84 -0
  463. package/skills/opendevbrowser-design-agent/artifacts/async-search-state-ownership.md +58 -0
  464. package/skills/opendevbrowser-design-agent/artifacts/component-pattern-index.md +130 -0
  465. package/skills/opendevbrowser-design-agent/artifacts/design-contract-playbook.md +157 -0
  466. package/skills/opendevbrowser-design-agent/artifacts/design-release-gate.md +40 -0
  467. package/skills/opendevbrowser-design-agent/artifacts/design-workflows.md +153 -0
  468. package/skills/opendevbrowser-design-agent/artifacts/existing-surface-adaptation.md +56 -0
  469. package/skills/opendevbrowser-design-agent/artifacts/external-pattern-synthesis.md +103 -0
  470. package/skills/opendevbrowser-design-agent/artifacts/frontend-evaluation-rubric.md +61 -0
  471. package/skills/opendevbrowser-design-agent/artifacts/implementation-anti-patterns.md +163 -0
  472. package/skills/opendevbrowser-design-agent/artifacts/isolated-preview-validation.md +68 -0
  473. package/skills/opendevbrowser-design-agent/artifacts/loading-and-feedback-surfaces.md +56 -0
  474. package/skills/opendevbrowser-design-agent/artifacts/opendevbrowser-ui-example-map.md +44 -0
  475. package/skills/opendevbrowser-design-agent/artifacts/performance-audit-playbook.md +70 -0
  476. package/skills/opendevbrowser-design-agent/artifacts/research-harvest-workflow.md +81 -0
  477. package/skills/opendevbrowser-design-agent/artifacts/scroll-reveal-surface-planning.md +64 -0
  478. package/skills/opendevbrowser-design-agent/artifacts/state-ownership-matrix.md +36 -0
  479. package/skills/opendevbrowser-design-agent/artifacts/theming-and-token-ownership.md +43 -0
  480. package/skills/opendevbrowser-design-agent/assets/templates/canvas-generation-plan.design.v1.json +58 -0
  481. package/skills/opendevbrowser-design-agent/assets/templates/design-audit-report.v1.md +34 -0
  482. package/skills/opendevbrowser-design-agent/assets/templates/design-brief.v1.md +40 -0
  483. package/skills/opendevbrowser-design-agent/assets/templates/design-contract.v1.json +226 -0
  484. package/skills/opendevbrowser-design-agent/assets/templates/design-release-gate.v1.json +35 -0
  485. package/skills/opendevbrowser-design-agent/assets/templates/design-review-checklist.json +57 -0
  486. package/skills/opendevbrowser-design-agent/assets/templates/real-surface-design-matrix.json +32 -0
  487. package/skills/opendevbrowser-design-agent/assets/templates/reference-pattern-board.v1.json +31 -0
  488. package/skills/opendevbrowser-design-agent/scripts/design-workflow.sh +171 -0
  489. package/skills/opendevbrowser-design-agent/scripts/extract-canvas-plan.sh +56 -0
  490. package/skills/opendevbrowser-design-agent/scripts/validate-skill-assets.sh +223 -0
  491. package/skills/opendevbrowser-form-testing/SKILL.md +19 -3
  492. package/skills/opendevbrowser-form-testing/artifacts/form-workflows.md +5 -4
  493. package/skills/opendevbrowser-form-testing/assets/templates/challenge-decision-tree.json +2 -0
  494. package/skills/opendevbrowser-form-testing/scripts/validate-skill-assets.sh +109 -0
  495. package/skills/opendevbrowser-login-automation/SKILL.md +21 -3
  496. package/skills/opendevbrowser-login-automation/artifacts/login-workflows.md +5 -4
  497. package/skills/opendevbrowser-login-automation/assets/templates/auth-signals.json +5 -0
  498. package/skills/opendevbrowser-login-automation/assets/templates/login-scenario-matrix.json +3 -2
  499. package/skills/opendevbrowser-login-automation/scripts/run-login-workflow.sh +17 -1
  500. package/skills/opendevbrowser-login-automation/scripts/validate-skill-assets.sh +133 -0
  501. package/skills/opendevbrowser-product-presentation-asset/SKILL.md +23 -11
  502. package/skills/opendevbrowser-product-presentation-asset/artifacts/asset-pack-assembly.md +5 -3
  503. package/skills/opendevbrowser-product-presentation-asset/assets/templates/shot-list.md +2 -0
  504. package/skills/opendevbrowser-product-presentation-asset/assets/templates/video-assembly.md +3 -2
  505. package/skills/opendevbrowser-product-presentation-asset/scripts/capture-screenshots.sh +5 -1
  506. package/skills/opendevbrowser-product-presentation-asset/scripts/collect-product.sh +6 -2
  507. package/skills/opendevbrowser-product-presentation-asset/scripts/download-images.sh +5 -1
  508. package/skills/opendevbrowser-product-presentation-asset/scripts/render-video-brief.sh +20 -7
  509. package/skills/opendevbrowser-product-presentation-asset/scripts/validate-skill-assets.sh +39 -0
  510. package/skills/opendevbrowser-product-presentation-asset/scripts/write-manifest.sh +5 -1
  511. package/skills/opendevbrowser-research/SKILL.md +14 -6
  512. package/skills/opendevbrowser-research/scripts/render-output.sh +5 -1
  513. package/skills/opendevbrowser-research/scripts/run-research.sh +5 -1
  514. package/skills/opendevbrowser-research/scripts/validate-skill-assets.sh +45 -0
  515. package/skills/opendevbrowser-research/scripts/write-artifacts.sh +5 -1
  516. package/skills/opendevbrowser-shopping/SKILL.md +20 -1
  517. package/skills/opendevbrowser-shopping/scripts/normalize-offers.sh +6 -2
  518. package/skills/opendevbrowser-shopping/scripts/run-deal-hunt.sh +5 -1
  519. package/skills/opendevbrowser-shopping/scripts/run-shopping.sh +5 -1
  520. package/skills/opendevbrowser-shopping/scripts/validate-skill-assets.sh +54 -0
  521. package/dist/chunk-5J3IFL3X.js +0 -16706
  522. package/dist/chunk-5J3IFL3X.js.map +0 -1
  523. package/dist/chunk-D633UO34.js +0 -8149
  524. package/dist/chunk-D633UO34.js.map +0 -1
  525. package/dist/chunk-V7KUDHDG.js +0 -276
  526. package/dist/chunk-V7KUDHDG.js.map +0 -1
  527. package/dist/runtime-factory-BICHDPE7.js +0 -13
  528. /package/dist/{providers-G3LRHQXX.js.map → providers-G36AM3Z2.js.map} +0 -0
  529. /package/dist/{runtime-factory-BICHDPE7.js.map → skills/skill-loader.js.map} +0 -0
package/dist/cli/index.js CHANGED
@@ -1,9 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ CLI_COMMANDS,
4
+ CLI_COMMAND_HELP_DETAILS,
3
5
  DaemonClient,
4
6
  EXIT_DISCONNECTED,
5
7
  EXIT_EXECUTION,
6
8
  EXIT_USAGE,
9
+ PUBLIC_CLI_COMMAND_GROUPS,
10
+ TOOL_SURFACE_ENTRIES,
11
+ VALID_EQUALS_FLAGS,
12
+ VALID_FLAGS,
7
13
  buildAnnotateResult,
8
14
  callDaemon,
9
15
  createOpenDevBrowserCore,
@@ -14,82 +20,35 @@ import {
14
20
  fetchWithTimeout,
15
21
  formatErrorPayload,
16
22
  generateSecureToken,
23
+ getChromeUserDataRoots,
17
24
  getExtensionPath,
25
+ getProfileDirs,
18
26
  loadGlobalConfig,
27
+ onboarding_metadata_default,
19
28
  readDaemonMetadata,
29
+ readProfilePreferences,
20
30
  resolveExitCode,
21
31
  startDaemon,
22
32
  toCliError
23
- } from "../chunk-5J3IFL3X.js";
33
+ } from "../chunk-L57D35TB.js";
34
+ import {
35
+ getBundledSkillsDir,
36
+ listBundledSkillDirectories
37
+ } from "../chunk-YBQECXZX.js";
24
38
  import "../chunk-Y2KL55OG.js";
25
39
  import {
26
40
  writeFileAtomic
27
41
  } from "../chunk-TBUCZX4A.js";
28
- import "../chunk-V7KUDHDG.js";
29
42
  import {
30
- cleanupExpiredArtifacts
31
- } from "../chunk-D633UO34.js";
43
+ cleanupExpiredArtifacts,
44
+ isChallengeAutomationMode,
45
+ setDefaultLogSink,
46
+ stderrSink,
47
+ summarizePrimaryProviderIssue
48
+ } from "../chunk-5FZQJRBQ.js";
32
49
  import "../chunk-FUSXMW3G.js";
33
50
 
34
51
  // src/cli/args.ts
35
- var CLI_COMMANDS = [
36
- "install",
37
- "update",
38
- "uninstall",
39
- "help",
40
- "version",
41
- "serve",
42
- "daemon",
43
- "native",
44
- "run",
45
- "launch",
46
- "connect",
47
- "disconnect",
48
- "status",
49
- "research",
50
- "shopping",
51
- "product-video",
52
- "artifacts",
53
- "goto",
54
- "wait",
55
- "snapshot",
56
- "click",
57
- "hover",
58
- "press",
59
- "check",
60
- "uncheck",
61
- "type",
62
- "select",
63
- "scroll",
64
- "scroll-into-view",
65
- "targets-list",
66
- "target-use",
67
- "target-new",
68
- "target-close",
69
- "page",
70
- "pages",
71
- "page-close",
72
- "dom-html",
73
- "dom-text",
74
- "dom-attr",
75
- "dom-value",
76
- "dom-visible",
77
- "dom-enabled",
78
- "dom-checked",
79
- "clone-page",
80
- "clone-component",
81
- "perf",
82
- "screenshot",
83
- "console-poll",
84
- "network-poll",
85
- "debug-trace-snapshot",
86
- "cookie-import",
87
- "cookie-list",
88
- "macro-resolve",
89
- "annotate",
90
- "canvas",
91
- "rpc"
92
- ];
93
52
  var CLI_COMMAND_SET = new Set(CLI_COMMANDS);
94
53
  var SHORT_FLAGS = {
95
54
  "-g": "--global",
@@ -153,158 +112,7 @@ function parseTransport(args) {
153
112
  }
154
113
  throw createUsageError(`Invalid --transport: ${value ?? "missing"}`);
155
114
  }
156
- var VALID_FLAGS = [
157
- "--global",
158
- "--local",
159
- "--update",
160
- "--uninstall",
161
- "--help",
162
- "--version",
163
- "--with-config",
164
- "--no-prompt",
165
- "--no-interactive",
166
- "--quiet",
167
- "--output-format",
168
- "--full",
169
- "--port",
170
- "--token",
171
- "--stop",
172
- "--script",
173
- "--headless",
174
- "--profile",
175
- "--persist-profile",
176
- "--chrome-path",
177
- "--start-url",
178
- "--flag",
179
- "--session-id",
180
- "--close-browser",
181
- "--ws-endpoint",
182
- "--host",
183
- "--cdp-port",
184
- "--url",
185
- "--wait-until",
186
- "--timeout-ms",
187
- "--ref",
188
- "--state",
189
- "--until",
190
- "--mode",
191
- "--max-chars",
192
- "--cursor",
193
- "--text",
194
- "--clear",
195
- "--submit",
196
- "--values",
197
- "--dy",
198
- "--key",
199
- "--attr",
200
- "--name",
201
- "--target-id",
202
- "--tab-id",
203
- "--include-urls",
204
- "--path",
205
- "--since-seq",
206
- "--max",
207
- "--since-console-seq",
208
- "--since-network-seq",
209
- "--since-exception-seq",
210
- "--request-id",
211
- "--cookies",
212
- "--cookies-file",
213
- "--strict",
214
- "--expression",
215
- "--default-provider",
216
- "--include-catalog",
217
- "--command",
218
- "--execute",
219
- "--params",
220
- "--params-file",
221
- "--unsafe-internal",
222
- "--daemon",
223
- "--transport",
224
- "--no-extension",
225
- "--extension-only",
226
- "--extension-legacy",
227
- "--wait-for-extension",
228
- "--wait-timeout-ms",
229
- "--skills-global",
230
- "--skills-local",
231
- "--no-skills",
232
- "--screenshot-mode",
233
- "--debug",
234
- "--context",
235
- "--stored",
236
- "--topic",
237
- "--days",
238
- "--from",
239
- "--to",
240
- "--source-selection",
241
- "--sources",
242
- "--include-engagement",
243
- "--limit-per-source",
244
- "--query",
245
- "--providers",
246
- "--budget",
247
- "--region",
248
- "--sort",
249
- "--product-url",
250
- "--product-name",
251
- "--provider-hint",
252
- "--include-screenshots",
253
- "--include-all-images",
254
- "--include-copy",
255
- "--output-dir",
256
- "--ttl-hours",
257
- "--expired-only"
258
- ];
259
115
  var VALID_FLAG_SET = new Set(VALID_FLAGS);
260
- var VALID_EQUALS_FLAGS = [
261
- "--output-format",
262
- "--transport",
263
- "--session-id",
264
- "--url",
265
- "--screenshot-mode",
266
- "--context",
267
- "--timeout-ms",
268
- "--since-seq",
269
- "--since-console-seq",
270
- "--since-network-seq",
271
- "--since-exception-seq",
272
- "--max",
273
- "--target-id",
274
- "--tab-id",
275
- "--name",
276
- "--cookies",
277
- "--cookies-file",
278
- "--persist-profile",
279
- "--expression",
280
- "--default-provider",
281
- "--command",
282
- "--request-id",
283
- "--strict",
284
- "--params",
285
- "--params-file",
286
- "--topic",
287
- "--days",
288
- "--from",
289
- "--to",
290
- "--source-selection",
291
- "--sources",
292
- "--mode",
293
- "--limit-per-source",
294
- "--query",
295
- "--providers",
296
- "--budget",
297
- "--region",
298
- "--sort",
299
- "--product-url",
300
- "--product-name",
301
- "--provider-hint",
302
- "--include-screenshots",
303
- "--include-all-images",
304
- "--include-copy",
305
- "--output-dir",
306
- "--ttl-hours"
307
- ];
308
116
  var VALID_EQUALS_FLAG_SET = new Set(VALID_EQUALS_FLAGS);
309
117
  function parseArgs(argv) {
310
118
  let args = expandShortFlags(argv.slice(2));
@@ -451,77 +259,28 @@ function listCommands() {
451
259
 
452
260
  // src/cli/help.ts
453
261
  var LABEL_WIDTH = 42;
454
- var EXPECTED_TOOL_COUNT = 49;
262
+ var DETAIL_LABEL_WIDTH = 9;
455
263
  var COMMAND_SET = new Set(CLI_COMMANDS);
456
264
  var FLAG_SET = new Set(VALID_FLAGS);
457
- var HELP_COMMAND_GROUPS = [
458
- {
459
- title: "Install & Lifecycle",
460
- summary: "Install, remove, and inspect CLI basics.",
461
- commands: ["install", "update", "uninstall", "help", "version"]
462
- },
463
- {
464
- title: "Daemon & Runtime",
465
- summary: "Run daemon services and single-process scripts.",
466
- commands: ["serve", "daemon", "native", "run"]
467
- },
468
- {
469
- title: "Session Lifecycle",
470
- summary: "Launch/connect sessions and manage browser state.",
471
- commands: ["launch", "connect", "disconnect", "status", "cookie-import", "cookie-list"]
472
- },
473
- {
474
- title: "Provider Workflows",
475
- summary: "Run research/shopping/media workflows and macro plans.",
476
- commands: ["research", "shopping", "product-video", "artifacts", "macro-resolve"]
477
- },
478
- {
479
- title: "Design Canvas",
480
- summary: "Execute design-canvas session, document, overlay, and preview commands.",
481
- commands: ["canvas"]
482
- },
483
- {
484
- title: "Navigation",
485
- summary: "Move through pages and capture fresh refs.",
486
- commands: ["goto", "wait", "snapshot"]
487
- },
488
- {
489
- title: "Interaction",
490
- summary: "Perform ref-based interactions in the active page.",
491
- commands: ["click", "hover", "press", "check", "uncheck", "type", "select", "scroll", "scroll-into-view"]
492
- },
493
- {
494
- title: "Targets & Pages",
495
- summary: "Manage tabs/targets and named pages.",
496
- commands: ["targets-list", "target-use", "target-new", "target-close", "page", "pages", "page-close"]
497
- },
498
- {
499
- title: "DOM & Export",
500
- summary: "Read DOM state and export page/component code.",
501
- commands: ["dom-html", "dom-text", "dom-attr", "dom-value", "dom-visible", "dom-enabled", "dom-checked", "clone-page", "clone-component"]
502
- },
503
- {
504
- title: "Diagnostics & Annotation",
505
- summary: "Collect runtime diagnostics and annotation payloads.",
506
- commands: ["perf", "screenshot", "console-poll", "network-poll", "debug-trace-snapshot", "annotate"]
507
- },
508
- {
509
- title: "Power",
510
- summary: "Unsafe internal command passthrough.",
511
- commands: ["rpc"]
512
- }
513
- ];
265
+ var TOOL_COUNT = TOOL_SURFACE_ENTRIES.length;
266
+ var formatFlags = (flags) => flags.length > 0 ? flags.join(", ") : "none";
267
+ var HELP_COMMAND_GROUPS = PUBLIC_CLI_COMMAND_GROUPS.map((group) => ({
268
+ title: group.title,
269
+ summary: group.summary,
270
+ commands: group.commands.map((command) => command.name)
271
+ }));
272
+ var COMMAND_HELP_DETAILS = CLI_COMMAND_HELP_DETAILS;
514
273
  var HELP_FLAG_GROUPS = [
515
274
  {
516
- title: "Install/Global Flags",
517
- summary: "Control installation scope and setup behavior.",
275
+ title: "Install / Global Flags",
276
+ summary: "Control install scope, prompting, and bundled skill setup.",
518
277
  flags: [
519
- { flag: "--global", alias: "-g", description: "Install into ~/.config/opencode/opencode.json." },
520
- { flag: "--local", alias: "-l", description: "Install into ./opencode.json for this project." },
278
+ { flag: "--global", alias: "-g", description: "Install into ~/.config/opencode/opencode.json.", example: "npx opendevbrowser --global --with-config" },
279
+ { flag: "--local", alias: "-l", description: "Install into ./opencode.json for this project.", example: "npx opendevbrowser --local --skills-local" },
521
280
  { flag: "--update", alias: "-u", description: "Alias for the update command." },
522
281
  { flag: "--uninstall", description: "Alias for the uninstall command." },
523
- { flag: "--with-config", description: "Also create opendevbrowser.jsonc defaults." },
524
- { flag: "--full", alias: "-f", description: "Install config and pre-extract extension assets." },
282
+ { flag: "--with-config", description: "Also create opendevbrowser.jsonc defaults.", example: "npx opendevbrowser --global --with-config" },
283
+ { flag: "--full", alias: "-f", description: "Install config and pre-extract extension assets.", example: "npx opendevbrowser --full" },
525
284
  { flag: "--no-prompt", description: "Run non-interactively using defaults." },
526
285
  { flag: "--no-interactive", description: "Alias of --no-prompt." },
527
286
  { flag: "--quiet", description: "Suppress non-error text output." },
@@ -531,179 +290,241 @@ var HELP_FLAG_GROUPS = [
531
290
  ]
532
291
  },
533
292
  {
534
- title: "Help/Output Flags",
535
- summary: "Inspect help/version and control output transport.",
293
+ title: "Help / Output Flags",
294
+ summary: "Inspect help or version and control output transport.",
536
295
  flags: [
537
- { flag: "--help", alias: "-h", description: "Show CLI help output." },
538
- { flag: "--version", alias: "-v", description: "Show CLI version." },
539
- { flag: "--output-format", description: "Output mode: text, json, stream-json." },
540
- { flag: "--transport", description: "Transport selector for transport-aware commands. `status` uses relay/native; `annotate` uses auto/direct/relay." }
296
+ { flag: "--help", alias: "-h", description: "Show CLI help output.", example: "npx opendevbrowser --help" },
297
+ { flag: "--version", alias: "-v", description: "Show CLI version.", example: "npx opendevbrowser --version" },
298
+ { flag: "--output-format", description: "Output mode: text, json, or stream-json.", example: "opendevbrowser status --daemon --output-format json" },
299
+ { flag: "--transport", description: "Transport selector for transport-aware commands. `status` uses relay/native; `annotate` uses auto/direct/relay.", example: "opendevbrowser status --session-id s1 --transport native" }
541
300
  ]
542
301
  },
543
302
  {
544
- title: "Daemon/Session/Launch Flags",
545
- summary: "Control daemon binding, connect, and launch behavior.",
303
+ title: "Daemon / Session / Launch Flags",
304
+ summary: "Control daemon binding, browser connect, and launch behavior. If ext=on but handshake=off, reopen the extension popup and click Connect again to re-establish a clean daemon-extension handshake.",
546
305
  flags: [
547
- { flag: "--port", description: "Daemon or relay port override." },
548
- { flag: "--token", description: "Relay/daemon auth token override." },
549
- { flag: "--stop", description: "Stop a running daemon." },
550
- { flag: "--daemon", description: "Target daemon status mode where supported." },
551
- { flag: "--script", description: "Path to a run-script JSON file." },
552
- { flag: "--session-id", description: "Target an existing daemon session." },
553
- { flag: "--close-browser", description: "Close managed browser on disconnect." },
554
- { flag: "--ws-endpoint", description: "Connect using explicit CDP WebSocket endpoint." },
555
- { flag: "--host", description: "CDP host for host/port connect mode." },
306
+ { flag: "--port", description: "Daemon or relay port override.", example: "opendevbrowser serve --port 8788" },
307
+ { flag: "--token", description: "Relay or daemon auth token override.", example: "opendevbrowser serve --token local-dev-token" },
308
+ { flag: "--stop", description: "Stop a running daemon.", example: "opendevbrowser serve --stop" },
309
+ { flag: "--daemon", description: "Target daemon status mode where supported.", example: "opendevbrowser status --daemon --output-format json" },
310
+ { flag: "--script", description: "Path to a run-script JSON file.", example: "opendevbrowser run --script ./workflow.json" },
311
+ { flag: "--session-id", description: "Target an existing browser or daemon session.", example: "opendevbrowser snapshot --session-id s1" },
312
+ { flag: "--close-browser", description: "Close the managed browser on disconnect." },
313
+ { flag: "--ws-endpoint", description: "Connect using an explicit CDP WebSocket endpoint.", example: "opendevbrowser connect --ws-endpoint ws://127.0.0.1:9222/devtools/browser/..." },
314
+ { flag: "--host", description: "CDP host for host/port connect mode.", example: "opendevbrowser connect --host 127.0.0.1 --cdp-port 9222" },
556
315
  { flag: "--cdp-port", description: "CDP port for host/port connect mode." },
557
- { flag: "--headless", description: "Launch managed browser in headless mode." },
316
+ { flag: "--headless", description: "Launch a managed browser in headless mode.", example: "opendevbrowser launch --no-extension --headless" },
558
317
  { flag: "--profile", description: "Use a named browser profile directory." },
559
- { flag: "--persist-profile", description: "Keep generated profile directory after exit." },
560
- { flag: "--chrome-path", description: "Use a specific Chrome/Chromium binary." },
318
+ { flag: "--persist-profile", description: "Keep the generated profile directory after exit. `run` uses a temporary profile by default unless this flag is enabled." },
319
+ { flag: "--chrome-path", description: "Use a specific Chrome, Chromium, or CfT binary." },
561
320
  { flag: "--start-url", description: "Open this URL immediately after launch or connect." },
562
321
  { flag: "--flag", description: "Pass one or more extra Chrome CLI flags." },
563
322
  { flag: "--no-extension", description: "Force managed mode without extension relay." },
564
- { flag: "--extension-only", description: "Fail unless extension relay is connected." },
565
- { flag: "--extension-legacy", description: "Use legacy /cdp relay mode instead of /ops." },
566
- { flag: "--wait-for-extension", description: "Wait for extension handshake before returning." },
323
+ { flag: "--extension-only", description: "Fail unless the extension relay websocket is connected and the daemon-extension handshake is complete." },
324
+ { flag: "--extension-legacy", description: "Use the legacy /cdp relay mode instead of /ops." },
325
+ { flag: "--wait-for-extension", description: "Wait for a clean daemon-extension handshake before returning." },
567
326
  { flag: "--wait-timeout-ms", description: "Handshake wait timeout in milliseconds." }
568
327
  ]
569
328
  },
570
329
  {
571
- title: "Navigation/Interaction/Diagnostics Flags",
572
- summary: "Command-specific flags for page actions and diagnostics.",
330
+ title: "Navigation / Interaction / Diagnostics Flags",
331
+ summary: "Command-specific flags for page actions, reads, and diagnostics.",
573
332
  flags: [
574
- { flag: "--url", description: "Target URL for navigation, connect, or workflow commands." },
575
- { flag: "--wait-until", description: "Navigation wait strategy (load, domcontentloaded, etc.)." },
576
- { flag: "--timeout-ms", description: "Operation timeout in milliseconds (for example goto, wait, screenshot, annotate, canvas, rpc, and macro-resolve)." },
577
- { flag: "--ref", description: "Snapshot ref id for element-targeted commands." },
333
+ { flag: "--url", description: "Target URL for navigation, connect, or workflow commands.", example: "opendevbrowser goto --session-id s1 --url https://example.com" },
334
+ { flag: "--wait-until", description: "Navigation wait strategy such as load or domcontentloaded." },
335
+ { flag: "--timeout-ms", description: "Operation timeout in milliseconds.", example: "opendevbrowser canvas --timeout-ms 120000 --command canvas.session.open ..." },
336
+ { flag: "--ref", description: "Snapshot ref id for element-targeted commands.", example: "opendevbrowser click --session-id s1 --ref r12" },
578
337
  { flag: "--state", description: "Wait state selector for wait-style commands." },
579
338
  { flag: "--until", description: "Wait condition selector for wait-style commands." },
580
339
  { flag: "--mode", description: "Mode selector for commands that accept variants." },
581
340
  { flag: "--max-chars", description: "Maximum text characters to return for DOM reads." },
582
341
  { flag: "--cursor", description: "Cursor token for paginated list commands." },
583
342
  { flag: "--text", description: "Text payload for type and related commands." },
584
- { flag: "--clear", description: "Clear existing input value before typing." },
585
- { flag: "--submit", description: "Submit form/input after typing." },
343
+ { flag: "--clear", description: "Clear the existing input value before typing." },
344
+ { flag: "--submit", description: "Submit the form or input after typing." },
586
345
  { flag: "--values", description: "CSV values for select commands." },
346
+ { flag: "--files", description: "CSV file paths for upload commands." },
587
347
  { flag: "--dy", description: "Vertical scroll delta for scroll commands." },
588
- { flag: "--key", description: "Keyboard key for press command." },
589
- { flag: "--attr", description: "DOM attribute name for dom-attr command." },
348
+ { flag: "--key", description: "Keyboard key for the press command." },
349
+ { flag: "--attr", description: "DOM attribute name for dom-attr." },
350
+ { flag: "--x", description: "Viewport x coordinate for pointer commands." },
351
+ { flag: "--y", description: "Viewport y coordinate for pointer commands." },
352
+ { flag: "--from-x", description: "Pointer drag start x coordinate." },
353
+ { flag: "--from-y", description: "Pointer drag start y coordinate." },
354
+ { flag: "--to-x", description: "Pointer drag end x coordinate." },
355
+ { flag: "--to-y", description: "Pointer drag end y coordinate." },
356
+ { flag: "--steps", description: "Interpolation step count for pointer move or drag commands." },
357
+ { flag: "--button", description: "Pointer button selector for pointer down or up." },
358
+ { flag: "--click-count", description: "Associated click count for pointer down or up." },
590
359
  { flag: "--name", description: "Named page identifier for page commands." },
591
- { flag: "--target-id", description: "Browser target id for target commands." },
592
- { flag: "--tab-id", description: "Browser tab id override for extension/annotation commands." },
360
+ { flag: "--target-id", description: "Browser target id for target commands.", example: "opendevbrowser target-use --session-id s1 --target-id page-2" },
361
+ { flag: "--window-id", description: "Desktop window id for direct window capture commands." },
362
+ { flag: "--tab-id", description: "Browser tab id override for extension and annotation commands." },
593
363
  { flag: "--include-urls", description: "Include page URLs in list output where supported." },
594
- { flag: "--path", description: "Filesystem path for command output/artifacts." },
595
- { flag: "--since-seq", description: "Poll from sequence id across diagnostics streams." },
596
- { flag: "--max", description: "Maximum number of records/items to return." },
597
- { flag: "--since-console-seq", description: "Console sequence cursor for debug trace snapshots." },
598
- { flag: "--since-network-seq", description: "Network sequence cursor for debug trace snapshots." },
599
- { flag: "--since-exception-seq", description: "Exception sequence cursor for debug trace snapshots." },
600
- { flag: "--request-id", description: "Attach/lookup request id for correlateable output." },
601
- { flag: "--cookies", description: "Inline cookie payload for cookie-import command." },
364
+ { flag: "--path", description: "Filesystem path for command output or artifacts.", example: "opendevbrowser screenshot --session-id s1 --path ./shot.png" },
365
+ { flag: "--screencast-id", description: "Recorded screencast id returned by screencast-start for the same session." },
366
+ { flag: "--reason", description: "Audit reason for desktop observation commands." },
367
+ { flag: "--full-page", description: "Capture the full scrollable page instead of the current viewport." },
368
+ { flag: "--action", description: "Dialog action: status, accept, or dismiss." },
369
+ { flag: "--prompt-text", description: "Prompt text to submit when accepting a prompt dialog." },
370
+ { flag: "--since-seq", description: "Poll from a sequence id across diagnostics streams." },
371
+ { flag: "--max", description: "Maximum number of records or items to return." },
372
+ { flag: "--interval-ms", description: "Frame capture interval for screencast recording." },
373
+ { flag: "--max-frames", description: "Maximum screencast frame count before auto-stop." },
374
+ { flag: "--since-console-seq", description: "Console sequence cursor for debug-trace snapshots." },
375
+ { flag: "--since-network-seq", description: "Network sequence cursor for debug-trace snapshots." },
376
+ { flag: "--since-exception-seq", description: "Exception sequence cursor for debug-trace snapshots." },
377
+ { flag: "--request-id", description: "Attach or lookup a request id for correlated output." },
378
+ { flag: "--cookies", description: "Inline cookie payload for cookie-import." },
602
379
  { flag: "--cookies-file", description: "File path containing cookies for cookie-import." },
603
380
  { flag: "--strict", description: "Fail cookie import on invalid entries." },
604
381
  { flag: "--screenshot-mode", description: "Annotation screenshot mode: visible, full, or none." },
605
382
  { flag: "--debug", description: "Enable debug-level annotation capture extras." },
606
- { flag: "--context", description: "Free-form annotation context for reviewers/agents." },
383
+ { flag: "--context", description: "Free-form annotation context for reviewers or agents." },
607
384
  { flag: "--stored", description: "Return the last stored annotation payload instead of starting a new capture." }
608
385
  ]
609
386
  },
610
387
  {
611
- title: "Macro/Provider/Power Flags",
612
- summary: "Workflow filters, provider selectors, and unsafe RPC options.",
388
+ title: "Macro / Provider / Power Flags",
389
+ summary: "Workflow filters, provider selectors, and unsafe RPC or /canvas options.",
613
390
  flags: [
614
- { flag: "--expression", description: "Macro expression to resolve/execute." },
391
+ { flag: "--expression", description: "Macro expression to resolve or execute.", example: `opendevbrowser macro-resolve --expression '@web.search("openai")'` },
615
392
  { flag: "--default-provider", description: "Provider fallback for shorthand macro expressions." },
616
- { flag: "--include-catalog", description: "Include macro catalog metadata in response." },
617
- { flag: "--execute", description: "Execute resolved macro action after planning (pair with --timeout-ms on slow runs)." },
618
- { flag: "--command", description: "Canvas command name for the canvas CLI command." },
393
+ { flag: "--include-catalog", description: "Include macro catalog metadata in the response." },
394
+ { flag: "--command", description: "Canvas command name for the canvas CLI command.", example: "opendevbrowser canvas --command canvas.session.open --params '{...}'" },
395
+ { flag: "--execute", description: "Execute a resolved macro action after planning." },
619
396
  { flag: "--params", description: "Inline JSON params for canvas or rpc commands." },
620
- { flag: "--params-file", description: "Path to JSON params file for canvas or rpc commands." },
621
- { flag: "--unsafe-internal", description: "Required safety gate for rpc command." },
397
+ { flag: "--params-file", description: "Path to a JSON params file for canvas or rpc commands.", example: "opendevbrowser canvas --command canvas.plan.set --params-file ./plan.json" },
398
+ { flag: "--unsafe-internal", description: "Required safety gate for the rpc command." },
622
399
  { flag: "--topic", description: "Research topic input." },
623
400
  { flag: "--days", description: "Lookback window in days for research commands." },
624
401
  { flag: "--from", description: "Start date boundary for research commands." },
625
402
  { flag: "--to", description: "End date boundary for research commands." },
626
- { flag: "--source-selection", description: "Research source-family selector." },
627
- { flag: "--sources", description: "Explicit source selectors within a source family." },
403
+ { flag: "--source-selection", description: "Research source-family selector. Use auto for generic topical research; add shopping only for deliberate commercial comparison." },
404
+ { flag: "--sources", description: "Explicit source selectors within a source family. Use shopping only when commercial intent is explicit." },
628
405
  { flag: "--include-engagement", description: "Include engagement metrics in research output." },
629
406
  { flag: "--limit-per-source", description: "Per-source result cap for research runs." },
630
407
  { flag: "--query", description: "Shopping query input." },
631
- { flag: "--providers", description: "Comma-separated provider ids for shopping/artifacts." },
408
+ { flag: "--providers", description: "Comma-separated provider ids for shopping or artifact commands." },
632
409
  { flag: "--budget", description: "Budget filter for shopping workflows." },
633
- { flag: "--region", description: "Region/country hint for provider selection." },
410
+ { flag: "--region", description: "Region or country hint for provider selection. Treat it as advisory unless output metadata reports `region_authoritative=true`." },
634
411
  { flag: "--sort", description: "Sort mode for shopping results." },
635
- { flag: "--product-url", description: "Target product URL for product-video/artifacts workflows." },
636
- { flag: "--product-name", description: "Product name override for media workflows." },
412
+ { flag: "--product-url", description: "Target product URL for product-video workflows." },
413
+ { flag: "--product-name", description: "Product name override for product-video workflows." },
637
414
  { flag: "--provider-hint", description: "Provider hint override for product workflows." },
638
415
  { flag: "--include-screenshots", description: "Include screenshots in product presentation output, or prefer screenshots when fetching stored annotations." },
639
416
  { flag: "--include-all-images", description: "Include all discovered product images." },
640
417
  { flag: "--include-copy", description: "Include product marketing copy metadata." },
641
- { flag: "--output-dir", description: "Directory where generated artifacts are written." },
418
+ { flag: "--use-cookies", description: "Enable or disable provider cookie injection for workflow runs; a bare flag means true.", example: "opendevbrowser shopping run --query 'usb hub' --use-cookies" },
419
+ { flag: "--browser-mode", description: "Shopping browser-recovery preference: auto, extension, or managed. Use managed for deterministic reruns and extension only when relay-backed auth/session state is required.", example: "opendevbrowser shopping run --query 'wireless ergonomic mouse' --providers shopping/bestbuy,shopping/ebay --browser-mode managed" },
420
+ { flag: "--challenge-automation-mode", description: "Per-run challenge automation mode for workflow runs and macro-resolve execute: off, browser, or browser_with_helper. Precedence is run > session > config, and the helper remains browser-scoped only.", example: `opendevbrowser macro-resolve --expression '@community.search("openai")' --execute --challenge-automation-mode browser_with_helper` },
421
+ { flag: "--cookie-policy-override", description: "Per-run workflow cookie policy override: off, auto, or required.", example: "opendevbrowser research run --topic 'agent workflows' --cookie-policy-override required" },
422
+ { flag: "--cookie-policy", description: "Alias of --cookie-policy-override." },
423
+ { flag: "--output-dir", description: "Directory where generated artifacts are written, including screencast replay output." },
642
424
  { flag: "--ttl-hours", description: "Artifact cache time-to-live in hours." },
643
425
  { flag: "--expired-only", description: "List only expired artifacts in artifacts commands." }
644
426
  ]
645
427
  }
646
428
  ];
647
- var HELP_TOOL_ENTRIES = [
648
- { name: "opendevbrowser_launch", description: "Launch a managed browser session." },
649
- { name: "opendevbrowser_connect", description: "Connect to an existing browser session." },
650
- { name: "opendevbrowser_disconnect", description: "Disconnect a managed or connected session." },
651
- { name: "opendevbrowser_status", description: "Inspect session and relay status." },
652
- { name: "opendevbrowser_targets_list", description: "List available page targets/tabs." },
653
- { name: "opendevbrowser_target_use", description: "Switch active target by id." },
654
- { name: "opendevbrowser_target_new", description: "Create a new target/tab." },
655
- { name: "opendevbrowser_target_close", description: "Close target/tab by id." },
656
- { name: "opendevbrowser_page", description: "Open or focus a named page." },
657
- { name: "opendevbrowser_list", description: "List named pages in the session." },
658
- { name: "opendevbrowser_close", description: "Close a named page." },
659
- { name: "opendevbrowser_goto", description: "Navigate to a URL." },
660
- { name: "opendevbrowser_wait", description: "Wait for load/ref/state conditions." },
661
- { name: "opendevbrowser_snapshot", description: "Capture AX-tree refs for actions." },
662
- { name: "opendevbrowser_click", description: "Click an element by ref." },
663
- { name: "opendevbrowser_hover", description: "Hover an element by ref." },
664
- { name: "opendevbrowser_press", description: "Send a keyboard key." },
665
- { name: "opendevbrowser_check", description: "Check checkbox/radio by ref." },
666
- { name: "opendevbrowser_uncheck", description: "Uncheck checkbox/radio by ref." },
667
- { name: "opendevbrowser_type", description: "Type text into an input by ref." },
668
- { name: "opendevbrowser_select", description: "Set select values by ref." },
669
- { name: "opendevbrowser_scroll", description: "Scroll page or element." },
670
- { name: "opendevbrowser_scroll_into_view", description: "Scroll target element into view." },
671
- { name: "opendevbrowser_dom_get_html", description: "Get HTML for page or ref." },
672
- { name: "opendevbrowser_dom_get_text", description: "Get text for page or ref." },
673
- { name: "opendevbrowser_get_attr", description: "Read a DOM attribute by ref." },
674
- { name: "opendevbrowser_get_value", description: "Read form/control value by ref." },
675
- { name: "opendevbrowser_is_visible", description: "Check ref visibility." },
676
- { name: "opendevbrowser_is_enabled", description: "Check ref enabled state." },
677
- { name: "opendevbrowser_is_checked", description: "Check ref checked state." },
678
- { name: "opendevbrowser_run", description: "Execute multi-action automation scripts." },
679
- { name: "opendevbrowser_prompting_guide", description: "Return best-practice prompting guidance." },
680
- { name: "opendevbrowser_console_poll", description: "Poll redacted console events." },
681
- { name: "opendevbrowser_network_poll", description: "Poll redacted network events." },
682
- { name: "opendevbrowser_debug_trace_snapshot", description: "Capture page + console + network diagnostics." },
683
- { name: "opendevbrowser_cookie_import", description: "Import validated cookies into session." },
684
- { name: "opendevbrowser_cookie_list", description: "List cookies in session with optional URL filters." },
685
- { name: "opendevbrowser_macro_resolve", description: "Resolve/execute provider macro expressions." },
686
- { name: "opendevbrowser_research_run", description: "Run research workflow directly." },
687
- { name: "opendevbrowser_shopping_run", description: "Run shopping workflow directly." },
688
- { name: "opendevbrowser_product_video_run", description: "Run product-video asset workflow directly." },
689
- { name: "opendevbrowser_canvas", description: "Execute a design-canvas command surface call." },
690
- { name: "opendevbrowser_clone_page", description: "Export active page into React code." },
691
- { name: "opendevbrowser_clone_component", description: "Export component by ref into React code." },
692
- { name: "opendevbrowser_perf", description: "Collect browser performance metrics." },
693
- { name: "opendevbrowser_screenshot", description: "Capture page screenshot." },
694
- { name: "opendevbrowser_annotate", description: "Capture interactive annotations." },
695
- { name: "opendevbrowser_skill_list", description: "List available skill packs." },
696
- { name: "opendevbrowser_skill_load", description: "Load a specific skill pack." }
429
+ var HELP_TOOL_ENTRIES = TOOL_SURFACE_ENTRIES;
430
+ var HELP_CAPABILITY_ENTRIES = [
431
+ {
432
+ label: "screencast / browser replay",
433
+ description: "Use the public browser replay lane when you need temporal browser evidence before or after a fragile flow.",
434
+ details: [
435
+ { label: "cli:", value: "screencast-start, screencast-stop" },
436
+ { label: "example:", value: "npx opendevbrowser screencast-start --session-id <id> --output-dir ./artifacts/replay" }
437
+ ]
438
+ },
439
+ {
440
+ label: "desktop observation",
441
+ description: "Use the public read-only desktop observation plane for sibling desktop evidence on macOS; window inventory and accessibility probes use the local swift command, while screenshots use screencapture outside extension relay.",
442
+ details: [
443
+ {
444
+ label: "cli:",
445
+ value: "desktop-status, desktop-windows, desktop-active-window, desktop-capture-desktop, desktop-capture-window, desktop-accessibility-snapshot"
446
+ },
447
+ { label: "example:", value: "npx opendevbrowser desktop-status --output-format json" }
448
+ ]
449
+ },
450
+ {
451
+ label: "computer use / browser-scoped computer use",
452
+ description: "Control the bounded browser-scoped computer-use challenge lane with --challenge-automation-mode; the optional helper is not a desktop agent.",
453
+ details: [
454
+ { label: "flag:", value: "--challenge-automation-mode off|browser|browser_with_helper" },
455
+ { label: "works:", value: "research run, shopping run, product-video run, macro-resolve --execute" },
456
+ { label: "proof:", value: "review, session-inspector, workflow fallback metadata" }
457
+ ]
458
+ }
459
+ ];
460
+ var HELP_ONBOARDING_ENTRIES = [
461
+ {
462
+ label: "prompting_guide",
463
+ description: "Load local best-practice guidance before low-level browser commands.",
464
+ details: [{ label: "tool:", value: onboarding_metadata_default.quickStartCommands.promptingGuide }]
465
+ },
466
+ {
467
+ label: "skill_load",
468
+ description: `Load ${onboarding_metadata_default.skillName} ${onboarding_metadata_default.skillTopic} directly when you want the canonical quick-start section.`,
469
+ details: [{ label: "tool:", value: onboarding_metadata_default.quickStartCommands.skillLoad }]
470
+ },
471
+ {
472
+ label: "validated_lanes",
473
+ description: `Load ${onboarding_metadata_default.skillName} ${onboarding_metadata_default.validatedSkillTopic} when you need the current proven transcript, research, and shopping runbook.`,
474
+ details: [{ label: "tool:", value: onboarding_metadata_default.quickStartCommands.validatedLanes }]
475
+ },
476
+ {
477
+ label: "skill_list",
478
+ description: "Inspect bundled and discovered skill packs when you need a different local lane.",
479
+ details: [{ label: "tool:", value: onboarding_metadata_default.quickStartCommands.skillList }]
480
+ },
481
+ {
482
+ label: "research_reliable",
483
+ description: "Generic topical research is currently safest with `--source-selection auto`; add shopping only when the task is explicitly commercial.",
484
+ details: [{ label: "cli:", value: onboarding_metadata_default.quickStartCommands.validatedResearch }]
485
+ },
486
+ {
487
+ label: "shopping_reliable",
488
+ description: "Start deal hunting with explicit providers in managed mode and only trust regional comparisons when the result reports `region_authoritative=true`.",
489
+ details: [{ label: "cli:", value: onboarding_metadata_default.quickStartCommands.validatedShopping }]
490
+ },
491
+ {
492
+ label: "happy_path",
493
+ description: "After guidance, verify a minimal managed happy path before widening into multi-step automation.",
494
+ details: [{ label: "cli:", value: onboarding_metadata_default.quickStartCommands.happyPath }]
495
+ },
496
+ {
497
+ label: "docs",
498
+ description: "Use the first-run checklist and canonical skill runbook for proof and deeper operating details.",
499
+ details: [{
500
+ label: "paths:",
501
+ value: `${onboarding_metadata_default.referencePaths.onboardingDoc}, ${onboarding_metadata_default.referencePaths.skillDoc}`
502
+ }]
503
+ }
697
504
  ];
698
505
  var HELP_REFERENCE_ENTRIES = [
699
- { label: "docs/CLI.md", description: "Full command docs, flag matrix, and examples." },
700
- { label: "docs/SURFACE_REFERENCE.md", description: "Canonical CLI/tool/channel inventory matrix." },
701
- { label: "src/tools/index.ts", description: "Code-level tool registry (source of truth)." },
506
+ { label: "src/cli/onboarding-metadata.json", description: "Canonical first-contact onboarding metadata shared by help, nudges, and proof lanes." },
507
+ { label: "src/public-surface/source.ts", description: "Authoritative command, usage, flag, and tool surface metadata." },
508
+ { label: "src/public-surface/generated-manifest.ts", description: "Checked-in generated public-surface snapshot consumed by help and parity tests." },
509
+ { label: "src/public-surface/generated-manifest.json", description: "Checked-in generated public-surface snapshot consumed by inventory scripts." },
510
+ { label: "src/cli/args.ts", description: "CLI argument parsing backed by the public-surface source." },
511
+ { label: "src/cli/help.ts", description: "Human-facing CLI formatting layered on the public-surface source, including the first-contact Find It Fast lookup block." },
512
+ { label: "src/tools/index.ts", description: "Code-level tool registry." },
513
+ { label: "docs/CLI.md", description: "Detailed CLI guide and release-gate runbooks." },
514
+ { label: onboarding_metadata_default.referencePaths.onboardingDoc, description: "First-run checklist for help-led onboarding and happy-path proof." },
515
+ { label: onboarding_metadata_default.referencePaths.skillDoc, description: "Canonical bundled best-practices runbook and quick-start guidance." },
516
+ { label: "docs/SURFACE_REFERENCE.md", description: "Canonical CLI, tool, and relay channel inventory." },
702
517
  { label: "opendevbrowser --help", description: "Primary full help invocation for quick discovery." },
703
518
  { label: "opendevbrowser help", description: "Alias that prints the same full help inventory." }
704
519
  ];
705
520
  function formatRows(rows) {
706
- return rows.map((row) => ` ${row.label.padEnd(LABEL_WIDTH)} ${row.description}`).join("\n");
521
+ return rows.map((row) => {
522
+ const lines = [` ${row.label.padEnd(LABEL_WIDTH)} ${row.description}`];
523
+ for (const detail of row.details ?? []) {
524
+ lines.push(` ${detail.label.padEnd(DETAIL_LABEL_WIDTH)} ${detail.value}`);
525
+ }
526
+ return lines.join("\n");
527
+ }).join("\n");
707
528
  }
708
529
  function getCommandDescriptions() {
709
530
  const descriptions = /* @__PURE__ */ new Map();
@@ -716,6 +537,7 @@ function assertCommandCoverage(commandDescriptions) {
716
537
  const seen = /* @__PURE__ */ new Set();
717
538
  for (const group of HELP_COMMAND_GROUPS) {
718
539
  for (const command of group.commands) {
540
+ const detail = COMMAND_HELP_DETAILS[command];
719
541
  if (!COMMAND_SET.has(command)) {
720
542
  throw new Error(`Help references unknown CLI command: ${command}`);
721
543
  }
@@ -725,6 +547,14 @@ function assertCommandCoverage(commandDescriptions) {
725
547
  if (seen.has(command)) {
726
548
  throw new Error(`Help command appears multiple times: ${command}`);
727
549
  }
550
+ if (!detail || !detail.usage.trim()) {
551
+ throw new Error(`Missing command help metadata: ${command}`);
552
+ }
553
+ for (const flag of detail.flags) {
554
+ if (!FLAG_SET.has(flag)) {
555
+ throw new Error(`Command help metadata references unknown flag ${flag} for ${command}`);
556
+ }
557
+ }
728
558
  seen.add(command);
729
559
  }
730
560
  }
@@ -752,26 +582,39 @@ function assertFlagCoverage() {
752
582
  }
753
583
  }
754
584
  function assertToolCoverage() {
755
- if (HELP_TOOL_ENTRIES.length !== EXPECTED_TOOL_COUNT) {
756
- throw new Error(`Help tool inventory must list ${EXPECTED_TOOL_COUNT} tools; got ${HELP_TOOL_ENTRIES.length}`);
757
- }
758
585
  const seen = /* @__PURE__ */ new Set();
759
586
  for (const entry of HELP_TOOL_ENTRIES) {
587
+ if (!entry.name.startsWith("opendevbrowser_")) {
588
+ throw new Error(`Invalid tool name in help inventory: ${entry.name}`);
589
+ }
590
+ if (!entry.description.trim()) {
591
+ throw new Error(`Help tool is missing a description: ${entry.name}`);
592
+ }
760
593
  if (seen.has(entry.name)) {
761
594
  throw new Error(`Help tool appears multiple times: ${entry.name}`);
762
595
  }
763
- if (!entry.name.startsWith("opendevbrowser_")) {
764
- throw new Error(`Invalid tool name in help inventory: ${entry.name}`);
596
+ if (entry.cliEquivalent && !COMMAND_SET.has(entry.cliEquivalent)) {
597
+ throw new Error(`Tool metadata references unknown CLI command: ${entry.cliEquivalent}`);
765
598
  }
766
599
  seen.add(entry.name);
767
600
  }
601
+ if (HELP_TOOL_ENTRIES.length !== TOOL_COUNT) {
602
+ throw new Error(`Help tool inventory must list ${TOOL_COUNT} tools; got ${HELP_TOOL_ENTRIES.length}`);
603
+ }
768
604
  }
769
605
  function formatCommandGroups(commandDescriptions) {
770
606
  return HELP_COMMAND_GROUPS.map((group) => {
771
- const rows = group.commands.map((command) => ({
772
- label: command,
773
- description: commandDescriptions.get(command) ?? "Missing command description."
774
- }));
607
+ const rows = group.commands.map((command) => {
608
+ const detail = COMMAND_HELP_DETAILS[command];
609
+ return {
610
+ label: command,
611
+ description: commandDescriptions.get(command) ?? "Missing command description.",
612
+ details: [
613
+ { label: "usage:", value: detail.usage },
614
+ { label: "flags:", value: formatFlags(detail.flags) }
615
+ ]
616
+ };
617
+ });
775
618
  return `${group.title}: ${group.summary}
776
619
  ${formatRows(rows)}`;
777
620
  }).join("\n\n");
@@ -780,7 +623,8 @@ function formatFlagGroups() {
780
623
  return HELP_FLAG_GROUPS.map((group) => {
781
624
  const rows = group.flags.map((entry) => ({
782
625
  label: entry.alias ? `${entry.flag} (${entry.alias})` : entry.flag,
783
- description: entry.description
626
+ description: entry.description,
627
+ details: entry.example ? [{ label: "example:", value: entry.example }] : []
784
628
  }));
785
629
  return `${group.title}: ${group.summary}
786
630
  ${formatRows(rows)}`;
@@ -789,9 +633,16 @@ ${formatRows(rows)}`;
789
633
  function formatToolEntries() {
790
634
  return formatRows(HELP_TOOL_ENTRIES.map((entry) => ({
791
635
  label: entry.name,
792
- description: entry.description
636
+ description: entry.description,
637
+ details: entry.cliEquivalent ? [{ label: "cli:", value: entry.cliEquivalent }] : [{ label: "scope:", value: "tool-only" }]
793
638
  })));
794
639
  }
640
+ function formatOnboardingEntries() {
641
+ return formatRows(HELP_ONBOARDING_ENTRIES);
642
+ }
643
+ function formatCapabilityEntries() {
644
+ return formatRows(HELP_CAPABILITY_ENTRIES);
645
+ }
795
646
  function formatReferenceEntries() {
796
647
  return formatRows(HELP_REFERENCE_ENTRIES.map((entry) => ({
797
648
  label: entry.label,
@@ -809,13 +660,21 @@ function getHelpText() {
809
660
  "Usage:",
810
661
  " npx opendevbrowser <command> [options]",
811
662
  "",
663
+ "Find It Fast:",
664
+ " Use these exact lookup terms when you need replay, desktop evidence, or browser-scoped computer use.",
665
+ formatCapabilityEntries(),
666
+ "",
667
+ `${onboarding_metadata_default.sectionTitle}:`,
668
+ ` ${onboarding_metadata_default.sectionSummary}`,
669
+ formatOnboardingEntries(),
670
+ "",
812
671
  `Command Inventory (all ${CLI_COMMANDS.length} commands):`,
813
672
  formatCommandGroups(commandDescriptions),
814
673
  "",
815
674
  "Flag Inventory (all supported flags):",
816
675
  formatFlagGroups(),
817
676
  "",
818
- `Tool Inventory (all ${EXPECTED_TOOL_COUNT} opendevbrowser_* tools):`,
677
+ `Tool Inventory (all ${TOOL_COUNT} opendevbrowser_* tools):`,
819
678
  formatToolEntries(),
820
679
  "",
821
680
  "Reference Pointers:",
@@ -1079,52 +938,15 @@ function installLocal(withConfig = false) {
1079
938
  }
1080
939
 
1081
940
  // src/cli/installers/skills.ts
1082
- import * as fs6 from "fs";
941
+ import * as crypto from "crypto";
942
+ import * as fs5 from "fs";
1083
943
  import * as path4 from "path";
1084
944
 
1085
945
  // src/cli/utils/skills.ts
1086
- import * as fs5 from "fs";
1087
946
  import * as path3 from "path";
1088
947
  import * as os3 from "os";
1089
- import { fileURLToPath } from "url";
1090
- var PACKAGE_NAME = "opendevbrowser";
1091
948
  var SKILL_DIR_NAME = "skill";
1092
949
  var SKILLS_DIR_NAME = "skills";
1093
- var cachedPackageRoot = null;
1094
- function findPackageRoot(startDir) {
1095
- let current = startDir;
1096
- while (true) {
1097
- const pkgPath = path3.join(current, "package.json");
1098
- if (fs5.existsSync(pkgPath)) {
1099
- try {
1100
- const parsed = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
1101
- if (parsed.name === PACKAGE_NAME) {
1102
- return current;
1103
- }
1104
- } catch {
1105
- }
1106
- }
1107
- const parent = path3.dirname(current);
1108
- if (parent === current) {
1109
- break;
1110
- }
1111
- current = parent;
1112
- }
1113
- throw new Error("Unable to locate opendevbrowser package root for skill installation.");
1114
- }
1115
- function getPackageRoot() {
1116
- if (cachedPackageRoot) return cachedPackageRoot;
1117
- const moduleDir = path3.dirname(fileURLToPath(import.meta.url));
1118
- cachedPackageRoot = findPackageRoot(moduleDir);
1119
- return cachedPackageRoot;
1120
- }
1121
- function getBundledSkillsDir() {
1122
- const skillsDir = path3.join(getPackageRoot(), "skills");
1123
- if (!fs5.existsSync(skillsDir)) {
1124
- throw new Error(`Bundled skills directory not found at ${skillsDir}`);
1125
- }
1126
- return skillsDir;
1127
- }
1128
950
  function getGlobalSkillDir() {
1129
951
  const configDir = process.env.OPENCODE_CONFIG_DIR || path3.join(os3.homedir(), ".config", "opencode");
1130
952
  return path3.join(configDir, SKILL_DIR_NAME);
@@ -1182,34 +1004,207 @@ function getLocalSkillTargets() {
1182
1004
  }
1183
1005
 
1184
1006
  // src/cli/installers/skills.ts
1185
- function installSkills(mode) {
1186
- const targets = mode === "global" ? getGlobalSkillTargets() : getLocalSkillTargets();
1007
+ var LEGACY_ALIAS_DIRS = [
1008
+ { name: "research", canonical: "opendevbrowser-research" },
1009
+ { name: "shopping", canonical: "opendevbrowser-shopping" }
1010
+ ];
1011
+ function getTargets(mode) {
1012
+ return mode === "global" ? getGlobalSkillTargets() : getLocalSkillTargets();
1013
+ }
1014
+ function getCanonicalBundledSkillNames() {
1015
+ return listBundledSkillDirectories().map((entry) => entry.name);
1016
+ }
1017
+ function hasCanonicalBundledSkillInTarget(targetDir, packNames) {
1018
+ return packNames.some((packName) => fs5.existsSync(path4.join(targetDir, packName)));
1019
+ }
1020
+ function formatSummary(parts, totalTargets, failures) {
1021
+ const summary = parts.length > 0 ? parts.join(", ") : "no lifecycle changes";
1022
+ const failureSummary = failures > 0 ? `, ${failures} failed` : "";
1023
+ return `${summary} across ${totalTargets} targets${failureSummary}`;
1024
+ }
1025
+ function hashDirectoryTree(dirPath) {
1026
+ const hash = crypto.createHash("sha256");
1027
+ const visit = (currentPath, relativePath) => {
1028
+ const entries = fs5.readdirSync(currentPath, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
1029
+ for (const entry of entries) {
1030
+ const absolutePath = path4.join(currentPath, entry.name);
1031
+ const entryRelativePath = relativePath ? path4.posix.join(relativePath, entry.name) : entry.name;
1032
+ if (entry.isDirectory()) {
1033
+ hash.update(`D:${entryRelativePath}\0`);
1034
+ visit(absolutePath, entryRelativePath);
1035
+ continue;
1036
+ }
1037
+ if (entry.isFile()) {
1038
+ hash.update(`F:${entryRelativePath}\0`);
1039
+ hash.update(fs5.readFileSync(absolutePath));
1040
+ hash.update("\0");
1041
+ continue;
1042
+ }
1043
+ if (entry.isSymbolicLink()) {
1044
+ hash.update(`L:${entryRelativePath}\0${fs5.readlinkSync(absolutePath)}\0`);
1045
+ }
1046
+ }
1047
+ };
1048
+ visit(dirPath, "");
1049
+ return hash.digest("hex");
1050
+ }
1051
+ function syncSkillDirectory(sourcePath, targetPath, sourceFingerprint) {
1052
+ if (!fs5.existsSync(targetPath)) {
1053
+ fs5.cpSync(sourcePath, targetPath, { recursive: true });
1054
+ return "installed";
1055
+ }
1056
+ const targetFingerprint = hashDirectoryTree(targetPath);
1057
+ if (targetFingerprint === sourceFingerprint) {
1058
+ return "unchanged";
1059
+ }
1060
+ const parentDir = path4.dirname(targetPath);
1061
+ const targetName = path4.basename(targetPath);
1062
+ const stagingRoot = fs5.mkdtempSync(path4.join(parentDir, `.${targetName}-sync-`));
1063
+ const stagedPath = path4.join(stagingRoot, targetName);
1064
+ const backupPath = path4.join(stagingRoot, `${targetName}-backup`);
1065
+ try {
1066
+ fs5.cpSync(sourcePath, stagedPath, { recursive: true });
1067
+ fs5.renameSync(targetPath, backupPath);
1068
+ try {
1069
+ fs5.renameSync(stagedPath, targetPath);
1070
+ } catch (error) {
1071
+ if (fs5.existsSync(backupPath) && !fs5.existsSync(targetPath)) {
1072
+ fs5.renameSync(backupPath, targetPath);
1073
+ }
1074
+ throw error;
1075
+ }
1076
+ fs5.rmSync(backupPath, { recursive: true, force: true });
1077
+ return "refreshed";
1078
+ } finally {
1079
+ if (fs5.existsSync(stagedPath)) {
1080
+ fs5.rmSync(stagedPath, { recursive: true, force: true });
1081
+ }
1082
+ if (fs5.existsSync(backupPath)) {
1083
+ fs5.rmSync(backupPath, { recursive: true, force: true });
1084
+ }
1085
+ fs5.rmSync(stagingRoot, { recursive: true, force: true });
1086
+ }
1087
+ }
1088
+ function cleanupLegacyAlias(targetDir, aliasName) {
1089
+ const aliasPath = path4.join(targetDir, aliasName);
1090
+ if (!fs5.existsSync(aliasPath)) {
1091
+ return { removed: [], preserved: [] };
1092
+ }
1093
+ let stats;
1094
+ try {
1095
+ stats = fs5.statSync(aliasPath);
1096
+ } catch {
1097
+ return {
1098
+ removed: [],
1099
+ preserved: [{ targetDir, name: aliasName, reason: "unknown_layout" }]
1100
+ };
1101
+ }
1102
+ if (!stats.isDirectory()) {
1103
+ return {
1104
+ removed: [],
1105
+ preserved: [{ targetDir, name: aliasName, reason: "unknown_layout" }]
1106
+ };
1107
+ }
1108
+ if (fs5.existsSync(path4.join(aliasPath, "SKILL.md"))) {
1109
+ return {
1110
+ removed: [],
1111
+ preserved: [{ targetDir, name: aliasName, reason: "contains_skill_md" }]
1112
+ };
1113
+ }
1114
+ const entries = fs5.readdirSync(aliasPath);
1115
+ if (entries.length === 0) {
1116
+ fs5.rmSync(aliasPath, { recursive: true, force: true });
1117
+ return { removed: [aliasName], preserved: [] };
1118
+ }
1119
+ return {
1120
+ removed: [],
1121
+ preserved: [{ targetDir, name: aliasName, reason: "non_empty" }]
1122
+ };
1123
+ }
1124
+ function cleanupLegacyAliases(targetDir) {
1125
+ const removed = [];
1126
+ const preserved = [];
1127
+ for (const alias of LEGACY_ALIAS_DIRS) {
1128
+ const result = cleanupLegacyAlias(targetDir, alias.name);
1129
+ removed.push(...result.removed);
1130
+ preserved.push(...result.preserved);
1131
+ }
1132
+ return { removed, preserved };
1133
+ }
1134
+ function buildSyncMessage(mode, result) {
1135
+ return `Skills ${mode} sync: ${formatSummary(
1136
+ [
1137
+ result.installed.length > 0 ? `${result.installed.length} installed` : "",
1138
+ result.refreshed.length > 0 ? `${result.refreshed.length} refreshed` : "",
1139
+ result.unchanged.length > 0 ? `${result.unchanged.length} unchanged` : "",
1140
+ result.removedLegacyAliases.length > 0 ? `${result.removedLegacyAliases.length} legacy aliases removed` : "",
1141
+ result.preservedLegacyAliases.length > 0 ? `${result.preservedLegacyAliases.length} legacy aliases preserved` : ""
1142
+ ].filter(Boolean),
1143
+ result.targets.length,
1144
+ result.targets.filter((entry) => !entry.success).length
1145
+ )}`;
1146
+ }
1147
+ function buildRemovalMessage(mode, result) {
1148
+ return `Skills ${mode} removal: ${formatSummary(
1149
+ [
1150
+ result.removed.length > 0 ? `${result.removed.length} removed` : "",
1151
+ result.missing.length > 0 ? `${result.missing.length} already absent` : "",
1152
+ result.removedLegacyAliases.length > 0 ? `${result.removedLegacyAliases.length} legacy aliases removed` : "",
1153
+ result.preservedLegacyAliases.length > 0 ? `${result.preservedLegacyAliases.length} legacy aliases preserved` : ""
1154
+ ].filter(Boolean),
1155
+ result.targets.length,
1156
+ result.targets.filter((entry) => !entry.success).length
1157
+ )}`;
1158
+ }
1159
+ function syncBundledSkills(mode) {
1160
+ const targets = getTargets(mode);
1187
1161
  const targetResults = [];
1188
1162
  try {
1189
1163
  const sourceDir = getBundledSkillsDir();
1190
- const entries = fs6.readdirSync(sourceDir, { withFileTypes: true });
1164
+ const packNames = getCanonicalBundledSkillNames();
1165
+ const bundledFingerprints = /* @__PURE__ */ new Map();
1166
+ for (const packName of packNames) {
1167
+ const sourcePath = path4.join(sourceDir, packName);
1168
+ if (!fs5.existsSync(sourcePath)) {
1169
+ throw new Error(`Bundled skill directory missing: ${packName}`);
1170
+ }
1171
+ bundledFingerprints.set(packName, hashDirectoryTree(sourcePath));
1172
+ }
1191
1173
  for (const target of targets) {
1192
- const installed2 = [];
1193
- const skipped2 = [];
1174
+ const installed = [];
1175
+ const refreshed = [];
1176
+ const unchanged = [];
1177
+ const removedLegacyAliases = [];
1178
+ const preservedLegacyAliases = [];
1194
1179
  try {
1195
1180
  ensureDir(target.dir);
1196
- for (const entry of entries) {
1197
- if (!entry.isDirectory()) continue;
1198
- const skillName = entry.name;
1199
- const sourcePath = path4.join(sourceDir, skillName);
1200
- const targetPath = path4.join(target.dir, skillName);
1201
- if (fs6.existsSync(targetPath)) {
1202
- skipped2.push(skillName);
1203
- continue;
1181
+ for (const packName of packNames) {
1182
+ const sourcePath = path4.join(sourceDir, packName);
1183
+ const targetPath = path4.join(target.dir, packName);
1184
+ const sourceFingerprint = bundledFingerprints.get(packName);
1185
+ if (!sourceFingerprint) {
1186
+ throw new Error(`Bundled fingerprint missing: ${packName}`);
1187
+ }
1188
+ const outcome = syncSkillDirectory(sourcePath, targetPath, sourceFingerprint);
1189
+ if (outcome === "installed") {
1190
+ installed.push(packName);
1191
+ } else if (outcome === "refreshed") {
1192
+ refreshed.push(packName);
1193
+ } else {
1194
+ unchanged.push(packName);
1204
1195
  }
1205
- fs6.cpSync(sourcePath, targetPath, { recursive: true });
1206
- installed2.push(skillName);
1207
1196
  }
1197
+ const legacyCleanup = cleanupLegacyAliases(target.dir);
1198
+ removedLegacyAliases.push(...legacyCleanup.removed);
1199
+ preservedLegacyAliases.push(...legacyCleanup.preserved);
1208
1200
  targetResults.push({
1209
1201
  agents: target.agents,
1210
1202
  targetDir: target.dir,
1211
- installed: installed2,
1212
- skipped: skipped2,
1203
+ installed,
1204
+ refreshed,
1205
+ unchanged,
1206
+ removedLegacyAliases,
1207
+ preservedLegacyAliases,
1213
1208
  success: true
1214
1209
  });
1215
1210
  } catch (error) {
@@ -1217,41 +1212,112 @@ function installSkills(mode) {
1217
1212
  targetResults.push({
1218
1213
  agents: target.agents,
1219
1214
  targetDir: target.dir,
1220
- installed: installed2,
1221
- skipped: skipped2,
1215
+ installed,
1216
+ refreshed,
1217
+ unchanged,
1218
+ removedLegacyAliases,
1219
+ preservedLegacyAliases,
1222
1220
  success: false,
1223
1221
  error: message
1224
1222
  });
1225
1223
  }
1226
1224
  }
1227
- const installed = targetResults.flatMap((result) => result.installed);
1228
- const skipped = targetResults.flatMap((result) => result.skipped);
1229
- const failures = targetResults.filter((result) => !result.success);
1230
- const failedSummary = failures.length > 0 ? `, ${failures.length} failed` : "";
1231
- const summary = `Skills ${mode} install: ${installed.length} installed${skipped.length ? `, ${skipped.length} skipped` : ""}${failedSummary} across ${targetResults.length} targets`;
1232
- return {
1233
- success: failures.length === 0,
1234
- message: summary,
1225
+ const result = {
1226
+ success: targetResults.every((entry) => entry.success),
1227
+ message: "",
1235
1228
  mode,
1236
1229
  targets: targetResults,
1237
- installed,
1238
- skipped
1230
+ installed: targetResults.flatMap((entry) => entry.installed),
1231
+ refreshed: targetResults.flatMap((entry) => entry.refreshed),
1232
+ unchanged: targetResults.flatMap((entry) => entry.unchanged),
1233
+ removedLegacyAliases: targetResults.flatMap((entry) => entry.removedLegacyAliases),
1234
+ preservedLegacyAliases: targetResults.flatMap((entry) => entry.preservedLegacyAliases)
1239
1235
  };
1236
+ result.message = buildSyncMessage(mode, result);
1237
+ return result;
1240
1238
  } catch (error) {
1241
1239
  const message = error instanceof Error ? error.message : String(error);
1242
- return {
1240
+ const result = {
1243
1241
  success: false,
1244
- message: `Failed to install skills (${mode}): ${message}`,
1242
+ message: "",
1245
1243
  mode,
1246
1244
  targets: targetResults,
1247
- installed: targetResults.flatMap((result) => result.installed),
1248
- skipped: targetResults.flatMap((result) => result.skipped)
1245
+ installed: targetResults.flatMap((entry) => entry.installed),
1246
+ refreshed: targetResults.flatMap((entry) => entry.refreshed),
1247
+ unchanged: targetResults.flatMap((entry) => entry.unchanged),
1248
+ removedLegacyAliases: targetResults.flatMap((entry) => entry.removedLegacyAliases),
1249
+ preservedLegacyAliases: targetResults.flatMap((entry) => entry.preservedLegacyAliases)
1249
1250
  };
1251
+ result.message = `Failed to sync skills (${mode}): ${message}`;
1252
+ return result;
1253
+ }
1254
+ }
1255
+ function removeBundledSkills(mode) {
1256
+ const targets = getTargets(mode);
1257
+ const packNames = getCanonicalBundledSkillNames();
1258
+ const targetResults = [];
1259
+ for (const target of targets) {
1260
+ const removed = [];
1261
+ const missing = [];
1262
+ const removedLegacyAliases = [];
1263
+ const preservedLegacyAliases = [];
1264
+ try {
1265
+ for (const packName of packNames) {
1266
+ const targetPath = path4.join(target.dir, packName);
1267
+ if (fs5.existsSync(targetPath)) {
1268
+ fs5.rmSync(targetPath, { recursive: true, force: true });
1269
+ removed.push(packName);
1270
+ } else {
1271
+ missing.push(packName);
1272
+ }
1273
+ }
1274
+ const legacyCleanup = cleanupLegacyAliases(target.dir);
1275
+ removedLegacyAliases.push(...legacyCleanup.removed);
1276
+ preservedLegacyAliases.push(...legacyCleanup.preserved);
1277
+ targetResults.push({
1278
+ agents: target.agents,
1279
+ targetDir: target.dir,
1280
+ removed,
1281
+ missing,
1282
+ removedLegacyAliases,
1283
+ preservedLegacyAliases,
1284
+ success: true
1285
+ });
1286
+ } catch (error) {
1287
+ const message = error instanceof Error ? error.message : String(error);
1288
+ targetResults.push({
1289
+ agents: target.agents,
1290
+ targetDir: target.dir,
1291
+ removed,
1292
+ missing,
1293
+ removedLegacyAliases,
1294
+ preservedLegacyAliases,
1295
+ success: false,
1296
+ error: message
1297
+ });
1298
+ }
1250
1299
  }
1300
+ const result = {
1301
+ success: targetResults.every((entry) => entry.success),
1302
+ message: "",
1303
+ mode,
1304
+ targets: targetResults,
1305
+ removed: targetResults.flatMap((entry) => entry.removed),
1306
+ missing: targetResults.flatMap((entry) => entry.missing),
1307
+ removedLegacyAliases: targetResults.flatMap((entry) => entry.removedLegacyAliases),
1308
+ preservedLegacyAliases: targetResults.flatMap((entry) => entry.preservedLegacyAliases)
1309
+ };
1310
+ result.message = buildRemovalMessage(mode, result);
1311
+ return result;
1312
+ }
1313
+ function hasBundledSkillArtifacts(mode) {
1314
+ const packNames = getCanonicalBundledSkillNames();
1315
+ const targets = getTargets(mode);
1316
+ return targets.some((target) => hasCanonicalBundledSkillInTarget(target.dir, packNames));
1251
1317
  }
1252
1318
 
1253
1319
  // src/cli/commands/update.ts
1254
- import * as fs7 from "fs";
1320
+ import * as fs6 from "fs";
1255
1321
  import * as path5 from "path";
1256
1322
  import * as os4 from "os";
1257
1323
  var PLUGIN_NAME2 = "opendevbrowser";
@@ -1265,8 +1331,8 @@ function rmdir(dirPath) {
1265
1331
  if (!resolvedPath.startsWith(resolvedCache + path5.sep) || resolvedPath === resolvedCache) {
1266
1332
  throw new Error(`Security: refusing to delete path outside cache directory: ${dirPath}`);
1267
1333
  }
1268
- if (fs7.existsSync(dirPath)) {
1269
- fs7.rmSync(dirPath, { recursive: true, force: true });
1334
+ if (fs6.existsSync(dirPath)) {
1335
+ fs6.rmSync(dirPath, { recursive: true, force: true });
1270
1336
  }
1271
1337
  }
1272
1338
  function runUpdate() {
@@ -1274,8 +1340,8 @@ function runUpdate() {
1274
1340
  const nodeModulesDir = path5.join(cacheDir, "node_modules");
1275
1341
  const pluginCacheDir = path5.join(nodeModulesDir, PLUGIN_NAME2);
1276
1342
  try {
1277
- if (!fs7.existsSync(pluginCacheDir)) {
1278
- if (fs7.existsSync(nodeModulesDir)) {
1343
+ if (!fs6.existsSync(pluginCacheDir)) {
1344
+ if (fs6.existsSync(nodeModulesDir)) {
1279
1345
  rmdir(nodeModulesDir);
1280
1346
  return {
1281
1347
  success: true,
@@ -1306,7 +1372,7 @@ function runUpdate() {
1306
1372
  }
1307
1373
 
1308
1374
  // src/cli/commands/uninstall.ts
1309
- import * as fs8 from "fs";
1375
+ import * as fs7 from "fs";
1310
1376
  import * as path6 from "path";
1311
1377
  import * as os5 from "os";
1312
1378
  function getPluginConfigPath2(mode) {
@@ -1318,8 +1384,8 @@ function getPluginConfigPath2(mode) {
1318
1384
  }
1319
1385
  function removePluginConfigFile(mode) {
1320
1386
  const configPath = getPluginConfigPath2(mode);
1321
- if (fs8.existsSync(configPath)) {
1322
- fs8.unlinkSync(configPath);
1387
+ if (fs7.existsSync(configPath)) {
1388
+ fs7.unlinkSync(configPath);
1323
1389
  return true;
1324
1390
  }
1325
1391
  return false;
@@ -1338,7 +1404,7 @@ function runUninstall(mode, deleteConfigFile = false) {
1338
1404
  };
1339
1405
  }
1340
1406
  const newContent = removePluginFromContent(content, "opendevbrowser");
1341
- fs8.writeFileSync(configPath, newContent, "utf-8");
1407
+ fs7.writeFileSync(configPath, newContent, "utf-8");
1342
1408
  let configFileDeleted = false;
1343
1409
  if (deleteConfigFile) {
1344
1410
  configFileDeleted = removePluginConfigFile(mode);
@@ -1398,6 +1464,16 @@ function parseNumberFlag(value, flag, options = {}) {
1398
1464
  }
1399
1465
  return parsed;
1400
1466
  }
1467
+ function parseBooleanFlag(value, flag) {
1468
+ const normalized = value.trim().toLowerCase();
1469
+ if (normalized === "true" || normalized === "1") {
1470
+ return true;
1471
+ }
1472
+ if (normalized === "false" || normalized === "0") {
1473
+ return false;
1474
+ }
1475
+ throw createUsageError(`Invalid ${flag}: ${value}`);
1476
+ }
1401
1477
  function parseOptionalStringFlag(rawArgs, flag) {
1402
1478
  for (let i = 0; i < rawArgs.length; i += 1) {
1403
1479
  const arg = rawArgs[i];
@@ -1418,13 +1494,23 @@ function parseOptionalStringFlag(rawArgs, flag) {
1418
1494
  }
1419
1495
  return void 0;
1420
1496
  }
1497
+ function parseStringArrayFlag(rawArgs, flag) {
1498
+ const value = parseOptionalStringFlag(rawArgs, flag);
1499
+ if (typeof value !== "string") {
1500
+ return void 0;
1501
+ }
1502
+ const items = value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
1503
+ if (items.length === 0) {
1504
+ throw createUsageError(`Missing value for ${flag}`);
1505
+ }
1506
+ return items;
1507
+ }
1421
1508
 
1422
1509
  // src/cli/commands/native.ts
1423
- import * as fs9 from "fs";
1510
+ import * as fs8 from "fs";
1424
1511
  import * as path7 from "path";
1425
- import { homedir as homedir6 } from "os";
1426
1512
  import { execFileSync } from "child_process";
1427
- import { fileURLToPath as fileURLToPath2 } from "url";
1513
+ import { fileURLToPath } from "url";
1428
1514
  var EXTENSION_ID_RE = /^[a-p]{32}$/;
1429
1515
  var EXTENSION_NAME = "OpenDevBrowser Relay";
1430
1516
  var ANNOTATION_COMMAND_NAME = "toggle-annotation";
@@ -1472,7 +1558,7 @@ var getManifestDir = () => {
1472
1558
  throw createUsageError(`Native messaging is not supported on ${process.platform}.`);
1473
1559
  };
1474
1560
  var getScriptsDir = () => {
1475
- const __filename = fileURLToPath2(import.meta.url);
1561
+ const __filename = fileURLToPath(import.meta.url);
1476
1562
  const startDir = path7.dirname(__filename);
1477
1563
  const rootsToScan = [startDir, process.cwd()];
1478
1564
  for (const root of rootsToScan) {
@@ -1480,7 +1566,7 @@ var getScriptsDir = () => {
1480
1566
  while (true) {
1481
1567
  const scriptsDir = path7.join(current, "scripts", "native");
1482
1568
  const packageJsonPath = path7.join(current, "package.json");
1483
- if (fs9.existsSync(scriptsDir) && fs9.existsSync(packageJsonPath)) {
1569
+ if (fs8.existsSync(scriptsDir) && fs8.existsSync(packageJsonPath)) {
1484
1570
  return scriptsDir;
1485
1571
  }
1486
1572
  const parent = path7.dirname(current);
@@ -1504,7 +1590,7 @@ var getWrapperPath = () => {
1504
1590
  };
1505
1591
  var readManifest = (manifestPath) => {
1506
1592
  try {
1507
- const raw = fs9.readFileSync(manifestPath, "utf8");
1593
+ const raw = fs8.readFileSync(manifestPath, "utf8");
1508
1594
  const data = JSON.parse(raw);
1509
1595
  const origins = Array.isArray(data.allowed_origins) ? data.allowed_origins : [];
1510
1596
  const match = origins.find((origin) => origin.startsWith("chrome-extension://"));
@@ -1541,57 +1627,11 @@ var readRegistryPath = () => {
1541
1627
  };
1542
1628
  var normalizePath = (value) => {
1543
1629
  try {
1544
- return fs9.realpathSync(value);
1630
+ return fs8.realpathSync(value);
1545
1631
  } catch {
1546
1632
  return path7.resolve(value);
1547
1633
  }
1548
1634
  };
1549
- var getChromeUserDataRoots = () => {
1550
- if (process.platform === "darwin") {
1551
- return [
1552
- path7.join(homedir6(), "Library", "Application Support", "Google", "Chrome"),
1553
- path7.join(homedir6(), "Library", "Application Support", "Chromium"),
1554
- path7.join(homedir6(), "Library", "Application Support", "BraveSoftware", "Brave-Browser")
1555
- ];
1556
- }
1557
- if (process.platform === "linux") {
1558
- return [
1559
- path7.join(homedir6(), ".config", "google-chrome"),
1560
- path7.join(homedir6(), ".config", "chromium"),
1561
- path7.join(homedir6(), ".config", "BraveSoftware", "Brave-Browser")
1562
- ];
1563
- }
1564
- if (process.platform === "win32") {
1565
- const base = process.env.LOCALAPPDATA || (process.env.USERPROFILE ? path7.join(process.env.USERPROFILE, "AppData", "Local") : "");
1566
- if (!base) return [];
1567
- return [
1568
- path7.join(base, "Google", "Chrome", "User Data"),
1569
- path7.join(base, "Chromium", "User Data"),
1570
- path7.join(base, "BraveSoftware", "Brave-Browser", "User Data")
1571
- ];
1572
- }
1573
- return [];
1574
- };
1575
- var PROFILE_PREFERENCES_FILES = ["Preferences", "Secure Preferences"];
1576
- var getProfileDirs = (root) => {
1577
- try {
1578
- const entries = fs9.readdirSync(root, { withFileTypes: true });
1579
- return entries.filter((entry) => entry.isDirectory() && (entry.name === "Default" || entry.name.startsWith("Profile "))).map((entry) => path7.join(root, entry.name)).filter((dir) => PROFILE_PREFERENCES_FILES.some((filename) => fs9.existsSync(path7.join(dir, filename))));
1580
- } catch {
1581
- return [];
1582
- }
1583
- };
1584
- var readProfilePreferences = (profileDir) => {
1585
- const records = [];
1586
- for (const filename of PROFILE_PREFERENCES_FILES) {
1587
- try {
1588
- const raw = fs9.readFileSync(path7.join(profileDir, filename), "utf8");
1589
- records.push(JSON.parse(raw));
1590
- } catch {
1591
- }
1592
- }
1593
- return records;
1594
- };
1595
1635
  var findExtensionIdInCommands = (preferences) => {
1596
1636
  const extensionCommands = preferences.extensions;
1597
1637
  const commandMaps = [
@@ -1651,7 +1691,7 @@ var getExtensionPathCandidates = () => {
1651
1691
  candidates.add(normalizePath(primary));
1652
1692
  }
1653
1693
  const cwdExtension = path7.join(process.cwd(), "extension");
1654
- if (fs9.existsSync(path7.join(cwdExtension, "manifest.json"))) {
1694
+ if (fs8.existsSync(path7.join(cwdExtension, "manifest.json"))) {
1655
1695
  candidates.add(normalizePath(cwdExtension));
1656
1696
  }
1657
1697
  if (candidates.size === 0) {
@@ -1736,13 +1776,13 @@ var getNativeStatusSnapshot = () => {
1736
1776
  let manifestExists = false;
1737
1777
  let wrapperExists = false;
1738
1778
  let extensionIdValue = null;
1739
- if (fs9.existsSync(manifestPath)) {
1779
+ if (fs8.existsSync(manifestPath)) {
1740
1780
  manifestExists = true;
1741
1781
  installed = true;
1742
1782
  const manifest = readManifest(manifestPath);
1743
1783
  extensionIdValue = manifest.extensionId;
1744
1784
  }
1745
- if (fs9.existsSync(wrapperPath)) {
1785
+ if (fs8.existsSync(wrapperPath)) {
1746
1786
  wrapperExists = true;
1747
1787
  }
1748
1788
  if (!manifestExists || !wrapperExists) {
@@ -1768,6 +1808,30 @@ var getNativeStatusSnapshot = () => {
1768
1808
  mismatch
1769
1809
  };
1770
1810
  };
1811
+ function assessNativeStatus(data) {
1812
+ if (!data.installed) {
1813
+ return {
1814
+ success: false,
1815
+ message: "Native host not installed.",
1816
+ summary: "not installed",
1817
+ exitCode: EXIT_DISCONNECTED
1818
+ };
1819
+ }
1820
+ if (data.mismatch && data.extensionId && data.expectedExtensionId) {
1821
+ return {
1822
+ success: false,
1823
+ message: `Native host targets ${data.extensionId}, but the current extension is ${data.expectedExtensionId}. Reinstall with \`opendevbrowser native install ${data.expectedExtensionId}\` or rerun \`opendevbrowser serve\`.`,
1824
+ summary: `mismatch (${data.extensionId} != ${data.expectedExtensionId})`,
1825
+ exitCode: EXIT_DISCONNECTED
1826
+ };
1827
+ }
1828
+ return {
1829
+ success: true,
1830
+ message: data.extensionId ? `Native host installed for extension ${data.extensionId}.` : "Native host installed (extension id missing).",
1831
+ summary: `installed${data.extensionId ? ` (${data.extensionId})` : ""}`,
1832
+ exitCode: null
1833
+ };
1834
+ }
1771
1835
  function installNativeHost(extensionId) {
1772
1836
  const normalized = normalizeExtensionId(extensionId);
1773
1837
  if (!normalized) {
@@ -1778,7 +1842,7 @@ function installNativeHost(extensionId) {
1778
1842
  };
1779
1843
  }
1780
1844
  const hostScript = getHostScriptPath();
1781
- if (!fs9.existsSync(hostScript)) {
1845
+ if (!fs8.existsSync(hostScript)) {
1782
1846
  return {
1783
1847
  success: false,
1784
1848
  message: `Native host not found at ${hostScript}.`,
@@ -1816,34 +1880,27 @@ async function runNativeCommand(args) {
1816
1880
  runScript(uninstallScript, []);
1817
1881
  return { success: true, message: "Native host uninstalled." };
1818
1882
  } catch (error) {
1819
- const message2 = error instanceof Error ? error.message : String(error);
1820
- return { success: false, message: `Native uninstall failed: ${message2}`, exitCode: EXIT_EXECUTION };
1883
+ const message = error instanceof Error ? error.message : String(error);
1884
+ return { success: false, message: `Native uninstall failed: ${message}`, exitCode: EXIT_EXECUTION };
1821
1885
  }
1822
1886
  }
1823
1887
  const data = getNativeStatusSnapshot();
1824
- if (!data.installed) {
1825
- return {
1826
- success: false,
1827
- message: "Native host not installed.",
1828
- data,
1829
- exitCode: EXIT_DISCONNECTED
1830
- };
1831
- }
1832
- if (data.mismatch && data.extensionId && data.expectedExtensionId) {
1833
- return {
1834
- success: false,
1835
- message: `Native host targets ${data.extensionId}, but the current extension is ${data.expectedExtensionId}. Reinstall with \`opendevbrowser native install ${data.expectedExtensionId}\` or rerun \`opendevbrowser serve\`.`,
1836
- data,
1837
- exitCode: EXIT_DISCONNECTED
1838
- };
1839
- }
1840
- const message = data.extensionId ? `Native host installed for extension ${data.extensionId}.` : "Native host installed (extension id missing).";
1841
- return { success: true, message, data };
1888
+ const assessment = assessNativeStatus(data);
1889
+ return {
1890
+ success: assessment.success,
1891
+ message: assessment.message,
1892
+ data,
1893
+ exitCode: assessment.exitCode
1894
+ };
1842
1895
  }
1843
1896
 
1844
1897
  // src/cli/commands/serve.ts
1845
1898
  var daemonHandle = null;
1846
1899
  var PS_MAX_BUFFER = 8 * 1024 * 1024;
1900
+ var SERVE_COMMAND_PATTERN = /(?:^|\s)(?:\S*[\\/])?(?:opendevbrowser|dist[\\/]+cli[\\/]+index\.js)(?=\s|$).*?\bserve\b/;
1901
+ var SERVE_STOP_PATTERN = /(?:^|\s)--stop(?:\s|$)/;
1902
+ var CURRENT_UID = typeof process.getuid === "function" ? process.getuid() : null;
1903
+ var CURRENT_EXECUTABLE = process.execPath;
1847
1904
  function resolveTokenCandidates(requestedToken, metadataToken, configToken) {
1848
1905
  return Array.from(new Set([requestedToken, metadataToken, configToken].filter((token) => typeof token === "string" && token.trim().length > 0)));
1849
1906
  }
@@ -1901,32 +1958,52 @@ function parseServeArgs(rawArgs) {
1901
1958
  }
1902
1959
  return parsed;
1903
1960
  }
1904
- function listServeProcessPids() {
1905
- const result = spawnSync("ps", ["-ax", "-o", "pid=,command="], {
1961
+ function parseServeProcessSnapshot(line) {
1962
+ const trimmed = line.trim();
1963
+ if (trimmed.length === 0) {
1964
+ return null;
1965
+ }
1966
+ const match = trimmed.match(/^(\d+)(?:\s+(\d+))?\s+(.*)$/);
1967
+ if (!match) {
1968
+ return null;
1969
+ }
1970
+ const pid = Number.parseInt(match[1] ?? "", 10);
1971
+ if (!Number.isInteger(pid) || pid <= 0) {
1972
+ return null;
1973
+ }
1974
+ const rawUid = match[2];
1975
+ const parsedUid = typeof rawUid === "string" && rawUid.length > 0 ? Number.parseInt(rawUid, 10) : null;
1976
+ const command = (match[3] ?? "").trim();
1977
+ if (command.length === 0) {
1978
+ return null;
1979
+ }
1980
+ return {
1981
+ pid,
1982
+ uid: typeof parsedUid === "number" && Number.isInteger(parsedUid) && parsedUid >= 0 ? parsedUid : null,
1983
+ command
1984
+ };
1985
+ }
1986
+ function listServeProcessSnapshots() {
1987
+ const result = spawnSync("ps", ["-axww", "-o", "pid=,uid=,command="], {
1906
1988
  encoding: "utf-8",
1907
1989
  maxBuffer: PS_MAX_BUFFER
1908
1990
  });
1909
1991
  if ((result.status ?? 1) !== 0) {
1910
1992
  return [];
1911
1993
  }
1912
- const servePattern = /\b(opendevbrowser|dist\/cli\/index\.js)\b.*\bserve\b/;
1913
- const lines = String(result.stdout ?? "").split(/\r?\n/);
1914
- const pids = /* @__PURE__ */ new Set();
1915
- for (const line of lines) {
1916
- const trimmed = line.trim();
1917
- if (!trimmed) continue;
1918
- const match = trimmed.match(/^(\d+)\s+(.+)$/);
1919
- if (!match) continue;
1920
- const pidText = match[1];
1921
- if (!pidText) continue;
1922
- const pid = Number.parseInt(pidText, 10);
1923
- const command = match[2] ?? "";
1924
- if (!Number.isInteger(pid) || pid <= 0) continue;
1925
- if (pid === process.pid || pid === process.ppid) continue;
1926
- if (!servePattern.test(command)) continue;
1927
- pids.add(pid);
1928
- }
1929
- return [...pids];
1994
+ return String(result.stdout ?? "").split("\n").map((line) => parseServeProcessSnapshot(line)).filter((snapshot) => snapshot !== null);
1995
+ }
1996
+ function isCurrentExecutableServeProcess(snapshot) {
1997
+ if (CURRENT_UID === null || snapshot.uid === null || snapshot.uid !== CURRENT_UID) {
1998
+ return false;
1999
+ }
2000
+ if (!snapshot.command.includes(CURRENT_EXECUTABLE)) {
2001
+ return false;
2002
+ }
2003
+ if (!SERVE_COMMAND_PATTERN.test(snapshot.command)) {
2004
+ return false;
2005
+ }
2006
+ return !SERVE_STOP_PATTERN.test(snapshot.command);
1930
2007
  }
1931
2008
  function terminateProcess(pid) {
1932
2009
  if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid || pid === process.ppid) {
@@ -1943,16 +2020,29 @@ function terminateProcess(pid) {
1943
2020
  }
1944
2021
  return true;
1945
2022
  }
1946
- function cleanupStaleServeProcesses(keepPid) {
1947
- const candidates = listServeProcessPids();
1948
- let cleaned = 0;
1949
- for (const pid of candidates) {
1950
- if (Number.isInteger(keepPid) && pid === keepPid) continue;
1951
- if (terminateProcess(pid)) {
1952
- cleaned += 1;
2023
+ function cleanupCompetingServeProcesses(keepPid) {
2024
+ const candidates = listServeProcessSnapshots().filter((snapshot) => {
2025
+ if (!isCurrentExecutableServeProcess(snapshot)) {
2026
+ return false;
2027
+ }
2028
+ if (snapshot.pid === process.pid || snapshot.pid === process.ppid) {
2029
+ return false;
1953
2030
  }
2031
+ if (Number.isInteger(keepPid) && snapshot.pid === keepPid) {
2032
+ return false;
2033
+ }
2034
+ return true;
2035
+ });
2036
+ if (candidates.length === 0) {
2037
+ return [];
1954
2038
  }
1955
- return cleaned;
2039
+ const clearedPids = [];
2040
+ for (const snapshot of candidates) {
2041
+ if (terminateProcess(snapshot.pid)) {
2042
+ clearedPids.push(snapshot.pid);
2043
+ }
2044
+ }
2045
+ return clearedPids;
1956
2046
  }
1957
2047
  async function runServe(args) {
1958
2048
  const serveArgs = parseServeArgs(args.rawArgs);
@@ -1986,10 +2076,13 @@ async function runServe(args) {
1986
2076
  const metadataToken = metadata?.port === requestedPort ? metadata.token : void 0;
1987
2077
  const tokenCandidates = resolveTokenCandidates(serveArgs.token, metadataToken, config.daemonToken);
1988
2078
  const existingDaemon = await resolveExistingDaemon(requestedPort, tokenCandidates);
1989
- const staleCleared = cleanupStaleServeProcesses(existingDaemon?.status.pid);
2079
+ const staleDaemonPids = new Set(cleanupCompetingServeProcesses(existingDaemon?.status.pid));
2080
+ const staleCleared = () => staleDaemonPids.size;
1990
2081
  if (existingDaemon) {
1991
2082
  const relayPort = existingDaemon.status.relay.port ?? config.relayPort;
1992
- const staleNote2 = staleCleared > 0 ? ` Cleared ${staleCleared} stale daemon process${staleCleared === 1 ? "" : "es"}.` : "";
2083
+ const clearedCount2 = staleCleared();
2084
+ const staleNote2 = clearedCount2 > 0 ? `
2085
+ Cleared ${clearedCount2} stale daemon process${clearedCount2 === 1 ? "" : "es"}.` : "";
1993
2086
  return {
1994
2087
  success: true,
1995
2088
  message: `Daemon already running on 127.0.0.1:${requestedPort} (pid=${existingDaemon.status.pid}, relay ${relayPort}).${staleNote2}`,
@@ -1998,7 +2091,7 @@ async function runServe(args) {
1998
2091
  pid: existingDaemon.status.pid,
1999
2092
  relayPort,
2000
2093
  alreadyRunning: true,
2001
- staleDaemonsCleared: staleCleared,
2094
+ staleDaemonsCleared: clearedCount2,
2002
2095
  relay: existingDaemon.status.relay
2003
2096
  },
2004
2097
  exitCode: null
@@ -2026,32 +2119,62 @@ async function runServe(args) {
2026
2119
  nativeMessage = "Native host not installed. Set nativeExtensionId in opendevbrowser.jsonc to auto-install.";
2027
2120
  }
2028
2121
  }
2029
- let handle;
2030
- try {
2031
- handle = await startDaemon({
2032
- port: serveArgs.port,
2033
- token: serveArgs.token,
2034
- config
2035
- });
2036
- } catch (error) {
2037
- const message2 = error instanceof Error ? error.message : String(error);
2038
- if (message2.includes("EADDRINUSE") || message2.includes("in use")) {
2122
+ let handle = null;
2123
+ let startError = null;
2124
+ for (let attempt = 0; attempt < 2; attempt += 1) {
2125
+ try {
2126
+ handle = await startDaemon({
2127
+ port: serveArgs.port,
2128
+ token: serveArgs.token,
2129
+ config
2130
+ });
2131
+ startError = null;
2132
+ break;
2133
+ } catch (error) {
2134
+ startError = error;
2135
+ const message2 = error instanceof Error ? error.message : String(error);
2136
+ if (!message2.includes("EADDRINUSE") && !message2.includes("in use")) {
2137
+ break;
2138
+ }
2039
2139
  const runningDaemon = await resolveExistingDaemon(requestedPort, tokenCandidates);
2040
2140
  if (runningDaemon) {
2041
2141
  const relayPort = runningDaemon.status.relay.port ?? config.relayPort;
2142
+ const clearedCount2 = staleCleared();
2143
+ const staleNote2 = clearedCount2 > 0 ? `
2144
+ Cleared ${clearedCount2} stale daemon process${clearedCount2 === 1 ? "" : "es"}.` : "";
2042
2145
  return {
2043
2146
  success: true,
2044
- message: `Daemon already running on 127.0.0.1:${requestedPort} (pid=${runningDaemon.status.pid}, relay ${relayPort}).`,
2147
+ message: `Daemon already running on 127.0.0.1:${requestedPort} (pid=${runningDaemon.status.pid}, relay ${relayPort}).${staleNote2}`,
2045
2148
  data: {
2046
2149
  port: requestedPort,
2047
2150
  pid: runningDaemon.status.pid,
2048
2151
  relayPort,
2049
2152
  alreadyRunning: true,
2153
+ staleDaemonsCleared: clearedCount2,
2050
2154
  relay: runningDaemon.status.relay
2051
2155
  },
2052
2156
  exitCode: null
2053
2157
  };
2054
2158
  }
2159
+ if (attempt === 0) {
2160
+ let clearedNewPid = false;
2161
+ for (const pid of cleanupCompetingServeProcesses()) {
2162
+ const previousSize = staleDaemonPids.size;
2163
+ staleDaemonPids.add(pid);
2164
+ if (staleDaemonPids.size > previousSize) {
2165
+ clearedNewPid = true;
2166
+ }
2167
+ }
2168
+ if (clearedNewPid) {
2169
+ continue;
2170
+ }
2171
+ }
2172
+ break;
2173
+ }
2174
+ }
2175
+ if (!handle) {
2176
+ const message2 = startError instanceof Error ? startError.message : String(startError);
2177
+ if (message2.includes("EADDRINUSE") || message2.includes("in use")) {
2055
2178
  return {
2056
2179
  success: false,
2057
2180
  message: `Daemon port ${requestedPort} is already in use by another process. If this is an existing daemon, run \`opendevbrowser status --daemon\` or \`opendevbrowser serve --stop\`.`,
@@ -2067,54 +2190,93 @@ async function runServe(args) {
2067
2190
  daemonHandle = handle;
2068
2191
  const { state } = handle;
2069
2192
  const baseMessage = `Daemon running on 127.0.0.1:${state.port} (relay ${state.relayPort})`;
2070
- const staleNote = staleCleared > 0 ? `
2071
- Cleared ${staleCleared} stale daemon process${staleCleared === 1 ? "" : "es"}.` : "";
2193
+ const clearedCount = staleCleared();
2194
+ const staleNote = clearedCount > 0 ? `
2195
+ Cleared ${clearedCount} stale daemon process${clearedCount === 1 ? "" : "es"}.` : "";
2072
2196
  const message = nativeMessage ? `${baseMessage}
2073
2197
  ${nativeMessage}${staleNote}` : `${baseMessage}${staleNote}`;
2074
2198
  return {
2075
2199
  success: true,
2076
2200
  message,
2077
- data: { port: state.port, pid: state.pid, relayPort: state.relayPort, native: nativeStatus, staleDaemonsCleared: staleCleared },
2201
+ data: { port: state.port, pid: state.pid, relayPort: state.relayPort, native: nativeStatus, staleDaemonsCleared: clearedCount },
2078
2202
  exitCode: null
2079
2203
  };
2080
2204
  }
2081
2205
 
2082
2206
  // src/cli/daemon-autostart.ts
2083
2207
  import { execFileSync as execFileSync2 } from "child_process";
2084
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
2085
- import { homedir as homedir7 } from "os";
2086
- import { dirname as dirname5, join as join8, resolve as resolve4 } from "path";
2087
- import { fileURLToPath as fileURLToPath3 } from "url";
2208
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
2209
+ import { homedir as homedir6, tmpdir } from "os";
2210
+ import { dirname as dirname5, isAbsolute, join as join8, relative, resolve as resolve4 } from "path";
2211
+ import { fileURLToPath as fileURLToPath2 } from "url";
2088
2212
  var MAC_LABEL = "com.opendevbrowser.daemon";
2089
2213
  var WIN_TASK_NAME = "OpenDevBrowser Daemon";
2214
+ var STABLE_DAEMON_INSTALL_GUIDANCE = "Run opendevbrowser daemon install from a stable install location (for example, a global npm install or a persistent local package install).";
2090
2215
  var defaultDeps = () => ({
2091
2216
  platform: process.platform,
2092
2217
  argv1: process.argv[1] ?? "",
2093
2218
  moduleUrl: import.meta.url,
2094
2219
  uid: typeof process.getuid === "function" ? process.getuid() : 0,
2095
- homedir: homedir7,
2096
- existsSync: existsSync8,
2220
+ homedir: homedir6,
2221
+ existsSync: existsSync7,
2097
2222
  mkdirSync: mkdirSync3,
2098
2223
  writeFileSync: writeFileSync4,
2099
2224
  unlinkSync: unlinkSync2,
2100
- execFileSync: execFileSync2
2225
+ execFileSync: execFileSync2,
2226
+ transientEntrypointRoots: void 0
2101
2227
  });
2228
+ var NPX_CACHE_SEGMENT_PATTERN = /[\\/]_npx(?:[\\/]|$)/;
2229
+ var formatCommand = (programArguments) => {
2230
+ return programArguments.map((value) => `"${value}"`).join(" ");
2231
+ };
2102
2232
  var resolveCliPathFromModule = (moduleUrl, exists) => {
2103
- const modulePath = fileURLToPath3(moduleUrl);
2233
+ const modulePath = fileURLToPath2(moduleUrl);
2104
2234
  const candidate = resolve4(dirname5(modulePath), "..", "index.js");
2105
2235
  if (!exists(candidate)) {
2106
2236
  throw new Error(`CLI entrypoint not found at ${candidate}`);
2107
2237
  }
2108
2238
  return candidate;
2109
2239
  };
2240
+ var normalizeComparisonPath = (value, platform) => {
2241
+ const resolvedPath = resolve4(value);
2242
+ return platform === "win32" ? resolvedPath.toLowerCase() : resolvedPath;
2243
+ };
2244
+ var isPathInsideRoot = (candidate, root, platform) => {
2245
+ const normalizedCandidate = normalizeComparisonPath(candidate, platform);
2246
+ const normalizedRoot = normalizeComparisonPath(root, platform);
2247
+ const relation = relative(normalizedRoot, normalizedCandidate);
2248
+ return relation === "" || !relation.startsWith("..") && !isAbsolute(relation);
2249
+ };
2250
+ var getTransientEntrypointRoots = (deps) => {
2251
+ const configuredRoots = deps.transientEntrypointRoots;
2252
+ if (configuredRoots && configuredRoots.length > 0) {
2253
+ return [...new Set(configuredRoots.map((root) => normalizeComparisonPath(root, deps.platform)))];
2254
+ }
2255
+ const roots = [tmpdir()];
2256
+ if (deps.platform === "darwin") {
2257
+ roots.push("/tmp", "/private/tmp");
2258
+ }
2259
+ return [...new Set(roots.map((root) => normalizeComparisonPath(root, deps.platform)))];
2260
+ };
2261
+ var isTransientCliPath = (cliPath, deps) => {
2262
+ const normalizedCliPath = normalizeComparisonPath(cliPath, deps.platform);
2263
+ return getTransientEntrypointRoots(deps).some((root) => isPathInsideRoot(cliPath, root, deps.platform)) || NPX_CACHE_SEGMENT_PATTERN.test(normalizedCliPath);
2264
+ };
2265
+ var TRANSIENT_AUTOSTART_INSTALL_ERROR_PREFIX = "Cannot install daemon autostart from transient CLI path";
2266
+ var isTransientAutostartInstallError = (error) => {
2267
+ const message = error instanceof Error ? error.message : String(error);
2268
+ return message.startsWith(TRANSIENT_AUTOSTART_INSTALL_ERROR_PREFIX);
2269
+ };
2110
2270
  var resolveCliEntrypoint = (deps = {}) => {
2111
2271
  const resolved = { ...defaultDeps(), ...deps };
2112
2272
  const exists = resolved.existsSync;
2113
2273
  let cliPath = null;
2274
+ let source = "module";
2114
2275
  if (resolved.argv1) {
2115
2276
  const candidate = resolve4(resolved.argv1);
2116
2277
  if (exists(candidate)) {
2117
2278
  cliPath = candidate;
2279
+ source = "argv1";
2118
2280
  }
2119
2281
  }
2120
2282
  if (!cliPath) {
@@ -2122,16 +2284,17 @@ var resolveCliEntrypoint = (deps = {}) => {
2122
2284
  }
2123
2285
  const nodePath = process.execPath;
2124
2286
  const args = [cliPath, "serve"];
2125
- const command = `"${nodePath}" "${cliPath}" serve`;
2126
- return { nodePath, cliPath, args, command };
2287
+ const command = formatCommand([nodePath, ...args]);
2288
+ const isTransient = isTransientCliPath(cliPath, resolved);
2289
+ return { nodePath, cliPath, args, command, source, isTransient };
2127
2290
  };
2128
- var getLaunchAgentPath = (home = homedir7()) => {
2291
+ var getLaunchAgentPath = (home = homedir6()) => {
2129
2292
  return join8(home, "Library", "LaunchAgents", `${MAC_LABEL}.plist`);
2130
2293
  };
2131
2294
  var buildLaunchAgentPlist = (entrypoint, options = {}) => {
2132
2295
  const label = options.label ?? MAC_LABEL;
2133
- const stdoutPath = options.stdoutPath ?? join8(homedir7(), "Library", "Logs", "opendevbrowser-daemon.log");
2134
- const stderrPath = options.stderrPath ?? join8(homedir7(), "Library", "Logs", "opendevbrowser-daemon.err.log");
2296
+ const stdoutPath = options.stdoutPath ?? join8(homedir6(), "Library", "Logs", "opendevbrowser-daemon.log");
2297
+ const stderrPath = options.stderrPath ?? join8(homedir6(), "Library", "Logs", "opendevbrowser-daemon.err.log");
2135
2298
  const programArgs = [entrypoint.nodePath, ...entrypoint.args].map((value) => ` <string>${value}</string>`).join("\n");
2136
2299
  return [
2137
2300
  '<?xml version="1.0" encoding="UTF-8"?>',
@@ -2158,7 +2321,7 @@ var buildLaunchAgentPlist = (entrypoint, options = {}) => {
2158
2321
  ].join("\n");
2159
2322
  };
2160
2323
  var buildWindowsTaskArgs = (entrypoint, taskName = WIN_TASK_NAME) => {
2161
- const command = `"${entrypoint.nodePath}" "${entrypoint.cliPath}" serve`;
2324
+ const command = formatCommand([entrypoint.nodePath, entrypoint.cliPath, "serve"]);
2162
2325
  const args = [
2163
2326
  "/Create",
2164
2327
  "/TN",
@@ -2173,6 +2336,146 @@ var buildWindowsTaskArgs = (entrypoint, taskName = WIN_TASK_NAME) => {
2173
2336
  ];
2174
2337
  return { taskName, command, args };
2175
2338
  };
2339
+ var createAutostartStatus = (status) => status;
2340
+ var createMacAutostartStatus = (entrypoint, location, overrides) => {
2341
+ return createAutostartStatus({
2342
+ platform: "darwin",
2343
+ supported: true,
2344
+ installed: false,
2345
+ health: "missing",
2346
+ needsRepair: false,
2347
+ location,
2348
+ label: MAC_LABEL,
2349
+ expectedCommand: entrypoint.isTransient ? void 0 : entrypoint.command,
2350
+ ...overrides
2351
+ });
2352
+ };
2353
+ var createWindowsAutostartStatus = (entrypoint, overrides) => {
2354
+ return createAutostartStatus({
2355
+ platform: "win32",
2356
+ supported: true,
2357
+ installed: false,
2358
+ health: "missing",
2359
+ needsRepair: false,
2360
+ taskName: WIN_TASK_NAME,
2361
+ expectedCommand: entrypoint.isTransient ? void 0 : entrypoint.command,
2362
+ ...overrides
2363
+ });
2364
+ };
2365
+ var classifyPersistedProgramArguments = (programArguments, deps) => {
2366
+ const actualNodePath = programArguments[0];
2367
+ const actualCliPath = programArguments[1];
2368
+ if (!actualNodePath || !deps.existsSync(actualNodePath)) {
2369
+ return { health: "needs_repair", reason: "missing_node_path" };
2370
+ }
2371
+ if (!actualCliPath || !deps.existsSync(actualCliPath)) {
2372
+ return { health: "needs_repair", reason: "missing_cli_path" };
2373
+ }
2374
+ if (isTransientCliPath(actualCliPath, deps)) {
2375
+ return { health: "needs_repair", reason: "transient_cli_path" };
2376
+ }
2377
+ if (programArguments.length !== 3 || programArguments[2] !== "serve") {
2378
+ return { health: "needs_repair", reason: "entrypoint_mismatch" };
2379
+ }
2380
+ return { health: "healthy" };
2381
+ };
2382
+ var hasMatchingProgramArgument = (programArguments, expectedValue, deps, isPath = false) => {
2383
+ const normalizedExpected = isPath ? normalizeComparisonPath(expectedValue, deps.platform) : expectedValue;
2384
+ return programArguments.some((value) => {
2385
+ const normalizedValue = isPath ? normalizeComparisonPath(value, deps.platform) : value;
2386
+ return normalizedValue === normalizedExpected;
2387
+ });
2388
+ };
2389
+ var classifyExpectedProgramArguments = (expectedArgs, actualArgs, deps) => {
2390
+ if (!hasMatchingProgramArgument([actualArgs[0] ?? ""], expectedArgs[0] ?? "", deps, true)) {
2391
+ return hasMatchingProgramArgument(actualArgs, expectedArgs[0] ?? "", deps, true) ? "entrypoint_mismatch" : "missing_node_path";
2392
+ }
2393
+ if (!hasMatchingProgramArgument([actualArgs[1] ?? ""], expectedArgs[1] ?? "", deps, true)) {
2394
+ return hasMatchingProgramArgument(actualArgs, expectedArgs[1] ?? "", deps, true) ? "entrypoint_mismatch" : "missing_cli_path";
2395
+ }
2396
+ if (actualArgs.length !== expectedArgs.length || !actualArgs.every((value, index) => {
2397
+ const expectedValue = expectedArgs[index] ?? "";
2398
+ return index < 2 ? normalizeComparisonPath(value, deps.platform) === normalizeComparisonPath(expectedValue, deps.platform) : value === expectedValue;
2399
+ })) {
2400
+ return "entrypoint_mismatch";
2401
+ }
2402
+ return void 0;
2403
+ };
2404
+ var readMacLaunchAgentProgramArguments = (plistPath, deps) => {
2405
+ try {
2406
+ const text = deps.execFileSync("plutil", ["-convert", "json", "-o", "-", plistPath], { encoding: "utf-8" });
2407
+ const parsed = JSON.parse(text);
2408
+ const programArguments = parsed?.ProgramArguments;
2409
+ if (!Array.isArray(programArguments) || programArguments.length < 2 || programArguments.some((value) => typeof value !== "string")) {
2410
+ return { ok: false, reason: "missing_program_arguments" };
2411
+ }
2412
+ const commandArgs = programArguments;
2413
+ return {
2414
+ ok: true,
2415
+ command: formatCommand(commandArgs),
2416
+ programArguments: commandArgs
2417
+ };
2418
+ } catch {
2419
+ return { ok: false, reason: "malformed_plist" };
2420
+ }
2421
+ };
2422
+ var classifyMacAutostartStatus = (entrypoint, location, deps) => {
2423
+ if (!deps.existsSync(location)) {
2424
+ return createMacAutostartStatus(entrypoint, location, {
2425
+ installed: false,
2426
+ health: "missing",
2427
+ needsRepair: false,
2428
+ reason: "missing_plist"
2429
+ });
2430
+ }
2431
+ const parsed = readMacLaunchAgentProgramArguments(location, deps);
2432
+ if (!parsed.ok) {
2433
+ return createMacAutostartStatus(entrypoint, location, {
2434
+ installed: true,
2435
+ health: "malformed",
2436
+ needsRepair: true,
2437
+ reason: parsed.reason
2438
+ });
2439
+ }
2440
+ const expectedNodePath = entrypoint.nodePath;
2441
+ const expectedCliPath = entrypoint.cliPath;
2442
+ const expectedArgs = [expectedNodePath, ...entrypoint.args];
2443
+ const actualArgs = parsed.programArguments;
2444
+ const actualStatus = classifyPersistedProgramArguments(actualArgs, deps);
2445
+ if (actualStatus.health !== "healthy") {
2446
+ return createMacAutostartStatus(entrypoint, location, {
2447
+ installed: true,
2448
+ health: "needs_repair",
2449
+ needsRepair: true,
2450
+ command: parsed.command,
2451
+ reason: actualStatus.reason
2452
+ });
2453
+ }
2454
+ if (entrypoint.isTransient) {
2455
+ return createMacAutostartStatus(entrypoint, location, {
2456
+ installed: true,
2457
+ health: "healthy",
2458
+ needsRepair: false,
2459
+ command: parsed.command
2460
+ });
2461
+ }
2462
+ const mismatchReason = classifyExpectedProgramArguments(expectedArgs, actualArgs, deps);
2463
+ if (mismatchReason) {
2464
+ return createMacAutostartStatus(entrypoint, location, {
2465
+ installed: true,
2466
+ health: "needs_repair",
2467
+ needsRepair: true,
2468
+ command: parsed.command,
2469
+ reason: mismatchReason
2470
+ });
2471
+ }
2472
+ return createMacAutostartStatus(entrypoint, location, {
2473
+ installed: true,
2474
+ health: "healthy",
2475
+ needsRepair: false,
2476
+ command: parsed.command
2477
+ });
2478
+ };
2176
2479
  var runCommand = (exec, command, args, ignoreFailure = false) => {
2177
2480
  try {
2178
2481
  exec(command, args, { stdio: "ignore" });
@@ -2182,41 +2485,57 @@ var runCommand = (exec, command, args, ignoreFailure = false) => {
2182
2485
  throw new Error(`${command} ${args.join(" ")} failed: ${message}`);
2183
2486
  }
2184
2487
  };
2488
+ var assertPersistentEntrypoint = (entrypoint) => {
2489
+ if (!entrypoint.isTransient) {
2490
+ return;
2491
+ }
2492
+ throw new Error(
2493
+ `${TRANSIENT_AUTOSTART_INSTALL_ERROR_PREFIX} "${entrypoint.cliPath}". ${STABLE_DAEMON_INSTALL_GUIDANCE} Do not use a temporary npx cache or onboarding workspace.`
2494
+ );
2495
+ };
2185
2496
  var installMacAutostart = (deps = {}) => {
2186
2497
  const resolved = { ...defaultDeps(), ...deps };
2187
2498
  const entrypoint = resolveCliEntrypoint(resolved);
2188
- const plistPath = getLaunchAgentPath(resolved.homedir());
2499
+ assertPersistentEntrypoint(entrypoint);
2500
+ const home = resolved.homedir();
2501
+ const plistPath = getLaunchAgentPath(home);
2502
+ const stdoutPath = join8(home, "Library", "Logs", "opendevbrowser-daemon.log");
2503
+ const stderrPath = join8(home, "Library", "Logs", "opendevbrowser-daemon.err.log");
2504
+ const logsDir = dirname5(stdoutPath);
2189
2505
  resolved.mkdirSync(dirname5(plistPath), { recursive: true });
2190
- resolved.writeFileSync(plistPath, buildLaunchAgentPlist(entrypoint), { encoding: "utf-8" });
2506
+ resolved.mkdirSync(logsDir, { recursive: true });
2507
+ resolved.writeFileSync(
2508
+ plistPath,
2509
+ buildLaunchAgentPlist(entrypoint, { stdoutPath, stderrPath }),
2510
+ { encoding: "utf-8" }
2511
+ );
2191
2512
  const uid = resolved.uid;
2192
2513
  runCommand(resolved.execFileSync, "launchctl", ["bootout", `gui/${uid}`, plistPath], true);
2193
2514
  runCommand(resolved.execFileSync, "launchctl", ["bootstrap", `gui/${uid}`, plistPath]);
2194
2515
  runCommand(resolved.execFileSync, "launchctl", ["enable", `gui/${uid}/${MAC_LABEL}`], true);
2195
2516
  runCommand(resolved.execFileSync, "launchctl", ["kickstart", "-k", `gui/${uid}/${MAC_LABEL}`], true);
2196
- return {
2197
- platform: "darwin",
2198
- supported: true,
2517
+ return createMacAutostartStatus(entrypoint, plistPath, {
2199
2518
  installed: true,
2200
- location: plistPath,
2201
- label: MAC_LABEL,
2519
+ health: "healthy",
2520
+ needsRepair: false,
2202
2521
  command: entrypoint.command
2203
- };
2522
+ });
2204
2523
  };
2205
2524
  var uninstallMacAutostart = (deps = {}) => {
2206
2525
  const resolved = { ...defaultDeps(), ...deps };
2526
+ const entrypoint = resolveCliEntrypoint(resolved);
2207
2527
  const plistPath = getLaunchAgentPath(resolved.homedir());
2208
2528
  const uid = resolved.uid;
2209
2529
  runCommand(resolved.execFileSync, "launchctl", ["bootout", `gui/${uid}`, plistPath], true);
2210
2530
  if (resolved.existsSync(plistPath)) {
2211
2531
  resolved.unlinkSync(plistPath);
2212
2532
  }
2213
- return {
2214
- platform: "darwin",
2215
- supported: true,
2533
+ return createMacAutostartStatus(entrypoint, plistPath, {
2216
2534
  installed: false,
2217
- location: plistPath,
2218
- label: MAC_LABEL
2219
- };
2535
+ health: "missing",
2536
+ needsRepair: false,
2537
+ reason: "missing_plist"
2538
+ });
2220
2539
  };
2221
2540
  var isWindowsTaskInstalled = (deps = {}) => {
2222
2541
  const resolved = { ...defaultDeps(), ...deps };
@@ -2227,55 +2546,175 @@ var isWindowsTaskInstalled = (deps = {}) => {
2227
2546
  return false;
2228
2547
  }
2229
2548
  };
2549
+ var decodeXmlText = (value) => {
2550
+ return value.replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
2551
+ };
2552
+ var readXmlTag = (xml, tagName) => {
2553
+ const match = xml.match(new RegExp(`<${tagName}>([\\s\\S]*?)</${tagName}>`, "i"));
2554
+ return match?.[1] ? decodeXmlText(match[1]) : void 0;
2555
+ };
2556
+ var splitCommandLine = (value) => {
2557
+ const args = [];
2558
+ let current = "";
2559
+ let inQuotes = false;
2560
+ let backslashes = 0;
2561
+ for (const char of value) {
2562
+ if (char === "\\") {
2563
+ backslashes += 1;
2564
+ continue;
2565
+ }
2566
+ if (char === '"') {
2567
+ if (backslashes > 0) {
2568
+ current += "\\".repeat(Math.floor(backslashes / 2));
2569
+ if (backslashes % 2 === 1) {
2570
+ current += '"';
2571
+ backslashes = 0;
2572
+ continue;
2573
+ }
2574
+ }
2575
+ backslashes = 0;
2576
+ inQuotes = !inQuotes;
2577
+ continue;
2578
+ }
2579
+ if (backslashes > 0) {
2580
+ current += "\\".repeat(backslashes);
2581
+ backslashes = 0;
2582
+ }
2583
+ if (!inQuotes && /\s/.test(char)) {
2584
+ if (current.length > 0) {
2585
+ args.push(current);
2586
+ current = "";
2587
+ }
2588
+ continue;
2589
+ }
2590
+ current += char;
2591
+ }
2592
+ if (backslashes > 0) {
2593
+ current += "\\".repeat(backslashes);
2594
+ }
2595
+ if (current.length > 0) {
2596
+ args.push(current);
2597
+ }
2598
+ return args;
2599
+ };
2600
+ var readWindowsTaskAction = (taskName, deps) => {
2601
+ try {
2602
+ const xml = deps.execFileSync("schtasks", ["/Query", "/TN", taskName, "/XML"], { encoding: "utf-8" });
2603
+ const command = readXmlTag(xml, "Command");
2604
+ const argumentsValue = readXmlTag(xml, "Arguments");
2605
+ if (!command) {
2606
+ return { ok: false };
2607
+ }
2608
+ const programArguments = argumentsValue !== void 0 ? [command, ...splitCommandLine(argumentsValue)] : splitCommandLine(command);
2609
+ if (programArguments.length === 0) {
2610
+ return { ok: false };
2611
+ }
2612
+ return {
2613
+ ok: true,
2614
+ command: formatCommand(programArguments),
2615
+ programArguments
2616
+ };
2617
+ } catch {
2618
+ return { ok: false };
2619
+ }
2620
+ };
2621
+ var classifyWindowsAutostartStatus = (entrypoint, deps) => {
2622
+ const installed = isWindowsTaskInstalled(deps);
2623
+ if (!installed) {
2624
+ return createWindowsAutostartStatus(entrypoint, {
2625
+ installed: false,
2626
+ health: "missing",
2627
+ needsRepair: false
2628
+ });
2629
+ }
2630
+ const parsed = readWindowsTaskAction(WIN_TASK_NAME, deps);
2631
+ if (!parsed.ok) {
2632
+ return createWindowsAutostartStatus(entrypoint, {
2633
+ installed: true,
2634
+ health: "needs_repair",
2635
+ needsRepair: true,
2636
+ reason: "entrypoint_mismatch"
2637
+ });
2638
+ }
2639
+ const expectedArgs = [entrypoint.nodePath, ...entrypoint.args];
2640
+ const actualStatus = classifyPersistedProgramArguments(parsed.programArguments, deps);
2641
+ if (actualStatus.health !== "healthy") {
2642
+ return createWindowsAutostartStatus(entrypoint, {
2643
+ installed: true,
2644
+ health: "needs_repair",
2645
+ needsRepair: true,
2646
+ command: parsed.command,
2647
+ reason: actualStatus.reason
2648
+ });
2649
+ }
2650
+ if (entrypoint.isTransient) {
2651
+ return createWindowsAutostartStatus(entrypoint, {
2652
+ installed: true,
2653
+ health: "healthy",
2654
+ needsRepair: false,
2655
+ command: parsed.command
2656
+ });
2657
+ }
2658
+ const mismatchReason = classifyExpectedProgramArguments(expectedArgs, parsed.programArguments, deps);
2659
+ if (mismatchReason) {
2660
+ return createWindowsAutostartStatus(entrypoint, {
2661
+ installed: true,
2662
+ health: "needs_repair",
2663
+ needsRepair: true,
2664
+ command: parsed.command,
2665
+ reason: mismatchReason
2666
+ });
2667
+ }
2668
+ return createWindowsAutostartStatus(entrypoint, {
2669
+ installed: true,
2670
+ health: "healthy",
2671
+ needsRepair: false,
2672
+ command: parsed.command
2673
+ });
2674
+ };
2230
2675
  var installWindowsAutostart = (deps = {}) => {
2231
2676
  const resolved = { ...defaultDeps(), ...deps };
2232
2677
  const entrypoint = resolveCliEntrypoint(resolved);
2678
+ assertPersistentEntrypoint(entrypoint);
2233
2679
  const { args } = buildWindowsTaskArgs(entrypoint, WIN_TASK_NAME);
2234
2680
  runCommand(resolved.execFileSync, "schtasks", args);
2235
- return {
2236
- platform: "win32",
2237
- supported: true,
2681
+ return createWindowsAutostartStatus(entrypoint, {
2238
2682
  installed: true,
2239
- taskName: WIN_TASK_NAME,
2683
+ health: "healthy",
2684
+ needsRepair: false,
2240
2685
  command: entrypoint.command
2241
- };
2686
+ });
2242
2687
  };
2243
2688
  var uninstallWindowsAutostart = (deps = {}) => {
2244
2689
  const resolved = { ...defaultDeps(), ...deps };
2690
+ const entrypoint = resolveCliEntrypoint(resolved);
2245
2691
  runCommand(resolved.execFileSync, "schtasks", ["/Delete", "/TN", WIN_TASK_NAME, "/F"], true);
2246
- return {
2247
- platform: "win32",
2248
- supported: true,
2692
+ return createWindowsAutostartStatus(entrypoint, {
2249
2693
  installed: false,
2250
- taskName: WIN_TASK_NAME
2251
- };
2694
+ health: "missing",
2695
+ needsRepair: false
2696
+ });
2252
2697
  };
2253
2698
  var getAutostartStatus = (deps = {}) => {
2254
2699
  const resolved = { ...defaultDeps(), ...deps };
2255
2700
  const platform = resolved.platform;
2256
2701
  if (platform === "darwin") {
2702
+ const entrypoint = resolveCliEntrypoint(resolved);
2257
2703
  const location = getLaunchAgentPath(resolved.homedir());
2258
- return {
2259
- platform,
2260
- supported: true,
2261
- installed: resolved.existsSync(location),
2262
- location,
2263
- label: MAC_LABEL
2264
- };
2704
+ return classifyMacAutostartStatus(entrypoint, location, resolved);
2265
2705
  }
2266
2706
  if (platform === "win32") {
2267
- return {
2268
- platform,
2269
- supported: true,
2270
- installed: isWindowsTaskInstalled(resolved),
2271
- taskName: WIN_TASK_NAME
2272
- };
2707
+ const entrypoint = resolveCliEntrypoint(resolved);
2708
+ return classifyWindowsAutostartStatus(entrypoint, resolved);
2273
2709
  }
2274
- return {
2710
+ return createAutostartStatus({
2275
2711
  platform,
2276
2712
  supported: false,
2277
- installed: false
2278
- };
2713
+ installed: false,
2714
+ health: "unsupported",
2715
+ needsRepair: false,
2716
+ reason: "unsupported_platform"
2717
+ });
2279
2718
  };
2280
2719
  var installAutostart = (deps = {}) => {
2281
2720
  const platform = deps.platform ?? process.platform;
@@ -2285,11 +2724,14 @@ var installAutostart = (deps = {}) => {
2285
2724
  if (platform === "win32") {
2286
2725
  return installWindowsAutostart(deps);
2287
2726
  }
2288
- return {
2727
+ return createAutostartStatus({
2289
2728
  platform,
2290
2729
  supported: false,
2291
- installed: false
2292
- };
2730
+ installed: false,
2731
+ health: "unsupported",
2732
+ needsRepair: false,
2733
+ reason: "unsupported_platform"
2734
+ });
2293
2735
  };
2294
2736
  var uninstallAutostart = (deps = {}) => {
2295
2737
  const platform = deps.platform ?? process.platform;
@@ -2299,11 +2741,14 @@ var uninstallAutostart = (deps = {}) => {
2299
2741
  if (platform === "win32") {
2300
2742
  return uninstallWindowsAutostart(deps);
2301
2743
  }
2302
- return {
2744
+ return createAutostartStatus({
2303
2745
  platform,
2304
2746
  supported: false,
2305
- installed: false
2306
- };
2747
+ installed: false,
2748
+ health: "unsupported",
2749
+ needsRepair: false,
2750
+ reason: "unsupported_platform"
2751
+ });
2307
2752
  };
2308
2753
 
2309
2754
  // src/cli/commands/daemon.ts
@@ -2329,20 +2774,55 @@ var stopDaemonIfRunning = async () => {
2329
2774
  return false;
2330
2775
  }
2331
2776
  };
2777
+ var formatReason = (reason) => {
2778
+ return reason ? reason.replace(/_/g, " ") : "unknown reason";
2779
+ };
2780
+ var buildStableAutostartGuidance = (action) => {
2781
+ return `${STABLE_DAEMON_INSTALL_GUIDANCE.replace(/\.$/, "")} to ${action} it.`;
2782
+ };
2783
+ var describeAutostartLocation = (autostart) => {
2784
+ if (autostart.location) {
2785
+ return ` at ${autostart.location}`;
2786
+ }
2787
+ if (autostart.taskName) {
2788
+ return ` (${autostart.taskName})`;
2789
+ }
2790
+ return "";
2791
+ };
2332
2792
  var buildStatusMessage = (autostart, running) => {
2793
+ const runningText = running ? "running" : "not running";
2333
2794
  if (!autostart.supported) {
2334
- return `Daemon autostart is not supported on ${autostart.platform}.`;
2795
+ return `Daemon autostart is not supported on ${autostart.platform}. Daemon is ${runningText}.`;
2335
2796
  }
2336
- const installed = autostart.installed ? "installed" : "not installed";
2337
- const runningText = running ? "running" : "not running";
2338
- const location = autostart.location ? ` at ${autostart.location}` : "";
2339
- const task = autostart.taskName ? ` (${autostart.taskName})` : "";
2340
- return `Autostart ${installed}${location}${task}. Daemon is ${runningText}.`;
2797
+ const location = describeAutostartLocation(autostart);
2798
+ if (autostart.health === "healthy") {
2799
+ return `Autostart is installed and healthy${location}. Daemon is ${runningText}.`;
2800
+ }
2801
+ if (autostart.health === "missing") {
2802
+ return `Autostart is not installed${location}. ${buildStableAutostartGuidance("install")} Daemon is ${runningText}.`;
2803
+ }
2804
+ if (autostart.health === "needs_repair") {
2805
+ return `Autostart is installed${location} but needs repair (${formatReason(autostart.reason)}). ${buildStableAutostartGuidance("repair")} Daemon is ${runningText}.`;
2806
+ }
2807
+ if (autostart.health === "malformed") {
2808
+ return `Autostart exists${location} but is malformed (${formatReason(autostart.reason)}). ${buildStableAutostartGuidance("repair")} Daemon is ${runningText}.`;
2809
+ }
2810
+ return `Daemon autostart is not supported on ${autostart.platform}. Daemon is ${runningText}.`;
2341
2811
  };
2342
2812
  async function runDaemonCommand(args) {
2343
2813
  const { subcommand } = parseDaemonArgs(args.rawArgs);
2344
2814
  if (subcommand === "install") {
2345
- const result = installAutostart();
2815
+ let result;
2816
+ try {
2817
+ result = installAutostart();
2818
+ } catch (error) {
2819
+ const message2 = error instanceof Error ? error.message : String(error);
2820
+ return {
2821
+ success: false,
2822
+ message: isTransientAutostartInstallError(error) ? message2 : `Daemon autostart install failed: ${message2}`,
2823
+ exitCode: EXIT_EXECUTION
2824
+ };
2825
+ }
2346
2826
  if (!result.supported) {
2347
2827
  return {
2348
2828
  success: false,
@@ -2400,7 +2880,21 @@ async function runDaemonCommand(args) {
2400
2880
 
2401
2881
  // src/cli/commands/artifacts.ts
2402
2882
  import { join as join9, resolve as resolve5 } from "path";
2403
- import { tmpdir } from "os";
2883
+ import { tmpdir as tmpdir2 } from "os";
2884
+ var PASSTHROUGH_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
2885
+ "--with-config",
2886
+ "--no-prompt",
2887
+ "--no-interactive",
2888
+ "--quiet",
2889
+ "--skills-global",
2890
+ "--skills-local",
2891
+ "--no-skills",
2892
+ "--full"
2893
+ ]);
2894
+ var PASSTHROUGH_VALUE_FLAGS = /* @__PURE__ */ new Set([
2895
+ "--output-format",
2896
+ "--transport"
2897
+ ]);
2404
2898
  var usageError = () => {
2405
2899
  throw createUsageError("Usage: opendevbrowser artifacts cleanup --expired-only [--output-dir <path>]");
2406
2900
  };
@@ -2411,6 +2905,24 @@ var requireValue = (rawArgs, index, flag) => {
2411
2905
  }
2412
2906
  return value;
2413
2907
  };
2908
+ var consumePassthroughFlag = (rawArgs, index) => {
2909
+ const arg = rawArgs[index];
2910
+ if (!arg) {
2911
+ return null;
2912
+ }
2913
+ if (PASSTHROUGH_BOOLEAN_FLAGS.has(arg)) {
2914
+ return index;
2915
+ }
2916
+ if (PASSTHROUGH_VALUE_FLAGS.has(arg)) {
2917
+ requireValue(rawArgs, index, arg);
2918
+ return index + 1;
2919
+ }
2920
+ const equalsFlag = arg.split("=", 2)[0];
2921
+ if (equalsFlag && PASSTHROUGH_VALUE_FLAGS.has(equalsFlag)) {
2922
+ return index;
2923
+ }
2924
+ return null;
2925
+ };
2414
2926
  var parseArtifactsArgs = (rawArgs) => {
2415
2927
  const [candidate, ...rest] = rawArgs;
2416
2928
  if (candidate !== "cleanup") {
@@ -2438,6 +2950,11 @@ var parseArtifactsArgs = (rawArgs) => {
2438
2950
  outputDir = value;
2439
2951
  continue;
2440
2952
  }
2953
+ const passthroughIndex = consumePassthroughFlag(rest, index);
2954
+ if (passthroughIndex !== null) {
2955
+ index = passthroughIndex;
2956
+ continue;
2957
+ }
2441
2958
  throw createUsageError(`Unknown artifacts flag: ${arg}`);
2442
2959
  }
2443
2960
  if (!expiredOnly) {
@@ -2451,7 +2968,7 @@ var parseArtifactsArgs = (rawArgs) => {
2451
2968
  };
2452
2969
  async function runArtifactsCommand(args) {
2453
2970
  const parsed = parseArtifactsArgs(args.rawArgs);
2454
- const rootDir = parsed.outputDir ? resolve5(parsed.outputDir) : join9(tmpdir(), "opendevbrowser");
2971
+ const rootDir = parsed.outputDir ? resolve5(parsed.outputDir) : join9(tmpdir2(), "opendevbrowser");
2455
2972
  const cleaned = await cleanupExpiredArtifacts(rootDir);
2456
2973
  return {
2457
2974
  success: true,
@@ -2467,10 +2984,139 @@ async function runArtifactsCommand(args) {
2467
2984
  };
2468
2985
  }
2469
2986
 
2987
+ // src/cli/install-autostart-reconciliation.ts
2988
+ var INSTALL_AUTOSTART_SKIP_ENV_VAR = "OPDEVBROWSER_SKIP_INSTALL_AUTOSTART_RECONCILIATION";
2989
+ var defaultDeps2 = () => ({
2990
+ getAutostartStatus,
2991
+ installAutostart
2992
+ });
2993
+ function shouldSkipInstallAutostartReconciliation(env = process.env) {
2994
+ const raw = env[INSTALL_AUTOSTART_SKIP_ENV_VAR];
2995
+ if (raw === void 0) {
2996
+ return false;
2997
+ }
2998
+ const normalized = raw.trim().toLowerCase();
2999
+ return normalized === "1" || normalized === "true" || normalized === "yes";
3000
+ }
3001
+ function reconcileInstallAutostart(installResult, deps = {}, options = {}) {
3002
+ if (!installResult.success || shouldSkipInstallAutostartReconciliation(options.env)) {
3003
+ return { attempted: false };
3004
+ }
3005
+ const resolved = { ...defaultDeps2(), ...deps };
3006
+ const status = resolved.getAutostartStatus();
3007
+ if (!status.supported) {
3008
+ return {
3009
+ attempted: false,
3010
+ autostart: status,
3011
+ autostartAction: "unsupported"
3012
+ };
3013
+ }
3014
+ if (status.health === "healthy") {
3015
+ return {
3016
+ attempted: false,
3017
+ autostart: status,
3018
+ autostartAction: "already_healthy"
3019
+ };
3020
+ }
3021
+ if (status.health !== "missing" && status.health !== "needs_repair" && status.health !== "malformed") {
3022
+ return {
3023
+ attempted: false,
3024
+ autostart: status
3025
+ };
3026
+ }
3027
+ try {
3028
+ const autostart = resolved.installAutostart();
3029
+ return {
3030
+ attempted: true,
3031
+ autostart,
3032
+ autostartAction: status.health === "missing" ? "installed" : "repaired"
3033
+ };
3034
+ } catch (error) {
3035
+ let autostart = status;
3036
+ try {
3037
+ autostart = resolved.getAutostartStatus();
3038
+ } catch {
3039
+ autostart = status;
3040
+ }
3041
+ return {
3042
+ attempted: true,
3043
+ autostart,
3044
+ autostartAction: "repair_failed",
3045
+ autostartError: error instanceof Error ? error.message : String(error)
3046
+ };
3047
+ }
3048
+ }
3049
+
3050
+ // src/cli/install-autostart-output.ts
3051
+ function createInstallAutostartOutputPayload(result) {
3052
+ if (!result) {
3053
+ return {};
3054
+ }
3055
+ return {
3056
+ autostart: result.autostart,
3057
+ autostartAction: result.autostartAction,
3058
+ ...result.autostartAction === "repair_failed" && result.autostartError ? { autostartError: result.autostartError } : {}
3059
+ };
3060
+ }
3061
+ function formatAutostartReconciliationMessage(result) {
3062
+ switch (result.autostartAction) {
3063
+ case "unsupported":
3064
+ return result.autostart ? `Autostart not supported on ${result.autostart.platform}.` : "Autostart not supported on this platform.";
3065
+ case "already_healthy":
3066
+ return "Autostart already healthy.";
3067
+ case "installed":
3068
+ return result.autostart ? `Autostart installed (${result.autostart.platform}).` : "Autostart installed.";
3069
+ case "repaired":
3070
+ return result.autostart ? `Autostart repaired (${result.autostart.platform}).` : "Autostart repaired.";
3071
+ case "repair_failed":
3072
+ if (result.autostartError && isTransientAutostartInstallError(result.autostartError)) {
3073
+ return `Plugin install succeeded but autostart repair was skipped because the current CLI path is transient: ${result.autostartError}`;
3074
+ }
3075
+ return `Plugin install succeeded but autostart repair failed: ${result.autostartError ?? "unknown error"}. Run opendevbrowser daemon install to repair it.`;
3076
+ default:
3077
+ return null;
3078
+ }
3079
+ }
3080
+
2470
3081
  // src/cli/commands/run.ts
2471
3082
  import { readFileSync as readFileSync4 } from "fs";
2472
3083
 
2473
3084
  // src/cli/output.ts
3085
+ var normalizeExitCode = (code) => {
3086
+ return Number.isInteger(code) ? Number(code) : 0;
3087
+ };
3088
+ var flushStream = (stream) => {
3089
+ return new Promise((resolve6) => {
3090
+ if (!stream || typeof stream.write !== "function") {
3091
+ resolve6();
3092
+ return;
3093
+ }
3094
+ try {
3095
+ stream.write("", () => resolve6());
3096
+ } catch {
3097
+ resolve6();
3098
+ }
3099
+ });
3100
+ };
3101
+ async function flushOutputAndExit(code, proc = process, timeoutMs = 250) {
3102
+ const finalCode = normalizeExitCode(code);
3103
+ proc.exitCode = finalCode;
3104
+ await new Promise((resolve6) => {
3105
+ let settled = false;
3106
+ const finish = () => {
3107
+ if (settled) return;
3108
+ settled = true;
3109
+ resolve6();
3110
+ };
3111
+ const timer = setTimeout(finish, Math.max(0, timeoutMs));
3112
+ timer.unref?.();
3113
+ void Promise.allSettled([flushStream(proc.stdout), flushStream(proc.stderr)]).finally(() => {
3114
+ clearTimeout(timer);
3115
+ finish();
3116
+ });
3117
+ });
3118
+ proc.exit(finalCode);
3119
+ }
2474
3120
  function writeOutput(payload, options) {
2475
3121
  if (options.quiet) {
2476
3122
  return;
@@ -2526,7 +3172,19 @@ function parseRunArgs(rawArgs) {
2526
3172
  continue;
2527
3173
  }
2528
3174
  if (arg === "--persist-profile") {
2529
- parsed.persistProfile = true;
3175
+ const value = rawArgs[i + 1];
3176
+ if (value && !value.startsWith("--")) {
3177
+ parsed.persistProfile = parseBooleanFlag(value, "--persist-profile");
3178
+ i += 1;
3179
+ } else {
3180
+ parsed.persistProfile = true;
3181
+ }
3182
+ continue;
3183
+ }
3184
+ if (arg?.startsWith("--persist-profile=")) {
3185
+ const value = arg.split("=", 2)[1];
3186
+ if (!value) throw createUsageError("Missing value for --persist-profile");
3187
+ parsed.persistProfile = parseBooleanFlag(value, "--persist-profile");
2530
3188
  continue;
2531
3189
  }
2532
3190
  if (arg === "--chrome-path") {
@@ -2613,7 +3271,7 @@ async function runScriptCommand(args) {
2613
3271
  startUrl: runArgs.startUrl,
2614
3272
  chromePath: runArgs.chromePath,
2615
3273
  flags: runArgs.flags.length ? runArgs.flags : void 0,
2616
- persistProfile: runArgs.persistProfile
3274
+ persistProfile: runArgs.persistProfile ?? false
2617
3275
  });
2618
3276
  try {
2619
3277
  const result = await core.runner.run(launchResult.sessionId, steps, true);
@@ -2632,13 +3290,8 @@ async function runScriptCommand(args) {
2632
3290
 
2633
3291
  // src/cli/commands/session/launch.ts
2634
3292
  var MIN_LAUNCH_CALL_TIMEOUT_MS = 3e4;
3293
+ var MANAGED_HEADED_LAUNCH_CALL_TIMEOUT_MS = 6e4;
2635
3294
  var LAUNCH_CALL_TIMEOUT_BUFFER_MS = 5e3;
2636
- var parseBooleanFlag = (value, flag) => {
2637
- const normalized = value.trim().toLowerCase();
2638
- if (normalized === "true" || normalized === "1") return true;
2639
- if (normalized === "false" || normalized === "0") return false;
2640
- throw createUsageError(`Invalid ${flag}: ${value}`);
2641
- };
2642
3295
  function parseLaunchArgs(rawArgs) {
2643
3296
  const parsed = { flags: [] };
2644
3297
  for (let i = 0; i < rawArgs.length; i += 1) {
@@ -2743,7 +3396,8 @@ function parseLaunchArgs(rawArgs) {
2743
3396
  }
2744
3397
  function deriveLaunchCallTimeoutMs(launchArgs) {
2745
3398
  const waitHintMs = typeof launchArgs.waitTimeoutMs === "number" ? launchArgs.waitTimeoutMs + LAUNCH_CALL_TIMEOUT_BUFFER_MS : 0;
2746
- return Math.max(MIN_LAUNCH_CALL_TIMEOUT_MS, waitHintMs);
3399
+ const managedHeadedHintMs = launchArgs.noExtension && launchArgs.headless !== true ? MANAGED_HEADED_LAUNCH_CALL_TIMEOUT_MS : 0;
3400
+ return Math.max(MIN_LAUNCH_CALL_TIMEOUT_MS, waitHintMs, managedHeadedHintMs);
2747
3401
  }
2748
3402
  async function runSessionLaunch(args) {
2749
3403
  const launchArgs = parseLaunchArgs(args.rawArgs);
@@ -2829,6 +3483,7 @@ function promptYesNo(question, defaultYes) {
2829
3483
  }
2830
3484
 
2831
3485
  // src/cli/commands/session/connect.ts
3486
+ var DEFAULT_CONNECT_TIMEOUT_MS = 3e4;
2832
3487
  function parseConnectArgs(rawArgs) {
2833
3488
  const parsed = {};
2834
3489
  for (let i = 0; i < rawArgs.length; i += 1) {
@@ -2896,7 +3551,9 @@ function parseConnectArgs(rawArgs) {
2896
3551
  }
2897
3552
  async function runSessionConnect(args) {
2898
3553
  const connectArgs = parseConnectArgs(args.rawArgs);
2899
- const result = await callDaemon("session.connect", connectArgs);
3554
+ const result = await callDaemon("session.connect", connectArgs, {
3555
+ timeoutMs: DEFAULT_CONNECT_TIMEOUT_MS
3556
+ });
2900
3557
  return {
2901
3558
  success: true,
2902
3559
  message: `Session connected: ${result.sessionId}`,
@@ -2932,10 +3589,120 @@ async function runSessionDisconnect(args) {
2932
3589
  if (!sessionId) {
2933
3590
  throw createUsageError("Missing --session-id");
2934
3591
  }
2935
- await callDaemon("session.disconnect", { sessionId, closeBrowser }, { timeoutMs: 2e4 });
3592
+ const timeoutMs = closeBrowser ? 12e4 : 2e4;
3593
+ await callDaemon("session.disconnect", { sessionId, closeBrowser }, { timeoutMs });
2936
3594
  return { success: true, message: `Session disconnected: ${sessionId}` };
2937
3595
  }
2938
3596
 
3597
+ // src/cli/commands/session/inspector.ts
3598
+ function parseSessionInspectorArgs(rawArgs) {
3599
+ const parsed = {};
3600
+ for (let index = 0; index < rawArgs.length; index += 1) {
3601
+ const arg = rawArgs[index];
3602
+ if (arg === "--session-id") {
3603
+ const value = rawArgs[index + 1];
3604
+ if (!value) throw createUsageError("Missing value for --session-id");
3605
+ parsed.sessionId = value;
3606
+ index += 1;
3607
+ continue;
3608
+ }
3609
+ if (arg?.startsWith("--session-id=")) {
3610
+ parsed.sessionId = arg.split("=", 2)[1];
3611
+ continue;
3612
+ }
3613
+ if (arg === "--include-urls") {
3614
+ parsed.includeUrls = true;
3615
+ continue;
3616
+ }
3617
+ if (arg === "--since-console-seq") {
3618
+ const value = rawArgs[index + 1];
3619
+ if (!value) throw createUsageError("Missing value for --since-console-seq");
3620
+ parsed.sinceConsoleSeq = parseNumberFlag(value, "--since-console-seq", { min: 0 });
3621
+ index += 1;
3622
+ continue;
3623
+ }
3624
+ if (arg?.startsWith("--since-console-seq=")) {
3625
+ const value = arg.split("=", 2)[1];
3626
+ if (!value) throw createUsageError("Missing value for --since-console-seq");
3627
+ parsed.sinceConsoleSeq = parseNumberFlag(value, "--since-console-seq", { min: 0 });
3628
+ continue;
3629
+ }
3630
+ if (arg === "--since-network-seq") {
3631
+ const value = rawArgs[index + 1];
3632
+ if (!value) throw createUsageError("Missing value for --since-network-seq");
3633
+ parsed.sinceNetworkSeq = parseNumberFlag(value, "--since-network-seq", { min: 0 });
3634
+ index += 1;
3635
+ continue;
3636
+ }
3637
+ if (arg?.startsWith("--since-network-seq=")) {
3638
+ const value = arg.split("=", 2)[1];
3639
+ if (!value) throw createUsageError("Missing value for --since-network-seq");
3640
+ parsed.sinceNetworkSeq = parseNumberFlag(value, "--since-network-seq", { min: 0 });
3641
+ continue;
3642
+ }
3643
+ if (arg === "--since-exception-seq") {
3644
+ const value = rawArgs[index + 1];
3645
+ if (!value) throw createUsageError("Missing value for --since-exception-seq");
3646
+ parsed.sinceExceptionSeq = parseNumberFlag(value, "--since-exception-seq", { min: 0 });
3647
+ index += 1;
3648
+ continue;
3649
+ }
3650
+ if (arg?.startsWith("--since-exception-seq=")) {
3651
+ const value = arg.split("=", 2)[1];
3652
+ if (!value) throw createUsageError("Missing value for --since-exception-seq");
3653
+ parsed.sinceExceptionSeq = parseNumberFlag(value, "--since-exception-seq", { min: 0 });
3654
+ continue;
3655
+ }
3656
+ if (arg === "--max") {
3657
+ const value = rawArgs[index + 1];
3658
+ if (!value) throw createUsageError("Missing value for --max");
3659
+ parsed.max = parseNumberFlag(value, "--max", { min: 1 });
3660
+ index += 1;
3661
+ continue;
3662
+ }
3663
+ if (arg?.startsWith("--max=")) {
3664
+ const value = arg.split("=", 2)[1];
3665
+ if (!value) throw createUsageError("Missing value for --max");
3666
+ parsed.max = parseNumberFlag(value, "--max", { min: 1 });
3667
+ continue;
3668
+ }
3669
+ if (arg === "--request-id") {
3670
+ const value = rawArgs[index + 1];
3671
+ if (!value) throw createUsageError("Missing value for --request-id");
3672
+ parsed.requestId = value;
3673
+ index += 1;
3674
+ continue;
3675
+ }
3676
+ if (arg?.startsWith("--request-id=")) {
3677
+ const value = arg.split("=", 2)[1];
3678
+ if (!value) throw createUsageError("Missing value for --request-id");
3679
+ parsed.requestId = value;
3680
+ continue;
3681
+ }
3682
+ }
3683
+ return parsed;
3684
+ }
3685
+ async function runSessionInspector(args) {
3686
+ const parsed = parseSessionInspectorArgs(args.rawArgs);
3687
+ if (!parsed.sessionId) {
3688
+ throw createUsageError("Missing --session-id");
3689
+ }
3690
+ const result = await callDaemon("session.inspect", {
3691
+ sessionId: parsed.sessionId,
3692
+ ...typeof parsed.includeUrls === "boolean" ? { includeUrls: parsed.includeUrls } : {},
3693
+ sinceConsoleSeq: parsed.sinceConsoleSeq,
3694
+ sinceNetworkSeq: parsed.sinceNetworkSeq,
3695
+ sinceExceptionSeq: parsed.sinceExceptionSeq,
3696
+ max: parsed.max,
3697
+ requestId: parsed.requestId
3698
+ });
3699
+ return {
3700
+ success: true,
3701
+ message: "Session inspector snapshot captured.",
3702
+ data: result
3703
+ };
3704
+ }
3705
+
2939
3706
  // src/cli/commands/session/status.ts
2940
3707
  function parseStatusArgs(rawArgs) {
2941
3708
  const parsed = {};
@@ -2965,6 +3732,11 @@ async function runSessionStatus(args) {
2965
3732
  }
2966
3733
 
2967
3734
  // src/cli/commands/status.ts
3735
+ var DAEMON_STATUS_READ_OPTIONS = {
3736
+ timeoutMs: 5e3,
3737
+ retryAttempts: 5,
3738
+ retryDelayMs: 250
3739
+ };
2968
3740
  var parseStatusArgs2 = (rawArgs) => {
2969
3741
  const parsed = { daemon: false };
2970
3742
  for (let i = 0; i < rawArgs.length; i += 1) {
@@ -2997,41 +3769,31 @@ async function runStatus(args) {
2997
3769
  }
2998
3770
  if (!daemon && args.transport === "native") {
2999
3771
  const nativeStatus2 = getNativeStatusSnapshot();
3000
- if (!nativeStatus2.installed) {
3001
- return {
3002
- success: false,
3003
- message: "Native host not installed.",
3004
- data: nativeStatus2,
3005
- exitCode: EXIT_DISCONNECTED
3006
- };
3007
- }
3008
- if (nativeStatus2.mismatch && nativeStatus2.extensionId && nativeStatus2.expectedExtensionId) {
3009
- return {
3010
- success: false,
3011
- message: `Native host targets ${nativeStatus2.extensionId}, but the current extension is ${nativeStatus2.expectedExtensionId}.`,
3012
- data: nativeStatus2,
3013
- exitCode: EXIT_DISCONNECTED
3014
- };
3015
- }
3772
+ const assessment = assessNativeStatus(nativeStatus2);
3016
3773
  return {
3017
- success: true,
3018
- message: nativeStatus2.extensionId ? `Native host installed for extension ${nativeStatus2.extensionId}.` : "Native host installed.",
3019
- data: nativeStatus2
3774
+ success: assessment.success,
3775
+ message: assessment.message,
3776
+ data: nativeStatus2,
3777
+ exitCode: assessment.exitCode ?? void 0
3020
3778
  };
3021
3779
  }
3022
- const daemonStatus = await fetchDaemonStatusFromMetadata();
3780
+ const daemonStatus = await fetchDaemonStatusFromMetadata(void 0, DAEMON_STATUS_READ_OPTIONS);
3023
3781
  if (!daemonStatus) {
3024
3782
  throw createUsageError("Daemon not running. Start with `opendevbrowser serve`.");
3025
3783
  }
3026
3784
  const nativeStatus = getNativeStatusSnapshot();
3027
- const nativeSummary = !nativeStatus.installed ? "not installed" : nativeStatus.mismatch && nativeStatus.extensionId && nativeStatus.expectedExtensionId ? `mismatch (${nativeStatus.extensionId} != ${nativeStatus.expectedExtensionId})` : `installed${nativeStatus.extensionId ? ` (${nativeStatus.extensionId})` : ""}`;
3028
- const baseMessage = [
3785
+ const nativeAssessment = assessNativeStatus(nativeStatus);
3786
+ const baseLines = [
3029
3787
  `Daemon OK (pid=${daemonStatus.pid})`,
3030
3788
  `Relay: port=${daemonStatus.relay.port ?? "n/a"} ext=${daemonStatus.relay.extensionConnected ? "on" : "off"} handshake=${daemonStatus.relay.extensionHandshakeComplete ? "on" : "off"} cdp=${daemonStatus.relay.cdpConnected ? "on" : "off"} annotate=${daemonStatus.relay.annotationConnected ? "on" : "off"} ops=${daemonStatus.relay.opsConnected ? "on" : "off"} canvas=${daemonStatus.relay.canvasConnected ? "on" : "off"} pairing=${daemonStatus.relay.pairingRequired ? "on" : "off"} health=${daemonStatus.relay.health?.reason ?? "n/a"}`,
3031
- `Native: ${nativeSummary}`,
3789
+ `Native: ${nativeAssessment.summary}`,
3032
3790
  daemonStatus.relay.lastHandshakeError ? `Relay last handshake error: ${daemonStatus.relay.lastHandshakeError.code} (${daemonStatus.relay.lastHandshakeError.message})` : "Relay last handshake error: none",
3033
3791
  "Legend: ext=extension websocket, handshake=extension handshake, cdp=active /cdp client, annotate=annotation channel, ops=ops clients, canvas=canvas clients, pairing=token required, health=relay status"
3034
- ].join("\n");
3792
+ ];
3793
+ if (!nativeAssessment.success) {
3794
+ baseLines.splice(3, 0, `Native detail: ${nativeAssessment.message}`);
3795
+ }
3796
+ const baseMessage = baseLines.join("\n");
3035
3797
  const message = daemon || args.outputFormat !== "text" ? baseMessage : [
3036
3798
  "Warning: `status` defaults to daemon status. Use --daemon explicitly or --session-id for session status.",
3037
3799
  baseMessage
@@ -3135,30 +3897,119 @@ function parseWaitArgs(rawArgs) {
3135
3897
  i += 1;
3136
3898
  continue;
3137
3899
  }
3138
- if (arg?.startsWith("--ref=")) {
3139
- parsed.ref = arg.split("=", 2)[1];
3900
+ if (arg?.startsWith("--ref=")) {
3901
+ parsed.ref = arg.split("=", 2)[1];
3902
+ continue;
3903
+ }
3904
+ if (arg === "--state") {
3905
+ const value = rawArgs[i + 1];
3906
+ if (!value) throw createUsageError("Missing value for --state");
3907
+ parsed.state = value;
3908
+ i += 1;
3909
+ continue;
3910
+ }
3911
+ if (arg?.startsWith("--state=")) {
3912
+ parsed.state = arg.split("=", 2)[1];
3913
+ continue;
3914
+ }
3915
+ if (arg === "--until") {
3916
+ const value = rawArgs[i + 1];
3917
+ if (!value) throw createUsageError("Missing value for --until");
3918
+ parsed.until = value;
3919
+ i += 1;
3920
+ continue;
3921
+ }
3922
+ if (arg?.startsWith("--until=")) {
3923
+ parsed.until = arg.split("=", 2)[1];
3924
+ continue;
3925
+ }
3926
+ if (arg === "--timeout-ms") {
3927
+ const value = rawArgs[i + 1];
3928
+ if (!value) throw createUsageError("Missing value for --timeout-ms");
3929
+ parsed.timeoutMs = parseNumberFlag(value, "--timeout-ms", { min: 1 });
3930
+ i += 1;
3931
+ continue;
3932
+ }
3933
+ if (arg?.startsWith("--timeout-ms=")) {
3934
+ const value = arg.split("=", 2)[1];
3935
+ if (!value) throw createUsageError("Missing value for --timeout-ms");
3936
+ parsed.timeoutMs = parseNumberFlag(value, "--timeout-ms", { min: 1 });
3937
+ continue;
3938
+ }
3939
+ }
3940
+ return parsed;
3941
+ }
3942
+ async function runWait(args) {
3943
+ const { sessionId, ref, state, until, timeoutMs } = parseWaitArgs(args.rawArgs);
3944
+ const targetId = parseOptionalStringFlag(args.rawArgs, "--target-id");
3945
+ if (!sessionId) throw createUsageError("Missing --session-id");
3946
+ const result = await callDaemon("nav.wait", {
3947
+ sessionId,
3948
+ ref,
3949
+ state,
3950
+ until,
3951
+ timeoutMs,
3952
+ ...typeof targetId === "string" ? { targetId } : {}
3953
+ });
3954
+ return { success: true, message: "Wait complete.", data: result };
3955
+ }
3956
+
3957
+ // src/cli/transport-timeouts.ts
3958
+ var DEFAULT_CLICK_TRANSPORT_TIMEOUT_MS = 6e4;
3959
+ var DEFAULT_DIALOG_TRANSPORT_TIMEOUT_MS = 3e4;
3960
+ var DEFAULT_TARGET_CREATION_TRANSPORT_TIMEOUT_MS = 3e4;
3961
+ var DEFAULT_SNAPSHOT_TRANSPORT_TIMEOUT_MS = 3e4;
3962
+ var DEFAULT_REVIEW_TRANSPORT_TIMEOUT_MS = 3e4;
3963
+ var DEFAULT_SCREENSHOT_TRANSPORT_TIMEOUT_MS = 3e4;
3964
+ var DEFAULT_WORKFLOW_TRANSPORT_TIMEOUT_MS = 12e4;
3965
+
3966
+ // src/cli/commands/nav/snapshot.ts
3967
+ function parseSnapshotArgs(rawArgs) {
3968
+ const parsed = {};
3969
+ for (let i = 0; i < rawArgs.length; i += 1) {
3970
+ const arg = rawArgs[i];
3971
+ if (arg === "--session-id") {
3972
+ const value = rawArgs[i + 1];
3973
+ if (!value) throw createUsageError("Missing value for --session-id");
3974
+ parsed.sessionId = value;
3975
+ i += 1;
3976
+ continue;
3977
+ }
3978
+ if (arg?.startsWith("--session-id=")) {
3979
+ parsed.sessionId = arg.split("=", 2)[1];
3980
+ continue;
3981
+ }
3982
+ if (arg === "--mode") {
3983
+ const value = rawArgs[i + 1];
3984
+ if (!value) throw createUsageError("Missing value for --mode");
3985
+ parsed.mode = value;
3986
+ i += 1;
3987
+ continue;
3988
+ }
3989
+ if (arg?.startsWith("--mode=")) {
3990
+ parsed.mode = arg.split("=", 2)[1];
3140
3991
  continue;
3141
3992
  }
3142
- if (arg === "--state") {
3993
+ if (arg === "--max-chars") {
3143
3994
  const value = rawArgs[i + 1];
3144
- if (!value) throw createUsageError("Missing value for --state");
3145
- parsed.state = value;
3995
+ if (!value) throw createUsageError("Missing value for --max-chars");
3996
+ parsed.maxChars = Number(value);
3146
3997
  i += 1;
3147
3998
  continue;
3148
3999
  }
3149
- if (arg?.startsWith("--state=")) {
3150
- parsed.state = arg.split("=", 2)[1];
4000
+ if (arg?.startsWith("--max-chars=")) {
4001
+ parsed.maxChars = Number(arg.split("=", 2)[1]);
3151
4002
  continue;
3152
4003
  }
3153
- if (arg === "--until") {
4004
+ if (arg === "--cursor") {
3154
4005
  const value = rawArgs[i + 1];
3155
- if (!value) throw createUsageError("Missing value for --until");
3156
- parsed.until = value;
4006
+ if (!value) throw createUsageError("Missing value for --cursor");
4007
+ parsed.cursor = value;
3157
4008
  i += 1;
3158
4009
  continue;
3159
4010
  }
3160
- if (arg?.startsWith("--until=")) {
3161
- parsed.until = arg.split("=", 2)[1];
4011
+ if (arg?.startsWith("--cursor=")) {
4012
+ parsed.cursor = arg.split("=", 2)[1];
3162
4013
  continue;
3163
4014
  }
3164
4015
  if (arg === "--timeout-ms") {
@@ -3172,28 +4023,29 @@ function parseWaitArgs(rawArgs) {
3172
4023
  const value = arg.split("=", 2)[1];
3173
4024
  if (!value) throw createUsageError("Missing value for --timeout-ms");
3174
4025
  parsed.timeoutMs = parseNumberFlag(value, "--timeout-ms", { min: 1 });
3175
- continue;
3176
4026
  }
3177
4027
  }
3178
4028
  return parsed;
3179
4029
  }
3180
- async function runWait(args) {
3181
- const { sessionId, ref, state, until, timeoutMs } = parseWaitArgs(args.rawArgs);
4030
+ async function runSnapshot(args) {
4031
+ const { sessionId, mode, maxChars, cursor, timeoutMs } = parseSnapshotArgs(args.rawArgs);
3182
4032
  const targetId = parseOptionalStringFlag(args.rawArgs, "--target-id");
3183
4033
  if (!sessionId) throw createUsageError("Missing --session-id");
3184
- const result = await callDaemon("nav.wait", {
4034
+ const payload = {
3185
4035
  sessionId,
3186
- ref,
3187
- state,
3188
- until,
3189
- timeoutMs,
4036
+ mode,
4037
+ maxChars,
4038
+ cursor,
3190
4039
  ...typeof targetId === "string" ? { targetId } : {}
4040
+ };
4041
+ const result = await callDaemon("nav.snapshot", payload, {
4042
+ timeoutMs: timeoutMs ?? DEFAULT_SNAPSHOT_TRANSPORT_TIMEOUT_MS
3191
4043
  });
3192
- return { success: true, message: "Wait complete.", data: result };
4044
+ return { success: true, message: "Snapshot captured.", data: result };
3193
4045
  }
3194
4046
 
3195
- // src/cli/commands/nav/snapshot.ts
3196
- function parseSnapshotArgs(rawArgs) {
4047
+ // src/cli/commands/nav/review.ts
4048
+ function parseReviewArgs(rawArgs) {
3197
4049
  const parsed = {};
3198
4050
  for (let i = 0; i < rawArgs.length; i += 1) {
3199
4051
  const arg = rawArgs[i];
@@ -3208,17 +4060,6 @@ function parseSnapshotArgs(rawArgs) {
3208
4060
  parsed.sessionId = arg.split("=", 2)[1];
3209
4061
  continue;
3210
4062
  }
3211
- if (arg === "--mode") {
3212
- const value = rawArgs[i + 1];
3213
- if (!value) throw createUsageError("Missing value for --mode");
3214
- parsed.mode = value;
3215
- i += 1;
3216
- continue;
3217
- }
3218
- if (arg?.startsWith("--mode=")) {
3219
- parsed.mode = arg.split("=", 2)[1];
3220
- continue;
3221
- }
3222
4063
  if (arg === "--max-chars") {
3223
4064
  const value = rawArgs[i + 1];
3224
4065
  if (!value) throw createUsageError("Missing value for --max-chars");
@@ -3241,21 +4082,35 @@ function parseSnapshotArgs(rawArgs) {
3241
4082
  parsed.cursor = arg.split("=", 2)[1];
3242
4083
  continue;
3243
4084
  }
4085
+ if (arg === "--timeout-ms") {
4086
+ const value = rawArgs[i + 1];
4087
+ if (!value) throw createUsageError("Missing value for --timeout-ms");
4088
+ parsed.timeoutMs = parseNumberFlag(value, "--timeout-ms", { min: 1 });
4089
+ i += 1;
4090
+ continue;
4091
+ }
4092
+ if (arg?.startsWith("--timeout-ms=")) {
4093
+ const value = arg.split("=", 2)[1];
4094
+ if (!value) throw createUsageError("Missing value for --timeout-ms");
4095
+ parsed.timeoutMs = parseNumberFlag(value, "--timeout-ms", { min: 1 });
4096
+ }
3244
4097
  }
3245
4098
  return parsed;
3246
4099
  }
3247
- async function runSnapshot(args) {
3248
- const { sessionId, mode, maxChars, cursor } = parseSnapshotArgs(args.rawArgs);
4100
+ async function runReview(args) {
4101
+ const { sessionId, maxChars, cursor, timeoutMs } = parseReviewArgs(args.rawArgs);
3249
4102
  const targetId = parseOptionalStringFlag(args.rawArgs, "--target-id");
3250
4103
  if (!sessionId) throw createUsageError("Missing --session-id");
3251
- const result = await callDaemon("nav.snapshot", {
4104
+ const payload = {
3252
4105
  sessionId,
3253
- mode,
3254
4106
  maxChars,
3255
4107
  cursor,
3256
4108
  ...typeof targetId === "string" ? { targetId } : {}
4109
+ };
4110
+ const result = await callDaemon("nav.review", payload, {
4111
+ timeoutMs: timeoutMs ?? DEFAULT_REVIEW_TRANSPORT_TIMEOUT_MS
3257
4112
  });
3258
- return { success: true, message: "Snapshot captured.", data: result };
4113
+ return { success: true, message: "Review captured.", data: result };
3259
4114
  }
3260
4115
 
3261
4116
  // src/cli/commands/annotate.ts
@@ -3435,8 +4290,6 @@ async function runAnnotate(args) {
3435
4290
  // src/cli/commands/canvas.ts
3436
4291
  import { readFileSync as readFileSync5 } from "fs";
3437
4292
  var DEFAULT_FEEDBACK_STREAM_TIMEOUT_MS = 3e4;
3438
- var MIN_FEEDBACK_STREAM_POLL_MS = 250;
3439
- var MAX_FEEDBACK_STREAM_POLL_MS = 1e3;
3440
4293
  var isRecord = (value) => {
3441
4294
  return typeof value === "object" && value !== null && !Array.isArray(value);
3442
4295
  };
@@ -3449,23 +4302,23 @@ var asNumber = (value) => {
3449
4302
  var asRecordArray = (value) => {
3450
4303
  return Array.isArray(value) ? value.filter(isRecord) : [];
3451
4304
  };
3452
- var sleep = async (ms) => {
3453
- await new Promise((resolve6) => setTimeout(resolve6, ms));
3454
- };
3455
- var toFeedbackPollResult = (value) => {
4305
+ var toFeedbackSubscribeResult = (value) => {
3456
4306
  if (!isRecord(value)) {
3457
- return { items: [], nextCursor: null };
4307
+ return {
4308
+ subscriptionId: null,
4309
+ initialItems: [],
4310
+ cursor: null,
4311
+ heartbeatMs: 15e3
4312
+ };
3458
4313
  }
3459
- const retention = isRecord(value.retention) ? {
3460
- activeTargetIds: Array.isArray(value.retention.activeTargetIds) ? value.retention.activeTargetIds.filter((entry) => typeof entry === "string") : void 0
3461
- } : void 0;
3462
4314
  return {
3463
- items: asRecordArray(value.items),
3464
- nextCursor: asString(value.nextCursor),
3465
- retention
4315
+ subscriptionId: asString(value.subscriptionId),
4316
+ initialItems: asRecordArray(value.initialItems),
4317
+ cursor: asString(value.cursor),
4318
+ heartbeatMs: Math.max(asNumber(value.heartbeatMs) ?? 15e3, 1e3)
3466
4319
  };
3467
4320
  };
3468
- async function streamFeedbackViaPolling(client, args, canvasArgs, params, initial) {
4321
+ async function streamFeedbackViaSubscription(client, args, canvasArgs, params, initial) {
3469
4322
  const outputOptions = { format: args.outputFormat, quiet: args.quiet };
3470
4323
  writeOutput({
3471
4324
  success: true,
@@ -3475,86 +4328,95 @@ async function streamFeedbackViaPolling(client, args, canvasArgs, params, initia
3475
4328
  result: initial
3476
4329
  }
3477
4330
  }, outputOptions);
3478
- const heartbeatMs = Math.max(asNumber(initial.heartbeatMs) ?? 15e3, 1e3);
3479
- const pollIntervalMs = Math.min(
3480
- MAX_FEEDBACK_STREAM_POLL_MS,
3481
- Math.max(MIN_FEEDBACK_STREAM_POLL_MS, Math.floor(heartbeatMs / 3))
3482
- );
4331
+ const initialResult = toFeedbackSubscribeResult(initial);
4332
+ const subscriptionId = initialResult.subscriptionId;
4333
+ const heartbeatMs = initialResult.heartbeatMs;
3483
4334
  const streamTimeoutMs = canvasArgs.timeoutMs ?? DEFAULT_FEEDBACK_STREAM_TIMEOUT_MS;
3484
4335
  const deadline = Date.now() + streamTimeoutMs;
3485
- let cursor = asString(initial.cursor);
3486
- let lastHeartbeatAt = Date.now();
3487
- while (Date.now() < deadline) {
3488
- await sleep(pollIntervalMs);
4336
+ let cursor = initialResult.cursor;
4337
+ for (const item of initialResult.initialItems) {
4338
+ const itemCursor = asString(item.cursor);
4339
+ if (itemCursor) {
4340
+ cursor = itemCursor;
4341
+ }
4342
+ writeOutput({
4343
+ success: true,
4344
+ message: `Canvas feedback: ${canvasArgs.command}`,
4345
+ data: {
4346
+ command: canvasArgs.command,
4347
+ streamEvent: {
4348
+ eventType: "feedback.item",
4349
+ item
4350
+ }
4351
+ }
4352
+ }, outputOptions);
4353
+ }
4354
+ while (subscriptionId && Date.now() < deadline) {
3489
4355
  const remainingMs = deadline - Date.now();
3490
4356
  if (remainingMs <= 0) {
3491
4357
  break;
3492
4358
  }
3493
- const polled = toFeedbackPollResult(await client.call(
4359
+ const nextEvent = await client.call(
3494
4360
  "canvas.execute",
3495
4361
  {
3496
- command: "canvas.feedback.poll",
4362
+ command: "canvas.feedback.next",
3497
4363
  params: {
3498
4364
  ...params,
3499
- afterCursor: cursor
4365
+ subscriptionId,
4366
+ timeoutMs: Math.min(heartbeatMs, remainingMs)
3500
4367
  }
3501
4368
  },
3502
4369
  { timeoutMs: remainingMs }
3503
- ));
3504
- if (polled.items.length > 0) {
3505
- for (const item of polled.items) {
3506
- const itemCursor = asString(item.cursor);
4370
+ );
4371
+ if (isRecord(nextEvent)) {
4372
+ if (nextEvent.eventType === "feedback.item" && isRecord(nextEvent.item)) {
4373
+ const itemCursor = asString(nextEvent.item.cursor);
3507
4374
  if (itemCursor) {
3508
4375
  cursor = itemCursor;
3509
4376
  }
3510
- writeOutput({
3511
- success: true,
3512
- message: `Canvas feedback: ${canvasArgs.command}`,
3513
- data: {
3514
- command: canvasArgs.command,
3515
- streamEvent: {
3516
- eventType: "feedback.item",
3517
- item,
3518
- cursor: cursor ?? null
3519
- }
3520
- }
3521
- }, outputOptions);
4377
+ } else if (typeof nextEvent.cursor === "string") {
4378
+ cursor = nextEvent.cursor;
3522
4379
  }
3523
- lastHeartbeatAt = Date.now();
3524
- continue;
3525
4380
  }
3526
- if (polled.nextCursor) {
3527
- cursor = polled.nextCursor;
4381
+ writeOutput({
4382
+ success: true,
4383
+ message: `Canvas feedback: ${canvasArgs.command}`,
4384
+ data: {
4385
+ command: canvasArgs.command,
4386
+ streamEvent: nextEvent
4387
+ }
4388
+ }, outputOptions);
4389
+ if (isRecord(nextEvent) && nextEvent.eventType === "feedback.complete") {
4390
+ break;
3528
4391
  }
3529
- if (Date.now() - lastHeartbeatAt >= heartbeatMs) {
3530
- writeOutput({
3531
- success: true,
3532
- message: `Canvas feedback: ${canvasArgs.command}`,
3533
- data: {
3534
- command: canvasArgs.command,
3535
- streamEvent: {
3536
- eventType: "feedback.heartbeat",
3537
- cursor: cursor ?? null,
3538
- ts: (/* @__PURE__ */ new Date()).toISOString(),
3539
- activeTargetIds: polled.retention?.activeTargetIds ?? []
3540
- }
4392
+ }
4393
+ if (Date.now() >= deadline) {
4394
+ writeOutput({
4395
+ success: true,
4396
+ message: `Canvas feedback: ${canvasArgs.command}`,
4397
+ data: {
4398
+ command: canvasArgs.command,
4399
+ streamEvent: {
4400
+ eventType: "feedback.complete",
4401
+ cursor: cursor ?? null,
4402
+ reason: "timeout"
3541
4403
  }
3542
- }, outputOptions);
3543
- lastHeartbeatAt = Date.now();
3544
- }
4404
+ }
4405
+ }, outputOptions);
3545
4406
  }
3546
- writeOutput({
3547
- success: true,
3548
- message: `Canvas feedback: ${canvasArgs.command}`,
3549
- data: {
3550
- command: canvasArgs.command,
3551
- streamEvent: {
3552
- eventType: "feedback.complete",
3553
- cursor: cursor ?? null,
3554
- completeReason: "timeout"
4407
+ if (subscriptionId) {
4408
+ await client.call(
4409
+ "canvas.execute",
4410
+ {
4411
+ command: "canvas.feedback.unsubscribe",
4412
+ params: {
4413
+ ...params,
4414
+ subscriptionId
4415
+ }
3555
4416
  }
3556
- }
3557
- }, outputOptions);
4417
+ ).catch(() => {
4418
+ });
4419
+ }
3558
4420
  }
3559
4421
  var requireValue3 = (value, flag) => {
3560
4422
  if (!value) throw createUsageError(`Missing value for ${flag}`);
@@ -3639,6 +4501,15 @@ var resolveCanvasParams = (canvasArgs) => {
3639
4501
  }
3640
4502
  return {};
3641
4503
  };
4504
+ var attachRepoRoot = (params) => {
4505
+ if (typeof params.repoRoot === "string" && params.repoRoot.trim().length > 0) {
4506
+ return params;
4507
+ }
4508
+ return {
4509
+ ...params,
4510
+ repoRoot: process.cwd()
4511
+ };
4512
+ };
3642
4513
  async function runCanvas(args) {
3643
4514
  const canvasArgs = parseCanvasArgs(args.rawArgs);
3644
4515
  if (!canvasArgs.command) {
@@ -3649,7 +4520,7 @@ async function runCanvas(args) {
3649
4520
  }
3650
4521
  const client = new DaemonClient({ autoRenew: true });
3651
4522
  try {
3652
- const params = resolveCanvasParams(canvasArgs);
4523
+ const params = attachRepoRoot(resolveCanvasParams(canvasArgs));
3653
4524
  const result = await client.call(
3654
4525
  "canvas.execute",
3655
4526
  {
@@ -3659,7 +4530,7 @@ async function runCanvas(args) {
3659
4530
  { timeoutMs: canvasArgs.timeoutMs }
3660
4531
  );
3661
4532
  if (canvasArgs.command === "canvas.feedback.subscribe" && args.outputFormat === "stream-json" && isRecord(result)) {
3662
- await streamFeedbackViaPolling(client, args, canvasArgs, params, result);
4533
+ await streamFeedbackViaSubscription(client, args, canvasArgs, params, result);
3663
4534
  return {
3664
4535
  success: true,
3665
4536
  message: `Canvas executed: ${canvasArgs.command}`,
@@ -3813,7 +4684,10 @@ async function runRpc(args) {
3813
4684
 
3814
4685
  // src/cli/commands/interact/click.ts
3815
4686
  function parseClickArgs(rawArgs) {
3816
- const parsed = {};
4687
+ const timeoutValue = parseOptionalStringFlag(rawArgs, "--timeout-ms");
4688
+ const parsed = {
4689
+ timeoutMs: typeof timeoutValue === "string" ? parseNumberFlag(timeoutValue, "--timeout-ms", { min: 1 }) : void 0
4690
+ };
3817
4691
  for (let i = 0; i < rawArgs.length; i += 1) {
3818
4692
  const arg = rawArgs[i];
3819
4693
  if (arg === "--session-id") {
@@ -3842,14 +4716,17 @@ function parseClickArgs(rawArgs) {
3842
4716
  return parsed;
3843
4717
  }
3844
4718
  async function runClick(args) {
3845
- const { sessionId, ref } = parseClickArgs(args.rawArgs);
4719
+ const { sessionId, ref, timeoutMs } = parseClickArgs(args.rawArgs);
3846
4720
  const targetId = parseOptionalStringFlag(args.rawArgs, "--target-id");
3847
4721
  if (!sessionId) throw createUsageError("Missing --session-id");
3848
4722
  if (!ref) throw createUsageError("Missing --ref");
3849
- const result = await callDaemon("interact.click", {
4723
+ const params = {
3850
4724
  sessionId,
3851
4725
  ref,
3852
4726
  ...typeof targetId === "string" ? { targetId } : {}
4727
+ };
4728
+ const result = await callDaemon("interact.click", params, {
4729
+ timeoutMs: timeoutMs ?? DEFAULT_CLICK_TRANSPORT_TIMEOUT_MS
3853
4730
  });
3854
4731
  return { success: true, message: "Click complete.", data: result };
3855
4732
  }
@@ -4260,6 +5137,146 @@ async function runScrollIntoView(args) {
4260
5137
  return { success: true, message: "Scroll into view complete.", data: result };
4261
5138
  }
4262
5139
 
5140
+ // src/cli/commands/interact/upload.ts
5141
+ function parseUploadArgs(rawArgs) {
5142
+ return {
5143
+ sessionId: parseOptionalStringFlag(rawArgs, "--session-id"),
5144
+ targetId: parseOptionalStringFlag(rawArgs, "--target-id"),
5145
+ ref: parseOptionalStringFlag(rawArgs, "--ref"),
5146
+ files: parseStringArrayFlag(rawArgs, "--files")
5147
+ };
5148
+ }
5149
+ async function runUpload(args) {
5150
+ const { sessionId, targetId, ref, files } = parseUploadArgs(args.rawArgs);
5151
+ if (!sessionId) throw createUsageError("Missing --session-id");
5152
+ if (!ref) throw createUsageError("Missing --ref");
5153
+ if (!files) throw createUsageError("Missing --files");
5154
+ const result = await callDaemon("interact.upload", {
5155
+ sessionId,
5156
+ ref,
5157
+ files,
5158
+ ...typeof targetId === "string" ? { targetId } : {}
5159
+ });
5160
+ return { success: true, message: "Upload complete.", data: result };
5161
+ }
5162
+
5163
+ // src/cli/commands/interact/pointer-shared.ts
5164
+ var POINTER_BUTTONS = /* @__PURE__ */ new Set(["left", "middle", "right"]);
5165
+ function requireFlagValue(rawArgs, flag) {
5166
+ const value = parseOptionalStringFlag(rawArgs, flag);
5167
+ if (!value) {
5168
+ throw createUsageError(`Missing ${flag}`);
5169
+ }
5170
+ return value;
5171
+ }
5172
+ function requirePointerCoordinate(rawArgs, flag) {
5173
+ return parseNumberFlag(requireFlagValue(rawArgs, flag), flag, { integer: false });
5174
+ }
5175
+ function parsePointerSteps(rawArgs) {
5176
+ const value = parseOptionalStringFlag(rawArgs, "--steps");
5177
+ return value ? parseNumberFlag(value, "--steps", { min: 1 }) : void 0;
5178
+ }
5179
+ function parsePointerClickCount(rawArgs) {
5180
+ const value = parseOptionalStringFlag(rawArgs, "--click-count");
5181
+ return value ? parseNumberFlag(value, "--click-count", { min: 1 }) : void 0;
5182
+ }
5183
+ function parsePointerButton(rawArgs) {
5184
+ const value = parseOptionalStringFlag(rawArgs, "--button");
5185
+ if (!value) {
5186
+ return void 0;
5187
+ }
5188
+ if (!POINTER_BUTTONS.has(value)) {
5189
+ throw createUsageError(`Invalid --button: ${value}`);
5190
+ }
5191
+ return value;
5192
+ }
5193
+
5194
+ // src/cli/commands/interact/pointer-move.ts
5195
+ async function runPointerMove(args) {
5196
+ const sessionId = parseOptionalStringFlag(args.rawArgs, "--session-id");
5197
+ const targetId = parseOptionalStringFlag(args.rawArgs, "--target-id");
5198
+ const x = requirePointerCoordinate(args.rawArgs, "--x");
5199
+ const y = requirePointerCoordinate(args.rawArgs, "--y");
5200
+ const steps = parsePointerSteps(args.rawArgs);
5201
+ if (!sessionId) {
5202
+ throw createUsageError("Missing --session-id");
5203
+ }
5204
+ const result = await callDaemon("pointer.move", {
5205
+ sessionId,
5206
+ x,
5207
+ y,
5208
+ ...typeof steps === "number" ? { steps } : {},
5209
+ ...typeof targetId === "string" ? { targetId } : {}
5210
+ });
5211
+ return { success: true, message: "Pointer move complete.", data: result };
5212
+ }
5213
+
5214
+ // src/cli/commands/interact/pointer-down.ts
5215
+ async function runPointerDown(args) {
5216
+ const sessionId = parseOptionalStringFlag(args.rawArgs, "--session-id");
5217
+ const targetId = parseOptionalStringFlag(args.rawArgs, "--target-id");
5218
+ const x = requirePointerCoordinate(args.rawArgs, "--x");
5219
+ const y = requirePointerCoordinate(args.rawArgs, "--y");
5220
+ const button = parsePointerButton(args.rawArgs);
5221
+ const clickCount = parsePointerClickCount(args.rawArgs);
5222
+ if (!sessionId) {
5223
+ throw createUsageError("Missing --session-id");
5224
+ }
5225
+ const result = await callDaemon("pointer.down", {
5226
+ sessionId,
5227
+ x,
5228
+ y,
5229
+ ...typeof button === "string" ? { button } : {},
5230
+ ...typeof clickCount === "number" ? { clickCount } : {},
5231
+ ...typeof targetId === "string" ? { targetId } : {}
5232
+ });
5233
+ return { success: true, message: "Pointer down complete.", data: result };
5234
+ }
5235
+
5236
+ // src/cli/commands/interact/pointer-up.ts
5237
+ async function runPointerUp(args) {
5238
+ const sessionId = parseOptionalStringFlag(args.rawArgs, "--session-id");
5239
+ const targetId = parseOptionalStringFlag(args.rawArgs, "--target-id");
5240
+ const x = requirePointerCoordinate(args.rawArgs, "--x");
5241
+ const y = requirePointerCoordinate(args.rawArgs, "--y");
5242
+ const button = parsePointerButton(args.rawArgs);
5243
+ const clickCount = parsePointerClickCount(args.rawArgs);
5244
+ if (!sessionId) {
5245
+ throw createUsageError("Missing --session-id");
5246
+ }
5247
+ const result = await callDaemon("pointer.up", {
5248
+ sessionId,
5249
+ x,
5250
+ y,
5251
+ ...typeof button === "string" ? { button } : {},
5252
+ ...typeof clickCount === "number" ? { clickCount } : {},
5253
+ ...typeof targetId === "string" ? { targetId } : {}
5254
+ });
5255
+ return { success: true, message: "Pointer up complete.", data: result };
5256
+ }
5257
+
5258
+ // src/cli/commands/interact/pointer-drag.ts
5259
+ async function runPointerDrag(args) {
5260
+ const sessionId = parseOptionalStringFlag(args.rawArgs, "--session-id");
5261
+ const targetId = parseOptionalStringFlag(args.rawArgs, "--target-id");
5262
+ const fromX = requirePointerCoordinate(args.rawArgs, "--from-x");
5263
+ const fromY = requirePointerCoordinate(args.rawArgs, "--from-y");
5264
+ const toX = requirePointerCoordinate(args.rawArgs, "--to-x");
5265
+ const toY = requirePointerCoordinate(args.rawArgs, "--to-y");
5266
+ const steps = parsePointerSteps(args.rawArgs);
5267
+ if (!sessionId) {
5268
+ throw createUsageError("Missing --session-id");
5269
+ }
5270
+ const result = await callDaemon("pointer.drag", {
5271
+ sessionId,
5272
+ from: { x: fromX, y: fromY },
5273
+ to: { x: toX, y: toY },
5274
+ ...typeof steps === "number" ? { steps } : {},
5275
+ ...typeof targetId === "string" ? { targetId } : {}
5276
+ });
5277
+ return { success: true, message: "Pointer drag complete.", data: result };
5278
+ }
5279
+
4263
5280
  // src/cli/commands/targets/list.ts
4264
5281
  function parseTargetsListArgs(rawArgs) {
4265
5282
  const parsed = {};
@@ -4360,7 +5377,11 @@ function parseTargetNewArgs(rawArgs) {
4360
5377
  async function runTargetNew(args) {
4361
5378
  const { sessionId, url } = parseTargetNewArgs(args.rawArgs);
4362
5379
  if (!sessionId) throw createUsageError("Missing --session-id");
4363
- const result = await callDaemon("targets.new", { sessionId, url });
5380
+ const result = await callDaemon(
5381
+ "targets.new",
5382
+ { sessionId, url },
5383
+ { timeoutMs: DEFAULT_TARGET_CREATION_TRANSPORT_TIMEOUT_MS }
5384
+ );
4364
5385
  return { success: true, message: "Target created.", data: result };
4365
5386
  }
4366
5387
 
@@ -4447,7 +5468,11 @@ async function runPageOpen(args) {
4447
5468
  const { sessionId, name, url } = parsePageOpenArgs(args.rawArgs);
4448
5469
  if (!sessionId) throw createUsageError("Missing --session-id");
4449
5470
  if (!name) throw createUsageError("Missing --name");
4450
- const result = await callDaemon("page.open", { sessionId, name, url });
5471
+ const result = await callDaemon(
5472
+ "page.open",
5473
+ { sessionId, name, url },
5474
+ { timeoutMs: DEFAULT_TARGET_CREATION_TRANSPORT_TIMEOUT_MS }
5475
+ );
4451
5476
  return { success: true, message: `Page ready: ${name}`, data: result };
4452
5477
  }
4453
5478
 
@@ -4945,97 +5970,162 @@ async function runCloneComponent(args) {
4945
5970
  });
4946
5971
  return { success: true, message: "Component cloned.", data: result };
4947
5972
  }
4948
-
4949
- // src/cli/commands/devtools/perf.ts
4950
- function parsePerfArgs(rawArgs) {
4951
- const parsed = {};
4952
- for (let i = 0; i < rawArgs.length; i += 1) {
4953
- const arg = rawArgs[i];
4954
- if (arg === "--session-id") {
4955
- const value = rawArgs[i + 1];
4956
- if (!value) throw createUsageError("Missing value for --session-id");
4957
- parsed.sessionId = value;
4958
- i += 1;
4959
- continue;
4960
- }
4961
- if (arg?.startsWith("--session-id=")) {
4962
- parsed.sessionId = arg.split("=", 2)[1];
4963
- continue;
4964
- }
4965
- }
4966
- return parsed;
4967
- }
4968
- async function runPerf(args) {
4969
- const { sessionId } = parsePerfArgs(args.rawArgs);
4970
- const targetId = parseOptionalStringFlag(args.rawArgs, "--target-id");
4971
- if (!sessionId) throw createUsageError("Missing --session-id");
4972
- const result = await callDaemon("devtools.perf", {
4973
- sessionId,
4974
- ...typeof targetId === "string" ? { targetId } : {}
4975
- });
4976
- return { success: true, message: "Performance metrics captured.", data: result };
4977
- }
4978
-
4979
- // src/cli/commands/devtools/screenshot.ts
4980
- var requireValue5 = (value, flag) => {
4981
- if (!value) throw createUsageError(`Missing value for ${flag}`);
4982
- return value;
4983
- };
4984
- function parseScreenshotArgs(rawArgs) {
4985
- const parsed = {};
4986
- for (let i = 0; i < rawArgs.length; i += 1) {
4987
- const arg = rawArgs[i];
4988
- if (arg === "--session-id") {
4989
- parsed.sessionId = requireValue5(rawArgs[i + 1], "--session-id");
4990
- i += 1;
4991
- continue;
4992
- }
4993
- if (arg?.startsWith("--session-id=")) {
4994
- parsed.sessionId = requireValue5(arg.split("=", 2)[1], "--session-id");
4995
- continue;
4996
- }
4997
- if (arg === "--target-id") {
4998
- parsed.targetId = requireValue5(rawArgs[i + 1], "--target-id");
4999
- i += 1;
5000
- continue;
5001
- }
5002
- if (arg?.startsWith("--target-id=")) {
5003
- parsed.targetId = requireValue5(arg.split("=", 2)[1], "--target-id");
5004
- continue;
5005
- }
5006
- if (arg === "--path") {
5007
- parsed.path = requireValue5(rawArgs[i + 1], "--path");
5008
- i += 1;
5009
- continue;
5010
- }
5011
- if (arg?.startsWith("--path=")) {
5012
- parsed.path = requireValue5(arg.split("=", 2)[1], "--path");
5013
- continue;
5014
- }
5015
- if (arg === "--timeout-ms") {
5016
- parsed.timeoutMs = parseNumberFlag(requireValue5(rawArgs[i + 1], "--timeout-ms"), "--timeout-ms", { min: 1 });
5973
+
5974
+ // src/cli/commands/devtools/perf.ts
5975
+ function parsePerfArgs(rawArgs) {
5976
+ const parsed = {};
5977
+ for (let i = 0; i < rawArgs.length; i += 1) {
5978
+ const arg = rawArgs[i];
5979
+ if (arg === "--session-id") {
5980
+ const value = rawArgs[i + 1];
5981
+ if (!value) throw createUsageError("Missing value for --session-id");
5982
+ parsed.sessionId = value;
5017
5983
  i += 1;
5018
5984
  continue;
5019
5985
  }
5020
- if (arg?.startsWith("--timeout-ms=")) {
5021
- parsed.timeoutMs = parseNumberFlag(requireValue5(arg.split("=", 2)[1], "--timeout-ms"), "--timeout-ms", { min: 1 });
5986
+ if (arg?.startsWith("--session-id=")) {
5987
+ parsed.sessionId = arg.split("=", 2)[1];
5022
5988
  continue;
5023
5989
  }
5024
5990
  }
5025
5991
  return parsed;
5026
5992
  }
5993
+ async function runPerf(args) {
5994
+ const { sessionId } = parsePerfArgs(args.rawArgs);
5995
+ const targetId = parseOptionalStringFlag(args.rawArgs, "--target-id");
5996
+ if (!sessionId) throw createUsageError("Missing --session-id");
5997
+ const result = await callDaemon("devtools.perf", {
5998
+ sessionId,
5999
+ ...typeof targetId === "string" ? { targetId } : {}
6000
+ });
6001
+ return { success: true, message: "Performance metrics captured.", data: result };
6002
+ }
6003
+
6004
+ // src/cli/commands/devtools/screenshot.ts
6005
+ function parseScreenshotArgs(rawArgs) {
6006
+ const timeoutValue = parseOptionalStringFlag(rawArgs, "--timeout-ms");
6007
+ const parsed = {
6008
+ sessionId: parseOptionalStringFlag(rawArgs, "--session-id"),
6009
+ targetId: parseOptionalStringFlag(rawArgs, "--target-id"),
6010
+ path: parseOptionalStringFlag(rawArgs, "--path"),
6011
+ ref: parseOptionalStringFlag(rawArgs, "--ref"),
6012
+ fullPage: rawArgs.includes("--full-page"),
6013
+ timeoutMs: typeof timeoutValue === "string" ? parseNumberFlag(timeoutValue, "--timeout-ms", { min: 1 }) : void 0
6014
+ };
6015
+ if (parsed.ref && parsed.fullPage) {
6016
+ throw createUsageError("Choose either --ref or --full-page.");
6017
+ }
6018
+ return parsed;
6019
+ }
5027
6020
  async function runScreenshot(args) {
5028
- const { sessionId, targetId, path: path8, timeoutMs } = parseScreenshotArgs(args.rawArgs);
6021
+ const { sessionId, targetId, path: path8, ref, fullPage, timeoutMs } = parseScreenshotArgs(args.rawArgs);
5029
6022
  if (!sessionId) throw createUsageError("Missing --session-id");
5030
6023
  const params = {
5031
6024
  sessionId,
5032
- path: path8,
5033
- ...typeof targetId === "string" ? { targetId } : {}
6025
+ ...typeof path8 === "string" ? { path: path8 } : {},
6026
+ ...typeof targetId === "string" ? { targetId } : {},
6027
+ ...typeof ref === "string" ? { ref } : {},
6028
+ ...fullPage === true ? { fullPage: true } : {}
5034
6029
  };
5035
- const result = typeof timeoutMs === "number" ? await callDaemon("page.screenshot", params, { timeoutMs }) : await callDaemon("page.screenshot", params);
6030
+ const result = await callDaemon("page.screenshot", params, {
6031
+ timeoutMs: timeoutMs ?? DEFAULT_SCREENSHOT_TRANSPORT_TIMEOUT_MS
6032
+ });
5036
6033
  return { success: true, message: "Screenshot captured.", data: result };
5037
6034
  }
5038
6035
 
6036
+ // src/cli/commands/devtools/screencast-start.ts
6037
+ function parseScreencastStartArgs(rawArgs) {
6038
+ const intervalValue = parseOptionalStringFlag(rawArgs, "--interval-ms");
6039
+ const maxFramesValue = parseOptionalStringFlag(rawArgs, "--max-frames");
6040
+ const timeoutValue = parseOptionalStringFlag(rawArgs, "--timeout-ms");
6041
+ return {
6042
+ sessionId: parseOptionalStringFlag(rawArgs, "--session-id"),
6043
+ targetId: parseOptionalStringFlag(rawArgs, "--target-id"),
6044
+ outputDir: parseOptionalStringFlag(rawArgs, "--output-dir"),
6045
+ intervalMs: typeof intervalValue === "string" ? parseNumberFlag(intervalValue, "--interval-ms", { min: 250 }) : void 0,
6046
+ maxFrames: typeof maxFramesValue === "string" ? parseNumberFlag(maxFramesValue, "--max-frames", { min: 1 }) : void 0,
6047
+ timeoutMs: typeof timeoutValue === "string" ? parseNumberFlag(timeoutValue, "--timeout-ms", { min: 1 }) : void 0
6048
+ };
6049
+ }
6050
+ async function runScreencastStart(args) {
6051
+ const { sessionId, targetId, outputDir, intervalMs, maxFrames, timeoutMs } = parseScreencastStartArgs(args.rawArgs);
6052
+ if (!sessionId) {
6053
+ throw createUsageError("Missing --session-id");
6054
+ }
6055
+ const result = await callDaemon("page.screencast.start", {
6056
+ sessionId,
6057
+ ...typeof targetId === "string" ? { targetId } : {},
6058
+ ...typeof outputDir === "string" ? { outputDir } : {},
6059
+ ...typeof intervalMs === "number" ? { intervalMs } : {},
6060
+ ...typeof maxFrames === "number" ? { maxFrames } : {}
6061
+ }, {
6062
+ timeoutMs: timeoutMs ?? DEFAULT_SCREENSHOT_TRANSPORT_TIMEOUT_MS
6063
+ });
6064
+ return { success: true, message: "Screencast started.", data: result };
6065
+ }
6066
+
6067
+ // src/cli/commands/devtools/screencast-stop.ts
6068
+ function parseScreencastStopArgs(rawArgs) {
6069
+ const timeoutValue = parseOptionalStringFlag(rawArgs, "--timeout-ms");
6070
+ return {
6071
+ sessionId: parseOptionalStringFlag(rawArgs, "--session-id"),
6072
+ screencastId: parseOptionalStringFlag(rawArgs, "--screencast-id"),
6073
+ timeoutMs: typeof timeoutValue === "string" ? parseNumberFlag(timeoutValue, "--timeout-ms", { min: 1 }) : void 0
6074
+ };
6075
+ }
6076
+ async function runScreencastStop(args) {
6077
+ const { sessionId, screencastId, timeoutMs } = parseScreencastStopArgs(args.rawArgs);
6078
+ if (!sessionId) {
6079
+ throw createUsageError("Missing --session-id");
6080
+ }
6081
+ if (!screencastId) {
6082
+ throw createUsageError("Missing --screencast-id");
6083
+ }
6084
+ const result = await callDaemon("page.screencast.stop", { sessionId, screencastId }, {
6085
+ timeoutMs: timeoutMs ?? DEFAULT_SCREENSHOT_TRANSPORT_TIMEOUT_MS
6086
+ });
6087
+ return { success: true, message: "Screencast stopped.", data: result };
6088
+ }
6089
+
6090
+ // src/cli/commands/devtools/dialog.ts
6091
+ function parseDialogAction(value) {
6092
+ if (!value) {
6093
+ return "status";
6094
+ }
6095
+ if (value === "status" || value === "accept" || value === "dismiss") {
6096
+ return value;
6097
+ }
6098
+ throw createUsageError(`Invalid --action: ${value}`);
6099
+ }
6100
+ function parseDialogArgs(rawArgs) {
6101
+ const timeoutValue = parseOptionalStringFlag(rawArgs, "--timeout-ms");
6102
+ const parsed = {
6103
+ sessionId: parseOptionalStringFlag(rawArgs, "--session-id"),
6104
+ targetId: parseOptionalStringFlag(rawArgs, "--target-id"),
6105
+ action: parseDialogAction(parseOptionalStringFlag(rawArgs, "--action")),
6106
+ promptText: parseOptionalStringFlag(rawArgs, "--prompt-text"),
6107
+ timeoutMs: typeof timeoutValue === "string" ? parseNumberFlag(timeoutValue, "--timeout-ms", { min: 1 }) : void 0
6108
+ };
6109
+ if (parsed.promptText && parsed.action !== "accept") {
6110
+ throw createUsageError("--prompt-text is only valid with --action accept");
6111
+ }
6112
+ return parsed;
6113
+ }
6114
+ async function runDialog(args) {
6115
+ const { sessionId, targetId, action, promptText, timeoutMs } = parseDialogArgs(args.rawArgs);
6116
+ if (!sessionId) throw createUsageError("Missing --session-id");
6117
+ const params = {
6118
+ sessionId,
6119
+ action,
6120
+ ...typeof targetId === "string" ? { targetId } : {},
6121
+ ...typeof promptText === "string" ? { promptText } : {}
6122
+ };
6123
+ const result = await callDaemon("page.dialog", params, {
6124
+ timeoutMs: timeoutMs ?? DEFAULT_DIALOG_TRANSPORT_TIMEOUT_MS
6125
+ });
6126
+ return { success: true, message: "Dialog request complete.", data: result };
6127
+ }
6128
+
5039
6129
  // src/cli/commands/devtools/console-poll.ts
5040
6130
  function parseConsolePollArgs(rawArgs) {
5041
6131
  const parsed = {};
@@ -5236,9 +6326,103 @@ async function runDebugTraceSnapshot(args) {
5236
6326
  };
5237
6327
  }
5238
6328
 
6329
+ // src/cli/commands/desktop/shared.ts
6330
+ function parseDesktopTimeoutArgs(rawArgs) {
6331
+ const timeoutValue = parseOptionalStringFlag(rawArgs, "--timeout-ms");
6332
+ return {
6333
+ timeoutMs: typeof timeoutValue === "string" ? parseNumberFlag(timeoutValue, "--timeout-ms", { min: 1 }) : void 0
6334
+ };
6335
+ }
6336
+ function parseDesktopReasonArgs(rawArgs) {
6337
+ return {
6338
+ ...parseDesktopTimeoutArgs(rawArgs),
6339
+ reason: parseOptionalStringFlag(rawArgs, "--reason")
6340
+ };
6341
+ }
6342
+ function parseDesktopWindowReasonArgs(rawArgs) {
6343
+ return {
6344
+ ...parseDesktopReasonArgs(rawArgs),
6345
+ windowId: parseOptionalStringFlag(rawArgs, "--window-id")
6346
+ };
6347
+ }
6348
+ function requireDesktopReason(reason) {
6349
+ if (!reason) {
6350
+ throw createUsageError("Missing --reason");
6351
+ }
6352
+ return reason;
6353
+ }
6354
+ function requireDesktopWindowId(windowId) {
6355
+ if (!windowId) {
6356
+ throw createUsageError("Missing --window-id");
6357
+ }
6358
+ return windowId;
6359
+ }
6360
+ async function callDesktopCommand(name, params, timeoutMs) {
6361
+ return callDaemon(name, params, {
6362
+ timeoutMs: timeoutMs ?? DEFAULT_SCREENSHOT_TRANSPORT_TIMEOUT_MS
6363
+ });
6364
+ }
6365
+ function desktopCommandResult(message, data) {
6366
+ return { success: true, message, data };
6367
+ }
6368
+
6369
+ // src/cli/commands/desktop/status.ts
6370
+ async function runDesktopStatus(args) {
6371
+ const { timeoutMs } = parseDesktopTimeoutArgs(args.rawArgs);
6372
+ const result = await callDesktopCommand("desktop.status", {}, timeoutMs);
6373
+ return desktopCommandResult("Desktop status captured.", result);
6374
+ }
6375
+
6376
+ // src/cli/commands/desktop/windows.ts
6377
+ async function runDesktopWindows(args) {
6378
+ const { reason, timeoutMs } = parseDesktopReasonArgs(args.rawArgs);
6379
+ const result = await callDesktopCommand("desktop.windows.list", {
6380
+ ...typeof reason === "string" ? { reason } : {}
6381
+ }, timeoutMs);
6382
+ return desktopCommandResult("Desktop windows listed.", result);
6383
+ }
6384
+
6385
+ // src/cli/commands/desktop/active-window.ts
6386
+ async function runDesktopActiveWindow(args) {
6387
+ const { reason, timeoutMs } = parseDesktopReasonArgs(args.rawArgs);
6388
+ const result = await callDesktopCommand("desktop.window.active", {
6389
+ ...typeof reason === "string" ? { reason } : {}
6390
+ }, timeoutMs);
6391
+ return desktopCommandResult("Active desktop window captured.", result);
6392
+ }
6393
+
6394
+ // src/cli/commands/desktop/capture-desktop.ts
6395
+ async function runDesktopCaptureDesktop(args) {
6396
+ const { reason, timeoutMs } = parseDesktopReasonArgs(args.rawArgs);
6397
+ const result = await callDesktopCommand("desktop.capture.desktop", {
6398
+ reason: requireDesktopReason(reason)
6399
+ }, timeoutMs);
6400
+ return desktopCommandResult("Desktop captured.", result);
6401
+ }
6402
+
6403
+ // src/cli/commands/desktop/capture-window.ts
6404
+ async function runDesktopCaptureWindow(args) {
6405
+ const { windowId, reason, timeoutMs } = parseDesktopWindowReasonArgs(args.rawArgs);
6406
+ const result = await callDesktopCommand("desktop.capture.window", {
6407
+ windowId: requireDesktopWindowId(windowId),
6408
+ reason: requireDesktopReason(reason)
6409
+ }, timeoutMs);
6410
+ return desktopCommandResult("Desktop window captured.", result);
6411
+ }
6412
+
6413
+ // src/cli/commands/desktop/accessibility-snapshot.ts
6414
+ async function runDesktopAccessibilitySnapshot(args) {
6415
+ const { windowId, reason, timeoutMs } = parseDesktopWindowReasonArgs(args.rawArgs);
6416
+ const result = await callDesktopCommand("desktop.accessibility.snapshot", {
6417
+ reason: requireDesktopReason(reason),
6418
+ ...typeof windowId === "string" ? { windowId } : {}
6419
+ }, timeoutMs);
6420
+ return desktopCommandResult("Desktop accessibility snapshot captured.", result);
6421
+ }
6422
+
5239
6423
  // src/cli/commands/session/cookie-import.ts
5240
6424
  import { readFileSync as readFileSync7 } from "fs";
5241
- var requireValue6 = (value, flag) => {
6425
+ var requireValue5 = (value, flag) => {
5242
6426
  if (!value) {
5243
6427
  throw createUsageError(`Missing value for ${flag}`);
5244
6428
  }
@@ -5255,30 +6439,30 @@ var parseCookieImportArgs = (rawArgs) => {
5255
6439
  for (let index = 0; index < rawArgs.length; index += 1) {
5256
6440
  const arg = rawArgs[index];
5257
6441
  if (arg === "--session-id") {
5258
- parsed.sessionId = requireValue6(rawArgs[index + 1], "--session-id");
6442
+ parsed.sessionId = requireValue5(rawArgs[index + 1], "--session-id");
5259
6443
  index += 1;
5260
6444
  continue;
5261
6445
  }
5262
6446
  if (arg?.startsWith("--session-id=")) {
5263
- parsed.sessionId = requireValue6(arg.split("=", 2)[1], "--session-id");
6447
+ parsed.sessionId = requireValue5(arg.split("=", 2)[1], "--session-id");
5264
6448
  continue;
5265
6449
  }
5266
6450
  if (arg === "--cookies") {
5267
- parsed.cookies = requireValue6(rawArgs[index + 1], "--cookies");
6451
+ parsed.cookies = requireValue5(rawArgs[index + 1], "--cookies");
5268
6452
  index += 1;
5269
6453
  continue;
5270
6454
  }
5271
6455
  if (arg?.startsWith("--cookies=")) {
5272
- parsed.cookies = requireValue6(arg.split("=", 2)[1], "--cookies");
6456
+ parsed.cookies = requireValue5(arg.split("=", 2)[1], "--cookies");
5273
6457
  continue;
5274
6458
  }
5275
6459
  if (arg === "--cookies-file") {
5276
- parsed.cookiesFile = requireValue6(rawArgs[index + 1], "--cookies-file");
6460
+ parsed.cookiesFile = requireValue5(rawArgs[index + 1], "--cookies-file");
5277
6461
  index += 1;
5278
6462
  continue;
5279
6463
  }
5280
6464
  if (arg?.startsWith("--cookies-file=")) {
5281
- parsed.cookiesFile = requireValue6(arg.split("=", 2)[1], "--cookies-file");
6465
+ parsed.cookiesFile = requireValue5(arg.split("=", 2)[1], "--cookies-file");
5282
6466
  continue;
5283
6467
  }
5284
6468
  if (arg === "--strict") {
@@ -5286,16 +6470,16 @@ var parseCookieImportArgs = (rawArgs) => {
5286
6470
  continue;
5287
6471
  }
5288
6472
  if (arg?.startsWith("--strict=")) {
5289
- parsed.strict = parseStrictValue(requireValue6(arg.split("=", 2)[1], "--strict"), "--strict");
6473
+ parsed.strict = parseStrictValue(requireValue5(arg.split("=", 2)[1], "--strict"), "--strict");
5290
6474
  continue;
5291
6475
  }
5292
6476
  if (arg === "--request-id") {
5293
- parsed.requestId = requireValue6(rawArgs[index + 1], "--request-id");
6477
+ parsed.requestId = requireValue5(rawArgs[index + 1], "--request-id");
5294
6478
  index += 1;
5295
6479
  continue;
5296
6480
  }
5297
6481
  if (arg?.startsWith("--request-id=")) {
5298
- parsed.requestId = requireValue6(arg.split("=", 2)[1], "--request-id");
6482
+ parsed.requestId = requireValue5(arg.split("=", 2)[1], "--request-id");
5299
6483
  continue;
5300
6484
  }
5301
6485
  }
@@ -5383,7 +6567,7 @@ async function runCookieImport(args) {
5383
6567
  }
5384
6568
 
5385
6569
  // src/cli/commands/session/cookie-list.ts
5386
- var requireValue7 = (value, flag) => {
6570
+ var requireValue6 = (value, flag) => {
5387
6571
  if (!value) {
5388
6572
  throw createUsageError(`Missing value for ${flag}`);
5389
6573
  }
@@ -5420,31 +6604,31 @@ var parseCookieListArgs = (rawArgs) => {
5420
6604
  for (let index = 0; index < rawArgs.length; index += 1) {
5421
6605
  const arg = rawArgs[index];
5422
6606
  if (arg === "--session-id") {
5423
- parsed.sessionId = requireValue7(rawArgs[index + 1], "--session-id");
6607
+ parsed.sessionId = requireValue6(rawArgs[index + 1], "--session-id");
5424
6608
  index += 1;
5425
6609
  continue;
5426
6610
  }
5427
6611
  if (arg?.startsWith("--session-id=")) {
5428
- parsed.sessionId = requireValue7(arg.split("=", 2)[1], "--session-id");
6612
+ parsed.sessionId = requireValue6(arg.split("=", 2)[1], "--session-id");
5429
6613
  continue;
5430
6614
  }
5431
6615
  if (arg === "--url") {
5432
- const rawValue = requireValue7(rawArgs[index + 1], "--url");
6616
+ const rawValue = requireValue6(rawArgs[index + 1], "--url");
5433
6617
  parsed.urls.push(...rawValue.split(","));
5434
6618
  index += 1;
5435
6619
  continue;
5436
6620
  }
5437
6621
  if (arg?.startsWith("--url=")) {
5438
- parsed.urls.push(...requireValue7(arg.split("=", 2)[1], "--url").split(","));
6622
+ parsed.urls.push(...requireValue6(arg.split("=", 2)[1], "--url").split(","));
5439
6623
  continue;
5440
6624
  }
5441
6625
  if (arg === "--request-id") {
5442
- parsed.requestId = requireValue7(rawArgs[index + 1], "--request-id");
6626
+ parsed.requestId = requireValue6(rawArgs[index + 1], "--request-id");
5443
6627
  index += 1;
5444
6628
  continue;
5445
6629
  }
5446
6630
  if (arg?.startsWith("--request-id=")) {
5447
- parsed.requestId = requireValue7(arg.split("=", 2)[1], "--request-id");
6631
+ parsed.requestId = requireValue6(arg.split("=", 2)[1], "--request-id");
5448
6632
  continue;
5449
6633
  }
5450
6634
  }
@@ -5470,7 +6654,7 @@ async function runCookieList(args) {
5470
6654
  }
5471
6655
 
5472
6656
  // src/cli/commands/macro-resolve.ts
5473
- var requireValue8 = (value, flag) => {
6657
+ var requireValue7 = (value, flag) => {
5474
6658
  if (!value) {
5475
6659
  throw createUsageError(`Missing value for ${flag}`);
5476
6660
  }
@@ -5481,21 +6665,21 @@ var parseMacroResolveArgs = (rawArgs) => {
5481
6665
  for (let index = 0; index < rawArgs.length; index += 1) {
5482
6666
  const arg = rawArgs[index];
5483
6667
  if (arg === "--expression") {
5484
- parsed.expression = requireValue8(rawArgs[index + 1], "--expression");
6668
+ parsed.expression = requireValue7(rawArgs[index + 1], "--expression");
5485
6669
  index += 1;
5486
6670
  continue;
5487
6671
  }
5488
6672
  if (arg?.startsWith("--expression=")) {
5489
- parsed.expression = requireValue8(arg.split("=", 2)[1], "--expression");
6673
+ parsed.expression = requireValue7(arg.split("=", 2)[1], "--expression");
5490
6674
  continue;
5491
6675
  }
5492
6676
  if (arg === "--default-provider") {
5493
- parsed.defaultProvider = requireValue8(rawArgs[index + 1], "--default-provider");
6677
+ parsed.defaultProvider = requireValue7(rawArgs[index + 1], "--default-provider");
5494
6678
  index += 1;
5495
6679
  continue;
5496
6680
  }
5497
6681
  if (arg?.startsWith("--default-provider=")) {
5498
- parsed.defaultProvider = requireValue8(arg.split("=", 2)[1], "--default-provider");
6682
+ parsed.defaultProvider = requireValue7(arg.split("=", 2)[1], "--default-provider");
5499
6683
  continue;
5500
6684
  }
5501
6685
  if (arg === "--include-catalog") {
@@ -5507,13 +6691,30 @@ var parseMacroResolveArgs = (rawArgs) => {
5507
6691
  continue;
5508
6692
  }
5509
6693
  if (arg === "--timeout-ms") {
5510
- const value = requireValue8(rawArgs[index + 1], "--timeout-ms");
6694
+ const value = requireValue7(rawArgs[index + 1], "--timeout-ms");
5511
6695
  parsed.timeoutMs = parseNumberFlag(value, "--timeout-ms", { min: 1 });
5512
6696
  index += 1;
5513
6697
  continue;
5514
6698
  }
5515
6699
  if (arg?.startsWith("--timeout-ms=")) {
5516
- parsed.timeoutMs = parseNumberFlag(requireValue8(arg.split("=", 2)[1], "--timeout-ms"), "--timeout-ms", { min: 1 });
6700
+ parsed.timeoutMs = parseNumberFlag(requireValue7(arg.split("=", 2)[1], "--timeout-ms"), "--timeout-ms", { min: 1 });
6701
+ continue;
6702
+ }
6703
+ if (arg === "--challenge-automation-mode") {
6704
+ const value = requireValue7(rawArgs[index + 1], "--challenge-automation-mode");
6705
+ if (!isChallengeAutomationMode(value)) {
6706
+ throw createUsageError(`Invalid --challenge-automation-mode: ${value}`);
6707
+ }
6708
+ parsed.challengeAutomationMode = value;
6709
+ index += 1;
6710
+ continue;
6711
+ }
6712
+ if (arg?.startsWith("--challenge-automation-mode=")) {
6713
+ const value = requireValue7(arg.split("=", 2)[1], "--challenge-automation-mode");
6714
+ if (!isChallengeAutomationMode(value)) {
6715
+ throw createUsageError(`Invalid --challenge-automation-mode: ${value}`);
6716
+ }
6717
+ parsed.challengeAutomationMode = value;
5517
6718
  continue;
5518
6719
  }
5519
6720
  }
@@ -5528,7 +6729,9 @@ async function runMacroResolve(args) {
5528
6729
  expression: parsed.expression,
5529
6730
  defaultProvider: parsed.defaultProvider,
5530
6731
  includeCatalog: parsed.includeCatalog ?? false,
5531
- execute: parsed.execute ?? false
6732
+ execute: parsed.execute ?? false,
6733
+ ...typeof parsed.timeoutMs === "number" ? { timeoutMs: parsed.timeoutMs } : {},
6734
+ ...parsed.challengeAutomationMode ? { challengeAutomationMode: parsed.challengeAutomationMode } : {}
5532
6735
  };
5533
6736
  const result = typeof parsed.timeoutMs === "number" ? await callDaemon("macro.resolve", params, { timeoutMs: parsed.timeoutMs }) : await callDaemon("macro.resolve", params);
5534
6737
  return {
@@ -5538,12 +6741,42 @@ async function runMacroResolve(args) {
5538
6741
  };
5539
6742
  }
5540
6743
 
6744
+ // src/cli/utils/workflow-message.ts
6745
+ var readMeta = (data) => {
6746
+ if (!data || typeof data !== "object" || Array.isArray(data)) return null;
6747
+ const meta = data.meta;
6748
+ return meta && typeof meta === "object" && !Array.isArray(meta) ? meta : null;
6749
+ };
6750
+ var readPrimarySummary = (data) => {
6751
+ const meta = readMeta(data);
6752
+ if (!meta) return null;
6753
+ const summary = meta.primaryConstraintSummary;
6754
+ return typeof summary === "string" && summary.trim().length > 0 ? summary.trim() : null;
6755
+ };
6756
+ var readFailures = (data) => {
6757
+ const meta = readMeta(data);
6758
+ if (!meta) return [];
6759
+ const failures = meta.failures;
6760
+ return Array.isArray(failures) ? failures.filter((entry) => Boolean(entry) && typeof entry === "object") : [];
6761
+ };
6762
+ var buildWorkflowCompletionMessage = (workflowLabel, data) => {
6763
+ const explicitSummary = readPrimarySummary(data);
6764
+ if (explicitSummary) {
6765
+ return `${workflowLabel} completed with provider follow-up required: ${explicitSummary}`;
6766
+ }
6767
+ const inferred = summarizePrimaryProviderIssue(readFailures(data));
6768
+ if (inferred) {
6769
+ return `${workflowLabel} completed with provider follow-up required: ${inferred.summary}`;
6770
+ }
6771
+ return `${workflowLabel} completed.`;
6772
+ };
6773
+
5541
6774
  // src/cli/commands/research.ts
5542
6775
  var SOURCE_VALUES = /* @__PURE__ */ new Set(["web", "community", "social", "shopping"]);
5543
6776
  var SOURCE_SELECTION_VALUES = /* @__PURE__ */ new Set(["auto", "web", "community", "social", "shopping", "all"]);
5544
6777
  var MODE_VALUES = /* @__PURE__ */ new Set(["compact", "json", "md", "context", "path"]);
5545
6778
  var COOKIE_POLICY_VALUES = /* @__PURE__ */ new Set(["off", "auto", "required"]);
5546
- var requireValue9 = (rawArgs, index, flag) => {
6779
+ var requireValue8 = (rawArgs, index, flag) => {
5547
6780
  const value = rawArgs[index + 1];
5548
6781
  if (!value) {
5549
6782
  throw createUsageError(`Missing value for ${flag}`);
@@ -5573,7 +6806,7 @@ var parseResearchRunArgs = (rawArgs) => {
5573
6806
  for (let index = 0; index < rawArgs.length; index += 1) {
5574
6807
  const arg = rawArgs[index];
5575
6808
  if (arg === "--topic") {
5576
- parsed.topic = requireValue9(rawArgs, index, "--topic");
6809
+ parsed.topic = requireValue8(rawArgs, index, "--topic");
5577
6810
  index += 1;
5578
6811
  continue;
5579
6812
  }
@@ -5582,7 +6815,7 @@ var parseResearchRunArgs = (rawArgs) => {
5582
6815
  continue;
5583
6816
  }
5584
6817
  if (arg === "--days") {
5585
- parsed.days = parseNumberFlag(requireValue9(rawArgs, index, "--days"), "--days", { min: 1, max: 365 });
6818
+ parsed.days = parseNumberFlag(requireValue8(rawArgs, index, "--days"), "--days", { min: 1, max: 365 });
5586
6819
  index += 1;
5587
6820
  continue;
5588
6821
  }
@@ -5591,7 +6824,7 @@ var parseResearchRunArgs = (rawArgs) => {
5591
6824
  continue;
5592
6825
  }
5593
6826
  if (arg === "--from") {
5594
- parsed.from = requireValue9(rawArgs, index, "--from");
6827
+ parsed.from = requireValue8(rawArgs, index, "--from");
5595
6828
  index += 1;
5596
6829
  continue;
5597
6830
  }
@@ -5600,7 +6833,7 @@ var parseResearchRunArgs = (rawArgs) => {
5600
6833
  continue;
5601
6834
  }
5602
6835
  if (arg === "--to") {
5603
- parsed.to = requireValue9(rawArgs, index, "--to");
6836
+ parsed.to = requireValue8(rawArgs, index, "--to");
5604
6837
  index += 1;
5605
6838
  continue;
5606
6839
  }
@@ -5609,7 +6842,7 @@ var parseResearchRunArgs = (rawArgs) => {
5609
6842
  continue;
5610
6843
  }
5611
6844
  if (arg === "--source-selection") {
5612
- const value = requireValue9(rawArgs, index, "--source-selection").toLowerCase();
6845
+ const value = requireValue8(rawArgs, index, "--source-selection").toLowerCase();
5613
6846
  if (!SOURCE_SELECTION_VALUES.has(value)) {
5614
6847
  throw createUsageError(`Invalid --source-selection: ${value}`);
5615
6848
  }
@@ -5626,7 +6859,7 @@ var parseResearchRunArgs = (rawArgs) => {
5626
6859
  continue;
5627
6860
  }
5628
6861
  if (arg === "--sources") {
5629
- parsed.sources = parseSources(requireValue9(rawArgs, index, "--sources"));
6862
+ parsed.sources = parseSources(requireValue8(rawArgs, index, "--sources"));
5630
6863
  index += 1;
5631
6864
  continue;
5632
6865
  }
@@ -5635,7 +6868,7 @@ var parseResearchRunArgs = (rawArgs) => {
5635
6868
  continue;
5636
6869
  }
5637
6870
  if (arg === "--mode") {
5638
- const value = requireValue9(rawArgs, index, "--mode").toLowerCase();
6871
+ const value = requireValue8(rawArgs, index, "--mode").toLowerCase();
5639
6872
  if (!MODE_VALUES.has(value)) {
5640
6873
  throw createUsageError(`Invalid --mode: ${value}`);
5641
6874
  }
@@ -5656,7 +6889,7 @@ var parseResearchRunArgs = (rawArgs) => {
5656
6889
  continue;
5657
6890
  }
5658
6891
  if (arg === "--limit-per-source") {
5659
- parsed.limitPerSource = parseNumberFlag(requireValue9(rawArgs, index, "--limit-per-source"), "--limit-per-source", { min: 1, max: 100 });
6892
+ parsed.limitPerSource = parseNumberFlag(requireValue8(rawArgs, index, "--limit-per-source"), "--limit-per-source", { min: 1, max: 100 });
5660
6893
  index += 1;
5661
6894
  continue;
5662
6895
  }
@@ -5664,8 +6897,17 @@ var parseResearchRunArgs = (rawArgs) => {
5664
6897
  parsed.limitPerSource = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--limit-per-source", { min: 1, max: 100 });
5665
6898
  continue;
5666
6899
  }
6900
+ if (arg === "--timeout-ms") {
6901
+ parsed.timeoutMs = parseNumberFlag(requireValue8(rawArgs, index, "--timeout-ms"), "--timeout-ms", { min: 1 });
6902
+ index += 1;
6903
+ continue;
6904
+ }
6905
+ if (arg?.startsWith("--timeout-ms=")) {
6906
+ parsed.timeoutMs = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--timeout-ms", { min: 1 });
6907
+ continue;
6908
+ }
5667
6909
  if (arg === "--output-dir") {
5668
- parsed.outputDir = requireValue9(rawArgs, index, "--output-dir");
6910
+ parsed.outputDir = requireValue8(rawArgs, index, "--output-dir");
5669
6911
  index += 1;
5670
6912
  continue;
5671
6913
  }
@@ -5674,7 +6916,7 @@ var parseResearchRunArgs = (rawArgs) => {
5674
6916
  continue;
5675
6917
  }
5676
6918
  if (arg === "--ttl-hours") {
5677
- parsed.ttlHours = parseNumberFlag(requireValue9(rawArgs, index, "--ttl-hours"), "--ttl-hours", { min: 1, max: 168 });
6919
+ parsed.ttlHours = parseNumberFlag(requireValue8(rawArgs, index, "--ttl-hours"), "--ttl-hours", { min: 1, max: 168 });
5678
6920
  index += 1;
5679
6921
  continue;
5680
6922
  }
@@ -5690,8 +6932,25 @@ var parseResearchRunArgs = (rawArgs) => {
5690
6932
  parsed.useCookies = parseBoolean(arg.split("=", 2)[1] ?? "", "--use-cookies");
5691
6933
  continue;
5692
6934
  }
6935
+ if (arg === "--challenge-automation-mode") {
6936
+ const value = requireValue8(rawArgs, index, "--challenge-automation-mode");
6937
+ if (!isChallengeAutomationMode(value)) {
6938
+ throw createUsageError(`Invalid --challenge-automation-mode: ${value}`);
6939
+ }
6940
+ parsed.challengeAutomationMode = value;
6941
+ index += 1;
6942
+ continue;
6943
+ }
6944
+ if (arg?.startsWith("--challenge-automation-mode=")) {
6945
+ const value = arg.split("=", 2)[1] ?? "";
6946
+ if (!isChallengeAutomationMode(value)) {
6947
+ throw createUsageError(`Invalid --challenge-automation-mode: ${value}`);
6948
+ }
6949
+ parsed.challengeAutomationMode = value;
6950
+ continue;
6951
+ }
5693
6952
  if (arg === "--cookie-policy-override" || arg === "--cookie-policy") {
5694
- const value = requireValue9(rawArgs, index, arg).toLowerCase();
6953
+ const value = requireValue8(rawArgs, index, arg).toLowerCase();
5695
6954
  if (!COOKIE_POLICY_VALUES.has(value)) {
5696
6955
  throw createUsageError(`Invalid ${arg}: ${value}`);
5697
6956
  }
@@ -5719,7 +6978,7 @@ async function runResearchCommand(args) {
5719
6978
  if (!parsed.topic?.trim()) {
5720
6979
  throw createUsageError("Missing --topic");
5721
6980
  }
5722
- const data = await callDaemon("research.run", {
6981
+ const payload = {
5723
6982
  topic: parsed.topic,
5724
6983
  days: parsed.days,
5725
6984
  from: parsed.from,
@@ -5729,14 +6988,17 @@ async function runResearchCommand(args) {
5729
6988
  mode: parsed.mode ?? "compact",
5730
6989
  includeEngagement: parsed.includeEngagement ?? false,
5731
6990
  limitPerSource: parsed.limitPerSource,
6991
+ timeoutMs: parsed.timeoutMs ?? DEFAULT_WORKFLOW_TRANSPORT_TIMEOUT_MS,
5732
6992
  outputDir: parsed.outputDir,
5733
6993
  ttlHours: parsed.ttlHours,
5734
6994
  useCookies: parsed.useCookies,
6995
+ challengeAutomationMode: parsed.challengeAutomationMode,
5735
6996
  cookiePolicyOverride: parsed.cookiePolicyOverride
5736
- });
6997
+ };
6998
+ const data = await callDaemon("research.run", payload);
5737
6999
  return {
5738
7000
  success: true,
5739
- message: "Research workflow completed.",
7001
+ message: buildWorkflowCompletionMessage("Research workflow", data),
5740
7002
  data
5741
7003
  };
5742
7004
  }
@@ -5745,7 +7007,15 @@ async function runResearchCommand(args) {
5745
7007
  var SORT_VALUES = /* @__PURE__ */ new Set(["best_deal", "lowest_price", "highest_rating", "fastest_shipping"]);
5746
7008
  var MODE_VALUES2 = /* @__PURE__ */ new Set(["compact", "json", "md", "context", "path"]);
5747
7009
  var COOKIE_POLICY_VALUES2 = /* @__PURE__ */ new Set(["off", "auto", "required"]);
5748
- var requireValue10 = (rawArgs, index, flag) => {
7010
+ var BROWSER_MODE_VALUES = /* @__PURE__ */ new Set(["auto", "extension", "managed"]);
7011
+ var SHOPPING_TRANSPORT_TIMEOUT_BUFFER_MS = 6e4;
7012
+ var deriveShoppingTransportTimeoutMs = (timeoutMs) => {
7013
+ return Math.max(
7014
+ DEFAULT_WORKFLOW_TRANSPORT_TIMEOUT_MS,
7015
+ timeoutMs + SHOPPING_TRANSPORT_TIMEOUT_BUFFER_MS
7016
+ );
7017
+ };
7018
+ var requireValue9 = (rawArgs, index, flag) => {
5749
7019
  const value = rawArgs[index + 1];
5750
7020
  if (!value) {
5751
7021
  throw createUsageError(`Missing value for ${flag}`);
@@ -5769,7 +7039,7 @@ var parseShoppingRunArgs = (rawArgs) => {
5769
7039
  for (let index = 0; index < rawArgs.length; index += 1) {
5770
7040
  const arg = rawArgs[index];
5771
7041
  if (arg === "--query") {
5772
- parsed.query = requireValue10(rawArgs, index, "--query");
7042
+ parsed.query = requireValue9(rawArgs, index, "--query");
5773
7043
  index += 1;
5774
7044
  continue;
5775
7045
  }
@@ -5778,7 +7048,7 @@ var parseShoppingRunArgs = (rawArgs) => {
5778
7048
  continue;
5779
7049
  }
5780
7050
  if (arg === "--providers") {
5781
- parsed.providers = parseProviders(requireValue10(rawArgs, index, "--providers"));
7051
+ parsed.providers = parseProviders(requireValue9(rawArgs, index, "--providers"));
5782
7052
  index += 1;
5783
7053
  continue;
5784
7054
  }
@@ -5787,7 +7057,7 @@ var parseShoppingRunArgs = (rawArgs) => {
5787
7057
  continue;
5788
7058
  }
5789
7059
  if (arg === "--budget") {
5790
- parsed.budget = parseNumberFlag(requireValue10(rawArgs, index, "--budget"), "--budget", { min: 1, integer: false });
7060
+ parsed.budget = parseNumberFlag(requireValue9(rawArgs, index, "--budget"), "--budget", { min: 1, integer: false });
5791
7061
  index += 1;
5792
7062
  continue;
5793
7063
  }
@@ -5796,7 +7066,7 @@ var parseShoppingRunArgs = (rawArgs) => {
5796
7066
  continue;
5797
7067
  }
5798
7068
  if (arg === "--region") {
5799
- parsed.region = requireValue10(rawArgs, index, "--region");
7069
+ parsed.region = requireValue9(rawArgs, index, "--region");
5800
7070
  index += 1;
5801
7071
  continue;
5802
7072
  }
@@ -5804,8 +7074,25 @@ var parseShoppingRunArgs = (rawArgs) => {
5804
7074
  parsed.region = arg.split("=", 2)[1];
5805
7075
  continue;
5806
7076
  }
7077
+ if (arg === "--browser-mode") {
7078
+ const value = requireValue9(rawArgs, index, "--browser-mode").toLowerCase();
7079
+ if (!BROWSER_MODE_VALUES.has(value)) {
7080
+ throw createUsageError(`Invalid --browser-mode: ${value}`);
7081
+ }
7082
+ parsed.browserMode = value;
7083
+ index += 1;
7084
+ continue;
7085
+ }
7086
+ if (arg?.startsWith("--browser-mode=")) {
7087
+ const value = (arg.split("=", 2)[1] ?? "").toLowerCase();
7088
+ if (!BROWSER_MODE_VALUES.has(value)) {
7089
+ throw createUsageError(`Invalid --browser-mode: ${value}`);
7090
+ }
7091
+ parsed.browserMode = value;
7092
+ continue;
7093
+ }
5807
7094
  if (arg === "--sort") {
5808
- const value = requireValue10(rawArgs, index, "--sort").toLowerCase();
7095
+ const value = requireValue9(rawArgs, index, "--sort").toLowerCase();
5809
7096
  if (!SORT_VALUES.has(value)) {
5810
7097
  throw createUsageError(`Invalid --sort: ${value}`);
5811
7098
  }
@@ -5822,7 +7109,7 @@ var parseShoppingRunArgs = (rawArgs) => {
5822
7109
  continue;
5823
7110
  }
5824
7111
  if (arg === "--mode") {
5825
- const value = requireValue10(rawArgs, index, "--mode").toLowerCase();
7112
+ const value = requireValue9(rawArgs, index, "--mode").toLowerCase();
5826
7113
  if (!MODE_VALUES2.has(value)) {
5827
7114
  throw createUsageError(`Invalid --mode: ${value}`);
5828
7115
  }
@@ -5839,7 +7126,7 @@ var parseShoppingRunArgs = (rawArgs) => {
5839
7126
  continue;
5840
7127
  }
5841
7128
  if (arg === "--timeout-ms") {
5842
- parsed.timeoutMs = parseNumberFlag(requireValue10(rawArgs, index, "--timeout-ms"), "--timeout-ms", { min: 1 });
7129
+ parsed.timeoutMs = parseNumberFlag(requireValue9(rawArgs, index, "--timeout-ms"), "--timeout-ms", { min: 1 });
5843
7130
  index += 1;
5844
7131
  continue;
5845
7132
  }
@@ -5848,7 +7135,7 @@ var parseShoppingRunArgs = (rawArgs) => {
5848
7135
  continue;
5849
7136
  }
5850
7137
  if (arg === "--output-dir") {
5851
- parsed.outputDir = requireValue10(rawArgs, index, "--output-dir");
7138
+ parsed.outputDir = requireValue9(rawArgs, index, "--output-dir");
5852
7139
  index += 1;
5853
7140
  continue;
5854
7141
  }
@@ -5857,7 +7144,7 @@ var parseShoppingRunArgs = (rawArgs) => {
5857
7144
  continue;
5858
7145
  }
5859
7146
  if (arg === "--ttl-hours") {
5860
- parsed.ttlHours = parseNumberFlag(requireValue10(rawArgs, index, "--ttl-hours"), "--ttl-hours", { min: 1, max: 168 });
7147
+ parsed.ttlHours = parseNumberFlag(requireValue9(rawArgs, index, "--ttl-hours"), "--ttl-hours", { min: 1, max: 168 });
5861
7148
  index += 1;
5862
7149
  continue;
5863
7150
  }
@@ -5873,8 +7160,25 @@ var parseShoppingRunArgs = (rawArgs) => {
5873
7160
  parsed.useCookies = parseBoolean2(arg.split("=", 2)[1] ?? "", "--use-cookies");
5874
7161
  continue;
5875
7162
  }
7163
+ if (arg === "--challenge-automation-mode") {
7164
+ const value = requireValue9(rawArgs, index, "--challenge-automation-mode");
7165
+ if (!isChallengeAutomationMode(value)) {
7166
+ throw createUsageError(`Invalid --challenge-automation-mode: ${value}`);
7167
+ }
7168
+ parsed.challengeAutomationMode = value;
7169
+ index += 1;
7170
+ continue;
7171
+ }
7172
+ if (arg?.startsWith("--challenge-automation-mode=")) {
7173
+ const value = arg.split("=", 2)[1] ?? "";
7174
+ if (!isChallengeAutomationMode(value)) {
7175
+ throw createUsageError(`Invalid --challenge-automation-mode: ${value}`);
7176
+ }
7177
+ parsed.challengeAutomationMode = value;
7178
+ continue;
7179
+ }
5876
7180
  if (arg === "--cookie-policy-override" || arg === "--cookie-policy") {
5877
- const value = requireValue10(rawArgs, index, arg).toLowerCase();
7181
+ const value = requireValue9(rawArgs, index, arg).toLowerCase();
5878
7182
  if (!COOKIE_POLICY_VALUES2.has(value)) {
5879
7183
  throw createUsageError(`Invalid ${arg}: ${value}`);
5880
7184
  }
@@ -5907,24 +7211,28 @@ async function runShoppingCommand(args) {
5907
7211
  providers: parsed.providers,
5908
7212
  budget: parsed.budget,
5909
7213
  region: parsed.region,
7214
+ browserMode: parsed.browserMode,
5910
7215
  sort: parsed.sort,
5911
7216
  mode: parsed.mode ?? "compact",
5912
- ...typeof parsed.timeoutMs === "number" ? { timeoutMs: parsed.timeoutMs } : {},
7217
+ timeoutMs: parsed.timeoutMs ?? DEFAULT_WORKFLOW_TRANSPORT_TIMEOUT_MS,
5913
7218
  outputDir: parsed.outputDir,
5914
7219
  ttlHours: parsed.ttlHours,
5915
7220
  useCookies: parsed.useCookies,
7221
+ challengeAutomationMode: parsed.challengeAutomationMode,
5916
7222
  cookiePolicyOverride: parsed.cookiePolicyOverride
5917
7223
  };
5918
- const data = typeof parsed.timeoutMs === "number" ? await callDaemon("shopping.run", payload, { timeoutMs: parsed.timeoutMs }) : await callDaemon("shopping.run", payload);
7224
+ const data = await callDaemon("shopping.run", payload, {
7225
+ timeoutMs: deriveShoppingTransportTimeoutMs(payload.timeoutMs)
7226
+ });
5919
7227
  return {
5920
7228
  success: true,
5921
- message: "Shopping workflow completed.",
7229
+ message: buildWorkflowCompletionMessage("Shopping workflow", data),
5922
7230
  data
5923
7231
  };
5924
7232
  }
5925
7233
 
5926
7234
  // src/cli/commands/product-video.ts
5927
- var requireValue11 = (rawArgs, index, flag) => {
7235
+ var requireValue10 = (rawArgs, index, flag) => {
5928
7236
  const value = rawArgs[index + 1];
5929
7237
  if (!value) {
5930
7238
  throw createUsageError(`Missing value for ${flag}`);
@@ -5942,7 +7250,7 @@ var parseProductVideoArgs = (rawArgs) => {
5942
7250
  for (let index = 0; index < rawArgs.length; index += 1) {
5943
7251
  const arg = rawArgs[index];
5944
7252
  if (arg === "--product-url") {
5945
- parsed.productUrl = requireValue11(rawArgs, index, "--product-url");
7253
+ parsed.productUrl = requireValue10(rawArgs, index, "--product-url");
5946
7254
  index += 1;
5947
7255
  continue;
5948
7256
  }
@@ -5951,7 +7259,7 @@ var parseProductVideoArgs = (rawArgs) => {
5951
7259
  continue;
5952
7260
  }
5953
7261
  if (arg === "--product-name") {
5954
- parsed.productName = requireValue11(rawArgs, index, "--product-name");
7262
+ parsed.productName = requireValue10(rawArgs, index, "--product-name");
5955
7263
  index += 1;
5956
7264
  continue;
5957
7265
  }
@@ -5960,7 +7268,7 @@ var parseProductVideoArgs = (rawArgs) => {
5960
7268
  continue;
5961
7269
  }
5962
7270
  if (arg === "--provider-hint") {
5963
- parsed.providerHint = requireValue11(rawArgs, index, "--provider-hint");
7271
+ parsed.providerHint = requireValue10(rawArgs, index, "--provider-hint");
5964
7272
  index += 1;
5965
7273
  continue;
5966
7274
  }
@@ -5993,7 +7301,7 @@ var parseProductVideoArgs = (rawArgs) => {
5993
7301
  continue;
5994
7302
  }
5995
7303
  if (arg === "--output-dir") {
5996
- parsed.outputDir = requireValue11(rawArgs, index, "--output-dir");
7304
+ parsed.outputDir = requireValue10(rawArgs, index, "--output-dir");
5997
7305
  index += 1;
5998
7306
  continue;
5999
7307
  }
@@ -6002,7 +7310,7 @@ var parseProductVideoArgs = (rawArgs) => {
6002
7310
  continue;
6003
7311
  }
6004
7312
  if (arg === "--ttl-hours") {
6005
- parsed.ttlHours = parseNumberFlag(requireValue11(rawArgs, index, "--ttl-hours"), "--ttl-hours", { min: 1, max: 168 });
7313
+ parsed.ttlHours = parseNumberFlag(requireValue10(rawArgs, index, "--ttl-hours"), "--ttl-hours", { min: 1, max: 168 });
6006
7314
  index += 1;
6007
7315
  continue;
6008
7316
  }
@@ -6011,7 +7319,7 @@ var parseProductVideoArgs = (rawArgs) => {
6011
7319
  continue;
6012
7320
  }
6013
7321
  if (arg === "--timeout-ms") {
6014
- parsed.timeoutMs = parseNumberFlag(requireValue11(rawArgs, index, "--timeout-ms"), "--timeout-ms", { min: 1 });
7322
+ parsed.timeoutMs = parseNumberFlag(requireValue10(rawArgs, index, "--timeout-ms"), "--timeout-ms", { min: 1 });
6015
7323
  index += 1;
6016
7324
  continue;
6017
7325
  }
@@ -6027,8 +7335,25 @@ var parseProductVideoArgs = (rawArgs) => {
6027
7335
  parsed.useCookies = parseBoolean3(arg.split("=", 2)[1] ?? "", "--use-cookies");
6028
7336
  continue;
6029
7337
  }
7338
+ if (arg === "--challenge-automation-mode") {
7339
+ const value = requireValue10(rawArgs, index, "--challenge-automation-mode");
7340
+ if (!isChallengeAutomationMode(value)) {
7341
+ throw createUsageError(`Invalid --challenge-automation-mode: ${value}`);
7342
+ }
7343
+ parsed.challengeAutomationMode = value;
7344
+ index += 1;
7345
+ continue;
7346
+ }
7347
+ if (arg?.startsWith("--challenge-automation-mode=")) {
7348
+ const value = arg.split("=", 2)[1] ?? "";
7349
+ if (!isChallengeAutomationMode(value)) {
7350
+ throw createUsageError(`Invalid --challenge-automation-mode: ${value}`);
7351
+ }
7352
+ parsed.challengeAutomationMode = value;
7353
+ continue;
7354
+ }
6030
7355
  if (arg === "--cookie-policy-override" || arg === "--cookie-policy") {
6031
- const value = requireValue11(rawArgs, index, arg).toLowerCase();
7356
+ const value = requireValue10(rawArgs, index, arg).toLowerCase();
6032
7357
  if (!COOKIE_POLICY_VALUES3.has(value)) {
6033
7358
  throw createUsageError(`Invalid ${arg}: ${value}`);
6034
7359
  }
@@ -6056,7 +7381,7 @@ async function runProductVideoCommand(args) {
6056
7381
  if (!parsed.productUrl && !parsed.productName) {
6057
7382
  throw createUsageError("Missing --product-url or --product-name");
6058
7383
  }
6059
- const timeoutMs = parsed.timeoutMs ?? 12e4;
7384
+ const timeoutMs = parsed.timeoutMs ?? DEFAULT_WORKFLOW_TRANSPORT_TIMEOUT_MS;
6060
7385
  const data = await callDaemon("product.video.run", {
6061
7386
  product_url: parsed.productUrl,
6062
7387
  product_name: parsed.productName,
@@ -6066,12 +7391,14 @@ async function runProductVideoCommand(args) {
6066
7391
  include_copy: parsed.includeCopy,
6067
7392
  output_dir: parsed.outputDir,
6068
7393
  ttl_hours: parsed.ttlHours,
7394
+ timeoutMs,
6069
7395
  useCookies: parsed.useCookies,
7396
+ challengeAutomationMode: parsed.challengeAutomationMode,
6070
7397
  cookiePolicyOverride: parsed.cookiePolicyOverride
6071
- }, { timeoutMs });
7398
+ });
6072
7399
  return {
6073
7400
  success: true,
6074
- message: "Product video asset workflow completed.",
7401
+ message: buildWorkflowCompletionMessage("Product video asset workflow", data),
6075
7402
  data
6076
7403
  };
6077
7404
  }
@@ -6079,8 +7406,8 @@ async function runProductVideoCommand(args) {
6079
7406
  // package.json
6080
7407
  var package_default = {
6081
7408
  name: "opendevbrowser",
6082
- version: "0.0.17",
6083
- description: "OpenCode plugin for browser automation via CDP with snapshot-refs-actions workflow",
7409
+ version: "0.0.18",
7410
+ description: "Browser automation runtime with snapshot-refs-actions, browser replay screencasts, public read-only desktop observation, and browser-scoped computer-use orchestration",
6084
7411
  type: "module",
6085
7412
  main: "dist/index.js",
6086
7413
  types: "dist/index.d.ts",
@@ -6106,7 +7433,14 @@ var package_default = {
6106
7433
  "playwright",
6107
7434
  "testing",
6108
7435
  "web-scraping",
6109
- "chrome"
7436
+ "chrome",
7437
+ "annotation",
7438
+ "design-canvas",
7439
+ "screencast",
7440
+ "desktop-observation",
7441
+ "browser-replay",
7442
+ "computer-use",
7443
+ "challenge-automation"
6110
7444
  ],
6111
7445
  license: "MIT",
6112
7446
  repository: {
@@ -6117,24 +7451,11 @@ var package_default = {
6117
7451
  node: ">=18"
6118
7452
  },
6119
7453
  scripts: {
6120
- build: `tsup src/index.ts src/cli/index.ts --format esm --clean --sourcemap && tsc --emitDeclarationOnly --declaration --declarationMap -p tsconfig.json && node --input-type=module -e "import { copyFileSync, existsSync } from 'node:fs';
6121
- import { resolve } from 'node:path';
6122
- const dist = resolve('dist');
6123
- const pairs = [
6124
- ['index.js', 'opendevbrowser.js'],
6125
- ['index.js.map', 'opendevbrowser.js.map'],
6126
- ['index.d.ts', 'opendevbrowser.d.ts'],
6127
- ['index.d.ts.map', 'opendevbrowser.d.ts.map'],
6128
- ];
6129
- for (const [src, dst] of pairs) {
6130
- const from = resolve(dist, src);
6131
- const to = resolve(dist, dst);
6132
- if (existsSync(from)) copyFileSync(from, to);
6133
- }"`,
6134
- dev: "tsup src/index.ts src/cli/index.ts --format esm --dts --watch",
7454
+ build: "tsup src/index.ts src/cli/index.ts src/skills/skill-loader.ts --format esm --clean --sourcemap && tsc --emitDeclarationOnly --declaration --declarationMap -p tsconfig.json && node scripts/postbuild-dist.mjs",
7455
+ dev: "tsup src/index.ts src/cli/index.ts src/skills/skill-loader.ts --format esm --dts --watch",
6135
7456
  lint: 'eslint "src/**/*.ts" "tests/**/*.ts"',
6136
7457
  typecheck: "tsc --noEmit -p tsconfig.json",
6137
- test: `node --input-type=module -e "import { mkdirSync } from 'node:fs'; mkdirSync('coverage/.tmp', { recursive: true });" && vitest run --coverage`,
7458
+ test: "node scripts/run-vitest-coverage.mjs",
6138
7459
  "test:release-gate": "node scripts/release-gate-test-groups.mjs",
6139
7460
  "test:release-gate:g1": "node scripts/release-gate-test-groups.mjs --group 1",
6140
7461
  "test:release-gate:g2": "node scripts/release-gate-test-groups.mjs --group 2",
@@ -6149,10 +7470,11 @@ for (const [src, dst] of pairs) {
6149
7470
  prepack: "npm run version:check && npm run build && npm run extension:build"
6150
7471
  },
6151
7472
  dependencies: {
6152
- "@opencode-ai/plugin": "^1.2.11",
7473
+ "@opencode-ai/plugin": "^1.2.25",
6153
7474
  "@puppeteer/browsers": "^2.13.0",
6154
7475
  "async-mutex": "^0.5.0",
6155
7476
  "jsonc-parser": "^3.2.0",
7477
+ parse5: "^8.0.0",
6156
7478
  "playwright-core": "^1.58.2",
6157
7479
  typescript: "^5.9.3",
6158
7480
  ws: "^8.19.0",
@@ -6175,6 +7497,29 @@ for (const [src, dst] of pairs) {
6175
7497
 
6176
7498
  // src/cli/index.ts
6177
7499
  var VERSION = typeof package_default.version === "string" ? package_default.version : "0.0.0";
7500
+ function resolveUpdateSkillModes(args) {
7501
+ if (args.rawArgs.includes("--no-skills")) {
7502
+ return [];
7503
+ }
7504
+ if (args.rawArgs.includes("--skills-global")) {
7505
+ return ["global"];
7506
+ }
7507
+ if (args.rawArgs.includes("--skills-local")) {
7508
+ return ["local"];
7509
+ }
7510
+ if (args.mode) {
7511
+ return [args.mode];
7512
+ }
7513
+ const installed = findInstalledConfigs();
7514
+ const modes = [];
7515
+ if (installed.global || hasBundledSkillArtifacts("global")) {
7516
+ modes.push("global");
7517
+ }
7518
+ if (installed.local || hasBundledSkillArtifacts("local")) {
7519
+ modes.push("local");
7520
+ }
7521
+ return modes;
7522
+ }
6178
7523
  async function promptInstallMode() {
6179
7524
  if (!process.stdin.isTTY) {
6180
7525
  console.log("Non-interactive mode detected. Using global install.");
@@ -6275,6 +7620,7 @@ async function main() {
6275
7620
  const args = parseArgs(process.argv);
6276
7621
  parseSucceeded = true;
6277
7622
  outputFormat = args.outputFormat;
7623
+ setDefaultLogSink(stderrSink);
6278
7624
  const outputOptions = { format: args.outputFormat, quiet: args.quiet };
6279
7625
  const emitResult = (result2, payload) => {
6280
7626
  const suppressOutput = Boolean(
@@ -6310,15 +7656,27 @@ async function main() {
6310
7656
  });
6311
7657
  registerCommand({
6312
7658
  name: "update",
6313
- description: "Clear cached plugin to trigger reinstall",
7659
+ description: "Clear cached plugin and refresh managed skill packs",
6314
7660
  run: () => {
6315
7661
  const result2 = runUpdate();
6316
- return { success: result2.success, message: result2.message };
7662
+ const skillModes = result2.success ? resolveUpdateSkillModes(args) : [];
7663
+ const skillResults = result2.success ? skillModes.map((mode) => syncBundledSkills(mode)) : [];
7664
+ const skillMessage = args.rawArgs.includes("--no-skills") ? "Managed skill refresh skipped (--no-skills)." : skillResults.length > 0 ? skillResults.map((entry) => entry.message).join("\n") : result2.success ? "No managed skill packs required refresh." : "";
7665
+ const message = [result2.message, skillMessage].filter(Boolean).join("\n");
7666
+ return {
7667
+ success: result2.success && skillResults.every((entry) => entry.success),
7668
+ message,
7669
+ data: {
7670
+ cacheCleared: result2.cleared,
7671
+ skillModes,
7672
+ skills: skillResults
7673
+ }
7674
+ };
6317
7675
  }
6318
7676
  });
6319
7677
  registerCommand({
6320
7678
  name: "uninstall",
6321
- description: "Remove plugin from config",
7679
+ description: "Remove plugin from config and clean managed skill packs",
6322
7680
  run: async () => {
6323
7681
  let mode = args.mode;
6324
7682
  if (!mode && !args.noPrompt) {
@@ -6331,12 +7689,22 @@ async function main() {
6331
7689
  return { success: false, message: "Error: Please specify --global or --local for uninstall.", exitCode: EXIT_USAGE };
6332
7690
  }
6333
7691
  const result2 = runUninstall(mode);
6334
- return { success: result2.success, message: result2.message };
7692
+ const skipSkills = args.rawArgs.includes("--no-skills");
7693
+ const skillsResult = result2.success && !skipSkills ? removeBundledSkills(mode) : void 0;
7694
+ const skillMessage = skipSkills ? "Managed skill cleanup skipped (--no-skills)." : skillsResult?.message ?? "";
7695
+ return {
7696
+ success: result2.success && (skillsResult?.success ?? true),
7697
+ message: [result2.message, skillMessage].filter(Boolean).join("\n"),
7698
+ data: {
7699
+ config: result2,
7700
+ skills: skillsResult
7701
+ }
7702
+ };
6335
7703
  }
6336
7704
  });
6337
7705
  registerCommand({
6338
7706
  name: "install",
6339
- description: "Install the plugin",
7707
+ description: "Install the plugin and sync bundled skill packs",
6340
7708
  run: async () => {
6341
7709
  const log = (...values) => {
6342
7710
  if (args.quiet) return;
@@ -6351,28 +7719,14 @@ async function main() {
6351
7719
  mode = await promptInstallMode();
6352
7720
  }
6353
7721
  const result2 = mode === "global" ? installGlobal(args.withConfig) : installLocal(args.withConfig);
6354
- const maybeInstallAutostart = () => {
6355
- const status = getAutostartStatus();
6356
- if (!status.supported) {
6357
- return { status, installed: false, message: `Autostart not supported on ${status.platform}.` };
6358
- }
6359
- if (status.installed) {
6360
- return { status, installed: true, message: "Autostart already installed." };
6361
- }
6362
- try {
6363
- const result3 = installAutostart();
6364
- return { status: result3, installed: result3.installed, message: `Autostart installed (${result3.platform}).` };
6365
- } catch (error) {
6366
- const message = error instanceof Error ? error.message : String(error);
6367
- return { status, installed: false, message };
6368
- }
6369
- };
7722
+ const autostart = result2.success ? reconcileInstallAutostart(result2) : void 0;
7723
+ const skillsResult = result2.success && args.skillsMode !== "none" ? syncBundledSkills(args.skillsMode) : void 0;
7724
+ const installSuccess = result2.success && (skillsResult?.success ?? true);
6370
7725
  if (args.outputFormat !== "text") {
6371
7726
  const payload = {
6372
7727
  alreadyInstalled: result2.alreadyInstalled
6373
7728
  };
6374
- if (result2.success && args.skillsMode !== "none") {
6375
- const skillsResult = installSkills(args.skillsMode);
7729
+ if (skillsResult) {
6376
7730
  payload.skills = skillsResult;
6377
7731
  }
6378
7732
  if (args.fullInstall && result2.success) {
@@ -6383,20 +7737,15 @@ async function main() {
6383
7737
  payload.extensionError = error instanceof Error ? error.message : String(error);
6384
7738
  }
6385
7739
  }
6386
- if (result2.success && !result2.alreadyInstalled) {
6387
- const autostart = maybeInstallAutostart();
6388
- payload.autostart = autostart.status;
6389
- if (!autostart.installed) {
6390
- payload.autostartError = autostart.message;
6391
- }
7740
+ if (autostart) {
7741
+ Object.assign(payload, createInstallAutostartOutputPayload(autostart));
6392
7742
  }
6393
- return { success: result2.success, message: result2.message, data: payload };
7743
+ return { success: installSuccess, message: result2.message, data: payload };
6394
7744
  }
6395
7745
  log(result2.message);
6396
7746
  if (args.skillsMode === "none") {
6397
7747
  log("Skill installation skipped (--no-skills).");
6398
- } else if (result2.success) {
6399
- const skillsResult = installSkills(args.skillsMode);
7748
+ } else if (skillsResult) {
6400
7749
  if (skillsResult.success) {
6401
7750
  log(skillsResult.message);
6402
7751
  } else {
@@ -6418,21 +7767,24 @@ async function main() {
6418
7767
  warn(`Extension pre-extraction failed: ${message}`);
6419
7768
  }
6420
7769
  }
6421
- if (result2.success && !result2.alreadyInstalled) {
6422
- const autostart = maybeInstallAutostart();
6423
- if (autostart.installed) {
6424
- log(autostart.message);
6425
- } else {
6426
- warn(`Autostart install skipped: ${autostart.message}`);
7770
+ if (autostart) {
7771
+ const autostartMessage = formatAutostartReconciliationMessage(autostart);
7772
+ if (autostartMessage) {
7773
+ if (autostart.autostartAction === "repair_failed") {
7774
+ warn(autostartMessage);
7775
+ } else {
7776
+ log(autostartMessage);
7777
+ }
6427
7778
  }
6428
7779
  }
6429
- if (result2.success && !result2.alreadyInstalled) {
7780
+ if (installSuccess && !result2.alreadyInstalled) {
6430
7781
  log("\nNext steps:");
6431
7782
  log(" 1. Start or restart OpenCode");
6432
- log(" 2. Use opendevbrowser_status to verify the plugin is loaded");
6433
- log("\nFor help: npx opendevbrowser --help");
7783
+ log(` 2. Read npx opendevbrowser --help and start with ${onboarding_metadata_default.quickStartCommands.promptingGuide}`);
7784
+ log(` 3. Or load ${onboarding_metadata_default.skillName} ${onboarding_metadata_default.skillTopic} directly via ${onboarding_metadata_default.quickStartCommands.skillLoad}`);
7785
+ log(" 4. Use opendevbrowser_status to verify the plugin is loaded");
6434
7786
  }
6435
- return { success: result2.success, message: result2.message };
7787
+ return { success: installSuccess, message: result2.message };
6436
7788
  }
6437
7789
  });
6438
7790
  registerCommand({
@@ -6475,6 +7827,11 @@ async function main() {
6475
7827
  description: "Get daemon or session status",
6476
7828
  run: async () => runStatus(args)
6477
7829
  });
7830
+ registerCommand({
7831
+ name: "session-inspector",
7832
+ description: "Capture a session-first diagnostic summary with relay health and trace proof",
7833
+ run: async () => runSessionInspector(args)
7834
+ });
6478
7835
  registerCommand({
6479
7836
  name: "goto",
6480
7837
  description: "Navigate current session to a URL",
@@ -6490,6 +7847,11 @@ async function main() {
6490
7847
  description: "Capture a snapshot of the active page",
6491
7848
  run: async () => runSnapshot(args)
6492
7849
  });
7850
+ registerCommand({
7851
+ name: "review",
7852
+ description: "Capture a first-class review payload for the active page",
7853
+ run: async () => runReview(args)
7854
+ });
6493
7855
  registerCommand({
6494
7856
  name: "annotate",
6495
7857
  description: "Request interactive annotations via direct or relay transport",
@@ -6550,6 +7912,31 @@ async function main() {
6550
7912
  description: "Scroll an element into view by ref",
6551
7913
  run: async () => runScrollIntoView(args)
6552
7914
  });
7915
+ registerCommand({
7916
+ name: "upload",
7917
+ description: "Upload files to a file input or chooser by ref",
7918
+ run: async () => runUpload(args)
7919
+ });
7920
+ registerCommand({
7921
+ name: "pointer-move",
7922
+ description: "Move the pointer to viewport coordinates",
7923
+ run: async () => runPointerMove(args)
7924
+ });
7925
+ registerCommand({
7926
+ name: "pointer-down",
7927
+ description: "Press a mouse button at viewport coordinates",
7928
+ run: async () => runPointerDown(args)
7929
+ });
7930
+ registerCommand({
7931
+ name: "pointer-up",
7932
+ description: "Release a mouse button at viewport coordinates",
7933
+ run: async () => runPointerUp(args)
7934
+ });
7935
+ registerCommand({
7936
+ name: "pointer-drag",
7937
+ description: "Drag the pointer between two viewport coordinates",
7938
+ run: async () => runPointerDrag(args)
7939
+ });
6553
7940
  registerCommand({
6554
7941
  name: "targets-list",
6555
7942
  description: "List page targets",
@@ -6640,6 +8027,21 @@ async function main() {
6640
8027
  description: "Capture a screenshot",
6641
8028
  run: async () => runScreenshot(args)
6642
8029
  });
8030
+ registerCommand({
8031
+ name: "screencast-start",
8032
+ description: "Start a browser replay screencast capture",
8033
+ run: async () => runScreencastStart(args)
8034
+ });
8035
+ registerCommand({
8036
+ name: "screencast-stop",
8037
+ description: "Stop a browser replay screencast capture",
8038
+ run: async () => runScreencastStop(args)
8039
+ });
8040
+ registerCommand({
8041
+ name: "dialog",
8042
+ description: "Inspect or handle a JavaScript dialog",
8043
+ run: async () => runDialog(args)
8044
+ });
6643
8045
  registerCommand({
6644
8046
  name: "console-poll",
6645
8047
  description: "Poll console events",
@@ -6655,6 +8057,36 @@ async function main() {
6655
8057
  description: "Capture page + console + network + exception diagnostics",
6656
8058
  run: async () => runDebugTraceSnapshot(args)
6657
8059
  });
8060
+ registerCommand({
8061
+ name: "desktop-status",
8062
+ description: "Inspect public read-only desktop observation availability",
8063
+ run: async () => runDesktopStatus(args)
8064
+ });
8065
+ registerCommand({
8066
+ name: "desktop-windows",
8067
+ description: "List windows exposed by the public read-only desktop observation plane",
8068
+ run: async () => runDesktopWindows(args)
8069
+ });
8070
+ registerCommand({
8071
+ name: "desktop-active-window",
8072
+ description: "Inspect the active window through the public read-only desktop observation plane",
8073
+ run: async () => runDesktopActiveWindow(args)
8074
+ });
8075
+ registerCommand({
8076
+ name: "desktop-capture-desktop",
8077
+ description: "Capture the current desktop surface through the public read-only desktop observation plane",
8078
+ run: async () => runDesktopCaptureDesktop(args)
8079
+ });
8080
+ registerCommand({
8081
+ name: "desktop-capture-window",
8082
+ description: "Capture a specific window through the public read-only desktop observation plane",
8083
+ run: async () => runDesktopCaptureWindow(args)
8084
+ });
8085
+ registerCommand({
8086
+ name: "desktop-accessibility-snapshot",
8087
+ description: "Capture desktop accessibility state through the public read-only desktop observation plane",
8088
+ run: async () => runDesktopAccessibilitySnapshot(args)
8089
+ });
6658
8090
  registerCommand({
6659
8091
  name: "cookie-import",
6660
8092
  description: "Import validated cookies into a session",
@@ -6700,17 +8132,19 @@ async function main() {
6700
8132
  if (exitCode === null) {
6701
8133
  return;
6702
8134
  }
6703
- process.exit(exitCode);
8135
+ await flushOutputAndExit(exitCode);
8136
+ return;
6704
8137
  } catch (error) {
6705
8138
  const format = outputFormat ?? detectOutputFormat(process.argv);
6706
8139
  const cliError = toCliError(error, parseSucceeded ? EXIT_EXECUTION : EXIT_USAGE);
6707
8140
  emitFatalError(cliError, format);
6708
- process.exit(cliError.exitCode);
8141
+ await flushOutputAndExit(cliError.exitCode);
8142
+ return;
6709
8143
  }
6710
8144
  }
6711
- main().catch((error) => {
8145
+ main().catch(async (error) => {
6712
8146
  const cliError = toCliError(error, EXIT_EXECUTION);
6713
8147
  emitFatalError(cliError, detectOutputFormat(process.argv));
6714
- process.exit(cliError.exitCode);
8148
+ await flushOutputAndExit(cliError.exitCode);
6715
8149
  });
6716
8150
  //# sourceMappingURL=index.js.map