agent-browser 0.3.0 → 0.3.2

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 (220) hide show
  1. package/bin/agent-browser +0 -0
  2. package/bin/agent-browser-darwin-arm64 +0 -0
  3. package/bin/agent-browser-darwin-x64 +0 -0
  4. package/bin/agent-browser-linux-arm64 +0 -0
  5. package/bin/agent-browser-linux-x64 +0 -0
  6. package/bin/agent-browser.cmd +35 -0
  7. package/dist/cli-light.d.ts +1 -1
  8. package/dist/cli-light.js +1 -1
  9. package/package.json +10 -4
  10. package/scripts/build-all-platforms.sh +68 -0
  11. package/scripts/copy-native.js +35 -0
  12. package/scripts/postinstall.js +113 -15
  13. package/.prettierrc +0 -7
  14. package/AGENTS.md +0 -26
  15. package/benchmark/benchmark.ts +0 -521
  16. package/benchmark/run.ts +0 -322
  17. package/cli/Cargo.lock +0 -114
  18. package/cli/Cargo.toml +0 -17
  19. package/cli/src/main.rs +0 -332
  20. package/cli/target/.rustc_info.json +0 -1
  21. package/cli/target/CACHEDIR.TAG +0 -3
  22. package/cli/target/release/.cargo-lock +0 -0
  23. package/cli/target/release/.fingerprint/agent-browser-5894536b887e2ce7/bin-agent-browser +0 -1
  24. package/cli/target/release/.fingerprint/agent-browser-5894536b887e2ce7/bin-agent-browser.json +0 -1
  25. package/cli/target/release/.fingerprint/agent-browser-5894536b887e2ce7/dep-bin-agent-browser +0 -0
  26. package/cli/target/release/.fingerprint/agent-browser-5894536b887e2ce7/invoked.timestamp +0 -1
  27. package/cli/target/release/.fingerprint/agent-browser-5894536b887e2ce7/output-bin-agent-browser +0 -2
  28. package/cli/target/release/.fingerprint/itoa-653b9192107a1caa/dep-lib-itoa +0 -0
  29. package/cli/target/release/.fingerprint/itoa-653b9192107a1caa/invoked.timestamp +0 -1
  30. package/cli/target/release/.fingerprint/itoa-653b9192107a1caa/lib-itoa +0 -1
  31. package/cli/target/release/.fingerprint/itoa-653b9192107a1caa/lib-itoa.json +0 -1
  32. package/cli/target/release/.fingerprint/libc-0303d277881093f4/build-script-build-script-build +0 -1
  33. package/cli/target/release/.fingerprint/libc-0303d277881093f4/build-script-build-script-build.json +0 -1
  34. package/cli/target/release/.fingerprint/libc-0303d277881093f4/dep-build-script-build-script-build +0 -0
  35. package/cli/target/release/.fingerprint/libc-0303d277881093f4/invoked.timestamp +0 -1
  36. package/cli/target/release/.fingerprint/libc-b8c0d8e35a1980d3/run-build-script-build-script-build +0 -1
  37. package/cli/target/release/.fingerprint/libc-b8c0d8e35a1980d3/run-build-script-build-script-build.json +0 -1
  38. package/cli/target/release/.fingerprint/libc-d843359d3dd4757b/dep-lib-libc +0 -0
  39. package/cli/target/release/.fingerprint/libc-d843359d3dd4757b/invoked.timestamp +0 -1
  40. package/cli/target/release/.fingerprint/libc-d843359d3dd4757b/lib-libc +0 -1
  41. package/cli/target/release/.fingerprint/libc-d843359d3dd4757b/lib-libc.json +0 -1
  42. package/cli/target/release/.fingerprint/memchr-dcaf8011940d18dd/dep-lib-memchr +0 -0
  43. package/cli/target/release/.fingerprint/memchr-dcaf8011940d18dd/invoked.timestamp +0 -1
  44. package/cli/target/release/.fingerprint/memchr-dcaf8011940d18dd/lib-memchr +0 -1
  45. package/cli/target/release/.fingerprint/memchr-dcaf8011940d18dd/lib-memchr.json +0 -1
  46. package/cli/target/release/.fingerprint/proc-macro2-291b57751730d5b3/build-script-build-script-build +0 -1
  47. package/cli/target/release/.fingerprint/proc-macro2-291b57751730d5b3/build-script-build-script-build.json +0 -1
  48. package/cli/target/release/.fingerprint/proc-macro2-291b57751730d5b3/dep-build-script-build-script-build +0 -0
  49. package/cli/target/release/.fingerprint/proc-macro2-291b57751730d5b3/invoked.timestamp +0 -1
  50. package/cli/target/release/.fingerprint/proc-macro2-a753b344a6b4aa98/dep-lib-proc_macro2 +0 -0
  51. package/cli/target/release/.fingerprint/proc-macro2-a753b344a6b4aa98/invoked.timestamp +0 -1
  52. package/cli/target/release/.fingerprint/proc-macro2-a753b344a6b4aa98/lib-proc_macro2 +0 -1
  53. package/cli/target/release/.fingerprint/proc-macro2-a753b344a6b4aa98/lib-proc_macro2.json +0 -1
  54. package/cli/target/release/.fingerprint/proc-macro2-fc2999f6676f03db/run-build-script-build-script-build +0 -1
  55. package/cli/target/release/.fingerprint/proc-macro2-fc2999f6676f03db/run-build-script-build-script-build.json +0 -1
  56. package/cli/target/release/.fingerprint/quote-352ae41707d371c9/run-build-script-build-script-build +0 -1
  57. package/cli/target/release/.fingerprint/quote-352ae41707d371c9/run-build-script-build-script-build.json +0 -1
  58. package/cli/target/release/.fingerprint/quote-7d13be3cbe4f9de4/build-script-build-script-build +0 -1
  59. package/cli/target/release/.fingerprint/quote-7d13be3cbe4f9de4/build-script-build-script-build.json +0 -1
  60. package/cli/target/release/.fingerprint/quote-7d13be3cbe4f9de4/dep-build-script-build-script-build +0 -0
  61. package/cli/target/release/.fingerprint/quote-7d13be3cbe4f9de4/invoked.timestamp +0 -1
  62. package/cli/target/release/.fingerprint/quote-833e6725e0f7d298/dep-lib-quote +0 -0
  63. package/cli/target/release/.fingerprint/quote-833e6725e0f7d298/invoked.timestamp +0 -1
  64. package/cli/target/release/.fingerprint/quote-833e6725e0f7d298/lib-quote +0 -1
  65. package/cli/target/release/.fingerprint/quote-833e6725e0f7d298/lib-quote.json +0 -1
  66. package/cli/target/release/.fingerprint/serde-b8c046c16de48f41/run-build-script-build-script-build +0 -1
  67. package/cli/target/release/.fingerprint/serde-b8c046c16de48f41/run-build-script-build-script-build.json +0 -1
  68. package/cli/target/release/.fingerprint/serde-d35d32ab52b82a81/build-script-build-script-build +0 -1
  69. package/cli/target/release/.fingerprint/serde-d35d32ab52b82a81/build-script-build-script-build.json +0 -1
  70. package/cli/target/release/.fingerprint/serde-d35d32ab52b82a81/dep-build-script-build-script-build +0 -0
  71. package/cli/target/release/.fingerprint/serde-d35d32ab52b82a81/invoked.timestamp +0 -1
  72. package/cli/target/release/.fingerprint/serde-d6fb44202dad3efd/dep-lib-serde +0 -0
  73. package/cli/target/release/.fingerprint/serde-d6fb44202dad3efd/invoked.timestamp +0 -1
  74. package/cli/target/release/.fingerprint/serde-d6fb44202dad3efd/lib-serde +0 -1
  75. package/cli/target/release/.fingerprint/serde-d6fb44202dad3efd/lib-serde.json +0 -1
  76. package/cli/target/release/.fingerprint/serde_core-0f7ba2581c8c0423/dep-lib-serde_core +0 -0
  77. package/cli/target/release/.fingerprint/serde_core-0f7ba2581c8c0423/invoked.timestamp +0 -1
  78. package/cli/target/release/.fingerprint/serde_core-0f7ba2581c8c0423/lib-serde_core +0 -1
  79. package/cli/target/release/.fingerprint/serde_core-0f7ba2581c8c0423/lib-serde_core.json +0 -1
  80. package/cli/target/release/.fingerprint/serde_core-74db491143173930/run-build-script-build-script-build +0 -1
  81. package/cli/target/release/.fingerprint/serde_core-74db491143173930/run-build-script-build-script-build.json +0 -1
  82. package/cli/target/release/.fingerprint/serde_core-f043ae3f4b601577/build-script-build-script-build +0 -1
  83. package/cli/target/release/.fingerprint/serde_core-f043ae3f4b601577/build-script-build-script-build.json +0 -1
  84. package/cli/target/release/.fingerprint/serde_core-f043ae3f4b601577/dep-build-script-build-script-build +0 -0
  85. package/cli/target/release/.fingerprint/serde_core-f043ae3f4b601577/invoked.timestamp +0 -1
  86. package/cli/target/release/.fingerprint/serde_derive-a5d13e0e658ceae3/dep-lib-serde_derive +0 -0
  87. package/cli/target/release/.fingerprint/serde_derive-a5d13e0e658ceae3/invoked.timestamp +0 -1
  88. package/cli/target/release/.fingerprint/serde_derive-a5d13e0e658ceae3/lib-serde_derive +0 -1
  89. package/cli/target/release/.fingerprint/serde_derive-a5d13e0e658ceae3/lib-serde_derive.json +0 -1
  90. package/cli/target/release/.fingerprint/serde_json-a8467019a959068f/run-build-script-build-script-build +0 -1
  91. package/cli/target/release/.fingerprint/serde_json-a8467019a959068f/run-build-script-build-script-build.json +0 -1
  92. package/cli/target/release/.fingerprint/serde_json-bfa3f43b57842d41/build-script-build-script-build +0 -1
  93. package/cli/target/release/.fingerprint/serde_json-bfa3f43b57842d41/build-script-build-script-build.json +0 -1
  94. package/cli/target/release/.fingerprint/serde_json-bfa3f43b57842d41/dep-build-script-build-script-build +0 -0
  95. package/cli/target/release/.fingerprint/serde_json-bfa3f43b57842d41/invoked.timestamp +0 -1
  96. package/cli/target/release/.fingerprint/serde_json-f61651a65bf0eb31/dep-lib-serde_json +0 -0
  97. package/cli/target/release/.fingerprint/serde_json-f61651a65bf0eb31/invoked.timestamp +0 -1
  98. package/cli/target/release/.fingerprint/serde_json-f61651a65bf0eb31/lib-serde_json +0 -1
  99. package/cli/target/release/.fingerprint/serde_json-f61651a65bf0eb31/lib-serde_json.json +0 -1
  100. package/cli/target/release/.fingerprint/syn-6f9a22f8c7f909b0/dep-lib-syn +0 -0
  101. package/cli/target/release/.fingerprint/syn-6f9a22f8c7f909b0/invoked.timestamp +0 -1
  102. package/cli/target/release/.fingerprint/syn-6f9a22f8c7f909b0/lib-syn +0 -1
  103. package/cli/target/release/.fingerprint/syn-6f9a22f8c7f909b0/lib-syn.json +0 -1
  104. package/cli/target/release/.fingerprint/unicode-ident-60c57228d30a23d0/dep-lib-unicode_ident +0 -0
  105. package/cli/target/release/.fingerprint/unicode-ident-60c57228d30a23d0/invoked.timestamp +0 -1
  106. package/cli/target/release/.fingerprint/unicode-ident-60c57228d30a23d0/lib-unicode_ident +0 -1
  107. package/cli/target/release/.fingerprint/unicode-ident-60c57228d30a23d0/lib-unicode_ident.json +0 -1
  108. package/cli/target/release/.fingerprint/zmij-60b0e0e9d7c08f71/run-build-script-build-script-build +0 -1
  109. package/cli/target/release/.fingerprint/zmij-60b0e0e9d7c08f71/run-build-script-build-script-build.json +0 -1
  110. package/cli/target/release/.fingerprint/zmij-9501bcbd6d8b933c/dep-lib-zmij +0 -0
  111. package/cli/target/release/.fingerprint/zmij-9501bcbd6d8b933c/invoked.timestamp +0 -1
  112. package/cli/target/release/.fingerprint/zmij-9501bcbd6d8b933c/lib-zmij +0 -1
  113. package/cli/target/release/.fingerprint/zmij-9501bcbd6d8b933c/lib-zmij.json +0 -1
  114. package/cli/target/release/.fingerprint/zmij-aa602f885104061e/build-script-build-script-build +0 -1
  115. package/cli/target/release/.fingerprint/zmij-aa602f885104061e/build-script-build-script-build.json +0 -1
  116. package/cli/target/release/.fingerprint/zmij-aa602f885104061e/dep-build-script-build-script-build +0 -0
  117. package/cli/target/release/.fingerprint/zmij-aa602f885104061e/invoked.timestamp +0 -1
  118. package/cli/target/release/agent-browser +0 -0
  119. package/cli/target/release/agent-browser.d +0 -1
  120. package/cli/target/release/build/libc-0303d277881093f4/build-script-build +0 -0
  121. package/cli/target/release/build/libc-0303d277881093f4/build_script_build-0303d277881093f4 +0 -0
  122. package/cli/target/release/build/libc-0303d277881093f4/build_script_build-0303d277881093f4.d +0 -5
  123. package/cli/target/release/build/libc-b8c0d8e35a1980d3/invoked.timestamp +0 -1
  124. package/cli/target/release/build/libc-b8c0d8e35a1980d3/output +0 -25
  125. package/cli/target/release/build/libc-b8c0d8e35a1980d3/root-output +0 -1
  126. package/cli/target/release/build/libc-b8c0d8e35a1980d3/stderr +0 -0
  127. package/cli/target/release/build/proc-macro2-291b57751730d5b3/build-script-build +0 -0
  128. package/cli/target/release/build/proc-macro2-291b57751730d5b3/build_script_build-291b57751730d5b3 +0 -0
  129. package/cli/target/release/build/proc-macro2-291b57751730d5b3/build_script_build-291b57751730d5b3.d +0 -5
  130. package/cli/target/release/build/proc-macro2-fc2999f6676f03db/invoked.timestamp +0 -1
  131. package/cli/target/release/build/proc-macro2-fc2999f6676f03db/output +0 -23
  132. package/cli/target/release/build/proc-macro2-fc2999f6676f03db/root-output +0 -1
  133. package/cli/target/release/build/proc-macro2-fc2999f6676f03db/stderr +0 -0
  134. package/cli/target/release/build/quote-352ae41707d371c9/invoked.timestamp +0 -1
  135. package/cli/target/release/build/quote-352ae41707d371c9/output +0 -2
  136. package/cli/target/release/build/quote-352ae41707d371c9/root-output +0 -1
  137. package/cli/target/release/build/quote-352ae41707d371c9/stderr +0 -0
  138. package/cli/target/release/build/quote-7d13be3cbe4f9de4/build-script-build +0 -0
  139. package/cli/target/release/build/quote-7d13be3cbe4f9de4/build_script_build-7d13be3cbe4f9de4 +0 -0
  140. package/cli/target/release/build/quote-7d13be3cbe4f9de4/build_script_build-7d13be3cbe4f9de4.d +0 -5
  141. package/cli/target/release/build/serde-b8c046c16de48f41/invoked.timestamp +0 -1
  142. package/cli/target/release/build/serde-b8c046c16de48f41/out/private.rs +0 -6
  143. package/cli/target/release/build/serde-b8c046c16de48f41/output +0 -13
  144. package/cli/target/release/build/serde-b8c046c16de48f41/root-output +0 -1
  145. package/cli/target/release/build/serde-b8c046c16de48f41/stderr +0 -0
  146. package/cli/target/release/build/serde-d35d32ab52b82a81/build-script-build +0 -0
  147. package/cli/target/release/build/serde-d35d32ab52b82a81/build_script_build-d35d32ab52b82a81 +0 -0
  148. package/cli/target/release/build/serde-d35d32ab52b82a81/build_script_build-d35d32ab52b82a81.d +0 -5
  149. package/cli/target/release/build/serde_core-74db491143173930/invoked.timestamp +0 -1
  150. package/cli/target/release/build/serde_core-74db491143173930/out/private.rs +0 -5
  151. package/cli/target/release/build/serde_core-74db491143173930/output +0 -11
  152. package/cli/target/release/build/serde_core-74db491143173930/root-output +0 -1
  153. package/cli/target/release/build/serde_core-74db491143173930/stderr +0 -0
  154. package/cli/target/release/build/serde_core-f043ae3f4b601577/build-script-build +0 -0
  155. package/cli/target/release/build/serde_core-f043ae3f4b601577/build_script_build-f043ae3f4b601577 +0 -0
  156. package/cli/target/release/build/serde_core-f043ae3f4b601577/build_script_build-f043ae3f4b601577.d +0 -5
  157. package/cli/target/release/build/serde_json-a8467019a959068f/invoked.timestamp +0 -1
  158. package/cli/target/release/build/serde_json-a8467019a959068f/output +0 -3
  159. package/cli/target/release/build/serde_json-a8467019a959068f/root-output +0 -1
  160. package/cli/target/release/build/serde_json-a8467019a959068f/stderr +0 -0
  161. package/cli/target/release/build/serde_json-bfa3f43b57842d41/build-script-build +0 -0
  162. package/cli/target/release/build/serde_json-bfa3f43b57842d41/build_script_build-bfa3f43b57842d41 +0 -0
  163. package/cli/target/release/build/serde_json-bfa3f43b57842d41/build_script_build-bfa3f43b57842d41.d +0 -5
  164. package/cli/target/release/build/zmij-60b0e0e9d7c08f71/invoked.timestamp +0 -1
  165. package/cli/target/release/build/zmij-60b0e0e9d7c08f71/output +0 -3
  166. package/cli/target/release/build/zmij-60b0e0e9d7c08f71/root-output +0 -1
  167. package/cli/target/release/build/zmij-60b0e0e9d7c08f71/stderr +0 -0
  168. package/cli/target/release/build/zmij-aa602f885104061e/build-script-build +0 -0
  169. package/cli/target/release/build/zmij-aa602f885104061e/build_script_build-aa602f885104061e +0 -0
  170. package/cli/target/release/build/zmij-aa602f885104061e/build_script_build-aa602f885104061e.d +0 -5
  171. package/cli/target/release/deps/agent_browser-5894536b887e2ce7 +0 -0
  172. package/cli/target/release/deps/agent_browser-5894536b887e2ce7.d +0 -5
  173. package/cli/target/release/deps/itoa-653b9192107a1caa.d +0 -8
  174. package/cli/target/release/deps/libc-d843359d3dd4757b.d +0 -45
  175. package/cli/target/release/deps/libitoa-653b9192107a1caa.rlib +0 -0
  176. package/cli/target/release/deps/libitoa-653b9192107a1caa.rmeta +0 -0
  177. package/cli/target/release/deps/liblibc-d843359d3dd4757b.rlib +0 -0
  178. package/cli/target/release/deps/liblibc-d843359d3dd4757b.rmeta +0 -0
  179. package/cli/target/release/deps/libmemchr-dcaf8011940d18dd.rlib +0 -0
  180. package/cli/target/release/deps/libmemchr-dcaf8011940d18dd.rmeta +0 -0
  181. package/cli/target/release/deps/libproc_macro2-a753b344a6b4aa98.rlib +0 -0
  182. package/cli/target/release/deps/libproc_macro2-a753b344a6b4aa98.rmeta +0 -0
  183. package/cli/target/release/deps/libquote-833e6725e0f7d298.rlib +0 -0
  184. package/cli/target/release/deps/libquote-833e6725e0f7d298.rmeta +0 -0
  185. package/cli/target/release/deps/libserde-d6fb44202dad3efd.rlib +0 -0
  186. package/cli/target/release/deps/libserde-d6fb44202dad3efd.rmeta +0 -0
  187. package/cli/target/release/deps/libserde_core-0f7ba2581c8c0423.rlib +0 -0
  188. package/cli/target/release/deps/libserde_core-0f7ba2581c8c0423.rmeta +0 -0
  189. package/cli/target/release/deps/libserde_derive-a5d13e0e658ceae3.dylib +0 -0
  190. package/cli/target/release/deps/libserde_json-f61651a65bf0eb31.rlib +0 -0
  191. package/cli/target/release/deps/libserde_json-f61651a65bf0eb31.rmeta +0 -0
  192. package/cli/target/release/deps/libsyn-6f9a22f8c7f909b0.rlib +0 -0
  193. package/cli/target/release/deps/libsyn-6f9a22f8c7f909b0.rmeta +0 -0
  194. package/cli/target/release/deps/libunicode_ident-60c57228d30a23d0.rlib +0 -0
  195. package/cli/target/release/deps/libunicode_ident-60c57228d30a23d0.rmeta +0 -0
  196. package/cli/target/release/deps/libzmij-9501bcbd6d8b933c.rlib +0 -0
  197. package/cli/target/release/deps/libzmij-9501bcbd6d8b933c.rmeta +0 -0
  198. package/cli/target/release/deps/memchr-dcaf8011940d18dd.d +0 -30
  199. package/cli/target/release/deps/proc_macro2-a753b344a6b4aa98.d +0 -17
  200. package/cli/target/release/deps/quote-833e6725e0f7d298.d +0 -13
  201. package/cli/target/release/deps/serde-d6fb44202dad3efd.d +0 -14
  202. package/cli/target/release/deps/serde_core-0f7ba2581c8c0423.d +0 -27
  203. package/cli/target/release/deps/serde_derive-a5d13e0e658ceae3.d +0 -34
  204. package/cli/target/release/deps/serde_json-f61651a65bf0eb31.d +0 -22
  205. package/cli/target/release/deps/syn-6f9a22f8c7f909b0.d +0 -49
  206. package/cli/target/release/deps/unicode_ident-60c57228d30a23d0.d +0 -8
  207. package/cli/target/release/deps/zmij-9501bcbd6d8b933c.d +0 -8
  208. package/src/actions.ts +0 -1670
  209. package/src/browser.test.ts +0 -157
  210. package/src/browser.ts +0 -686
  211. package/src/cli-light.ts +0 -457
  212. package/src/client.ts +0 -150
  213. package/src/daemon.ts +0 -187
  214. package/src/index.ts +0 -1185
  215. package/src/protocol.test.ts +0 -216
  216. package/src/protocol.ts +0 -852
  217. package/src/snapshot.ts +0 -380
  218. package/src/types.ts +0 -913
  219. package/tsconfig.json +0 -28
  220. package/vitest.config.ts +0 -9
package/src/index.ts DELETED
@@ -1,1185 +0,0 @@
1
- #!/usr/bin/env node
2
- import * as fs from 'fs';
3
- import * as os from 'os';
4
- import * as path from 'path';
5
- import { execSync, spawnSync } from 'child_process';
6
- import { send, setDebug, setSession, getSession } from './client.js';
7
- import type { Response } from './types.js';
8
-
9
- // ============================================================================
10
- // System Dependencies Installation
11
- // ============================================================================
12
-
13
- // Common dependencies needed for Playwright browsers on Linux
14
- const LINUX_DEPS = {
15
- // Shared libraries for Chromium/Firefox/WebKit
16
- apt: [
17
- 'libxcb-shm0',
18
- 'libx11-xcb1',
19
- 'libx11-6',
20
- 'libxcb1',
21
- 'libxext6',
22
- 'libxrandr2',
23
- 'libxcomposite1',
24
- 'libxcursor1',
25
- 'libxdamage1',
26
- 'libxfixes3',
27
- 'libxi6',
28
- 'libgtk-3-0',
29
- 'libpangocairo-1.0-0',
30
- 'libpango-1.0-0',
31
- 'libatk1.0-0',
32
- 'libcairo-gobject2',
33
- 'libcairo2',
34
- 'libgdk-pixbuf-2.0-0',
35
- 'libxrender1',
36
- 'libasound2',
37
- 'libfreetype6',
38
- 'libfontconfig1',
39
- 'libdbus-1-3',
40
- 'libnss3',
41
- 'libnspr4',
42
- 'libatk-bridge2.0-0',
43
- 'libdrm2',
44
- 'libxkbcommon0',
45
- 'libatspi2.0-0',
46
- 'libcups2',
47
- 'libxshmfence1',
48
- 'libgbm1',
49
- ],
50
- dnf: [
51
- 'libxcb',
52
- 'libX11-xcb',
53
- 'libX11',
54
- 'libXext',
55
- 'libXrandr',
56
- 'libXcomposite',
57
- 'libXcursor',
58
- 'libXdamage',
59
- 'libXfixes',
60
- 'libXi',
61
- 'gtk3',
62
- 'pango',
63
- 'atk',
64
- 'cairo-gobject',
65
- 'cairo',
66
- 'gdk-pixbuf2',
67
- 'libXrender',
68
- 'alsa-lib',
69
- 'freetype',
70
- 'fontconfig',
71
- 'dbus-libs',
72
- 'nss',
73
- 'nspr',
74
- 'at-spi2-atk',
75
- 'libdrm',
76
- 'libxkbcommon',
77
- 'at-spi2-core',
78
- 'cups-libs',
79
- 'libxshmfence',
80
- 'mesa-libgbm',
81
- 'libwayland-client',
82
- 'libwayland-server',
83
- ],
84
- yum: [
85
- 'libxcb',
86
- 'libX11-xcb',
87
- 'libX11',
88
- 'libXext',
89
- 'libXrandr',
90
- 'libXcomposite',
91
- 'libXcursor',
92
- 'libXdamage',
93
- 'libXfixes',
94
- 'libXi',
95
- 'gtk3',
96
- 'pango',
97
- 'atk',
98
- 'cairo-gobject',
99
- 'cairo',
100
- 'gdk-pixbuf2',
101
- 'libXrender',
102
- 'alsa-lib',
103
- 'freetype',
104
- 'fontconfig',
105
- 'dbus-libs',
106
- 'nss',
107
- 'nspr',
108
- 'at-spi2-atk',
109
- 'libdrm',
110
- 'libxkbcommon',
111
- 'at-spi2-core',
112
- 'cups-libs',
113
- 'libxshmfence',
114
- 'mesa-libgbm',
115
- ],
116
- };
117
-
118
- function detectPackageManager(): 'apt' | 'dnf' | 'yum' | null {
119
- const managers = ['apt-get', 'dnf', 'yum'] as const;
120
- for (const mgr of managers) {
121
- try {
122
- execSync(`which ${mgr}`, { stdio: 'ignore' });
123
- return mgr === 'apt-get' ? 'apt' : mgr;
124
- } catch {
125
- // Not found, try next
126
- }
127
- }
128
- return null;
129
- }
130
-
131
- function installSystemDeps(): void {
132
- if (os.platform() !== 'linux') {
133
- console.log('System dependency installation is only needed on Linux');
134
- return;
135
- }
136
-
137
- const pkgMgr = detectPackageManager();
138
- if (!pkgMgr) {
139
- throw new Error('No supported package manager found (apt-get, dnf, or yum)');
140
- }
141
-
142
- const deps = LINUX_DEPS[pkgMgr];
143
- if (!deps || deps.length === 0) {
144
- throw new Error(`No dependencies defined for package manager: ${pkgMgr}`);
145
- }
146
-
147
- console.log(`Detected package manager: ${pkgMgr}`);
148
- console.log(`Installing ${deps.length} dependencies...`);
149
-
150
- let cmd: string;
151
- switch (pkgMgr) {
152
- case 'apt':
153
- cmd = `apt-get update && apt-get install -y ${deps.join(' ')}`;
154
- break;
155
- case 'dnf':
156
- cmd = `dnf install -y ${deps.join(' ')}`;
157
- break;
158
- case 'yum':
159
- cmd = `yum install -y ${deps.join(' ')}`;
160
- break;
161
- }
162
-
163
- // Run with sudo if not root
164
- const isRoot = process.getuid?.() === 0;
165
- if (!isRoot) {
166
- cmd = `sudo ${cmd}`;
167
- }
168
-
169
- execSync(cmd, { stdio: 'inherit' });
170
- }
171
-
172
- // ============================================================================
173
- // Utilities
174
- // ============================================================================
175
-
176
- function listSessions(): string[] {
177
- const tmpDir = os.tmpdir();
178
- try {
179
- const files = fs.readdirSync(tmpDir);
180
- const sessions: string[] = [];
181
- for (const file of files) {
182
- const match = file.match(/^agent-browser-(.+)\.pid$/);
183
- if (match) {
184
- const pidFile = path.join(tmpDir, file);
185
- try {
186
- const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
187
- process.kill(pid, 0);
188
- sessions.push(match[1]);
189
- } catch {
190
- /* Process not running */
191
- }
192
- }
193
- }
194
- return sessions;
195
- } catch {
196
- return [];
197
- }
198
- }
199
-
200
- const colors = {
201
- reset: '\x1b[0m',
202
- bold: '\x1b[1m',
203
- dim: '\x1b[2m',
204
- red: '\x1b[31m',
205
- green: '\x1b[32m',
206
- yellow: '\x1b[33m',
207
- cyan: '\x1b[36m',
208
- };
209
-
210
- const c = (color: keyof typeof colors, text: string) => `${colors[color]}${text}${colors.reset}`;
211
-
212
- function genId(): string {
213
- return Math.random().toString(36).slice(2, 10);
214
- }
215
-
216
- function err(msg: string): never {
217
- console.error(c('red', 'Error:'), msg);
218
- process.exit(1);
219
- }
220
-
221
- // ============================================================================
222
- // Help
223
- // ============================================================================
224
-
225
- function printHelp(): void {
226
- console.log(`
227
- ${c('bold', 'agent-browser')} - headless browser automation for AI agents
228
-
229
- ${c('yellow', 'Usage:')} agent-browser <command> [options]
230
-
231
- ${c('yellow', 'Core Commands:')}
232
- ${c('cyan', 'open')} <url> Navigate to URL
233
- ${c('cyan', 'click')} <sel> Click element (or @ref)
234
- ${c('cyan', 'type')} <sel> <text> Type into element
235
- ${c('cyan', 'fill')} <sel> <text> Clear and fill
236
- ${c('cyan', 'press')} <key> Press key (Enter, Tab, Control+a)
237
- ${c('cyan', 'hover')} <sel> Hover element
238
- ${c('cyan', 'select')} <sel> <val> Select dropdown option
239
- ${c('cyan', 'scroll')} <dir> [px] Scroll (up/down/left/right)
240
- ${c('cyan', 'wait')} <sel|ms> Wait for element or time
241
- ${c('cyan', 'screenshot')} [path] Take screenshot
242
- ${c('cyan', 'snapshot')} Accessibility tree with refs (for AI)
243
- ${c('cyan', 'eval')} <js> Run JavaScript
244
- ${c('cyan', 'close')} Close browser
245
-
246
- ${c('yellow', 'Selectors:')} CSS, XPath, text=, or ${c('green', '@ref')} from snapshot
247
- ${c('dim', 'CSS:')} "#id", ".class", "button"
248
- ${c('dim', 'XPath:')} "xpath=//button"
249
- ${c('dim', 'Text:')} "text=Submit"
250
- ${c('dim', 'Ref:')} ${c('green', '@e1')}, ${c('green', '@e2')} (from snapshot output)
251
-
252
- ${c('yellow', 'Get Info:')} agent-browser get <what> [selector]
253
- text, html, value, attr, title, url, count, box
254
-
255
- ${c('yellow', 'Check State:')} agent-browser is <what> <selector>
256
- visible, enabled, checked
257
-
258
- ${c('yellow', 'Find Elements:')} agent-browser find <locator> <action> [value]
259
- role, text, label, placeholder, alt, title, testid, first, last, nth
260
-
261
- ${c('yellow', 'Mouse:')} agent-browser mouse <action> [args]
262
- move <x> <y>, down, up, wheel <dy>
263
-
264
- ${c('yellow', 'Storage:')}
265
- ${c('cyan', 'cookies')} [get|set|clear] Manage cookies
266
- ${c('cyan', 'storage')} <local|session> Manage web storage
267
-
268
- ${c('yellow', 'Browser:')} agent-browser set <setting> [value]
269
- viewport, device, geo, offline, headers, credentials
270
-
271
- ${c('yellow', 'Network:')} agent-browser network <action>
272
- route, unroute, requests
273
-
274
- ${c('yellow', 'Tabs:')}
275
- ${c('cyan', 'tab')} [new|list|close|<n>] Manage tabs
276
-
277
- ${c('yellow', 'Debug:')}
278
- ${c('cyan', 'trace')} start|stop <path> Record trace
279
- ${c('cyan', 'console')} View console logs
280
- ${c('cyan', 'errors')} View page errors
281
-
282
- ${c('yellow', 'Setup:')}
283
- ${c('cyan', 'install')} Install browser binaries
284
- ${c('cyan', 'install')} --with-deps Also install system dependencies (Linux)
285
-
286
- ${c('yellow', 'Options:')}
287
- --session <name> Isolated session (or AGENT_BROWSER_SESSION env)
288
- --json JSON output
289
- --full, -f Full page screenshot
290
- --headed Show browser window (not headless)
291
- --debug Debug output
292
-
293
- ${c('yellow', 'Examples:')}
294
- agent-browser open example.com
295
- agent-browser snapshot # Get tree with refs
296
- agent-browser click @e2 # Click by ref from snapshot
297
- agent-browser fill @e3 "test@example.com" # Fill by ref
298
- agent-browser click "#submit" # CSS selector still works
299
- agent-browser get text @e1 # Get text by ref
300
- agent-browser find role button click --name Submit
301
- `);
302
- }
303
-
304
- // ============================================================================
305
- // Response Printing
306
- // ============================================================================
307
-
308
- function printResponse(response: Response, jsonMode: boolean): void {
309
- if (jsonMode) {
310
- console.log(JSON.stringify(response));
311
- return;
312
- }
313
-
314
- if (!response.success) {
315
- console.error(c('red', '✗ Error:'), response.error);
316
- process.exit(1);
317
- }
318
-
319
- const data = response.data as Record<string, unknown>;
320
-
321
- if (data.url && data.title) {
322
- console.log(c('green', '✓'), c('bold', data.title as string));
323
- console.log(c('dim', ` ${data.url}`));
324
- } else if (data.text !== undefined) {
325
- console.log(data.text ?? c('dim', 'null'));
326
- } else if (data.html !== undefined) {
327
- console.log(data.html);
328
- } else if (data.value !== undefined) {
329
- console.log(data.value ?? c('dim', 'null'));
330
- } else if (data.result !== undefined) {
331
- const result = data.result;
332
- console.log(typeof result === 'object' ? JSON.stringify(result, null, 2) : result);
333
- } else if (data.snapshot) {
334
- console.log(data.snapshot);
335
- } else if (data.visible !== undefined) {
336
- console.log(data.visible ? c('green', 'true') : c('red', 'false'));
337
- } else if (data.enabled !== undefined) {
338
- console.log(data.enabled ? c('green', 'true') : c('red', 'false'));
339
- } else if (data.checked !== undefined) {
340
- console.log(data.checked ? c('green', 'true') : c('red', 'false'));
341
- } else if (data.count !== undefined) {
342
- console.log(data.count);
343
- } else if (data.box) {
344
- const box = data.box as { x: number; y: number; width: number; height: number };
345
- console.log(`x:${box.x} y:${box.y} w:${box.width} h:${box.height}`);
346
- } else if (data.url) {
347
- console.log(data.url);
348
- } else if (data.title) {
349
- console.log(data.title);
350
- } else if (data.base64) {
351
- console.log(c('green', '✓'), 'Screenshot captured');
352
- } else if (data.path) {
353
- console.log(c('green', '✓'), `Saved: ${data.path}`);
354
- } else if (data.cookies) {
355
- const cookies = data.cookies as Array<{ name: string; value: string }>;
356
- if (cookies.length === 0) console.log(c('dim', 'No cookies'));
357
- else cookies.forEach((ck) => console.log(`${c('cyan', ck.name)}: ${ck.value}`));
358
- } else if (data.tabs) {
359
- const tabs = data.tabs as Array<{ index: number; url: string; title: string; active: boolean }>;
360
- tabs.forEach((t) => {
361
- const marker = t.active ? c('green', '→') : ' ';
362
- console.log(`${marker} [${t.index}] ${t.title || c('dim', '(untitled)')}`);
363
- if (t.url) console.log(c('dim', ` ${t.url}`));
364
- });
365
- } else if (data.index !== undefined && data.total !== undefined) {
366
- console.log(c('green', '✓'), `Tab ${data.index} (${data.total} total)`);
367
- } else if (data.messages) {
368
- const msgs = data.messages as Array<{ type: string; text: string }>;
369
- if (msgs.length === 0) console.log(c('dim', 'No messages'));
370
- else
371
- msgs.forEach((m) => {
372
- const col = m.type === 'error' ? 'red' : m.type === 'warning' ? 'yellow' : 'dim';
373
- console.log(`${c(col, `[${m.type}]`)} ${m.text}`);
374
- });
375
- } else if (data.errors) {
376
- const errs = data.errors as Array<{ message: string }>;
377
- if (errs.length === 0) console.log(c('dim', 'No errors'));
378
- else errs.forEach((e) => console.log(c('red', '✗'), e.message));
379
- } else if (data.requests) {
380
- const reqs = data.requests as Array<{ method: string; url: string }>;
381
- if (reqs.length === 0) console.log(c('dim', 'No requests'));
382
- else reqs.forEach((r) => console.log(`${c('cyan', r.method)} ${r.url}`));
383
- } else if (data.moved) {
384
- console.log(c('green', '✓'), `Moved to (${data.x}, ${data.y})`);
385
- } else if (data.body !== undefined && data.status !== undefined) {
386
- // Response body
387
- console.log(c('green', '✓'), `${data.status} ${data.url}`);
388
- console.log(typeof data.body === 'object' ? JSON.stringify(data.body, null, 2) : data.body);
389
- } else if (data.filename) {
390
- // Download
391
- console.log(c('green', '✓'), `Downloaded: ${data.filename}`);
392
- console.log(c('dim', ` Path: ${data.path}`));
393
- } else if (data.inserted) {
394
- console.log(c('green', '✓'), 'Text inserted');
395
- } else if (data.key) {
396
- console.log(c('green', '✓'), `Key ${data.down ? 'down' : 'up'}: ${data.key}`);
397
- } else if (data.note) {
398
- console.log(c('yellow', '⚠'), data.note);
399
- } else if (data.closed === true) {
400
- console.log(c('green', '✓'), 'Browser closed');
401
- } else if (data.launched) {
402
- console.log(c('green', '✓'), 'Browser launched');
403
- } else if (data.state) {
404
- console.log(c('green', '✓'), `Load state: ${data.state}`);
405
- } else if (
406
- Object.keys(data).some((k) =>
407
- [
408
- 'clicked',
409
- 'typed',
410
- 'filled',
411
- 'pressed',
412
- 'hovered',
413
- 'scrolled',
414
- 'selected',
415
- 'waited',
416
- 'checked',
417
- 'unchecked',
418
- 'focused',
419
- 'set',
420
- 'cleared',
421
- 'started',
422
- 'down',
423
- 'up',
424
- ].includes(k)
425
- )
426
- ) {
427
- console.log(c('green', '✓'), 'Done');
428
- } else {
429
- console.log(c('green', '✓'), JSON.stringify(data));
430
- }
431
- }
432
-
433
- // ============================================================================
434
- // Command Handlers
435
- // ============================================================================
436
-
437
- async function handleGet(args: string[], id: string): Promise<Record<string, unknown>> {
438
- const what = args[0];
439
- const selector = args[1];
440
-
441
- switch (what) {
442
- case 'text':
443
- if (!selector) err('Selector required: agent-browser get text <selector>');
444
- return { id, action: 'gettext', selector };
445
- case 'html':
446
- if (!selector) err('Selector required: agent-browser get html <selector>');
447
- return { id, action: 'innerhtml', selector };
448
- case 'value':
449
- if (!selector) err('Selector required: agent-browser get value <selector>');
450
- return { id, action: 'inputvalue', selector };
451
- case 'attr':
452
- if (!selector || !args[2]) err('Usage: agent-browser get attr <selector> <attribute>');
453
- return { id, action: 'getattribute', selector, attribute: args[2] };
454
- case 'title':
455
- return { id, action: 'title' };
456
- case 'url':
457
- return { id, action: 'url' };
458
- case 'count':
459
- if (!selector) err('Selector required: agent-browser get count <selector>');
460
- return { id, action: 'count', selector };
461
- case 'box':
462
- if (!selector) err('Selector required: agent-browser get box <selector>');
463
- return { id, action: 'boundingbox', selector };
464
- default:
465
- err(`Unknown: agent-browser get ${what}. Options: text, html, value, attr, title, url, count, box`);
466
- }
467
- }
468
-
469
- async function handleIs(args: string[], id: string): Promise<Record<string, unknown>> {
470
- const what = args[0];
471
- const selector = args[1];
472
-
473
- if (!selector) err(`Selector required: agent-browser is ${what} <selector>`);
474
-
475
- switch (what) {
476
- case 'visible':
477
- return { id, action: 'isvisible', selector };
478
- case 'enabled':
479
- return { id, action: 'isenabled', selector };
480
- case 'checked':
481
- return { id, action: 'ischecked', selector };
482
- default:
483
- err(`Unknown: agent-browser is ${what}. Options: visible, enabled, checked`);
484
- }
485
- }
486
-
487
- async function handleFind(
488
- args: string[],
489
- id: string,
490
- flags: Flags
491
- ): Promise<Record<string, unknown>> {
492
- const locator = args[0];
493
- const value = args[1];
494
- const subaction = args[2] || 'click';
495
- const fillValue = args[3];
496
-
497
- if (!value) err(`Value required: agent-browser find ${locator} <value> <action>`);
498
-
499
- const exact = flags.exact;
500
- const name = flags.name;
501
-
502
- switch (locator) {
503
- case 'role':
504
- return { id, action: 'getbyrole', role: value, subaction, value: fillValue, name, exact };
505
- case 'text':
506
- return { id, action: 'getbytext', text: value, subaction, exact };
507
- case 'label':
508
- return { id, action: 'getbylabel', label: value, subaction, value: fillValue, exact };
509
- case 'placeholder':
510
- return {
511
- id,
512
- action: 'getbyplaceholder',
513
- placeholder: value,
514
- subaction,
515
- value: fillValue,
516
- exact,
517
- };
518
- case 'alt':
519
- return { id, action: 'getbyalttext', text: value, subaction, exact };
520
- case 'title':
521
- return { id, action: 'getbytitle', text: value, subaction, exact };
522
- case 'testid':
523
- return { id, action: 'getbytestid', testId: value, subaction, value: fillValue };
524
- case 'first':
525
- return { id, action: 'nth', selector: value, index: 0, subaction, value: fillValue };
526
- case 'last':
527
- return { id, action: 'nth', selector: value, index: -1, subaction, value: fillValue };
528
- case 'nth': {
529
- const idx = parseInt(value, 10);
530
- const sel = args[2];
531
- const act = args[3] || 'click';
532
- const val = args[4];
533
- if (isNaN(idx) || !sel) err('Usage: agent-browser find nth <index> <selector> <action>');
534
- return { id, action: 'nth', selector: sel, index: idx, subaction: act, value: val };
535
- }
536
- default:
537
- err(
538
- `Unknown locator: ${locator}. Options: role, text, label, placeholder, alt, title, testid, first, last, nth`
539
- );
540
- }
541
- }
542
-
543
- async function handleMouse(args: string[], id: string): Promise<Record<string, unknown>> {
544
- const action = args[0];
545
-
546
- switch (action) {
547
- case 'move': {
548
- const x = parseInt(args[1], 10);
549
- const y = parseInt(args[2], 10);
550
- if (isNaN(x) || isNaN(y)) err('Usage: agent-browser mouse move <x> <y>');
551
- return { id, action: 'mousemove', x, y };
552
- }
553
- case 'down':
554
- return { id, action: 'mousedown', button: args[1] || 'left' };
555
- case 'up':
556
- return { id, action: 'mouseup', button: args[1] || 'left' };
557
- case 'wheel': {
558
- const dy = parseInt(args[1], 10) || 100;
559
- const dx = parseInt(args[2], 10) || 0;
560
- return { id, action: 'wheel', deltaY: dy, deltaX: dx };
561
- }
562
- default:
563
- err(`Unknown: agent-browser mouse ${action}. Options: move, down, up, wheel`);
564
- }
565
- }
566
-
567
- async function handleSet(args: string[], id: string): Promise<Record<string, unknown>> {
568
- const setting = args[0];
569
-
570
- switch (setting) {
571
- case 'viewport': {
572
- const w = parseInt(args[1], 10);
573
- const h = parseInt(args[2], 10);
574
- if (isNaN(w) || isNaN(h)) err('Usage: agent-browser set viewport <width> <height>');
575
- return { id, action: 'viewport', width: w, height: h };
576
- }
577
- case 'device':
578
- if (!args[1]) err('Usage: agent-browser set device <name>');
579
- return { id, action: 'device', device: args[1] };
580
- case 'geo':
581
- case 'geolocation': {
582
- const lat = parseFloat(args[1]);
583
- const lng = parseFloat(args[2]);
584
- if (isNaN(lat) || isNaN(lng)) err('Usage: agent-browser set geo <lat> <lng>');
585
- return { id, action: 'geolocation', latitude: lat, longitude: lng };
586
- }
587
- case 'offline':
588
- return { id, action: 'offline', offline: args[1] !== 'off' && args[1] !== 'false' };
589
- case 'headers':
590
- if (!args[1]) err('Usage: agent-browser set headers <json>');
591
- try {
592
- return { id, action: 'headers', headers: JSON.parse(args[1]) };
593
- } catch {
594
- err('Invalid JSON for headers');
595
- }
596
- break;
597
- case 'credentials':
598
- case 'auth':
599
- if (!args[1] || !args[2]) err('Usage: agent-browser set credentials <user> <pass>');
600
- return { id, action: 'credentials', username: args[1], password: args[2] };
601
- case 'media': {
602
- const colorScheme = args.includes('dark')
603
- ? 'dark'
604
- : args.includes('light')
605
- ? 'light'
606
- : undefined;
607
- const media = args.includes('print')
608
- ? 'print'
609
- : args.includes('screen')
610
- ? 'screen'
611
- : undefined;
612
- return { id, action: 'emulatemedia', colorScheme, media };
613
- }
614
- default:
615
- err(
616
- `Unknown: agent-browser set ${setting}. Options: viewport, device, geo, offline, headers, credentials, media`
617
- );
618
- }
619
- return {};
620
- }
621
-
622
- async function handleNetwork(
623
- args: string[],
624
- id: string,
625
- allArgs: string[]
626
- ): Promise<Record<string, unknown>> {
627
- const action = args[0];
628
-
629
- switch (action) {
630
- case 'route': {
631
- const url = args[1];
632
- if (!url) err('Usage: agent-browser network route <url> [--abort|--body <json>]');
633
- const abort = allArgs.includes('--abort');
634
- const bodyIdx = allArgs.indexOf('--body');
635
- const body = bodyIdx !== -1 ? allArgs[bodyIdx + 1] : undefined;
636
- return {
637
- id,
638
- action: 'route',
639
- url,
640
- abort,
641
- response: body ? { body, contentType: 'application/json' } : undefined,
642
- };
643
- }
644
- case 'unroute':
645
- return { id, action: 'unroute', url: args[1] };
646
- case 'requests': {
647
- const clear = allArgs.includes('--clear');
648
- const filterIdx = allArgs.indexOf('--filter');
649
- const filter = filterIdx !== -1 ? allArgs[filterIdx + 1] : undefined;
650
- return { id, action: 'requests', clear, filter };
651
- }
652
- default:
653
- err(`Unknown: agent-browser network ${action}. Options: route, unroute, requests`);
654
- }
655
- return {};
656
- }
657
-
658
- async function handleStorage(args: string[], id: string): Promise<Record<string, unknown>> {
659
- const type = args[0] as 'local' | 'session';
660
- const sub = args[1];
661
-
662
- if (type !== 'local' && type !== 'session') {
663
- err('Usage: agent-browser storage <local|session> [get|set|clear] [key] [value]');
664
- }
665
-
666
- if (sub === 'set') {
667
- if (!args[2] || !args[3]) err(`Usage: agent-browser storage ${type} set <key> <value>`);
668
- return { id, action: 'storage_set', type, key: args[2], value: args[3] };
669
- } else if (sub === 'clear') {
670
- return { id, action: 'storage_clear', type };
671
- } else {
672
- // get (default)
673
- return { id, action: 'storage_get', type, key: sub };
674
- }
675
- }
676
-
677
- async function handleCookies(args: string[], id: string): Promise<Record<string, unknown>> {
678
- const sub = args[0];
679
-
680
- if (sub === 'set') {
681
- if (!args[1]) err('Usage: agent-browser cookies set <json>');
682
- try {
683
- return { id, action: 'cookies_set', cookies: JSON.parse(args[1]) };
684
- } catch {
685
- err('Invalid JSON for cookies');
686
- }
687
- } else if (sub === 'clear') {
688
- return { id, action: 'cookies_clear' };
689
- } else {
690
- return { id, action: 'cookies_get' };
691
- }
692
- return {};
693
- }
694
-
695
- async function handleTab(args: string[], id: string): Promise<Record<string, unknown>> {
696
- const sub = args[0];
697
-
698
- if (sub === 'new') {
699
- return { id, action: 'tab_new' };
700
- } else if (sub === 'list' || sub === 'ls' || !sub) {
701
- return { id, action: 'tab_list' };
702
- } else if (sub === 'close') {
703
- const idx = args[1] !== undefined ? parseInt(args[1], 10) : undefined;
704
- return { id, action: 'tab_close', index: idx };
705
- } else {
706
- const idx = parseInt(sub, 10);
707
- if (isNaN(idx)) err(`Unknown: agent-browser tab ${sub}. Options: new, list, close, <index>`);
708
- return { id, action: 'tab_switch', index: idx };
709
- }
710
- }
711
-
712
- async function handleTrace(args: string[], id: string): Promise<Record<string, unknown>> {
713
- const sub = args[0];
714
-
715
- if (sub === 'start') {
716
- return { id, action: 'trace_start', screenshots: true, snapshots: true };
717
- } else if (sub === 'stop') {
718
- if (!args[1]) err('Usage: agent-browser trace stop <path>');
719
- return { id, action: 'trace_stop', path: args[1] };
720
- } else {
721
- err('Usage: agent-browser trace start|stop');
722
- }
723
- return {};
724
- }
725
-
726
- async function handleState(args: string[], id: string): Promise<Record<string, unknown>> {
727
- const sub = args[0];
728
- const path = args[1];
729
-
730
- if (sub === 'save') {
731
- if (!path) err('Usage: agent-browser state save <path>');
732
- return { id, action: 'state_save', path };
733
- } else if (sub === 'load') {
734
- if (!path) err('Usage: agent-browser state load <path>');
735
- return { id, action: 'state_load', path };
736
- } else {
737
- err('Usage: agent-browser state save|load <path>');
738
- }
739
- return {};
740
- }
741
-
742
- // ============================================================================
743
- // Flags Parser
744
- // ============================================================================
745
-
746
- interface Flags {
747
- json: boolean;
748
- full: boolean;
749
- text: boolean;
750
- debug: boolean;
751
- headed: boolean;
752
- session: string;
753
- selector?: string;
754
- name?: string;
755
- exact: boolean;
756
- url?: string;
757
- load?: string;
758
- fn?: string;
759
- }
760
-
761
- function parseFlags(args: string[]): { flags: Flags; cleanArgs: string[] } {
762
- const flags: Flags = {
763
- json: false,
764
- full: false,
765
- text: false,
766
- debug: false,
767
- headed: false,
768
- session: process.env.AGENT_BROWSER_SESSION || 'default',
769
- exact: false,
770
- };
771
-
772
- const cleanArgs: string[] = [];
773
- let i = 0;
774
-
775
- while (i < args.length) {
776
- const arg = args[i];
777
-
778
- if (arg === '--json') {
779
- flags.json = true;
780
- } else if (arg === '--full' || arg === '-f') {
781
- flags.full = true;
782
- } else if (arg === '--text' || arg === '-t') {
783
- flags.text = true;
784
- } else if (arg === '--debug') {
785
- flags.debug = true;
786
- } else if (arg === '--headed' || arg === '--head') {
787
- flags.headed = true;
788
- } else if (arg === '--exact') {
789
- flags.exact = true;
790
- } else if (arg === '--session' && args[i + 1]) {
791
- flags.session = args[++i];
792
- } else if ((arg === '--selector' || arg === '-s') && args[i + 1]) {
793
- flags.selector = args[++i];
794
- } else if ((arg === '--name' || arg === '-n') && args[i + 1]) {
795
- flags.name = args[++i];
796
- } else if (arg === '--url' && args[i + 1]) {
797
- flags.url = args[++i];
798
- } else if (arg === '--load' && args[i + 1]) {
799
- flags.load = args[++i];
800
- } else if ((arg === '--fn' || arg === '--function') && args[i + 1]) {
801
- flags.fn = args[++i];
802
- } else if (!arg.startsWith('-')) {
803
- cleanArgs.push(arg);
804
- }
805
- i++;
806
- }
807
-
808
- return { flags, cleanArgs };
809
- }
810
-
811
- // ============================================================================
812
- // Main
813
- // ============================================================================
814
-
815
- async function main(): Promise<void> {
816
- const rawArgs = process.argv.slice(2);
817
- const { flags, cleanArgs } = parseFlags(rawArgs);
818
-
819
- if (flags.debug) setDebug(true);
820
- setSession(flags.session);
821
-
822
- if (cleanArgs.length === 0 || rawArgs.includes('--help') || rawArgs.includes('-h')) {
823
- printHelp();
824
- process.exit(0);
825
- }
826
-
827
- const command = cleanArgs[0];
828
- const args = cleanArgs.slice(1);
829
- const id = genId();
830
-
831
- let cmd: Record<string, unknown>;
832
-
833
- switch (command) {
834
- // === Core Commands ===
835
- case 'open':
836
- case 'goto':
837
- case 'navigate': {
838
- if (!args[0]) err('URL required');
839
- const url = args[0].startsWith('http') ? args[0] : `https://${args[0]}`;
840
- // If --headed, launch with headless=false first
841
- if (flags.headed) {
842
- await send({ id: genId(), action: 'launch', headless: false });
843
- }
844
- cmd = { id, action: 'navigate', url };
845
- break;
846
- }
847
-
848
- case 'click':
849
- if (!args[0]) err('Selector required');
850
- cmd = { id, action: 'click', selector: args[0] };
851
- break;
852
-
853
- case 'dblclick':
854
- if (!args[0]) err('Selector required');
855
- cmd = { id, action: 'dblclick', selector: args[0] };
856
- break;
857
-
858
- case 'type':
859
- if (!args[0] || !args[1]) err('Usage: agent-browser type <selector> <text>');
860
- cmd = { id, action: 'type', selector: args[0], text: args.slice(1).join(' ') };
861
- break;
862
-
863
- case 'fill':
864
- if (!args[0] || !args[1]) err('Usage: agent-browser fill <selector> <text>');
865
- cmd = { id, action: 'fill', selector: args[0], value: args.slice(1).join(' ') };
866
- break;
867
-
868
- case 'press':
869
- case 'key':
870
- if (!args[0]) err('Key required');
871
- cmd = { id, action: 'press', key: args[0] };
872
- break;
873
-
874
- case 'keydown':
875
- if (!args[0]) err('Key required');
876
- cmd = { id, action: 'keydown', key: args[0] };
877
- break;
878
-
879
- case 'keyup':
880
- if (!args[0]) err('Key required');
881
- cmd = { id, action: 'keyup', key: args[0] };
882
- break;
883
-
884
- case 'hover':
885
- if (!args[0]) err('Selector required');
886
- cmd = { id, action: 'hover', selector: args[0] };
887
- break;
888
-
889
- case 'focus':
890
- if (!args[0]) err('Selector required');
891
- cmd = { id, action: 'focus', selector: args[0] };
892
- break;
893
-
894
- case 'check':
895
- if (!args[0]) err('Selector required');
896
- cmd = { id, action: 'check', selector: args[0] };
897
- break;
898
-
899
- case 'uncheck':
900
- if (!args[0]) err('Selector required');
901
- cmd = { id, action: 'uncheck', selector: args[0] };
902
- break;
903
-
904
- case 'select':
905
- if (!args[0] || !args[1]) err('Usage: agent-browser select <selector> <value>');
906
- cmd = { id, action: 'select', selector: args[0], value: args[1] };
907
- break;
908
-
909
- case 'drag':
910
- if (!args[0] || !args[1]) err('Usage: agent-browser drag <source> <target>');
911
- cmd = { id, action: 'drag', source: args[0], target: args[1] };
912
- break;
913
-
914
- case 'upload':
915
- if (!args[0] || !args[1]) err('Usage: agent-browser upload <selector> <files...>');
916
- cmd = { id, action: 'upload', selector: args[0], files: args.slice(1) };
917
- break;
918
-
919
- case 'scroll': {
920
- const dir = args[0] || 'down';
921
- const amount = parseInt(args[1], 10) || 300;
922
- cmd = { id, action: 'scroll', direction: dir, amount, selector: flags.selector };
923
- break;
924
- }
925
-
926
- case 'wait': {
927
- const target = args[0];
928
- // Check for flags
929
- if (flags.fn) {
930
- cmd = { id, action: 'waitforfunction', expression: flags.fn };
931
- } else if (flags.url) {
932
- cmd = { id, action: 'waitforurl', url: flags.url };
933
- } else if (flags.load) {
934
- cmd = { id, action: 'waitforloadstate', state: flags.load };
935
- } else if (flags.text) {
936
- if (!target) err('Text required with --text flag');
937
- cmd = { id, action: 'wait', text: target };
938
- } else if (target && /^\d+$/.test(target)) {
939
- cmd = { id, action: 'wait', timeout: parseInt(target, 10) };
940
- } else if (target) {
941
- cmd = { id, action: 'wait', selector: target };
942
- } else {
943
- err('Usage: agent-browser wait <selector|ms|--text|--url|--load|--fn>');
944
- }
945
- break;
946
- }
947
-
948
- case 'screenshot': {
949
- const path = args[0];
950
- cmd = { id, action: 'screenshot', path, fullPage: flags.full, selector: flags.selector };
951
- break;
952
- }
953
-
954
- case 'pdf':
955
- if (!args[0]) err('Path required');
956
- cmd = { id, action: 'pdf', path: args[0] };
957
- break;
958
-
959
- case 'snapshot':
960
- cmd = { id, action: 'snapshot' };
961
- break;
962
-
963
- case 'eval':
964
- if (!args[0]) err('Script required');
965
- cmd = { id, action: 'evaluate', script: args.join(' ') };
966
- break;
967
-
968
- case 'close':
969
- case 'quit':
970
- case 'exit':
971
- cmd = { id, action: 'close' };
972
- break;
973
-
974
- // === Navigation ===
975
- case 'back':
976
- cmd = { id, action: 'back' };
977
- break;
978
-
979
- case 'forward':
980
- cmd = { id, action: 'forward' };
981
- break;
982
-
983
- case 'reload':
984
- cmd = { id, action: 'reload' };
985
- break;
986
-
987
- // === Grouped Commands ===
988
- case 'get':
989
- cmd = await handleGet(args, id);
990
- break;
991
-
992
- case 'is':
993
- cmd = await handleIs(args, id);
994
- break;
995
-
996
- case 'find':
997
- cmd = await handleFind(args, id, flags);
998
- break;
999
-
1000
- case 'mouse':
1001
- cmd = await handleMouse(args, id);
1002
- break;
1003
-
1004
- case 'set':
1005
- cmd = await handleSet(args, id);
1006
- break;
1007
-
1008
- case 'network':
1009
- cmd = await handleNetwork(args, id, rawArgs);
1010
- break;
1011
-
1012
- case 'storage':
1013
- cmd = await handleStorage(args, id);
1014
- break;
1015
-
1016
- case 'cookies':
1017
- cmd = await handleCookies(args, id);
1018
- break;
1019
-
1020
- case 'tab':
1021
- cmd = await handleTab(args, id);
1022
- break;
1023
-
1024
- case 'window':
1025
- if (args[0] === 'new') {
1026
- cmd = { id, action: 'window_new' };
1027
- } else {
1028
- err('Usage: agent-browser window new');
1029
- }
1030
- break;
1031
-
1032
- case 'frame':
1033
- if (!args[0]) err('Selector required');
1034
- if (args[0] === 'main') {
1035
- cmd = { id, action: 'mainframe' };
1036
- } else {
1037
- cmd = { id, action: 'frame', selector: args[0] };
1038
- }
1039
- break;
1040
-
1041
- case 'dialog':
1042
- if (args[0] === 'accept') {
1043
- cmd = { id, action: 'dialog', response: 'accept', promptText: args[1] };
1044
- } else if (args[0] === 'dismiss') {
1045
- cmd = { id, action: 'dialog', response: 'dismiss' };
1046
- } else {
1047
- err('Usage: agent-browser dialog accept|dismiss');
1048
- }
1049
- break;
1050
-
1051
- case 'trace':
1052
- cmd = await handleTrace(args, id);
1053
- break;
1054
-
1055
- case 'state':
1056
- cmd = await handleState(args, id);
1057
- break;
1058
-
1059
- case 'console':
1060
- cmd = { id, action: 'console', clear: rawArgs.includes('--clear') };
1061
- break;
1062
-
1063
- case 'errors':
1064
- cmd = { id, action: 'errors', clear: rawArgs.includes('--clear') };
1065
- break;
1066
-
1067
- case 'highlight':
1068
- if (!args[0]) err('Selector required');
1069
- cmd = { id, action: 'highlight', selector: args[0] };
1070
- break;
1071
-
1072
- case 'scrollintoview':
1073
- case 'scrollinto':
1074
- if (!args[0]) err('Selector required');
1075
- cmd = { id, action: 'scrollintoview', selector: args[0] };
1076
- break;
1077
-
1078
- case 'initscript':
1079
- if (!args[0]) err('Script required');
1080
- cmd = { id, action: 'addinitscript', script: args.join(' ') };
1081
- break;
1082
-
1083
- case 'inserttext':
1084
- case 'insert':
1085
- if (!args[0]) err('Text required');
1086
- cmd = { id, action: 'inserttext', text: args.join(' ') };
1087
- break;
1088
-
1089
- case 'multiselect':
1090
- if (!args[0] || args.length < 2)
1091
- err('Usage: agent-browser multiselect <selector> <value1> [value2...]');
1092
- cmd = { id, action: 'multiselect', selector: args[0], values: args.slice(1) };
1093
- break;
1094
-
1095
- case 'download':
1096
- cmd = { id, action: 'waitfordownload', path: args[0] };
1097
- break;
1098
-
1099
- case 'response':
1100
- if (!args[0]) err('URL pattern required');
1101
- cmd = { id, action: 'responsebody', url: args[0] };
1102
- break;
1103
-
1104
- case 'session':
1105
- if (args[0] === 'list' || args[0] === 'ls') {
1106
- const sessions = listSessions();
1107
- const current = getSession();
1108
- if (sessions.length === 0) {
1109
- console.log(c('dim', 'No active sessions'));
1110
- } else {
1111
- sessions.forEach((s) => {
1112
- const marker = s === current ? c('green', '→') : ' ';
1113
- console.log(`${marker} ${c('cyan', s)}`);
1114
- });
1115
- }
1116
- process.exit(0);
1117
- } else {
1118
- console.log(c('cyan', getSession()));
1119
- process.exit(0);
1120
- }
1121
-
1122
- case 'install': {
1123
- const withDeps = rawArgs.includes('--with-deps') || rawArgs.includes('-d');
1124
-
1125
- // Install system dependencies first if requested
1126
- if (withDeps) {
1127
- console.log(c('cyan', 'Installing system dependencies...'));
1128
- try {
1129
- installSystemDeps();
1130
- console.log(c('green', '✓'), 'System dependencies installed');
1131
- } catch (error) {
1132
- const msg = error instanceof Error ? error.message : String(error);
1133
- console.error(c('red', '✗'), 'Failed to install system dependencies:', msg);
1134
- process.exit(1);
1135
- }
1136
- }
1137
-
1138
- // Install browsers
1139
- console.log(c('cyan', 'Installing Playwright browsers...'));
1140
- try {
1141
- execSync('npx playwright install', { stdio: 'inherit' });
1142
- console.log(c('green', '✓'), 'Browsers installed successfully');
1143
- process.exit(0);
1144
- } catch (error) {
1145
- console.error(c('red', '✗'), 'Failed to install browsers');
1146
- process.exit(1);
1147
- }
1148
- }
1149
-
1150
- // === Legacy aliases for backwards compatibility ===
1151
- case 'url':
1152
- cmd = { id, action: 'url' };
1153
- break;
1154
- case 'title':
1155
- cmd = { id, action: 'title' };
1156
- break;
1157
- case 'gettext':
1158
- cmd = { id, action: 'gettext', selector: args[0] };
1159
- break;
1160
- case 'extract':
1161
- cmd = { id, action: 'content', selector: args[0] };
1162
- break;
1163
-
1164
- default:
1165
- console.error(c('red', 'Unknown command:'), command);
1166
- console.error(c('dim', 'Run: agent-browser --help'));
1167
- process.exit(1);
1168
- }
1169
-
1170
- try {
1171
- const response = await send(cmd);
1172
- printResponse(response, flags.json);
1173
- process.exit(0);
1174
- } catch (error) {
1175
- const message = error instanceof Error ? error.message : String(error);
1176
- if (flags.json) {
1177
- console.log(JSON.stringify({ id, success: false, error: message }));
1178
- } else {
1179
- console.error(c('red', '✗ Error:'), message);
1180
- }
1181
- process.exit(1);
1182
- }
1183
- }
1184
-
1185
- main();