agent-browser 0.1.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/AGENTS.md +26 -0
  2. package/README.md +68 -11
  3. package/benchmark/benchmark.ts +521 -0
  4. package/benchmark/run.ts +322 -0
  5. package/bin/agent-browser +0 -0
  6. package/cli/Cargo.lock +114 -0
  7. package/cli/Cargo.toml +17 -0
  8. package/cli/src/main.rs +332 -0
  9. package/cli/target/.rustc_info.json +1 -0
  10. package/cli/target/CACHEDIR.TAG +3 -0
  11. package/cli/target/release/.cargo-lock +0 -0
  12. package/cli/target/release/.fingerprint/agent-browser-5894536b887e2ce7/bin-agent-browser +1 -0
  13. package/cli/target/release/.fingerprint/agent-browser-5894536b887e2ce7/bin-agent-browser.json +1 -0
  14. package/cli/target/release/.fingerprint/agent-browser-5894536b887e2ce7/dep-bin-agent-browser +0 -0
  15. package/cli/target/release/.fingerprint/agent-browser-5894536b887e2ce7/invoked.timestamp +1 -0
  16. package/cli/target/release/.fingerprint/agent-browser-5894536b887e2ce7/output-bin-agent-browser +2 -0
  17. package/cli/target/release/.fingerprint/itoa-653b9192107a1caa/dep-lib-itoa +0 -0
  18. package/cli/target/release/.fingerprint/itoa-653b9192107a1caa/invoked.timestamp +1 -0
  19. package/cli/target/release/.fingerprint/itoa-653b9192107a1caa/lib-itoa +1 -0
  20. package/cli/target/release/.fingerprint/itoa-653b9192107a1caa/lib-itoa.json +1 -0
  21. package/cli/target/release/.fingerprint/libc-0303d277881093f4/build-script-build-script-build +1 -0
  22. package/cli/target/release/.fingerprint/libc-0303d277881093f4/build-script-build-script-build.json +1 -0
  23. package/cli/target/release/.fingerprint/libc-0303d277881093f4/dep-build-script-build-script-build +0 -0
  24. package/cli/target/release/.fingerprint/libc-0303d277881093f4/invoked.timestamp +1 -0
  25. package/cli/target/release/.fingerprint/libc-b8c0d8e35a1980d3/run-build-script-build-script-build +1 -0
  26. package/cli/target/release/.fingerprint/libc-b8c0d8e35a1980d3/run-build-script-build-script-build.json +1 -0
  27. package/cli/target/release/.fingerprint/libc-d843359d3dd4757b/dep-lib-libc +0 -0
  28. package/cli/target/release/.fingerprint/libc-d843359d3dd4757b/invoked.timestamp +1 -0
  29. package/cli/target/release/.fingerprint/libc-d843359d3dd4757b/lib-libc +1 -0
  30. package/cli/target/release/.fingerprint/libc-d843359d3dd4757b/lib-libc.json +1 -0
  31. package/cli/target/release/.fingerprint/memchr-dcaf8011940d18dd/dep-lib-memchr +0 -0
  32. package/cli/target/release/.fingerprint/memchr-dcaf8011940d18dd/invoked.timestamp +1 -0
  33. package/cli/target/release/.fingerprint/memchr-dcaf8011940d18dd/lib-memchr +1 -0
  34. package/cli/target/release/.fingerprint/memchr-dcaf8011940d18dd/lib-memchr.json +1 -0
  35. package/cli/target/release/.fingerprint/proc-macro2-291b57751730d5b3/build-script-build-script-build +1 -0
  36. package/cli/target/release/.fingerprint/proc-macro2-291b57751730d5b3/build-script-build-script-build.json +1 -0
  37. package/cli/target/release/.fingerprint/proc-macro2-291b57751730d5b3/dep-build-script-build-script-build +0 -0
  38. package/cli/target/release/.fingerprint/proc-macro2-291b57751730d5b3/invoked.timestamp +1 -0
  39. package/cli/target/release/.fingerprint/proc-macro2-a753b344a6b4aa98/dep-lib-proc_macro2 +0 -0
  40. package/cli/target/release/.fingerprint/proc-macro2-a753b344a6b4aa98/invoked.timestamp +1 -0
  41. package/cli/target/release/.fingerprint/proc-macro2-a753b344a6b4aa98/lib-proc_macro2 +1 -0
  42. package/cli/target/release/.fingerprint/proc-macro2-a753b344a6b4aa98/lib-proc_macro2.json +1 -0
  43. package/cli/target/release/.fingerprint/proc-macro2-fc2999f6676f03db/run-build-script-build-script-build +1 -0
  44. package/cli/target/release/.fingerprint/proc-macro2-fc2999f6676f03db/run-build-script-build-script-build.json +1 -0
  45. package/cli/target/release/.fingerprint/quote-352ae41707d371c9/run-build-script-build-script-build +1 -0
  46. package/cli/target/release/.fingerprint/quote-352ae41707d371c9/run-build-script-build-script-build.json +1 -0
  47. package/cli/target/release/.fingerprint/quote-7d13be3cbe4f9de4/build-script-build-script-build +1 -0
  48. package/cli/target/release/.fingerprint/quote-7d13be3cbe4f9de4/build-script-build-script-build.json +1 -0
  49. package/cli/target/release/.fingerprint/quote-7d13be3cbe4f9de4/dep-build-script-build-script-build +0 -0
  50. package/cli/target/release/.fingerprint/quote-7d13be3cbe4f9de4/invoked.timestamp +1 -0
  51. package/cli/target/release/.fingerprint/quote-833e6725e0f7d298/dep-lib-quote +0 -0
  52. package/cli/target/release/.fingerprint/quote-833e6725e0f7d298/invoked.timestamp +1 -0
  53. package/cli/target/release/.fingerprint/quote-833e6725e0f7d298/lib-quote +1 -0
  54. package/cli/target/release/.fingerprint/quote-833e6725e0f7d298/lib-quote.json +1 -0
  55. package/cli/target/release/.fingerprint/serde-b8c046c16de48f41/run-build-script-build-script-build +1 -0
  56. package/cli/target/release/.fingerprint/serde-b8c046c16de48f41/run-build-script-build-script-build.json +1 -0
  57. package/cli/target/release/.fingerprint/serde-d35d32ab52b82a81/build-script-build-script-build +1 -0
  58. package/cli/target/release/.fingerprint/serde-d35d32ab52b82a81/build-script-build-script-build.json +1 -0
  59. package/cli/target/release/.fingerprint/serde-d35d32ab52b82a81/dep-build-script-build-script-build +0 -0
  60. package/cli/target/release/.fingerprint/serde-d35d32ab52b82a81/invoked.timestamp +1 -0
  61. package/cli/target/release/.fingerprint/serde-d6fb44202dad3efd/dep-lib-serde +0 -0
  62. package/cli/target/release/.fingerprint/serde-d6fb44202dad3efd/invoked.timestamp +1 -0
  63. package/cli/target/release/.fingerprint/serde-d6fb44202dad3efd/lib-serde +1 -0
  64. package/cli/target/release/.fingerprint/serde-d6fb44202dad3efd/lib-serde.json +1 -0
  65. package/cli/target/release/.fingerprint/serde_core-0f7ba2581c8c0423/dep-lib-serde_core +0 -0
  66. package/cli/target/release/.fingerprint/serde_core-0f7ba2581c8c0423/invoked.timestamp +1 -0
  67. package/cli/target/release/.fingerprint/serde_core-0f7ba2581c8c0423/lib-serde_core +1 -0
  68. package/cli/target/release/.fingerprint/serde_core-0f7ba2581c8c0423/lib-serde_core.json +1 -0
  69. package/cli/target/release/.fingerprint/serde_core-74db491143173930/run-build-script-build-script-build +1 -0
  70. package/cli/target/release/.fingerprint/serde_core-74db491143173930/run-build-script-build-script-build.json +1 -0
  71. package/cli/target/release/.fingerprint/serde_core-f043ae3f4b601577/build-script-build-script-build +1 -0
  72. package/cli/target/release/.fingerprint/serde_core-f043ae3f4b601577/build-script-build-script-build.json +1 -0
  73. package/cli/target/release/.fingerprint/serde_core-f043ae3f4b601577/dep-build-script-build-script-build +0 -0
  74. package/cli/target/release/.fingerprint/serde_core-f043ae3f4b601577/invoked.timestamp +1 -0
  75. package/cli/target/release/.fingerprint/serde_derive-a5d13e0e658ceae3/dep-lib-serde_derive +0 -0
  76. package/cli/target/release/.fingerprint/serde_derive-a5d13e0e658ceae3/invoked.timestamp +1 -0
  77. package/cli/target/release/.fingerprint/serde_derive-a5d13e0e658ceae3/lib-serde_derive +1 -0
  78. package/cli/target/release/.fingerprint/serde_derive-a5d13e0e658ceae3/lib-serde_derive.json +1 -0
  79. package/cli/target/release/.fingerprint/serde_json-a8467019a959068f/run-build-script-build-script-build +1 -0
  80. package/cli/target/release/.fingerprint/serde_json-a8467019a959068f/run-build-script-build-script-build.json +1 -0
  81. package/cli/target/release/.fingerprint/serde_json-bfa3f43b57842d41/build-script-build-script-build +1 -0
  82. package/cli/target/release/.fingerprint/serde_json-bfa3f43b57842d41/build-script-build-script-build.json +1 -0
  83. package/cli/target/release/.fingerprint/serde_json-bfa3f43b57842d41/dep-build-script-build-script-build +0 -0
  84. package/cli/target/release/.fingerprint/serde_json-bfa3f43b57842d41/invoked.timestamp +1 -0
  85. package/cli/target/release/.fingerprint/serde_json-f61651a65bf0eb31/dep-lib-serde_json +0 -0
  86. package/cli/target/release/.fingerprint/serde_json-f61651a65bf0eb31/invoked.timestamp +1 -0
  87. package/cli/target/release/.fingerprint/serde_json-f61651a65bf0eb31/lib-serde_json +1 -0
  88. package/cli/target/release/.fingerprint/serde_json-f61651a65bf0eb31/lib-serde_json.json +1 -0
  89. package/cli/target/release/.fingerprint/syn-6f9a22f8c7f909b0/dep-lib-syn +0 -0
  90. package/cli/target/release/.fingerprint/syn-6f9a22f8c7f909b0/invoked.timestamp +1 -0
  91. package/cli/target/release/.fingerprint/syn-6f9a22f8c7f909b0/lib-syn +1 -0
  92. package/cli/target/release/.fingerprint/syn-6f9a22f8c7f909b0/lib-syn.json +1 -0
  93. package/cli/target/release/.fingerprint/unicode-ident-60c57228d30a23d0/dep-lib-unicode_ident +0 -0
  94. package/cli/target/release/.fingerprint/unicode-ident-60c57228d30a23d0/invoked.timestamp +1 -0
  95. package/cli/target/release/.fingerprint/unicode-ident-60c57228d30a23d0/lib-unicode_ident +1 -0
  96. package/cli/target/release/.fingerprint/unicode-ident-60c57228d30a23d0/lib-unicode_ident.json +1 -0
  97. package/cli/target/release/.fingerprint/zmij-60b0e0e9d7c08f71/run-build-script-build-script-build +1 -0
  98. package/cli/target/release/.fingerprint/zmij-60b0e0e9d7c08f71/run-build-script-build-script-build.json +1 -0
  99. package/cli/target/release/.fingerprint/zmij-9501bcbd6d8b933c/dep-lib-zmij +0 -0
  100. package/cli/target/release/.fingerprint/zmij-9501bcbd6d8b933c/invoked.timestamp +1 -0
  101. package/cli/target/release/.fingerprint/zmij-9501bcbd6d8b933c/lib-zmij +1 -0
  102. package/cli/target/release/.fingerprint/zmij-9501bcbd6d8b933c/lib-zmij.json +1 -0
  103. package/cli/target/release/.fingerprint/zmij-aa602f885104061e/build-script-build-script-build +1 -0
  104. package/cli/target/release/.fingerprint/zmij-aa602f885104061e/build-script-build-script-build.json +1 -0
  105. package/cli/target/release/.fingerprint/zmij-aa602f885104061e/dep-build-script-build-script-build +0 -0
  106. package/cli/target/release/.fingerprint/zmij-aa602f885104061e/invoked.timestamp +1 -0
  107. package/cli/target/release/agent-browser +0 -0
  108. package/cli/target/release/agent-browser.d +1 -0
  109. package/cli/target/release/build/libc-0303d277881093f4/build-script-build +0 -0
  110. package/cli/target/release/build/libc-0303d277881093f4/build_script_build-0303d277881093f4 +0 -0
  111. package/cli/target/release/build/libc-0303d277881093f4/build_script_build-0303d277881093f4.d +5 -0
  112. package/cli/target/release/build/libc-b8c0d8e35a1980d3/invoked.timestamp +1 -0
  113. package/cli/target/release/build/libc-b8c0d8e35a1980d3/output +25 -0
  114. package/cli/target/release/build/libc-b8c0d8e35a1980d3/root-output +1 -0
  115. package/cli/target/release/build/libc-b8c0d8e35a1980d3/stderr +0 -0
  116. package/cli/target/release/build/proc-macro2-291b57751730d5b3/build-script-build +0 -0
  117. package/cli/target/release/build/proc-macro2-291b57751730d5b3/build_script_build-291b57751730d5b3 +0 -0
  118. package/cli/target/release/build/proc-macro2-291b57751730d5b3/build_script_build-291b57751730d5b3.d +5 -0
  119. package/cli/target/release/build/proc-macro2-fc2999f6676f03db/invoked.timestamp +1 -0
  120. package/cli/target/release/build/proc-macro2-fc2999f6676f03db/output +23 -0
  121. package/cli/target/release/build/proc-macro2-fc2999f6676f03db/root-output +1 -0
  122. package/cli/target/release/build/proc-macro2-fc2999f6676f03db/stderr +0 -0
  123. package/cli/target/release/build/quote-352ae41707d371c9/invoked.timestamp +1 -0
  124. package/cli/target/release/build/quote-352ae41707d371c9/output +2 -0
  125. package/cli/target/release/build/quote-352ae41707d371c9/root-output +1 -0
  126. package/cli/target/release/build/quote-352ae41707d371c9/stderr +0 -0
  127. package/cli/target/release/build/quote-7d13be3cbe4f9de4/build-script-build +0 -0
  128. package/cli/target/release/build/quote-7d13be3cbe4f9de4/build_script_build-7d13be3cbe4f9de4 +0 -0
  129. package/cli/target/release/build/quote-7d13be3cbe4f9de4/build_script_build-7d13be3cbe4f9de4.d +5 -0
  130. package/cli/target/release/build/serde-b8c046c16de48f41/invoked.timestamp +1 -0
  131. package/cli/target/release/build/serde-b8c046c16de48f41/out/private.rs +6 -0
  132. package/cli/target/release/build/serde-b8c046c16de48f41/output +13 -0
  133. package/cli/target/release/build/serde-b8c046c16de48f41/root-output +1 -0
  134. package/cli/target/release/build/serde-b8c046c16de48f41/stderr +0 -0
  135. package/cli/target/release/build/serde-d35d32ab52b82a81/build-script-build +0 -0
  136. package/cli/target/release/build/serde-d35d32ab52b82a81/build_script_build-d35d32ab52b82a81 +0 -0
  137. package/cli/target/release/build/serde-d35d32ab52b82a81/build_script_build-d35d32ab52b82a81.d +5 -0
  138. package/cli/target/release/build/serde_core-74db491143173930/invoked.timestamp +1 -0
  139. package/cli/target/release/build/serde_core-74db491143173930/out/private.rs +5 -0
  140. package/cli/target/release/build/serde_core-74db491143173930/output +11 -0
  141. package/cli/target/release/build/serde_core-74db491143173930/root-output +1 -0
  142. package/cli/target/release/build/serde_core-74db491143173930/stderr +0 -0
  143. package/cli/target/release/build/serde_core-f043ae3f4b601577/build-script-build +0 -0
  144. package/cli/target/release/build/serde_core-f043ae3f4b601577/build_script_build-f043ae3f4b601577 +0 -0
  145. package/cli/target/release/build/serde_core-f043ae3f4b601577/build_script_build-f043ae3f4b601577.d +5 -0
  146. package/cli/target/release/build/serde_json-a8467019a959068f/invoked.timestamp +1 -0
  147. package/cli/target/release/build/serde_json-a8467019a959068f/output +3 -0
  148. package/cli/target/release/build/serde_json-a8467019a959068f/root-output +1 -0
  149. package/cli/target/release/build/serde_json-a8467019a959068f/stderr +0 -0
  150. package/cli/target/release/build/serde_json-bfa3f43b57842d41/build-script-build +0 -0
  151. package/cli/target/release/build/serde_json-bfa3f43b57842d41/build_script_build-bfa3f43b57842d41 +0 -0
  152. package/cli/target/release/build/serde_json-bfa3f43b57842d41/build_script_build-bfa3f43b57842d41.d +5 -0
  153. package/cli/target/release/build/zmij-60b0e0e9d7c08f71/invoked.timestamp +1 -0
  154. package/cli/target/release/build/zmij-60b0e0e9d7c08f71/output +3 -0
  155. package/cli/target/release/build/zmij-60b0e0e9d7c08f71/root-output +1 -0
  156. package/cli/target/release/build/zmij-60b0e0e9d7c08f71/stderr +0 -0
  157. package/cli/target/release/build/zmij-aa602f885104061e/build-script-build +0 -0
  158. package/cli/target/release/build/zmij-aa602f885104061e/build_script_build-aa602f885104061e +0 -0
  159. package/cli/target/release/build/zmij-aa602f885104061e/build_script_build-aa602f885104061e.d +5 -0
  160. package/cli/target/release/deps/agent_browser-5894536b887e2ce7 +0 -0
  161. package/cli/target/release/deps/agent_browser-5894536b887e2ce7.d +5 -0
  162. package/cli/target/release/deps/itoa-653b9192107a1caa.d +8 -0
  163. package/cli/target/release/deps/libc-d843359d3dd4757b.d +45 -0
  164. package/cli/target/release/deps/libitoa-653b9192107a1caa.rlib +0 -0
  165. package/cli/target/release/deps/libitoa-653b9192107a1caa.rmeta +0 -0
  166. package/cli/target/release/deps/liblibc-d843359d3dd4757b.rlib +0 -0
  167. package/cli/target/release/deps/liblibc-d843359d3dd4757b.rmeta +0 -0
  168. package/cli/target/release/deps/libmemchr-dcaf8011940d18dd.rlib +0 -0
  169. package/cli/target/release/deps/libmemchr-dcaf8011940d18dd.rmeta +0 -0
  170. package/cli/target/release/deps/libproc_macro2-a753b344a6b4aa98.rlib +0 -0
  171. package/cli/target/release/deps/libproc_macro2-a753b344a6b4aa98.rmeta +0 -0
  172. package/cli/target/release/deps/libquote-833e6725e0f7d298.rlib +0 -0
  173. package/cli/target/release/deps/libquote-833e6725e0f7d298.rmeta +0 -0
  174. package/cli/target/release/deps/libserde-d6fb44202dad3efd.rlib +0 -0
  175. package/cli/target/release/deps/libserde-d6fb44202dad3efd.rmeta +0 -0
  176. package/cli/target/release/deps/libserde_core-0f7ba2581c8c0423.rlib +0 -0
  177. package/cli/target/release/deps/libserde_core-0f7ba2581c8c0423.rmeta +0 -0
  178. package/cli/target/release/deps/libserde_derive-a5d13e0e658ceae3.dylib +0 -0
  179. package/cli/target/release/deps/libserde_json-f61651a65bf0eb31.rlib +0 -0
  180. package/cli/target/release/deps/libserde_json-f61651a65bf0eb31.rmeta +0 -0
  181. package/cli/target/release/deps/libsyn-6f9a22f8c7f909b0.rlib +0 -0
  182. package/cli/target/release/deps/libsyn-6f9a22f8c7f909b0.rmeta +0 -0
  183. package/cli/target/release/deps/libunicode_ident-60c57228d30a23d0.rlib +0 -0
  184. package/cli/target/release/deps/libunicode_ident-60c57228d30a23d0.rmeta +0 -0
  185. package/cli/target/release/deps/libzmij-9501bcbd6d8b933c.rlib +0 -0
  186. package/cli/target/release/deps/libzmij-9501bcbd6d8b933c.rmeta +0 -0
  187. package/cli/target/release/deps/memchr-dcaf8011940d18dd.d +30 -0
  188. package/cli/target/release/deps/proc_macro2-a753b344a6b4aa98.d +17 -0
  189. package/cli/target/release/deps/quote-833e6725e0f7d298.d +13 -0
  190. package/cli/target/release/deps/serde-d6fb44202dad3efd.d +14 -0
  191. package/cli/target/release/deps/serde_core-0f7ba2581c8c0423.d +27 -0
  192. package/cli/target/release/deps/serde_derive-a5d13e0e658ceae3.d +34 -0
  193. package/cli/target/release/deps/serde_json-f61651a65bf0eb31.d +22 -0
  194. package/cli/target/release/deps/syn-6f9a22f8c7f909b0.d +49 -0
  195. package/cli/target/release/deps/unicode_ident-60c57228d30a23d0.d +8 -0
  196. package/cli/target/release/deps/zmij-9501bcbd6d8b933c.d +8 -0
  197. package/dist/actions.d.ts.map +1 -1
  198. package/dist/actions.js +46 -35
  199. package/dist/actions.js.map +1 -1
  200. package/dist/browser.d.ts +30 -1
  201. package/dist/browser.d.ts.map +1 -1
  202. package/dist/browser.js +59 -0
  203. package/dist/browser.js.map +1 -1
  204. package/dist/cli-light.d.ts +11 -0
  205. package/dist/cli-light.d.ts.map +1 -0
  206. package/dist/cli-light.js +409 -0
  207. package/dist/cli-light.js.map +1 -0
  208. package/dist/index.js +13 -8
  209. package/dist/index.js.map +1 -1
  210. package/dist/protocol.d.ts.map +1 -1
  211. package/dist/protocol.js +4 -0
  212. package/dist/protocol.js.map +1 -1
  213. package/dist/snapshot.d.ts +63 -0
  214. package/dist/snapshot.d.ts.map +1 -0
  215. package/dist/snapshot.js +298 -0
  216. package/dist/snapshot.js.map +1 -0
  217. package/package.json +6 -4
  218. package/src/actions.ts +50 -36
  219. package/src/browser.ts +70 -0
  220. package/src/cli-light.ts +457 -0
  221. package/src/index.ts +13 -8
  222. package/src/protocol.ts +4 -0
  223. package/src/snapshot.ts +380 -0
  224. package/tsconfig.json +12 -3
@@ -0,0 +1,457 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Lightweight CLI client for agent-browser
4
+ *
5
+ * This file contains ONLY the client logic (no Playwright imports).
6
+ * It can be compiled with Bun for fast startup times.
7
+ *
8
+ * The actual browser automation runs in a separate daemon process.
9
+ */
10
+
11
+ import * as net from 'net';
12
+ import * as fs from 'fs';
13
+ import * as os from 'os';
14
+ import * as path from 'path';
15
+ import { spawn } from 'child_process';
16
+
17
+ // ============================================================================
18
+ // Configuration
19
+ // ============================================================================
20
+
21
+ const SESSION = process.env.AGENT_BROWSER_SESSION || 'default';
22
+ const SOCKET_PATH = path.join(os.tmpdir(), `agent-browser-${SESSION}.sock`);
23
+ const PID_FILE = path.join(os.tmpdir(), `agent-browser-${SESSION}.pid`);
24
+
25
+ // ============================================================================
26
+ // Daemon Management
27
+ // ============================================================================
28
+
29
+ function isDaemonRunning(): boolean {
30
+ if (!fs.existsSync(PID_FILE)) return false;
31
+ try {
32
+ const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim(), 10);
33
+ process.kill(pid, 0);
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ async function ensureDaemon(): Promise<void> {
41
+ if (isDaemonRunning() && fs.existsSync(SOCKET_PATH)) {
42
+ return;
43
+ }
44
+
45
+ // Find the daemon script - look relative to this script
46
+ const scriptDir = path.dirname(process.argv[1]);
47
+ let daemonPath = path.join(scriptDir, 'daemon.js');
48
+
49
+ // Fallback paths
50
+ if (!fs.existsSync(daemonPath)) {
51
+ daemonPath = path.join(scriptDir, '../dist/daemon.js');
52
+ }
53
+ if (!fs.existsSync(daemonPath)) {
54
+ daemonPath = path.join(process.cwd(), 'dist/daemon.js');
55
+ }
56
+
57
+ if (!fs.existsSync(daemonPath)) {
58
+ throw new Error(`Daemon not found. Looked in: ${daemonPath}`);
59
+ }
60
+
61
+ const child = spawn('node', [daemonPath], {
62
+ detached: true,
63
+ stdio: 'ignore',
64
+ env: { ...process.env, AGENT_BROWSER_DAEMON: '1', AGENT_BROWSER_SESSION: SESSION },
65
+ });
66
+ child.unref();
67
+
68
+ // Wait for socket
69
+ for (let i = 0; i < 50; i++) {
70
+ if (fs.existsSync(SOCKET_PATH)) return;
71
+ await new Promise(r => setTimeout(r, 100));
72
+ }
73
+ throw new Error('Failed to start daemon');
74
+ }
75
+
76
+ // ============================================================================
77
+ // Command Execution
78
+ // ============================================================================
79
+
80
+ interface Response {
81
+ id: string;
82
+ success: boolean;
83
+ data?: unknown;
84
+ error?: string;
85
+ }
86
+
87
+ async function sendCommand(cmd: Record<string, unknown>): Promise<Response> {
88
+ return new Promise((resolve, reject) => {
89
+ let buffer = '';
90
+ let resolved = false;
91
+ const socket = net.createConnection(SOCKET_PATH);
92
+
93
+ const cleanup = () => {
94
+ socket.removeAllListeners();
95
+ socket.destroy();
96
+ };
97
+
98
+ socket.on('connect', () => {
99
+ socket.write(JSON.stringify(cmd) + '\n');
100
+ });
101
+
102
+ socket.on('data', (data) => {
103
+ buffer += data.toString();
104
+ const idx = buffer.indexOf('\n');
105
+ if (idx !== -1 && !resolved) {
106
+ resolved = true;
107
+ try {
108
+ const response = JSON.parse(buffer.substring(0, idx)) as Response;
109
+ cleanup();
110
+ resolve(response);
111
+ } catch {
112
+ cleanup();
113
+ reject(new Error('Invalid JSON response'));
114
+ }
115
+ }
116
+ });
117
+
118
+ socket.on('error', (err) => {
119
+ if (!resolved) {
120
+ resolved = true;
121
+ cleanup();
122
+ reject(err);
123
+ }
124
+ });
125
+
126
+ socket.on('close', () => {
127
+ if (!resolved && buffer.trim()) {
128
+ resolved = true;
129
+ try {
130
+ resolve(JSON.parse(buffer.trim()) as Response);
131
+ } catch {
132
+ reject(new Error('Connection closed'));
133
+ }
134
+ }
135
+ });
136
+
137
+ setTimeout(() => {
138
+ if (!resolved) {
139
+ resolved = true;
140
+ cleanup();
141
+ reject(new Error('Timeout'));
142
+ }
143
+ }, 30000);
144
+ });
145
+ }
146
+
147
+ // ============================================================================
148
+ // Command Parsing
149
+ // ============================================================================
150
+
151
+ function parseCommand(parts: string[]): Record<string, unknown> | null {
152
+ if (parts.length === 0) return null;
153
+
154
+ const command = parts[0];
155
+ const rest = parts.slice(1);
156
+ const id = Math.random().toString(36).slice(2, 10);
157
+
158
+ switch (command) {
159
+ case 'open':
160
+ case 'goto':
161
+ case 'navigate':
162
+ return { id, action: 'navigate', url: rest[0]?.startsWith('http') ? rest[0] : `https://${rest[0]}` };
163
+
164
+ case 'click':
165
+ return { id, action: 'click', selector: rest[0] };
166
+
167
+ case 'fill':
168
+ return { id, action: 'fill', selector: rest[0], value: rest.slice(1).join(' ') };
169
+
170
+ case 'type':
171
+ return { id, action: 'type', selector: rest[0], text: rest.slice(1).join(' ') };
172
+
173
+ case 'hover':
174
+ return { id, action: 'hover', selector: rest[0] };
175
+
176
+ case 'snapshot': {
177
+ const opts: Record<string, unknown> = { id, action: 'snapshot' };
178
+ // Parse snapshot options from rest args
179
+ for (let i = 0; i < rest.length; i++) {
180
+ const arg = rest[i];
181
+ if (arg === '-i' || arg === '--interactive') {
182
+ opts.interactive = true;
183
+ } else if (arg === '-c' || arg === '--compact') {
184
+ opts.compact = true;
185
+ } else if (arg === '--depth' || arg === '-d') {
186
+ opts.maxDepth = parseInt(rest[++i], 10);
187
+ } else if (arg === '--selector' || arg === '-s') {
188
+ opts.selector = rest[++i];
189
+ }
190
+ }
191
+ return opts;
192
+ }
193
+
194
+ case 'screenshot':
195
+ return { id, action: 'screenshot', path: rest[0] };
196
+
197
+ case 'close':
198
+ case 'quit':
199
+ return { id, action: 'close' };
200
+
201
+ case 'get':
202
+ if (rest[0] === 'text') return { id, action: 'gettext', selector: rest[1] };
203
+ if (rest[0] === 'url') return { id, action: 'url' };
204
+ if (rest[0] === 'title') return { id, action: 'title' };
205
+ return null;
206
+
207
+ case 'press':
208
+ return { id, action: 'press', key: rest[0] };
209
+
210
+ case 'wait':
211
+ if (/^\d+$/.test(rest[0])) {
212
+ return { id, action: 'wait', timeout: parseInt(rest[0], 10) };
213
+ }
214
+ return { id, action: 'wait', selector: rest[0] };
215
+
216
+ case 'back':
217
+ return { id, action: 'back' };
218
+
219
+ case 'forward':
220
+ return { id, action: 'forward' };
221
+
222
+ case 'reload':
223
+ return { id, action: 'reload' };
224
+
225
+ case 'eval':
226
+ return { id, action: 'evaluate', script: rest.join(' ') };
227
+
228
+ default:
229
+ return null;
230
+ }
231
+ }
232
+
233
+ function parseBatchCommands(args: string[]): Record<string, unknown>[] {
234
+ const commands: Record<string, unknown>[] = [];
235
+
236
+ // Each argument after 'batch' is a command string
237
+ for (const arg of args) {
238
+ // Split the command string into parts
239
+ const parts = arg.match(/(?:[^\s"]+|"[^"]*")+/g) || [];
240
+ const cleanParts = parts.map(p => p.replace(/^"|"$/g, ''));
241
+
242
+ const cmd = parseCommand(cleanParts);
243
+ if (cmd) {
244
+ commands.push(cmd);
245
+ }
246
+ }
247
+
248
+ return commands;
249
+ }
250
+
251
+ // ============================================================================
252
+ // Output Formatting
253
+ // ============================================================================
254
+
255
+ function formatResponse(response: Response): string {
256
+ if (!response.success) {
257
+ return `\x1b[31m✗ Error:\x1b[0m ${response.error}`;
258
+ }
259
+
260
+ const data = response.data as Record<string, unknown>;
261
+
262
+ if (data?.url && data?.title) {
263
+ return `\x1b[32m✓\x1b[0m \x1b[1m${data.title}\x1b[0m\n\x1b[2m ${data.url}\x1b[0m`;
264
+ } else if (data?.snapshot) {
265
+ return String(data.snapshot);
266
+ } else if (data?.text !== undefined) {
267
+ return String(data.text);
268
+ } else if (data?.url) {
269
+ return String(data.url);
270
+ } else if (data?.title) {
271
+ return String(data.title);
272
+ } else if (data?.result !== undefined) {
273
+ return typeof data.result === 'object' ? JSON.stringify(data.result, null, 2) : String(data.result);
274
+ } else if (data?.closed) {
275
+ return '\x1b[32m✓\x1b[0m Browser closed';
276
+ } else {
277
+ return '\x1b[32m✓\x1b[0m Done';
278
+ }
279
+ }
280
+
281
+ function printResponse(response: Response, json: boolean): void {
282
+ if (json) {
283
+ console.log(JSON.stringify(response));
284
+ } else {
285
+ console.log(formatResponse(response));
286
+ }
287
+ }
288
+
289
+ // ============================================================================
290
+ // Main
291
+ // ============================================================================
292
+
293
+ const HELP = `
294
+ agent-browser - fast browser automation CLI
295
+
296
+ Usage:
297
+ agent-browser <command> [args] [--json]
298
+ agent-browser batch <cmd1> <cmd2> ... [--json]
299
+
300
+ Commands:
301
+ open <url> Navigate to URL
302
+ click <sel> Click element (use @ref from snapshot)
303
+ fill <sel> <text> Fill input
304
+ type <sel> <text> Type text
305
+ hover <sel> Hover element
306
+ snapshot [options] Get accessibility tree with refs
307
+ screenshot [path] Take screenshot
308
+ get text <sel> Get text content
309
+ get url Get current URL
310
+ get title Get page title
311
+ press <key> Press keyboard key
312
+ wait <ms|sel> Wait for time or element
313
+ eval <js> Evaluate JavaScript
314
+ close Close browser
315
+
316
+ Snapshot Options:
317
+ -i, --interactive Only show interactive elements (buttons, links, inputs)
318
+ -c, --compact Remove empty structural elements
319
+ -d, --depth <n> Limit tree depth (e.g., --depth 3)
320
+ -s, --selector <sel> Scope snapshot to CSS selector
321
+
322
+ Batch Mode:
323
+ batch <cmd1> <cmd2> ... Execute multiple commands in sequence
324
+ Each command is a quoted string
325
+
326
+ Options:
327
+ --json Output JSON (for AI agents)
328
+
329
+ Examples:
330
+ agent-browser open example.com
331
+ agent-browser snapshot
332
+ agent-browser click @e2
333
+ agent-browser fill @e3 "hello"
334
+
335
+ # Batch mode - execute multiple commands efficiently
336
+ agent-browser batch "open example.com" "snapshot" "click a"
337
+ agent-browser batch "open google.com" "snapshot" "get title" --json
338
+ `;
339
+
340
+ async function runBatch(commands: Record<string, unknown>[], json: boolean): Promise<void> {
341
+ const results: Response[] = [];
342
+ let hasError = false;
343
+
344
+ for (const cmd of commands) {
345
+ try {
346
+ const response = await sendCommand(cmd);
347
+ results.push(response);
348
+
349
+ if (!json) {
350
+ // Print each result as we go for non-JSON mode
351
+ console.log(`\x1b[36m[${cmd.action}]\x1b[0m`);
352
+ console.log(formatResponse(response));
353
+ console.log();
354
+ }
355
+
356
+ if (!response.success) {
357
+ hasError = true;
358
+ break; // Stop on first error
359
+ }
360
+ } catch (err) {
361
+ const message = err instanceof Error ? err.message : String(err);
362
+ const errorResponse: Response = {
363
+ id: String(cmd.id),
364
+ success: false,
365
+ error: message,
366
+ };
367
+ results.push(errorResponse);
368
+ hasError = true;
369
+
370
+ if (!json) {
371
+ console.log(`\x1b[36m[${cmd.action}]\x1b[0m`);
372
+ console.log(`\x1b[31m✗ Error:\x1b[0m ${message}`);
373
+ }
374
+ break;
375
+ }
376
+ }
377
+
378
+ if (json) {
379
+ console.log(JSON.stringify({
380
+ success: !hasError,
381
+ results,
382
+ completed: results.length,
383
+ total: commands.length,
384
+ }));
385
+ } else {
386
+ console.log(`\x1b[2m─────────────────────────────────────\x1b[0m`);
387
+ console.log(`Completed ${results.length}/${commands.length} commands`);
388
+ }
389
+
390
+ process.exit(hasError ? 1 : 0);
391
+ }
392
+
393
+ async function main(): Promise<void> {
394
+ const args = process.argv.slice(2);
395
+ const json = args.includes('--json');
396
+ const cleanArgs = args.filter(a => !a.startsWith('--'));
397
+
398
+ if (cleanArgs.length === 0 || args.includes('--help') || args.includes('-h')) {
399
+ console.log(HELP);
400
+ process.exit(0);
401
+ }
402
+
403
+ // Check for batch mode
404
+ if (cleanArgs[0] === 'batch') {
405
+ const batchArgs = cleanArgs.slice(1);
406
+ if (batchArgs.length === 0) {
407
+ console.error('\x1b[31mBatch mode requires at least one command\x1b[0m');
408
+ console.log('\nExample: agent-browser batch "open example.com" "snapshot"');
409
+ process.exit(1);
410
+ }
411
+
412
+ const commands = parseBatchCommands(batchArgs);
413
+ if (commands.length === 0) {
414
+ console.error('\x1b[31mNo valid commands found\x1b[0m');
415
+ process.exit(1);
416
+ }
417
+
418
+ try {
419
+ await ensureDaemon();
420
+ await runBatch(commands, json);
421
+ } catch (err) {
422
+ const message = err instanceof Error ? err.message : String(err);
423
+ if (json) {
424
+ console.log(JSON.stringify({ success: false, error: message }));
425
+ } else {
426
+ console.error('\x1b[31m✗ Error:\x1b[0m', message);
427
+ }
428
+ process.exit(1);
429
+ }
430
+ return;
431
+ }
432
+
433
+ // Single command mode
434
+ const cmd = parseCommand(cleanArgs);
435
+
436
+ if (!cmd) {
437
+ console.error('\x1b[31mUnknown command:\x1b[0m', cleanArgs[0]);
438
+ process.exit(1);
439
+ }
440
+
441
+ try {
442
+ await ensureDaemon();
443
+ const response = await sendCommand(cmd);
444
+ printResponse(response, json);
445
+ process.exit(response.success ? 0 : 1);
446
+ } catch (err) {
447
+ const message = err instanceof Error ? err.message : String(err);
448
+ if (json) {
449
+ console.log(JSON.stringify({ success: false, error: message }));
450
+ } else {
451
+ console.error('\x1b[31m✗ Error:\x1b[0m', message);
452
+ }
453
+ process.exit(1);
454
+ }
455
+ }
456
+
457
+ main();
package/src/index.ts CHANGED
@@ -230,7 +230,7 @@ ${c('yellow', 'Usage:')} agent-browser <command> [options]
230
230
 
231
231
  ${c('yellow', 'Core Commands:')}
232
232
  ${c('cyan', 'open')} <url> Navigate to URL
233
- ${c('cyan', 'click')} <sel> Click element
233
+ ${c('cyan', 'click')} <sel> Click element (or @ref)
234
234
  ${c('cyan', 'type')} <sel> <text> Type into element
235
235
  ${c('cyan', 'fill')} <sel> <text> Clear and fill
236
236
  ${c('cyan', 'press')} <key> Press key (Enter, Tab, Control+a)
@@ -239,10 +239,16 @@ ${c('yellow', 'Core Commands:')}
239
239
  ${c('cyan', 'scroll')} <dir> [px] Scroll (up/down/left/right)
240
240
  ${c('cyan', 'wait')} <sel|ms> Wait for element or time
241
241
  ${c('cyan', 'screenshot')} [path] Take screenshot
242
- ${c('cyan', 'snapshot')} Accessibility tree (for AI)
242
+ ${c('cyan', 'snapshot')} Accessibility tree with refs (for AI)
243
243
  ${c('cyan', 'eval')} <js> Run JavaScript
244
244
  ${c('cyan', 'close')} Close browser
245
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
+
246
252
  ${c('yellow', 'Get Info:')} agent-browser get <what> [selector]
247
253
  text, html, value, attr, title, url, count, box
248
254
 
@@ -286,13 +292,12 @@ ${c('yellow', 'Options:')}
286
292
 
287
293
  ${c('yellow', 'Examples:')}
288
294
  agent-browser open example.com
289
- agent-browser click "#submit"
290
- agent-browser fill "#email" "test@example.com"
291
- agent-browser get text "h1"
292
- agent-browser is visible ".modal"
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
293
300
  agent-browser find role button click --name Submit
294
- agent-browser wait 2000
295
- agent-browser wait --load networkidle
296
301
  `);
297
302
  }
298
303
 
package/src/protocol.ts CHANGED
@@ -601,6 +601,10 @@ const screenshotSchema = baseCommandSchema.extend({
601
601
 
602
602
  const snapshotSchema = baseCommandSchema.extend({
603
603
  action: z.literal('snapshot'),
604
+ interactive: z.boolean().optional(),
605
+ maxDepth: z.number().nonnegative().optional(),
606
+ compact: z.boolean().optional(),
607
+ selector: z.string().optional(),
604
608
  });
605
609
 
606
610
  const evaluateSchema = baseCommandSchema.extend({