oh-my-opencode 5.0.0-beta.85 → 5.0.0-beta.87

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 (237) hide show
  1. package/.agents/skills/codex-qa/SKILL.md +2 -0
  2. package/.agents/skills/hyperplan/SKILL.md +1 -1
  3. package/.agents/skills/senpi-qa/SKILL.md +9 -3
  4. package/.agents/skills/senpi-qa/scripts/resolve-evidence-dir.mjs +4 -4
  5. package/.opencode/skills/hyperplan/SKILL.md +1 -1
  6. package/README.md +1 -1
  7. package/dist/cli/index.js +68 -41
  8. package/dist/cli-node/index.js +68 -41
  9. package/dist/index.js +126 -107
  10. package/dist/skills/browser/ATTRIBUTION.md +26 -14
  11. package/dist/skills/browser/SKILL.md +65 -52
  12. package/dist/skills/browser/references/commands.md +81 -66
  13. package/dist/skills/browser/references/install.md +31 -34
  14. package/dist/skills/browser/references/owned-engine/README.md +41 -21
  15. package/dist/skills/browser/references/owned-engine/frames-and-humans.md +33 -20
  16. package/dist/skills/browser/references/owned-engine/ladder.md +24 -22
  17. package/dist/skills/browser/references/owned-engine/network.md +35 -15
  18. package/dist/skills/browser/references/recipes/1password.md +13 -11
  19. package/dist/skills/browser/references/remote.md +5 -4
  20. package/dist/skills/browser/runtime/omowright/index.js +1534 -0
  21. package/dist/skills/browser/runtime/omowright/manifest.json +9 -0
  22. package/dist/skills/browser/runtime/omowright/page-bundle.js +1395 -0
  23. package/dist/skills/browser/scripts/browser-doctor.mjs +37 -32
  24. package/dist/skills/browser/scripts/browser-install.mjs +31 -44
  25. package/dist/skills/browser/scripts/omowright.mjs +25 -0
  26. package/dist/skills/debugging/SKILL.md +2 -2
  27. package/dist/skills/debugging/references/methodology/06-fix.md +3 -3
  28. package/dist/skills/debugging/references/methodology/08-qa.md +1 -1
  29. package/dist/skills/debugging/references/tools/browser-qa.md +104 -0
  30. package/dist/skills/frontend/SKILL.md +1 -1
  31. package/dist/skills/frontend/references/design/clone-from-url.md +1 -1
  32. package/dist/skills/programming/SKILL.md +12 -18
  33. package/dist/skills/programming/references/rust/README.md +43 -15
  34. package/dist/skills/programming/references/rust/api-design.md +81 -0
  35. package/dist/skills/programming/references/rust/async-tokio.md +60 -28
  36. package/dist/skills/programming/references/rust/axum-stack.md +1 -13
  37. package/dist/skills/programming/references/rust/cargo-strict.md +44 -8
  38. package/dist/skills/programming/references/rust/clap-stack.md +8 -3
  39. package/dist/skills/programming/references/rust/concurrency.md +66 -52
  40. package/dist/skills/programming/references/rust/libraries.md +35 -25
  41. package/dist/skills/programming/references/rust/macros.md +63 -0
  42. package/dist/skills/programming/references/rust/one-liners.md +5 -3
  43. package/dist/skills/programming/references/rust/proptest-insta.md +8 -0
  44. package/dist/skills/programming/references/rust/type-state.md +50 -12
  45. package/dist/skills/programming/references/rust/unsafe-discipline.md +34 -6
  46. package/dist/skills/programming/references/rust/zero-cost-safety.md +62 -52
  47. package/dist/skills/programming/references/rust-ub/miri-sanitizers-loom.md +1 -1
  48. package/dist/skills/programming/references/rust-ub/ub-taxonomy.md +6 -3
  49. package/dist/skills/programming/scripts/rust/check-no-excuse-rules.sh +86 -75
  50. package/dist/skills/programming/scripts/rust/check-no-excuse-rules.test.ts +83 -0
  51. package/dist/skills/programming/scripts/rust/new-project.py +31 -28
  52. package/dist/skills/review-work/SKILL.md +1 -1
  53. package/dist/skills/ultimate-browsing/SKILL.md +27 -20
  54. package/dist/skills/ultimate-browsing/engine/AGENTS.md +1 -1
  55. package/dist/skills/ultimate-browsing/references/chrome-stealth.md +32 -100
  56. package/dist/skills/ultimate-browsing/references/insane-search/README.md +5 -11
  57. package/dist/skills/ultimate-browsing/references/insane-search/playwright.md +20 -37
  58. package/dist/skills/ulw-execute/SKILL.md +4 -4
  59. package/dist/skills/visual-qa/SKILL.md +1 -1
  60. package/dist/skills/visual-qa/references/browser-setup.md +46 -46
  61. package/dist/tui.js +47 -23
  62. package/package.json +15 -14
  63. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  64. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +2 -0
  65. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  66. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  67. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  68. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  69. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  70. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  71. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  72. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  73. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  74. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  75. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  76. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-6.md +1 -1
  77. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  78. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  79. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  80. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  81. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  82. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  83. package/packages/omo-codex/plugin/components/ultrawork/README.md +1 -1
  84. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +2 -2
  85. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +63 -89
  86. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  87. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  88. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  89. package/packages/omo-codex/plugin/components/ulw-execute-continuation/directive.md +2 -2
  90. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  91. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  92. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +63 -89
  93. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  94. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  95. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/define-goal.md +2 -3
  96. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +2 -2
  97. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  98. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  99. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  100. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  101. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  102. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  103. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  104. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  105. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  106. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  107. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  108. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  109. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  110. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  111. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  112. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  113. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  114. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  115. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  116. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  117. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  118. package/packages/omo-codex/plugin/package-lock.json +12 -12
  119. package/packages/omo-codex/plugin/package.json +1 -1
  120. package/packages/omo-codex/plugin/scripts/materialize-shared-upstreams.mjs +8 -2
  121. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +2 -2
  122. package/packages/omo-codex/plugin/skills/browser/ATTRIBUTION.md +26 -14
  123. package/packages/omo-codex/plugin/skills/browser/SKILL.md +65 -52
  124. package/packages/omo-codex/plugin/skills/browser/references/commands.md +81 -66
  125. package/packages/omo-codex/plugin/skills/browser/references/install.md +31 -34
  126. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/README.md +41 -21
  127. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/frames-and-humans.md +33 -20
  128. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/ladder.md +24 -22
  129. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/network.md +35 -15
  130. package/packages/omo-codex/plugin/skills/browser/references/recipes/1password.md +13 -11
  131. package/packages/omo-codex/plugin/skills/browser/references/remote.md +5 -4
  132. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/index.js +1534 -0
  133. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +9 -0
  134. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/page-bundle.js +1395 -0
  135. package/packages/omo-codex/plugin/skills/browser/scripts/browser-doctor.mjs +37 -32
  136. package/packages/omo-codex/plugin/skills/browser/scripts/browser-install.mjs +31 -44
  137. package/packages/omo-codex/plugin/skills/browser/scripts/omowright.mjs +25 -0
  138. package/packages/omo-codex/plugin/skills/debugging/SKILL.md +2 -2
  139. package/packages/omo-codex/plugin/skills/debugging/references/methodology/06-fix.md +3 -3
  140. package/packages/omo-codex/plugin/skills/debugging/references/methodology/08-qa.md +1 -1
  141. package/packages/omo-codex/plugin/skills/debugging/references/tools/browser-qa.md +104 -0
  142. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +1 -1
  143. package/packages/omo-codex/plugin/skills/frontend/references/design/clone-from-url.md +1 -1
  144. package/packages/omo-codex/plugin/skills/programming/SKILL.md +12 -18
  145. package/packages/omo-codex/plugin/skills/programming/references/rust/README.md +43 -15
  146. package/packages/omo-codex/plugin/skills/programming/references/rust/api-design.md +81 -0
  147. package/packages/omo-codex/plugin/skills/programming/references/rust/async-tokio.md +60 -28
  148. package/packages/omo-codex/plugin/skills/programming/references/rust/axum-stack.md +1 -13
  149. package/packages/omo-codex/plugin/skills/programming/references/rust/cargo-strict.md +44 -8
  150. package/packages/omo-codex/plugin/skills/programming/references/rust/clap-stack.md +8 -3
  151. package/packages/omo-codex/plugin/skills/programming/references/rust/concurrency.md +66 -52
  152. package/packages/omo-codex/plugin/skills/programming/references/rust/libraries.md +35 -25
  153. package/packages/omo-codex/plugin/skills/programming/references/rust/macros.md +63 -0
  154. package/packages/omo-codex/plugin/skills/programming/references/rust/one-liners.md +5 -3
  155. package/packages/omo-codex/plugin/skills/programming/references/rust/proptest-insta.md +8 -0
  156. package/packages/omo-codex/plugin/skills/programming/references/rust/type-state.md +50 -12
  157. package/packages/omo-codex/plugin/skills/programming/references/rust/unsafe-discipline.md +34 -6
  158. package/packages/omo-codex/plugin/skills/programming/references/rust/zero-cost-safety.md +62 -52
  159. package/packages/omo-codex/plugin/skills/programming/references/rust-ub/miri-sanitizers-loom.md +1 -1
  160. package/packages/omo-codex/plugin/skills/programming/references/rust-ub/ub-taxonomy.md +6 -3
  161. package/packages/omo-codex/plugin/skills/programming/scripts/rust/check-no-excuse-rules.sh +86 -75
  162. package/packages/omo-codex/plugin/skills/programming/scripts/rust/new-project.py +31 -28
  163. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +1 -1
  164. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +27 -20
  165. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +1 -1
  166. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/chrome-stealth.md +32 -100
  167. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +5 -11
  168. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/playwright.md +20 -37
  169. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +63 -89
  170. package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +4 -4
  171. package/packages/omo-codex/plugin/skills/ulw-loop/references/define-goal.md +2 -3
  172. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +2 -2
  173. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +1 -1
  174. package/packages/omo-codex/plugin/skills/visual-qa/references/browser-setup.md +46 -46
  175. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +2 -2
  176. package/packages/omo-codex/scripts/install-dist/install-local.mjs +4 -2
  177. package/packages/prompts-core/prompts/ultrawork/codex.md +63 -89
  178. package/packages/shared-skills/skills/browser/ATTRIBUTION.md +26 -14
  179. package/packages/shared-skills/skills/browser/SKILL.md +65 -52
  180. package/packages/shared-skills/skills/browser/references/commands.md +81 -66
  181. package/packages/shared-skills/skills/browser/references/install.md +31 -34
  182. package/packages/shared-skills/skills/browser/references/owned-engine/README.md +41 -21
  183. package/packages/shared-skills/skills/browser/references/owned-engine/frames-and-humans.md +33 -20
  184. package/packages/shared-skills/skills/browser/references/owned-engine/ladder.md +24 -22
  185. package/packages/shared-skills/skills/browser/references/owned-engine/network.md +35 -15
  186. package/packages/shared-skills/skills/browser/references/recipes/1password.md +13 -11
  187. package/packages/shared-skills/skills/browser/references/remote.md +5 -4
  188. package/packages/shared-skills/skills/browser/runtime/omowright/index.js +1534 -0
  189. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +9 -0
  190. package/packages/shared-skills/skills/browser/runtime/omowright/page-bundle.js +1395 -0
  191. package/packages/shared-skills/skills/browser/scripts/browser-doctor.mjs +37 -32
  192. package/packages/shared-skills/skills/browser/scripts/browser-install.mjs +31 -44
  193. package/packages/shared-skills/skills/browser/scripts/omowright.mjs +25 -0
  194. package/packages/shared-skills/skills/debugging/SKILL.md +2 -2
  195. package/packages/shared-skills/skills/debugging/references/methodology/06-fix.md +3 -3
  196. package/packages/shared-skills/skills/debugging/references/methodology/08-qa.md +1 -1
  197. package/packages/shared-skills/skills/debugging/references/tools/browser-qa.md +104 -0
  198. package/packages/shared-skills/skills/frontend/SKILL.md +1 -1
  199. package/packages/shared-skills/skills/frontend/references/design/clone-from-url.md +1 -1
  200. package/packages/shared-skills/skills/programming/SKILL.md +12 -18
  201. package/packages/shared-skills/skills/programming/references/rust/README.md +43 -15
  202. package/packages/shared-skills/skills/programming/references/rust/api-design.md +81 -0
  203. package/packages/shared-skills/skills/programming/references/rust/async-tokio.md +60 -28
  204. package/packages/shared-skills/skills/programming/references/rust/axum-stack.md +1 -13
  205. package/packages/shared-skills/skills/programming/references/rust/cargo-strict.md +44 -8
  206. package/packages/shared-skills/skills/programming/references/rust/clap-stack.md +8 -3
  207. package/packages/shared-skills/skills/programming/references/rust/concurrency.md +66 -52
  208. package/packages/shared-skills/skills/programming/references/rust/libraries.md +35 -25
  209. package/packages/shared-skills/skills/programming/references/rust/macros.md +63 -0
  210. package/packages/shared-skills/skills/programming/references/rust/one-liners.md +5 -3
  211. package/packages/shared-skills/skills/programming/references/rust/proptest-insta.md +8 -0
  212. package/packages/shared-skills/skills/programming/references/rust/type-state.md +50 -12
  213. package/packages/shared-skills/skills/programming/references/rust/unsafe-discipline.md +34 -6
  214. package/packages/shared-skills/skills/programming/references/rust/zero-cost-safety.md +62 -52
  215. package/packages/shared-skills/skills/programming/references/rust-ub/miri-sanitizers-loom.md +1 -1
  216. package/packages/shared-skills/skills/programming/references/rust-ub/ub-taxonomy.md +6 -3
  217. package/packages/shared-skills/skills/programming/scripts/rust/check-no-excuse-rules.sh +86 -75
  218. package/packages/shared-skills/skills/programming/scripts/rust/check-no-excuse-rules.test.ts +83 -0
  219. package/packages/shared-skills/skills/programming/scripts/rust/new-project.py +31 -28
  220. package/packages/shared-skills/skills/review-work/SKILL.md +1 -1
  221. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +27 -20
  222. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +1 -1
  223. package/packages/shared-skills/skills/ultimate-browsing/references/chrome-stealth.md +32 -100
  224. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +5 -11
  225. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/playwright.md +20 -37
  226. package/packages/shared-skills/skills/ulw-execute/SKILL.md +4 -4
  227. package/packages/shared-skills/skills/visual-qa/SKILL.md +1 -1
  228. package/packages/shared-skills/skills/visual-qa/references/browser-setup.md +46 -46
  229. package/dist/skills/browser/scripts/browser-env.mjs +0 -41
  230. package/dist/skills/debugging/references/tools/playwright-cli.md +0 -112
  231. package/dist/skills/programming/scripts/rust/check-no-excuse-rules.py +0 -296
  232. package/packages/omo-codex/plugin/skills/browser/scripts/browser-env.mjs +0 -41
  233. package/packages/omo-codex/plugin/skills/debugging/references/tools/playwright-cli.md +0 -112
  234. package/packages/omo-codex/plugin/skills/programming/scripts/rust/check-no-excuse-rules.py +0 -296
  235. package/packages/shared-skills/skills/browser/scripts/browser-env.mjs +0 -41
  236. package/packages/shared-skills/skills/debugging/references/tools/playwright-cli.md +0 -112
  237. package/packages/shared-skills/skills/programming/scripts/rust/check-no-excuse-rules.py +0 -296
@@ -49,7 +49,7 @@ Before any reviewer sees an image, verify each capture yourself: the file signat
49
49
  ### Web
50
50
 
51
51
  1. Capture a REFERENCE image: the user's mock/target, generated page snapshot, Figma export, source-site capture, or known-good baseline. Save as PNG. If the user provided overview text or annotations, save them next to the image and treat them as part of the reference packet.
52
- 2. Capture the ACTUAL rendered screenshot at the reference viewport. Use two browser tiers from js eval: (1) `new Bun.WebView()` on Bun >= 1.4 (macOS default; Linux/Windows need installed Chrome/Chromium/Edge); (2) otherwise, or for Chrome semantics, stealth, trace, or authenticated profiles, WRITE a `playwright-core` script and run it from the kernel against local Chrome (`chromium.launch({ channel: "chrome" })` / `launchPersistentContext` on a CLONED profile, never the live one). In Codex, prefer `browser:control-in-app-browser` for ordinary captures. Save PNG and return its path; close the browser context. See `$SKILL_DIR/references/browser-setup.md` for fixed-viewport examples and prerequisites.
52
+ 2. Capture the ACTUAL rendered screenshot at the reference viewport with omowright from js eval (the library is staged in the `browser` skill): the owned engine (`connectPipe` on a task-owned profile, viewport pinned with `emulate`, then `page.screenshot()`) for anything unauthenticated, or the attached engine (`connectBrowserSkill()` → `session.screenshot()`) when the page needs the user's login — never a clone of, or a launch against, the user's live profile. Save PNG and return its path; close the browser or stop the session. See `$SKILL_DIR/references/browser-setup.md` for fixed-viewport examples and prerequisites.
53
53
  3. Run the diff and keep the JSON:
54
54
 
55
55
  ```
@@ -1,75 +1,75 @@
1
1
  # Browser setup (Web capture)
2
2
 
3
- Use the js-eval kernel for both tiers. In Codex, prefer
4
- `browser:control-in-app-browser` for ordinary captures.
3
+ Capture with omowright from the js-eval kernel. The library is staged inside the `browser` skill;
4
+ load it once per session:
5
5
 
6
- ## Tier 1: Bun.WebView
6
+ ```js
7
+ const { loadOmowright } = await import("<browser-skill-root>/scripts/omowright.mjs")
8
+ const { omowright } = await loadOmowright()
9
+ ```
10
+
11
+ ## Owned engine (default for QA)
7
12
 
8
- On a Bun >= 1.4 kernel, `new Bun.WebView()` is the macOS default (system
9
- WebKit, no browser download). Linux/Windows require `backend: "chrome"`
10
- and installed Chrome/Chromium/Edge. WebView is headless; WebKit has no CDP,
11
- and `type()` emits no keyboard events. Use tier 2 when these differences matter.
13
+ A browser your code launches, with a task-owned profile, pinned viewport and no user state.
14
+ `connectPipe` opens no listening port and reaps the process on `close()`.
12
15
 
13
16
  ```js
14
17
  // js-eval cell; url and pngPath belong to this QA run.
15
- const view = new Bun.WebView({ width: 1280, height: 720 })
18
+ const { mkdtempSync, rmSync } = await import("node:fs")
19
+ const profile = mkdtempSync(`${(await import("node:os")).tmpdir()}/visual-qa-`)
20
+ const browser = await omowright.connectPipe({
21
+ browserPath: chromeBinary, // installed Chrome, Chromium, CloakBrowser or chrome-headless-shell
22
+ browserArgs: ["--headless", "--no-first-run", `--user-data-dir=${profile}`],
23
+ storageRoot: profile,
24
+ })
16
25
  try {
17
- await view.navigate(url)
18
- await Bun.write(pngPath, await view.screenshot())
26
+ const page = await browser.newTab("about:blank")
27
+ await omowright.emulate(page, { width: 1280, height: 720, deviceScaleFactor: 1, mobile: false, hasTouch: false })
28
+ await page.goto(url, { waitUntil: "load" })
29
+ await Bun.write(pngPath, await page.screenshot())
19
30
  console.log(pngPath)
20
31
  } finally {
21
- view[Symbol.dispose]()
32
+ await browser.close()
33
+ rmSync(profile, { recursive: true, force: true })
22
34
  }
23
35
  ```
24
36
 
25
- ## Tier 2: playwright-core with local Chrome
37
+ Chrome must already be installed; report an absent executable rather than downloading a managed
38
+ browser. For bot-scored or WAF targets use `connectCloakProfile({ profileDir })` (CloakBrowser
39
+ with a pinned fingerprint seed) — the `browser` skill's `references/owned-engine/README.md`
40
+ covers it.
41
+
42
+ ## Attached engine (authenticated pages)
26
43
 
27
- For other kernels, real-Chrome semantics, stealth, traces, or authenticated
28
- profiles, WRITE a script beside the project's installed `playwright-core`
29
- dependency and execute it from js eval. The user installs `playwright-core`
30
- once with `bun add playwright-core` if needed; Chrome must already exist.
31
- No managed browser download is needed.
44
+ When the capture needs the user's login, drive the browser they are signed into instead of
45
+ cloning its profile:
32
46
 
33
47
  ```js
34
- // capture.mjs
35
- import { chromium } from "playwright-core"
36
- const [url, pngPath] = process.argv.slice(2)
37
- const browser = await chromium.launch({ channel: "chrome", headless: true })
48
+ const session = await omowright.connectBrowserSkill({ name: "visual-qa capture", focused: false })
38
49
  try {
39
- const page = await browser.newPage({ viewport: { width: 1280, height: 720 }, deviceScaleFactor: 1 })
40
- await page.goto(url, { waitUntil: "load", timeout: 30000 })
41
- await page.screenshot({ path: pngPath })
42
- console.log(pngPath)
50
+ await session.navigate(url, { waitUntil: "load" })
51
+ await session.resize(1280, 720)
52
+ const shot = await session.screenshot() // { buffer, width, height }
53
+ await Bun.write(pngPath, shot.buffer)
43
54
  } finally {
44
- await browser.close()
55
+ await session.stop()
45
56
  }
46
57
  ```
47
58
 
48
- Run from the js-eval kernel (also works on a Node kernel):
49
-
50
- ```js
51
- const { promisify } = await import("node:util")
52
- const { execFile } = await import("node:child_process")
53
- const result = await promisify(execFile)("node", [scriptPath, url, pngPath], { timeout: 45000 })
54
- console.log(result.stdout)
55
- ```
56
-
57
- For auth, CLONE the user-data directory to a private task-owned directory
58
- and use `chromium.launchPersistentContext(clonePath, { channel: "chrome" })`.
59
- NEVER launch against or clear cookies/cache/site data from the live profile.
60
- Close the context and remove only the clone after QA. For script-based stealth,
61
- read the ultimate-browsing skill's `references/chrome-stealth.md`.
59
+ NEVER launch anything against, or clear cookies/cache/site data from, the user's live profile;
60
+ the attached engine is the only sanctioned way to a signed-in page. If no extension is connected,
61
+ run the `browser` skill's `scripts/browser-install.mjs`, relay its one human step, and wait — do
62
+ not fall back to the owned engine for an authenticated criterion.
62
63
 
63
64
  ## Capture a screenshot at a fixed viewport
64
65
 
65
- Match CSS viewport AND PNG dimensions: WebKit follows native display scale
66
- (a 1280x720 viewport can yield 2560x1440 pixels). Use matching reference
67
- captures or Chrome's `deviceScaleFactor`, not resizing to force a pass.
68
- Wait for the specific page state, not a sleep, then compare:
66
+ Match CSS viewport AND PNG dimensions: pin `deviceScaleFactor` through `emulate` (owned) or
67
+ `resize` (attached) instead of resizing the PNG to force a pass. Wait for the specific page state
68
+ (a locator, a `waitForURL`, a `createNetworkSnoop(page).waitFor(...)`), not a sleep, then compare:
69
69
 
70
70
  ```sh
71
71
  node "$SKILL_DIR/scripts/visual-qa.mjs" image-diff reference.png actual.png
72
72
  ```
73
73
 
74
- Inspect `dimensionsMatch` and `diffRatio`, then inspect the image. Close every
75
- WebView/browser context and the fixture server, even on a failed capture.
74
+ Inspect `dimensionsMatch` and `diffRatio`, then inspect the image. Close every browser and session
75
+ and the fixture server, even on a failed capture; remove the owned profile in the same `finally`.
@@ -1,41 +0,0 @@
1
- import { execFile } from "node:child_process"
2
- import { existsSync } from "node:fs"
3
- import { homedir, platform } from "node:os"
4
- import { join } from "node:path"
5
- import { promisify } from "node:util"
6
-
7
- const run = promisify(execFile)
8
-
9
- export const STORE_LISTINGS = {
10
- chrome: "https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi",
11
- edge: "https://microsoftedge.microsoft.com/addons/detail/browserskill/emacgiaaaiojkkpkddmmdfhmokgmnikg",
12
- }
13
-
14
- export const SUPPORTED_PLATFORMS = new Set(["darwin", "linux", "win32"])
15
-
16
- export function localBinCandidates() {
17
- const bin = join(homedir(), ".local", "bin")
18
- return platform() === "win32" ? [join(bin, "bsk.exe"), join(bin, "bsk")] : [join(bin, "bsk")]
19
- }
20
-
21
- export async function resolveCli() {
22
- for (const candidate of localBinCandidates()) {
23
- if (existsSync(candidate)) return candidate
24
- }
25
- try {
26
- await run("bsk", ["--version"], { timeout: 15000, windowsHide: true })
27
- return "bsk"
28
- } catch {
29
- return undefined
30
- }
31
- }
32
-
33
- export async function readStatus(cli) {
34
- const { stdout } = await run(cli, ["status", "--json"], { timeout: 30000, windowsHide: true })
35
- return JSON.parse(stdout)
36
- }
37
-
38
- export function connectedBrowsers(status) {
39
- const browsers = status?.browsers
40
- return Array.isArray(browsers) ? browsers : []
41
- }
@@ -1,112 +0,0 @@
1
- # Browser QA — Scripts from js eval
2
-
3
- A browser UI bug needs a rendered browser, not curl. Use two tiers from the
4
- js-eval kernel: `new Bun.WebView()` on Bun >= 1.4 for ordinary captures (macOS
5
- uses WebKit; Linux/Windows need installed Chrome/Chromium/Edge), otherwise a
6
- written `playwright-core` script against local Chrome. Use the script tier for
7
- Chrome semantics, stealth, traces, keyboard events, or authenticated profiles.
8
- In Codex, prefer `browser:control-in-app-browser` for ordinary page control.
9
-
10
- ## When to reach for Playwright
11
-
12
- Use the script tier for forms, hydration, responsive CSS, cookie/session flows,
13
- service workers, or client-side navigation. For a backend-only status/body bug,
14
- use HTTP. WebView is headless, WebKit has no CDP, and `type()` emits no keyboard
15
- events; choose real Chrome when these distinctions affect the reproduction.
16
-
17
- ## Install (per-project)
18
-
19
- Reuse installed `playwright-core`. If absent, the user can install the script
20
- dependency once with `bun add playwright-core`. Chrome must already be installed;
21
- report an absent executable rather than downloading a managed browser. Write the
22
- script beside the dependency so normal module resolution works.
23
-
24
- For stealth, use the ultimate-browsing reference `references/chrome-stealth.md`:
25
- its optional plugins are user-installed once in the engine directory and executed
26
- as a script, not injected into WebView.
27
-
28
- ## The four things you'll actually use
29
-
30
- ### 1. Write a reproduction script
31
-
32
- Record the exact URL, inputs, viewport, expected UI state, and output artifact.
33
- For auth, CLONE the user-data directory and use `launchPersistentContext` on the
34
- clone. NEVER launch against or clear data from the live profile.
35
-
36
- ### 2. Reproduce + capture
37
-
38
- ```js
39
- // debug-repro.mjs
40
- import { chromium } from 'playwright-core';
41
- const [url, pngPath, tracePath] = process.argv.slice(2);
42
- const browser = await chromium.launch({ channel: 'chrome', headless: true });
43
- try {
44
- const context = await browser.newContext({ viewport: { width: 1280, height: 720 } });
45
- await context.tracing.start({ screenshots: true, snapshots: true });
46
- try {
47
- const page = await context.newPage();
48
- page.on('pageerror', error => console.error(error));
49
- await page.goto(url, { waitUntil: 'load', timeout: 30000 });
50
- // Add the scenario's actions and locator/state assertions here.
51
- await page.screenshot({ path: pngPath });
52
- console.log(pngPath);
53
- } finally {
54
- await context.tracing.stop({ path: tracePath });
55
- await context.close();
56
- }
57
- } finally {
58
- await browser.close();
59
- }
60
- ```
61
-
62
- A screenshot alone is not a reproduction assertion. Use locators for the exact
63
- observable state; fail the script when that state is absent.
64
-
65
- ### 3. Run from the js-eval kernel
66
-
67
- ```js
68
- const { promisify } = await import('node:util')
69
- const { execFile } = await import('node:child_process')
70
- const result = await promisify(execFile)('node', [scriptPath, url, pngPath, tracePath], { timeout: 60000 })
71
- console.log(result.stdout)
72
- ```
73
-
74
- ### 4. Inspect the trace
75
-
76
- Retain the trace ZIP for the project's existing trace viewer and inspect the PNG.
77
- Trace snapshots, network events, and screenshots identify where the UI diverged.
78
- Keep auth-bearing traces private; do not upload them to public viewers.
79
-
80
- ## Headless vs headed during debugging
81
-
82
- Use `headless: false` with an available display when reproducing headed-only
83
- behavior. State which mode produced the evidence; do not claim a headless capture
84
- proves desktop-browser permissions or window behavior.
85
-
86
- ## Catching the silent-failure patterns Playwright is good at
87
-
88
- Subscribe before triggering the action: `page.on('pageerror', ...)`,
89
- `page.on('requestfailed', ...)`, or a bounded `page.waitForResponse(...)` promise.
90
- Await that specific response/state rather than sleeping or waiting for generic
91
- network idleness. Propagate failed assertions and process exit codes.
92
-
93
- ## Viewport and device emulation
94
-
95
- Use `page.setViewportSize({ width: 375, height: 667 })` for a narrow capture.
96
- `devices` comes from `playwright-core`; apply the desired preset when creating
97
- a context. Match the reference's pixel scale as well as its viewport.
98
-
99
- ## Gotchas
100
-
101
- - Wait for state, not time: locator visibility or the exact response is the signal.
102
- - Locators re-resolve after UI changes; stale element handles do not.
103
- - Fresh task-owned contexts avoid cached state leaking between runs.
104
- - Auth belongs in a private clone, never the live browser profile.
105
- - Give every run its own output directory and bounded process lifetime.
106
-
107
- ## Phase 9 cleanup specifics
108
-
109
- Close WebView/browser contexts even on failure, stop the fixture server, and remove
110
- only this run's temporary scripts and profile clone. Preserve requested PNG/trace
111
- artifacts, then record the cleanup receipt. Do not kill unrelated browsers or
112
- remove a user's installed Chrome.
@@ -1,296 +0,0 @@
1
- #!/usr/bin/env -S uv run --script
2
- # /// script
3
- # requires-python = ">=3.11"
4
- # dependencies = []
5
- # ///
6
- #
7
- # How to run:
8
- # uv run --script check-no-excuse-rules.py src/lib.rs src/main.rs
9
- # uv run --script check-no-excuse-rules.py src/ # recursively finds .rs files
10
- # uv run --script check-no-excuse-rules.py . # entire tree
11
- #
12
- # No-excuse rule checker for Rust files — Python rewrite of check-no-excuse-rules.sh.
13
- # Only rules enforceable via pure text matching live here.
14
- # Everything semantic is on clippy + miri + nextest.
15
- #
16
- # Rules:
17
- # unwrap .unwrap() outside tests without // SAFE-UNWRAP:
18
- # expect .expect() outside tests without // SAFE-EXPECT:
19
- # placeholder-macro todo!/unimplemented!/unreachable!/unreachable_unchecked! in committed code
20
- # box-dyn-error Box<dyn Error> in non-test code
21
- # lib-panic panic!() in library code
22
- # unsafe-no-safety unsafe { without // SAFETY: in preceding 5 lines
23
- # unjustified-clippy-allow #[allow(clippy::...)] without // CLIPPY-ALLOW:
24
- # narrowing-as-cast possible narrowing 'as' cast
25
- #
26
- # Opt-out: place the appropriate comment on the previous line:
27
- # // SAFE-UNWRAP: <reason>
28
- # // SAFE-EXPECT: <reason>
29
- # // SAFETY: <reason> (for unsafe blocks, within 5 lines above)
30
- # // CLIPPY-ALLOW: <reason>
31
- #
32
- # Test paths (exempt from unwrap/expect/placeholder/box-dyn-error/lib-panic):
33
- # tests/, benches/, examples/, build.rs, *_test.rs, #[cfg(test)] regions
34
-
35
- from __future__ import annotations
36
-
37
- import re
38
- import sys
39
- from pathlib import Path
40
-
41
- # ---------------------------------------------------------------------------
42
- # Patterns (compiled once)
43
- # ---------------------------------------------------------------------------
44
-
45
- RE_UNWRAP = re.compile(r"\.unwrap\(\)")
46
- RE_EXPECT = re.compile(r"\.expect\(")
47
- RE_PLACEHOLDER = re.compile(r"\b(todo!|unimplemented!|unreachable!|unreachable_unchecked!)")
48
- RE_BOX_DYN_ERROR = re.compile(r"Box<dyn\s+Error")
49
- RE_PANIC = re.compile(r"\bpanic!\(")
50
- RE_UNSAFE_BLOCK = re.compile(r"\bunsafe\s*\{")
51
- RE_CLIPPY_ALLOW = re.compile(r"#\[allow\(clippy::")
52
- RE_CFG_TEST = re.compile(r"#\[cfg\(test\)\]")
53
- RE_SAFE_UNWRAP = re.compile(r"//\s*SAFE-UNWRAP:")
54
- RE_SAFE_EXPECT = re.compile(r"//\s*SAFE-EXPECT:")
55
- RE_SAFETY = re.compile(r"//\s*SAFETY:")
56
- RE_CLIPPY_ALLOW_JUST = re.compile(r"//\s*CLIPPY-ALLOW:")
57
-
58
- # Narrowing cast: (wider) as (narrower)
59
- # Wider types that lose bits when cast to narrower targets.
60
- # No leading \b — must match e.g. `999u64 as u32` where a digit precedes the type.
61
- _WIDER = r"(?:u16|u32|u64|u128|usize|i16|i32|i64|i128|isize)"
62
- _NARROWER = r"(?:u8|u16|u32|i8|i16|i32)"
63
- RE_NARROWING_CAST = re.compile(
64
- rf"{_WIDER}\s+as\s+{_NARROWER}"
65
- )
66
-
67
- # Test-path fragments.
68
- _TEST_PATH_PARTS = {"tests", "benches", "examples"}
69
-
70
-
71
- # ---------------------------------------------------------------------------
72
- # Helpers
73
- # ---------------------------------------------------------------------------
74
-
75
- violations = 0
76
-
77
-
78
- def report(file: str, line: int, rule: str, detail: str) -> None:
79
- """Emit a GitHub-Actions-compatible error annotation to stderr."""
80
- global violations
81
- print(f"::error file={file},line={line}::[{rule}] {detail}", file=sys.stderr)
82
- violations += 1
83
-
84
-
85
- def is_test_path(path: Path) -> bool:
86
- """Return True if *path* is in a test/bench/example directory or is a test file."""
87
- parts = path.parts
88
- for part in parts:
89
- if part in _TEST_PATH_PARTS:
90
- return True
91
- if path.name == "build.rs":
92
- return True
93
- if path.name.endswith("_test.rs"):
94
- return True
95
- return False
96
-
97
-
98
- def is_lib_path(file: Path) -> bool:
99
- """Heuristic: is this file library code (not main.rs, not src/bin/*)."""
100
- parts = path_parts_str(file)
101
- # Must live under src/
102
- if "src" not in parts:
103
- return False
104
- if file.name == "main.rs":
105
- return False
106
- # src/bin/* is binary code
107
- try:
108
- src_idx = parts.index("src")
109
- if src_idx + 1 < len(parts) and parts[src_idx + 1] == "bin":
110
- return False
111
- except ValueError:
112
- return False
113
- return True
114
-
115
-
116
- def path_parts_str(p: Path) -> list[str]:
117
- return list(p.parts)
118
-
119
-
120
- def strip_line_comment(line: str) -> str:
121
- """Return the portion of *line* before any ``//`` line comment.
122
-
123
- This is a crude heuristic — it does not handle ``//`` inside string
124
- literals, but matches the behaviour of the bash version.
125
- """
126
- idx = line.find("//")
127
- if idx == -1:
128
- return line
129
- return line[:idx]
130
-
131
-
132
- def collect_rs_files(args: list[str]) -> list[Path]:
133
- """Expand CLI arguments: files are kept as-is, directories are walked."""
134
- result: list[Path] = []
135
- for arg in args:
136
- p = Path(arg)
137
- if p.is_file():
138
- if p.suffix == ".rs":
139
- result.append(p)
140
- elif p.is_dir():
141
- result.extend(sorted(p.rglob("*.rs")))
142
- # Ignore non-existent / non-.rs
143
- return result
144
-
145
-
146
- # ---------------------------------------------------------------------------
147
- # Main checker
148
- # ---------------------------------------------------------------------------
149
-
150
- def check_file(file: Path) -> None:
151
- in_test_file = is_test_path(file)
152
- in_cfg_test = False
153
- cfg_test_brace_depth = 0
154
-
155
- try:
156
- lines = file.read_text(encoding="utf-8", errors="replace").splitlines()
157
- except OSError as exc:
158
- print(f"warning: cannot read {file}: {exc}", file=sys.stderr)
159
- return
160
-
161
- for line_no_0, raw_line in enumerate(lines):
162
- line_no = line_no_0 + 1 # 1-indexed
163
-
164
- # --- #[cfg(test)] region tracker ---
165
- if RE_CFG_TEST.search(raw_line):
166
- in_cfg_test = True
167
- cfg_test_brace_depth = 0
168
-
169
- if in_cfg_test:
170
- opens = raw_line.count("{")
171
- closes = raw_line.count("}")
172
- cfg_test_brace_depth += opens - closes
173
- if cfg_test_brace_depth <= 0 and not RE_CFG_TEST.search(raw_line):
174
- in_cfg_test = False
175
-
176
- exempt = in_test_file or in_cfg_test
177
- code_only = strip_line_comment(raw_line)
178
-
179
- if not exempt:
180
- # .unwrap()
181
- if RE_UNWRAP.search(code_only):
182
- prev = lines[line_no_0 - 1] if line_no_0 > 0 else ""
183
- if not RE_SAFE_UNWRAP.search(prev):
184
- report(
185
- str(file), line_no, "unwrap",
186
- ".unwrap() outside tests - use ? / ok_or / pattern match "
187
- "or annotate previous line with // SAFE-UNWRAP: <reason>",
188
- )
189
-
190
- # .expect(...)
191
- if RE_EXPECT.search(code_only):
192
- prev = lines[line_no_0 - 1] if line_no_0 > 0 else ""
193
- if not RE_SAFE_EXPECT.search(prev):
194
- report(
195
- str(file), line_no, "expect",
196
- ".expect() outside tests - use ? or annotate previous "
197
- "line with // SAFE-EXPECT: <reason>",
198
- )
199
-
200
- # todo!/unimplemented!/unreachable!/unreachable_unchecked!
201
- if RE_PLACEHOLDER.search(code_only):
202
- report(
203
- str(file), line_no, "placeholder-macro",
204
- "todo!/unimplemented!/unreachable! in committed code",
205
- )
206
-
207
- # Box<dyn Error>
208
- if RE_BOX_DYN_ERROR.search(code_only):
209
- report(
210
- str(file), line_no, "box-dyn-error",
211
- "Box<dyn Error> in non-test code - use anyhow::Error (apps) "
212
- "or thiserror enum (libs)",
213
- )
214
-
215
- # panic!() in library code
216
- if is_lib_path(file) and RE_PANIC.search(code_only):
217
- report(
218
- str(file), line_no, "lib-panic",
219
- "panic!() in library code - return Result",
220
- )
221
-
222
- # unsafe { without // SAFETY: — always enforced, even in tests
223
- if RE_UNSAFE_BLOCK.search(code_only):
224
- start = max(0, line_no_0 - 5)
225
- window = "\n".join(lines[start : line_no_0 + 1])
226
- if not RE_SAFETY.search(window):
227
- report(
228
- str(file), line_no, "unsafe-no-safety-comment",
229
- "unsafe block without // SAFETY: comment in preceding 5 lines",
230
- )
231
-
232
- # #[allow(clippy::...)] without // CLIPPY-ALLOW: — always enforced
233
- if RE_CLIPPY_ALLOW.search(code_only):
234
- prev = lines[line_no_0 - 1] if line_no_0 > 0 else ""
235
- if not RE_CLIPPY_ALLOW_JUST.search(prev):
236
- report(
237
- str(file), line_no, "unjustified-clippy-allow",
238
- "#[allow(clippy::...)] without // CLIPPY-ALLOW: <reason> on "
239
- "previous line",
240
- )
241
-
242
- # Narrowing numeric `as` casts
243
- if RE_NARROWING_CAST.search(code_only):
244
- report(
245
- str(file), line_no, "narrowing-as-cast",
246
- "possible narrowing 'as' cast - use TryFrom / try_into() for "
247
- "fallible conversion",
248
- )
249
-
250
-
251
- # ---------------------------------------------------------------------------
252
- # Entry point
253
- # ---------------------------------------------------------------------------
254
-
255
- def main() -> None:
256
- global violations
257
-
258
- if len(sys.argv) < 2:
259
- print(f"Usage: {sys.argv[0]} <file.rs|dir> [file.rs|dir ...]", file=sys.stderr)
260
- sys.exit(2)
261
-
262
- files = collect_rs_files(sys.argv[1:])
263
- if not files:
264
- print("warning: no .rs files found in the given arguments", file=sys.stderr)
265
- sys.exit(0)
266
-
267
- for f in files:
268
- check_file(f)
269
-
270
- if violations > 0:
271
- print("", file=sys.stderr)
272
- print(
273
- f"rust-programmer: {violations} violation(s). Fix before declaring work done.",
274
- file=sys.stderr,
275
- )
276
- print("", file=sys.stderr)
277
- print("Then run the full toolchain gate:", file=sys.stderr)
278
- print(" cargo +stable fmt --all -- --check", file=sys.stderr)
279
- print(
280
- " cargo +stable clippy --all-targets --all-features -- -D warnings",
281
- file=sys.stderr,
282
- )
283
- print(" cargo nextest run --all-targets --all-features", file=sys.stderr)
284
- print(
285
- " cargo +nightly miri nextest run --all-features # if unsafe touched",
286
- file=sys.stderr,
287
- )
288
- print(" cargo machete", file=sys.stderr)
289
- print(" cargo deny check", file=sys.stderr)
290
- sys.exit(1)
291
-
292
- print(f"rust-programmer: no-excuse rules passed for {len(files)} file(s).")
293
-
294
-
295
- if __name__ == "__main__":
296
- main()
@@ -1,41 +0,0 @@
1
- import { execFile } from "node:child_process"
2
- import { existsSync } from "node:fs"
3
- import { homedir, platform } from "node:os"
4
- import { join } from "node:path"
5
- import { promisify } from "node:util"
6
-
7
- const run = promisify(execFile)
8
-
9
- export const STORE_LISTINGS = {
10
- chrome: "https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi",
11
- edge: "https://microsoftedge.microsoft.com/addons/detail/browserskill/emacgiaaaiojkkpkddmmdfhmokgmnikg",
12
- }
13
-
14
- export const SUPPORTED_PLATFORMS = new Set(["darwin", "linux", "win32"])
15
-
16
- export function localBinCandidates() {
17
- const bin = join(homedir(), ".local", "bin")
18
- return platform() === "win32" ? [join(bin, "bsk.exe"), join(bin, "bsk")] : [join(bin, "bsk")]
19
- }
20
-
21
- export async function resolveCli() {
22
- for (const candidate of localBinCandidates()) {
23
- if (existsSync(candidate)) return candidate
24
- }
25
- try {
26
- await run("bsk", ["--version"], { timeout: 15000, windowsHide: true })
27
- return "bsk"
28
- } catch {
29
- return undefined
30
- }
31
- }
32
-
33
- export async function readStatus(cli) {
34
- const { stdout } = await run(cli, ["status", "--json"], { timeout: 30000, windowsHide: true })
35
- return JSON.parse(stdout)
36
- }
37
-
38
- export function connectedBrowsers(status) {
39
- const browsers = status?.browsers
40
- return Array.isArray(browsers) ? browsers : []
41
- }