abtars 0.1.0-alpha.1

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 (312) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +84 -0
  3. package/bundle/_registry.generated-M4WY2MMI.js +35 -0
  4. package/bundle/_registry.generated-M4WY2MMI.js.map +7 -0
  5. package/bundle/abtars-browser.js +162 -0
  6. package/bundle/abtars-browser.js.map +7 -0
  7. package/bundle/abtars-cli.js +1438 -0
  8. package/bundle/abtars-cli.js.map +7 -0
  9. package/bundle/abtars-restart.js +12 -0
  10. package/bundle/abtars-restart.js.map +7 -0
  11. package/bundle/abtars-rss.js +165 -0
  12. package/bundle/abtars-rss.js.map +7 -0
  13. package/bundle/abtars-task.js +258 -0
  14. package/bundle/abtars-task.js.map +7 -0
  15. package/bundle/abtars.js +4072 -0
  16. package/bundle/abtars.js.map +7 -0
  17. package/bundle/agent-api-rate-limit-OQNFMXTZ.js +38 -0
  18. package/bundle/agent-api-rate-limit-OQNFMXTZ.js.map +7 -0
  19. package/bundle/agent-registry-LT4JNQH6.js +18 -0
  20. package/bundle/agent-registry-LT4JNQH6.js.map +7 -0
  21. package/bundle/agents/default.md +29 -0
  22. package/bundle/anthropic-adapter-2APTH3LA.js +40 -0
  23. package/bundle/anthropic-adapter-2APTH3LA.js.map +7 -0
  24. package/bundle/bridge-lock-transport-4AC2G5G6.js +39 -0
  25. package/bundle/bridge-lock-transport-4AC2G5G6.js.map +7 -0
  26. package/bundle/browse-delivery-JXBY36GK.js +17 -0
  27. package/bundle/browse-delivery-JXBY36GK.js.map +7 -0
  28. package/bundle/browser-ELNDVPLC.js +18 -0
  29. package/bundle/browser-ELNDVPLC.js.map +7 -0
  30. package/bundle/capability-CIL3G4FI.js +17 -0
  31. package/bundle/capability-CIL3G4FI.js.map +7 -0
  32. package/bundle/chunk-265TPOPC.js +289 -0
  33. package/bundle/chunk-265TPOPC.js.map +7 -0
  34. package/bundle/chunk-2UENBO6M.js +223 -0
  35. package/bundle/chunk-2UENBO6M.js.map +7 -0
  36. package/bundle/chunk-2UPU3OW6.js +67 -0
  37. package/bundle/chunk-2UPU3OW6.js.map +7 -0
  38. package/bundle/chunk-2XU2X4OI.js +125 -0
  39. package/bundle/chunk-2XU2X4OI.js.map +7 -0
  40. package/bundle/chunk-3B7BBE4F.js +758 -0
  41. package/bundle/chunk-3B7BBE4F.js.map +7 -0
  42. package/bundle/chunk-3E545J66.js +69 -0
  43. package/bundle/chunk-3E545J66.js.map +7 -0
  44. package/bundle/chunk-5R2ANXQ7.js +510 -0
  45. package/bundle/chunk-5R2ANXQ7.js.map +7 -0
  46. package/bundle/chunk-6CPN4IGS.js +507 -0
  47. package/bundle/chunk-6CPN4IGS.js.map +7 -0
  48. package/bundle/chunk-6NR3OHEW.js +88 -0
  49. package/bundle/chunk-6NR3OHEW.js.map +7 -0
  50. package/bundle/chunk-6SETMHNN.js +206 -0
  51. package/bundle/chunk-6SETMHNN.js.map +7 -0
  52. package/bundle/chunk-6UCRKRWR.js +644 -0
  53. package/bundle/chunk-6UCRKRWR.js.map +7 -0
  54. package/bundle/chunk-AR6GO6YC.js +83 -0
  55. package/bundle/chunk-AR6GO6YC.js.map +7 -0
  56. package/bundle/chunk-AZJIODTQ.js +54 -0
  57. package/bundle/chunk-AZJIODTQ.js.map +7 -0
  58. package/bundle/chunk-BHMZ4RCC.js +3706 -0
  59. package/bundle/chunk-BHMZ4RCC.js.map +7 -0
  60. package/bundle/chunk-BQ2L4GMG.js +9175 -0
  61. package/bundle/chunk-BQ2L4GMG.js.map +7 -0
  62. package/bundle/chunk-BSSBCSCL.js +159 -0
  63. package/bundle/chunk-BSSBCSCL.js.map +7 -0
  64. package/bundle/chunk-BUUVFUPO.js +157 -0
  65. package/bundle/chunk-BUUVFUPO.js.map +7 -0
  66. package/bundle/chunk-CEVRHKJY.js +131 -0
  67. package/bundle/chunk-CEVRHKJY.js.map +7 -0
  68. package/bundle/chunk-CWOHNFUV.js +39 -0
  69. package/bundle/chunk-CWOHNFUV.js.map +7 -0
  70. package/bundle/chunk-D2DCBO6M.js +228 -0
  71. package/bundle/chunk-D2DCBO6M.js.map +7 -0
  72. package/bundle/chunk-FMWKEPM7.js +31 -0
  73. package/bundle/chunk-FMWKEPM7.js.map +7 -0
  74. package/bundle/chunk-GRNENTPA.js +145 -0
  75. package/bundle/chunk-GRNENTPA.js.map +7 -0
  76. package/bundle/chunk-GST5T3WZ.js +93 -0
  77. package/bundle/chunk-GST5T3WZ.js.map +7 -0
  78. package/bundle/chunk-GUQVJC3U.js +299 -0
  79. package/bundle/chunk-GUQVJC3U.js.map +7 -0
  80. package/bundle/chunk-HX7Y7EYP.js +3659 -0
  81. package/bundle/chunk-HX7Y7EYP.js.map +7 -0
  82. package/bundle/chunk-JCJS4ZIB.js +296 -0
  83. package/bundle/chunk-JCJS4ZIB.js.map +7 -0
  84. package/bundle/chunk-JW6RU47G.js +184 -0
  85. package/bundle/chunk-JW6RU47G.js.map +7 -0
  86. package/bundle/chunk-LSPKJQCI.js +24 -0
  87. package/bundle/chunk-LSPKJQCI.js.map +7 -0
  88. package/bundle/chunk-M6VBAPNT.js +16 -0
  89. package/bundle/chunk-M6VBAPNT.js.map +7 -0
  90. package/bundle/chunk-MPX525QO.js +129 -0
  91. package/bundle/chunk-MPX525QO.js.map +7 -0
  92. package/bundle/chunk-MW6WDLU7.js +130 -0
  93. package/bundle/chunk-MW6WDLU7.js.map +7 -0
  94. package/bundle/chunk-NT3OBORC.js +215 -0
  95. package/bundle/chunk-NT3OBORC.js.map +7 -0
  96. package/bundle/chunk-NWDBD4PA.js +50 -0
  97. package/bundle/chunk-NWDBD4PA.js.map +7 -0
  98. package/bundle/chunk-OP7BTAWY.js +29 -0
  99. package/bundle/chunk-OP7BTAWY.js.map +7 -0
  100. package/bundle/chunk-PLCY3GFH.js +77 -0
  101. package/bundle/chunk-PLCY3GFH.js.map +7 -0
  102. package/bundle/chunk-PNEDC45Y.js +97 -0
  103. package/bundle/chunk-PNEDC45Y.js.map +7 -0
  104. package/bundle/chunk-QBGBT5QS.js +81 -0
  105. package/bundle/chunk-QBGBT5QS.js.map +7 -0
  106. package/bundle/chunk-RVE2N7FA.js +70 -0
  107. package/bundle/chunk-RVE2N7FA.js.map +7 -0
  108. package/bundle/chunk-TZHIDLDS.js +71910 -0
  109. package/bundle/chunk-TZHIDLDS.js.map +7 -0
  110. package/bundle/chunk-UCQ2WC3B.js +126 -0
  111. package/bundle/chunk-UCQ2WC3B.js.map +7 -0
  112. package/bundle/chunk-UHRP745J.js +214 -0
  113. package/bundle/chunk-UHRP745J.js.map +7 -0
  114. package/bundle/chunk-V76TVMCM.js +58 -0
  115. package/bundle/chunk-V76TVMCM.js.map +7 -0
  116. package/bundle/chunk-VVEDVGCR.js +981 -0
  117. package/bundle/chunk-VVEDVGCR.js.map +7 -0
  118. package/bundle/chunk-W6FAL35D.js +102 -0
  119. package/bundle/chunk-W6FAL35D.js.map +7 -0
  120. package/bundle/chunk-X6TERNVJ.js +15902 -0
  121. package/bundle/chunk-X6TERNVJ.js.map +7 -0
  122. package/bundle/chunk-X76UX47U.js +47 -0
  123. package/bundle/chunk-X76UX47U.js.map +7 -0
  124. package/bundle/chunk-XREWVCUO.js +518 -0
  125. package/bundle/chunk-XREWVCUO.js.map +7 -0
  126. package/bundle/chunk-Y6XAEX2Q.js +408 -0
  127. package/bundle/chunk-Y6XAEX2Q.js.map +7 -0
  128. package/bundle/chunk-YOCTDKKL.js +28 -0
  129. package/bundle/chunk-YOCTDKKL.js.map +7 -0
  130. package/bundle/chunk-ZXPXCDA6.js +160 -0
  131. package/bundle/chunk-ZXPXCDA6.js.map +7 -0
  132. package/bundle/commands-BHVUOU3V.js +31 -0
  133. package/bundle/commands-BHVUOU3V.js.map +7 -0
  134. package/bundle/completion-buffer-P253ONKF.js +13 -0
  135. package/bundle/completion-buffer-P253ONKF.js.map +7 -0
  136. package/bundle/config-RGSDAPZN.js +19 -0
  137. package/bundle/config-RGSDAPZN.js.map +7 -0
  138. package/bundle/config-show-ERTATR6E.js +40 -0
  139. package/bundle/config-show-ERTATR6E.js.map +7 -0
  140. package/bundle/context-HCEGZNDC.js +72 -0
  141. package/bundle/context-HCEGZNDC.js.map +7 -0
  142. package/bundle/delegation-tools-GYTS2D6A.js +27 -0
  143. package/bundle/delegation-tools-GYTS2D6A.js.map +7 -0
  144. package/bundle/deploy-lib-import-32ZFKHWP.js +49 -0
  145. package/bundle/deploy-lib-import-32ZFKHWP.js.map +7 -0
  146. package/bundle/digital-signature-OFCGSHWO.js +13 -0
  147. package/bundle/digital-signature-OFCGSHWO.js.map +7 -0
  148. package/bundle/direct-api-transport-YR7SXXNN.js +860 -0
  149. package/bundle/direct-api-transport-YR7SXXNN.js.map +7 -0
  150. package/bundle/discord-adapter-YYWVMPPU.js +584 -0
  151. package/bundle/discord-adapter-YYWVMPPU.js.map +7 -0
  152. package/bundle/dist-MTMKARCP.js +1969 -0
  153. package/bundle/dist-MTMKARCP.js.map +7 -0
  154. package/bundle/dns-wakeup-27M7D2MR.js +107 -0
  155. package/bundle/dns-wakeup-27M7D2MR.js.map +7 -0
  156. package/bundle/doctor-QNUSDY73.js +248 -0
  157. package/bundle/doctor-QNUSDY73.js.map +7 -0
  158. package/bundle/ensure-invariants-NMXNS476.js +49 -0
  159. package/bundle/ensure-invariants-NMXNS476.js.map +7 -0
  160. package/bundle/env-schema-2KBHBDGN.js +19 -0
  161. package/bundle/env-schema-2KBHBDGN.js.map +7 -0
  162. package/bundle/esm-DDP6NCZG.js +100663 -0
  163. package/bundle/esm-DDP6NCZG.js.map +7 -0
  164. package/bundle/fallback-policy-L4QV2PEJ.js +46 -0
  165. package/bundle/fallback-policy-L4QV2PEJ.js.map +7 -0
  166. package/bundle/health-check-SPA7NT6N.js +56 -0
  167. package/bundle/health-check-SPA7NT6N.js.map +7 -0
  168. package/bundle/hook-system-6Q5YTR53.js +17 -0
  169. package/bundle/hook-system-6Q5YTR53.js.map +7 -0
  170. package/bundle/hotskills-K7BM4YLB.js +12 -0
  171. package/bundle/hotskills-K7BM4YLB.js.map +7 -0
  172. package/bundle/install-6HRZVKUM.js +15 -0
  173. package/bundle/install-6HRZVKUM.js.map +7 -0
  174. package/bundle/install-log-IAPHYKD4.js +28 -0
  175. package/bundle/install-log-IAPHYKD4.js.map +7 -0
  176. package/bundle/install-manifest-SPQRUNXL.js +102 -0
  177. package/bundle/install-manifest-SPQRUNXL.js.map +7 -0
  178. package/bundle/install-validate-PVLZXYLQ.js +53 -0
  179. package/bundle/install-validate-PVLZXYLQ.js.map +7 -0
  180. package/bundle/irc-adapter-OI5UZSQF.js +293 -0
  181. package/bundle/irc-adapter-OI5UZSQF.js.map +7 -0
  182. package/bundle/irc-config-55YO6EGB.js +88 -0
  183. package/bundle/irc-config-55YO6EGB.js.map +7 -0
  184. package/bundle/logs-ZNYXX5PA.js +19 -0
  185. package/bundle/logs-ZNYXX5PA.js.map +7 -0
  186. package/bundle/media-utils-XNNDTYFI.js +4662 -0
  187. package/bundle/media-utils-XNNDTYFI.js.map +7 -0
  188. package/bundle/message-pipeline-LLH5SYMO.js +33 -0
  189. package/bundle/message-pipeline-LLH5SYMO.js.map +7 -0
  190. package/bundle/meta.json +41304 -0
  191. package/bundle/model-health-registry-35LQNVQR.js +11 -0
  192. package/bundle/model-health-registry-35LQNVQR.js.map +7 -0
  193. package/bundle/notification-Y5S5MMLV.js +13 -0
  194. package/bundle/notification-Y5S5MMLV.js.map +7 -0
  195. package/bundle/openrouter-credits-EDY7ETAU.js +32 -0
  196. package/bundle/openrouter-credits-EDY7ETAU.js.map +7 -0
  197. package/bundle/passwd-RRFV4CC5.js +133 -0
  198. package/bundle/passwd-RRFV4CC5.js.map +7 -0
  199. package/bundle/paths-G33RZWZ7.js +17 -0
  200. package/bundle/paths-G33RZWZ7.js.map +7 -0
  201. package/bundle/peer-client-52XYMNI7.js +156 -0
  202. package/bundle/peer-client-52XYMNI7.js.map +7 -0
  203. package/bundle/peer-config-VK6EDLN5.js +16 -0
  204. package/bundle/peer-config-VK6EDLN5.js.map +7 -0
  205. package/bundle/peer-sessions-EAXTNQ36.js +49 -0
  206. package/bundle/peer-sessions-EAXTNQ36.js.map +7 -0
  207. package/bundle/pending-callback-RIMQZ7FJ.js +40 -0
  208. package/bundle/pending-callback-RIMQZ7FJ.js.map +7 -0
  209. package/bundle/phase-transport-KYERDL2O.js +22 -0
  210. package/bundle/phase-transport-KYERDL2O.js.map +7 -0
  211. package/bundle/public/css/dashboard.css +542 -0
  212. package/bundle/public/index.html +180 -0
  213. package/bundle/public/js/app.js +437 -0
  214. package/bundle/public/memory-universe.js +384 -0
  215. package/bundle/responses-adapter-AAQTY3K4.js +30 -0
  216. package/bundle/responses-adapter-AAQTY3K4.js.map +7 -0
  217. package/bundle/restore-ZE3SEPSS.js +46 -0
  218. package/bundle/restore-ZE3SEPSS.js.map +7 -0
  219. package/bundle/self-healer-utils-DMUUXC47.js +43 -0
  220. package/bundle/self-healer-utils-DMUUXC47.js.map +7 -0
  221. package/bundle/skill-stats-LLEXEXLR.js +22 -0
  222. package/bundle/skill-stats-LLEXEXLR.js.map +7 -0
  223. package/bundle/sleep-OYIUOVQD.js +19 -0
  224. package/bundle/sleep-OYIUOVQD.js.map +7 -0
  225. package/bundle/soul-loader-54WCVNLJ.js +16 -0
  226. package/bundle/soul-loader-54WCVNLJ.js.map +7 -0
  227. package/bundle/src-JL4PVO23.js +8 -0
  228. package/bundle/src-JL4PVO23.js.map +7 -0
  229. package/bundle/sse-parser-anthropic-P7CE2MH2.js +72 -0
  230. package/bundle/sse-parser-anthropic-P7CE2MH2.js.map +7 -0
  231. package/bundle/sse-parser-responses-EQQA5FWN.js +63 -0
  232. package/bundle/sse-parser-responses-EQQA5FWN.js.map +7 -0
  233. package/bundle/ssrf-guard-FZCBYIVW.js +64 -0
  234. package/bundle/ssrf-guard-FZCBYIVW.js.map +7 -0
  235. package/bundle/start-FH3GRMJ4.js +35 -0
  236. package/bundle/start-FH3GRMJ4.js.map +7 -0
  237. package/bundle/stream-single-WSG4D53C.js +33 -0
  238. package/bundle/stream-single-WSG4D53C.js.map +7 -0
  239. package/bundle/stt-2UH3RITX.js +14 -0
  240. package/bundle/stt-2UH3RITX.js.map +7 -0
  241. package/bundle/subagent-runtime-LE2ZXH3G.js +12 -0
  242. package/bundle/subagent-runtime-LE2ZXH3G.js.map +7 -0
  243. package/bundle/system-message-T5R3EYYN.js +30 -0
  244. package/bundle/system-message-T5R3EYYN.js.map +7 -0
  245. package/bundle/system-status-KQ6KHFJ6.js +189 -0
  246. package/bundle/system-status-KQ6KHFJ6.js.map +7 -0
  247. package/bundle/task-store-K7CQDEPI.js +22 -0
  248. package/bundle/task-store-K7CQDEPI.js.map +7 -0
  249. package/bundle/telegram-adapter-2V3XUMT5.js +1060 -0
  250. package/bundle/telegram-adapter-2V3XUMT5.js.map +7 -0
  251. package/bundle/tool-registry-MU3OX4UI.js +38 -0
  252. package/bundle/tool-registry-MU3OX4UI.js.map +7 -0
  253. package/bundle/tool-sandbox-VYOK4ZOA.js +20 -0
  254. package/bundle/tool-sandbox-VYOK4ZOA.js.map +7 -0
  255. package/bundle/transport-config-YLXU33RO.js +57 -0
  256. package/bundle/transport-config-YLXU33RO.js.map +7 -0
  257. package/bundle/update-QCW5LXRN.js +13 -0
  258. package/bundle/update-QCW5LXRN.js.map +7 -0
  259. package/bundle/update-check-27KZSAP6.js +12 -0
  260. package/bundle/update-check-27KZSAP6.js.map +7 -0
  261. package/bundle/usage-tracker-OVVEVMOY.js +17 -0
  262. package/bundle/usage-tracker-OVVEVMOY.js.map +7 -0
  263. package/bundle/user-registry-D4SD73UV.js +16 -0
  264. package/bundle/user-registry-D4SD73UV.js.map +7 -0
  265. package/core/professor.json +14 -0
  266. package/core/prompts/browsing_prompt.md +39 -0
  267. package/core/prompts/compaction.md +32 -0
  268. package/core/skills/memory/classification/SKILL.md +37 -0
  269. package/core/skills/memory/memory-anomalies/SKILL.md +39 -0
  270. package/core/skills/memory/memory-search/SKILL.md +48 -0
  271. package/core/skills/memory/topic-save/SKILL.md +44 -0
  272. package/core/skills/ops/cron/SKILL.md +51 -0
  273. package/core/skills/ops/gdrive-backup/SKILL.md +15 -0
  274. package/core/skills/ops/session-start/SKILL.md +11 -0
  275. package/core/skills/ops/skill-authoring/SKILL.md +54 -0
  276. package/core/skills/ops/system-health/SKILL.md +104 -0
  277. package/core/skills/ops/troubleshooting/SKILL.md +48 -0
  278. package/core/skills/ops/trust-gating/SKILL.md +30 -0
  279. package/core/skills/tools/a2a-communication/SKILL.md +68 -0
  280. package/core/skills/tools/browse-delegate/SKILL.md +27 -0
  281. package/core/skills/tools/browser/SKILL.md +36 -0
  282. package/core/skills/tools/clawhub/SKILL.md +44 -0
  283. package/core/skills/tools/delegation/SKILL.md +48 -0
  284. package/core/skills/tools/fxtwitter/SKILL.md +52 -0
  285. package/core/skills/tools/gmail/SKILL.md +44 -0
  286. package/core/skills/tools/irc-chat/SKILL.md +84 -0
  287. package/core/skills/tools/linear/SKILL.md +90 -0
  288. package/core/skills/tools/mcporter/SKILL.md +46 -0
  289. package/core/skills/tools/model-scout/SKILL.md +132 -0
  290. package/core/skills/tools/model-scout/scout-add-model.py +67 -0
  291. package/core/skills/tools/model-scout/scout-ollama.py +116 -0
  292. package/core/skills/tools/model-scout/scout-openrouter.py +85 -0
  293. package/core/skills/tools/nlm/SKILL.md +40 -0
  294. package/core/skills/tools/todo/SKILL.md +30 -0
  295. package/core/skills/tools/twitterX/SKILL.md +52 -0
  296. package/core/skills/tools/twitterX/scripts/abtars-tweet.js +532 -0
  297. package/core/skills/tools/twitterX/scripts/package.json +1 -0
  298. package/core/skills/tools/web-fetch/SKILL.md +29 -0
  299. package/package.json +59 -0
  300. package/scripts/abtars-daemon.service +23 -0
  301. package/scripts/abtars-fetch.sh +42 -0
  302. package/scripts/abtars-watchdog.service +13 -0
  303. package/scripts/abtars.sh +14 -0
  304. package/scripts/abtars@.service +21 -0
  305. package/scripts/browser-patchright.sh +79 -0
  306. package/scripts/com.abtars.daemon.plist +24 -0
  307. package/scripts/com.abtars.watchdog.plist +27 -0
  308. package/scripts/daily-backup.sh +62 -0
  309. package/scripts/doctor.sh +553 -0
  310. package/scripts/hooks/audit-logger.sh +22 -0
  311. package/scripts/upgrade-deps.sh +64 -0
  312. package/scripts/watchdog.sh +309 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/capabilities/browser/browser-manager.ts", "../src/capabilities/browser/browser-tool.ts", "../src/components/content-extractor.ts", "../src/capabilities/browser/browser-ipc-server.ts", "../src/capabilities/browser/domain-allowlist.ts"],
4
+ "sourcesContent": ["import { logAndSwallow } from \"../../components/log-and-swallow.js\";\nimport { logInfo, logWarn } from \"../../components/logger.js\";\nimport { getEnv } from \"../../components/env-schema.js\";\nimport { chromium } from \"patchright\";\nimport type { Browser, BrowserContext, Page } from \"patchright\";\nimport { execFileSync } from \"node:child_process\";\nimport type { BrowserSession } from \"../../types/browser.js\";\n\nconst TAG = \"browser_mgr\";\n\n// ---------------------------------------------------------------------------\n// Environment variable parsing\n\nfunction readPositiveInt(key: string, fallback: number): number {\n const raw = process.env[key];\n if (!raw) return fallback;\n const n = Number(raw);\n return Number.isFinite(n) && Number.isInteger(n) && n > 0 ? n : fallback;\n}\n// ---------------------------------------------------------------------------\n\n\nconst DEFAULTS = {\n BROWSER_SESSION_TIMEOUT_MS: 300_000,\n BROWSER_MAX_SESSIONS: 3,\n WEB_SCRAPE_USER_AGENT: \"Mozilla/5.0 (compatible; Abtars/1.0)\",\n} as const;\n\nexport type BrowserEngine = \"patchright\";\n\nexport interface BrowserConfig {\n sessionTimeoutMs: number;\n maxSessions: number;\n userAgent: string;\n engine: BrowserEngine;\n}\n\n/**\n * Read and validate browser-related env vars, returning resolved config.\n * Invalid values trigger a console.warn and fall back to defaults.\n * Exported for testability (Property 16).\n */\nexport function parseBrowserConfig(): BrowserConfig {\n const sessionTimeoutMs = readPositiveInt(\n \"BROWSER_SESSION_TIMEOUT_MS\",\n DEFAULTS.BROWSER_SESSION_TIMEOUT_MS,\n );\n const maxSessions = readPositiveInt(\n \"BROWSER_MAX_SESSIONS\",\n DEFAULTS.BROWSER_MAX_SESSIONS,\n );\n // Intentional: raw process.env \u2014 tests mutate this at runtime, getEnv() cache won't reflect changes\n const userAgent = process.env[\"WEB_SCRAPE_USER_AGENT\"]?.trim() || DEFAULTS.WEB_SCRAPE_USER_AGENT;\n\n const engine = \"patchright\" as BrowserEngine;\n\n return { sessionTimeoutMs, maxSessions, userAgent, engine };\n}\n\n// ---------------------------------------------------------------------------\n// BrowserManager singleton\n// ---------------------------------------------------------------------------\n\nexport class BrowserManager {\n private static _instance: BrowserManager | null = null;\n\n private _browser: Browser | null = null;\n private _launching: Promise<Browser> | null = null;\n private readonly _sessions = new Map<string, BrowserSession>();\n private _idleTimer: ReturnType<typeof setInterval> | null = null;\n private readonly _config: BrowserConfig;\n private _lastActivityAt = 0;\n private readonly _containerIdleStopMs: number;\n\n constructor(config?: BrowserConfig) {\n this._config = config ?? parseBrowserConfig();\n this._containerIdleStopMs = getEnv().browserIdleStopMin * 60_000;\n this._startIdleCheck();\n }\n\n /** Get (or create) the singleton instance. */\n static getInstance(): BrowserManager {\n if (!BrowserManager._instance) {\n BrowserManager._instance = new BrowserManager();\n }\n return BrowserManager._instance;\n }\n\n /** Reset singleton \u2014 primarily for testing. */\n static resetInstance(): void {\n BrowserManager._instance = null;\n }\n\n // -------------------------------------------------------------------------\n // Browser lifecycle\n // -------------------------------------------------------------------------\n\n /** Lazily launch or return the existing browser instance. */\n private async _ensureBrowser(): Promise<Browser> {\n if (this._browser?.isConnected()) return this._browser;\n\n // Avoid duplicate launches if multiple callers race.\n if (this._launching) return this._launching;\n\n this._launching = this._launchPatchright().then((browser) => {\n this._browser = browser;\n this._launching = null;\n browser.on(\"disconnected\", () => {\n this._browser = null;\n this._sessions.clear();\n });\n return browser;\n }).catch((err) => {\n this._launching = null;\n throw err;\n });\n\n return this._launching;\n }\n\n private async _launchPatchright(): Promise<Browser> {\n const headed = getEnv().browserHeaded;\n const args = headed ? [] : [\"--headless=new\"];\n if (getEnv().browserNoSandbox) args.push(\"--no-sandbox\");\n return chromium.launch({\n headless: !headed,\n channel: getEnv().browserChannel,\n args,\n });\n }\n\n // -------------------------------------------------------------------------\n // Named sessions\n // -------------------------------------------------------------------------\n\n /**\n * Get or create a named browser session.\n * Reusing an existing session updates `lastActivityAt`.\n */\n async getSession(sessionId: string): Promise<BrowserSession> {\n const existing = this._sessions.get(sessionId);\n if (existing) {\n existing.lastActivityAt = Date.now();\n return existing;\n }\n\n // Enforce max sessions.\n if (this._sessions.size >= this._config.maxSessions) {\n const activeIds = [...this._sessions.keys()].join(\", \");\n throw new Error(\n `Maximum concurrent sessions (${this._config.maxSessions}) reached. ` +\n `Close an existing session first. Active sessions: ${activeIds}`,\n );\n }\n\n const browser = await this._ensureBrowser();\n const context = await browser.newContext({\n userAgent: this._config.userAgent,\n });\n const page = await context.newPage();\n const now = Date.now();\n\n const session: BrowserSession = {\n sessionId,\n context,\n page,\n createdAt: now,\n lastActivityAt: now,\n };\n\n this._sessions.set(sessionId, session);\n this._lastActivityAt = now;\n return session;\n }\n\n /** Close a named session and release its resources. */\n async closeSession(sessionId: string): Promise<void> {\n const session = this._sessions.get(sessionId);\n if (!session) return;\n\n this._sessions.delete(sessionId);\n try {\n await session.context.close();\n } catch (err) {\n logAndSwallow(TAG, \"closeSession context.close\", err);\n }\n }\n\n // -------------------------------------------------------------------------\n // One-off contexts (ingestion scrapes)\n // -------------------------------------------------------------------------\n\n /** Create a disposable context + page for ingestion. No session tracking. */\n async createOneOffContext(): Promise<{ context: BrowserContext; page: Page }> {\n const browser = await this._ensureBrowser();\n const context = await browser.newContext({\n userAgent: this._config.userAgent,\n });\n const page = await context.newPage();\n return { context, page };\n }\n\n /** Close a one-off context after use. */\n async closeContext(context: BrowserContext): Promise<void> {\n try {\n await context.close();\n } catch (err) {\n logAndSwallow(TAG, \"closeContext\", err);\n }\n }\n\n // -------------------------------------------------------------------------\n // Shutdown & cleanup\n // -------------------------------------------------------------------------\n\n /** Shut down everything: all sessions, the browser, cleanup timers. */\n async shutdown(): Promise<void> {\n this._stopIdleCheck();\n\n // Close all named sessions.\n const closePromises = [...this._sessions.keys()].map((id) =>\n this.closeSession(id),\n );\n await Promise.all(closePromises);\n\n // Close the browser itself.\n if (this._browser) {\n try {\n await this._browser.close();\n } catch (err) {\n logAndSwallow(TAG, \"browser.close shutdown\", err);\n }\n this._browser = null;\n }\n }\n\n // -------------------------------------------------------------------------\n // Idle-check interval\n // -------------------------------------------------------------------------\n\n private _startIdleCheck(): void {\n // Check every 30 seconds for idle sessions.\n this._idleTimer = setInterval(() => {\n void this._sweepIdleSessions();\n }, 30_000);\n\n // Don't prevent Node from exiting.\n if (this._idleTimer && typeof this._idleTimer === \"object\" && \"unref\" in this._idleTimer) {\n this._idleTimer.unref();\n }\n }\n\n private _stopIdleCheck(): void {\n if (this._idleTimer) {\n clearInterval(this._idleTimer);\n this._idleTimer = null;\n }\n }\n\n private async _sweepIdleSessions(): Promise<void> {\n const now = Date.now();\n const expired: string[] = [];\n\n for (const [id, session] of this._sessions) {\n if (now - session.lastActivityAt > this._config.sessionTimeoutMs) {\n expired.push(id);\n }\n }\n\n for (const id of expired) {\n logWarn(\"browser\", `Closing idle session \"${id}\".`);\n await this.closeSession(id);\n }\n\n // Stop container if no sessions and idle long enough\n if (this._sessions.size === 0 && this._browser && this._lastActivityAt > 0 && now - this._lastActivityAt > this._containerIdleStopMs) {\n logInfo(\"browser\", `No sessions for ${Math.round((now - this._lastActivityAt) / 60_000)}min \u2014 stopping container.`);\n await this.shutdown();\n this._stopContainer();\n }\n }\n\n private _stopContainer(): void {\n try {\n execFileSync(\"docker\", [\"stop\", \"abtars-browser\"], { stdio: \"pipe\", timeout: 10_000 });\n } catch (err) { logAndSwallow(\"browser_manager\", \"op\", err); }\n }\n\n // -------------------------------------------------------------------------\n // Accessors\n // -------------------------------------------------------------------------\n\n /** Number of active named sessions. */\n get activeSessionCount(): number {\n return this._sessions.size;\n }\n\n /** Exposed for testing \u2014 the resolved config. */\n get config(): BrowserConfig {\n return this._config;\n }\n}\n", "import { logDebug } from \"../../components/logger.js\";\nimport { logAndSwallow } from \"../../components/log-and-swallow.js\";\nimport { getEnv } from \"../../components/env-schema.js\";\n\nconst TAG = \"browser_tool\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { readFileSync } from \"node:fs\";\nimport type { BrowserAction, BrowserToolResult, PageElement } from \"../../types/browser.js\";\nimport type { BrowserManager } from \"./browser-manager.js\";\nimport type { DomainAllowlist } from \"./domain-allowlist.js\";\nimport { extractTextFromPage } from \"../../components/content-extractor.js\";\n\nconst LOG_PREFIX = \"[browser-tool]\";\n\n/** Max characters returned by extract_text before truncation. */\nconst TEXT_TRUNCATION_LIMIT = 4000;\n\n/** Max interactive elements returned by get_page_info. */\nconst MAX_PAGE_ELEMENTS = 50;\n\n/** Read navigation timeout from env, default 30 000 ms. */\nfunction getNavigationTimeout(): number {\n const raw = String(getEnv().webScrapePlaywrightTimeoutMs);\n if (raw === undefined || raw === \"\") return 30_000;\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return 30_000;\n return n;\n}\n\n/**\n * Implements the seven browser actions (navigate, click, fill, extract_text,\n * screenshot, get_page_info, close_session). Pure action dispatch \u2014 receives\n * parsed args, calls BrowserManager + Playwright APIs, returns structured JSON.\n */\nexport class BrowserTool {\n private readonly _browserManager: BrowserManager;\n private readonly _domainAllowlist: DomainAllowlist;\n\n constructor(browserManager: BrowserManager, domainAllowlist: DomainAllowlist) {\n this._browserManager = browserManager;\n this._domainAllowlist = domainAllowlist;\n }\n\n /** Execute a browser action and return a JSON-serializable result. */\n async execute(action: BrowserAction): Promise<BrowserToolResult> {\n try {\n switch (action.action) {\n case \"navigate\":\n return await this._handleNavigate(action);\n case \"click\":\n return await this._handleClick(action);\n case \"fill\":\n return await this._handleFill(action);\n case \"extract_text\":\n return await this._handleExtractText(action);\n case \"screenshot\":\n return await this._handleScreenshot(action);\n case \"get_page_info\":\n return await this._handleGetPageInfo(action);\n case \"close_session\":\n return await this._handleCloseSession(action);\n case \"set_cookie\":\n return await this._handleSetCookie(action);\n default:\n return { success: false, error: `Unknown action: ${String((action as BrowserAction).action)}` };\n }\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: message };\n }\n }\n\n // -------------------------------------------------------------------------\n // navigate\n // -------------------------------------------------------------------------\n\n private async _handleNavigate(action: BrowserAction): Promise<BrowserToolResult> {\n const url = action.url;\n if (!url) {\n return { success: false, error: \"navigate action requires a url\" };\n }\n\n // Domain allowlist check\n if (!this._domainAllowlist.isAllowed(url)) {\n let hostname: string;\n try {\n hostname = new URL(url).hostname;\n } catch (err) {\n logAndSwallow(TAG, \"parse URL hostname\", err);\n hostname = url;\n }\n return {\n success: false,\n error: `Domain \"${hostname}\" is not in the allowed list. Allowed patterns: ${this._domainAllowlist.patterns.join(\", \")}`,\n };\n }\n\n // SSRF protection: reject private/internal IPs\n if (getEnv().ssrfCheck) {\n try {\n const { hostname } = new URL(url);\n const { isPrivateHost } = await import(\"./ssrf-guard.js\");\n if (await isPrivateHost(hostname)) {\n return { success: false, error: `Blocked: \"${hostname}\" resolves to a private/internal IP address` };\n }\n } catch (err) {\n logAndSwallow(TAG, \"SSRF check\", err);\n return { success: false, error: \"Invalid URL\" };\n }\n }\n\n const session = await this._browserManager.getSession(action.sessionId);\n const timeout = getNavigationTimeout();\n\n logDebug(\"browser\", `${LOG_PREFIX} navigate session=\"${action.sessionId}\" url=\"${url}\"`);\n\n try {\n const response = await session.page.goto(url, {\n waitUntil: \"domcontentloaded\",\n timeout,\n });\n\n const title = await session.page.title();\n const finalUrl = session.page.url();\n const status = response?.status();\n\n return { success: true, title, url: finalUrl, status };\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: `Navigation failed for ${url}: ${message}` };\n }\n }\n\n // -------------------------------------------------------------------------\n // click\n // -------------------------------------------------------------------------\n\n private async _handleClick(action: BrowserAction): Promise<BrowserToolResult> {\n const selector = action.selector;\n if (!selector) {\n return { success: false, error: \"click action requires a selector\" };\n }\n\n const session = await this._browserManager.getSession(action.sessionId);\n\n logDebug(\"browser\", `${LOG_PREFIX} click session=\"${action.sessionId}\" selector=\"${selector}\"`);\n\n try {\n // Use Promise.race to detect if click triggers navigation\n const navigationPromise = session.page\n .waitForNavigation({ waitUntil: \"domcontentloaded\", timeout: 5000 })\n .catch(err => { logAndSwallow(TAG, \"waitForNavigation click\", err); return null; });\n\n await session.page.click(selector);\n\n const navResult = await navigationPromise;\n\n if (navResult) {\n const title = await session.page.title();\n const url = session.page.url();\n return { success: true, navigated: true, title, url };\n }\n\n return { success: true, navigated: false };\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes(\"waiting for selector\") || message.includes(\"No element\")) {\n return { success: false, error: `Selector not found: \"${selector}\"` };\n }\n return { success: false, error: `Click failed for selector \"${selector}\": ${message}` };\n }\n }\n\n // -------------------------------------------------------------------------\n // fill\n // -------------------------------------------------------------------------\n\n private async _handleFill(action: BrowserAction): Promise<BrowserToolResult> {\n const selector = action.selector;\n const value = action.value;\n if (!selector) {\n return { success: false, error: \"fill action requires a selector\" };\n }\n if (value === undefined) {\n return { success: false, error: \"fill action requires a value\" };\n }\n\n const session = await this._browserManager.getSession(action.sessionId);\n\n // Check if the target is a password field to mask in logs\n let isPassword = false;\n try {\n isPassword = await session.page.evaluate(\n `(sel) => { const el = document.querySelector(sel); return el instanceof HTMLInputElement && el.type === \"password\"; }`,\n selector,\n ) as boolean;\n } catch (err) {\n logAndSwallow(TAG, \"evaluate isPassword\", err);\n }\n\n const logValue = isPassword ? \"***\" : value;\n logDebug(\"browser\",\n `${LOG_PREFIX} fill session=\"${action.sessionId}\" selector=\"${selector}\" value=\"${logValue}\"`,\n );\n\n try {\n await session.page.fill(selector, value);\n return { success: true };\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes(\"waiting for selector\") || message.includes(\"No element\")) {\n return { success: false, error: `Selector not found: \"${selector}\"` };\n }\n return { success: false, error: `Fill failed for selector \"${selector}\": ${message}` };\n }\n }\n\n // -------------------------------------------------------------------------\n // extract_text\n // -------------------------------------------------------------------------\n\n private async _handleExtractText(action: BrowserAction): Promise<BrowserToolResult> {\n const session = await this._browserManager.getSession(action.sessionId);\n\n logDebug(\"browser\",\n `${LOG_PREFIX} extract_text session=\"${action.sessionId}\" selector=\"${action.selector ?? \"(full page)\"}\"`,\n );\n\n try {\n const text = await extractTextFromPage(session.page, action.selector ?? undefined);\n\n if (!text || text.trim().length === 0) {\n return { success: false, error: \"No text content found on the page\" };\n }\n\n const truncated = text.length > TEXT_TRUNCATION_LIMIT;\n const resultText = truncated ? text.slice(0, TEXT_TRUNCATION_LIMIT) : text;\n\n return { success: true, text: resultText, truncated };\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes(\"waiting for selector\") || message.includes(\"No element\")) {\n return { success: false, error: `Selector not found: \"${action.selector}\"` };\n }\n return { success: false, error: `Text extraction failed: ${message}` };\n }\n }\n\n // -------------------------------------------------------------------------\n // screenshot\n // -------------------------------------------------------------------------\n\n private async _handleScreenshot(action: BrowserAction): Promise<BrowserToolResult> {\n const session = await this._browserManager.getSession(action.sessionId);\n\n const tmpFile = path.join(\n os.tmpdir(),\n `abtars-screenshot-${Date.now()}.png`,\n );\n\n logDebug(\"browser\",\n `${LOG_PREFIX} screenshot session=\"${action.sessionId}\" fullPage=${action.fullPage ?? false} path=\"${tmpFile}\"`,\n );\n\n await session.page.screenshot({\n fullPage: action.fullPage ?? false,\n path: tmpFile,\n });\n\n return { success: true, filePath: tmpFile };\n }\n\n // -------------------------------------------------------------------------\n // get_page_info\n // -------------------------------------------------------------------------\n\n private async _handleGetPageInfo(action: BrowserAction): Promise<BrowserToolResult> {\n const session = await this._browserManager.getSession(action.sessionId);\n\n logDebug(\"browser\", `${LOG_PREFIX} get_page_info session=\"${action.sessionId}\"`);\n\n const title = await session.page.title();\n const url = session.page.url();\n\n const elements = await session.page.evaluate(`(maxElements) => {\n const selectors = \"a, button, input, select, textarea, [role='button'], [role='link']\";\n const nodes = document.querySelectorAll(selectors);\n const results = [];\n\n for (let i = 0; i < nodes.length && results.length < maxElements; i++) {\n const el = nodes[i];\n if (!el || !(el instanceof HTMLElement)) continue;\n\n const style = window.getComputedStyle(el);\n if (style.display === \"none\" || style.visibility === \"hidden\") continue;\n\n let selector = el.tagName.toLowerCase();\n if (el.id) {\n selector = \"#\" + el.id;\n } else if (el.className && typeof el.className === \"string\" && el.className.trim()) {\n const cls = el.className.trim().split(/\\\\s+/).slice(0, 2).join(\".\");\n selector = el.tagName.toLowerCase() + \".\" + cls;\n } else {\n const parent = el.parentElement;\n if (parent) {\n const siblings = parent.querySelectorAll(\":scope > \" + el.tagName.toLowerCase());\n if (siblings.length > 1) {\n const idx = Array.from(siblings).indexOf(el) + 1;\n selector = el.tagName.toLowerCase() + \":nth-of-type(\" + idx + \")\";\n }\n }\n }\n\n const entry = { tag: el.tagName.toLowerCase(), selector };\n const text = (el.textContent || \"\").trim().slice(0, 100);\n if (text) entry.text = text;\n\n if (el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement) {\n if (el instanceof HTMLInputElement && el.type) entry.type = el.type;\n if (el.name) entry.name = el.name;\n if (el instanceof HTMLInputElement && el.placeholder) entry.placeholder = el.placeholder;\n if (el instanceof HTMLTextAreaElement && el.placeholder) entry.placeholder = el.placeholder;\n }\n\n if (el instanceof HTMLAnchorElement && el.href) {\n entry.href = el.href;\n }\n\n results.push(entry);\n }\n\n return results;\n }`, MAX_PAGE_ELEMENTS) as PageElement[];\n\n return {\n success: true,\n url,\n title,\n elements,\n };\n }\n\n // -------------------------------------------------------------------------\n // close_session\n // -------------------------------------------------------------------------\n\n private async _handleCloseSession(action: BrowserAction): Promise<BrowserToolResult> {\n logDebug(\"browser\", `${LOG_PREFIX} close_session session=\"${action.sessionId}\"`);\n await this._browserManager.closeSession(action.sessionId);\n return { success: true };\n }\n\n // -------------------------------------------------------------------------\n // set_cookie\n // -------------------------------------------------------------------------\n\n private async _handleSetCookie(action: BrowserAction): Promise<BrowserToolResult> {\n const file = action.cookieFile;\n if (!file) return { success: false, error: \"set_cookie requires --cookie-file\" };\n\n // Only allow files under /run/browser/cookies (mounted read-only)\n const COOKIES_DIR = \"/run/browser/cookies\";\n const resolved = path.resolve(file);\n if (!resolved.startsWith(COOKIES_DIR)) {\n return { success: false, error: `cookie file must be under ${COOKIES_DIR}` };\n }\n\n const raw = readFileSync(resolved, \"utf-8\");\n const json = JSON.parse(raw) as Record<string, string>;\n\n const session = await this._browserManager.getSession(action.sessionId);\n const url = action.url ?? session.page.url();\n let domain: string;\n try { domain = new URL(url).hostname; } catch (err) { logAndSwallow(TAG, \"parse cookie domain\", err); domain = \"\"; }\n\n const cookies = Object.entries(json).map(([name, value]) => ({\n name, value: String(value), domain, path: \"/\",\n }));\n\n await session.context.addCookies(cookies);\n logDebug(\"browser\", `${LOG_PREFIX} set_cookie session=\"${action.sessionId}\" loaded ${cookies.length} cookies for ${domain}`);\n return { success: true, text: `Loaded ${cookies.length} cookies for ${domain}` };\n }}\n", "import type { Page } from \"patchright\";\n\n/**\n * Strips non-content HTML elements and returns clean plain text.\n * Shared by both the agent tool's extract_text action and the ingestion WebScraper.\n *\n * Uses regex-based approach for raw HTML strings (no DOM available in Node.js).\n * Uses page.evaluate() for live Playwright pages (DOM APIs available in browser).\n */\n\n/** Tags whose entire content (including children) should be removed. */\nconst STRIPPED_TAGS = [\"script\", \"style\", \"nav\", \"footer\", \"header\", \"aside\"];\n\n/** Build a regex that matches an opening tag through its closing tag (non-greedy, case-insensitive). */\nfunction buildTagStripRegex(tag: string): RegExp {\n return new RegExp(`<${tag}[^>]*>[\\\\s\\\\S]*?<\\\\/${tag}>`, \"gi\");\n}\n\n/** Regex to match elements with role=\"navigation\" or role=\"banner\". */\nconst ROLE_STRIP_REGEX =\n /<[a-z][a-z0-9]*\\s[^>]*?\\brole\\s*=\\s*[\"'](navigation|banner)[\"'][^>]*>[\\s\\S]*?<\\/[a-z][a-z0-9]*>/gi;\n\n/** Regex to match any remaining HTML tag. */\nconst HTML_TAG_REGEX = /<[^>]+>/g;\n\n/** Common HTML entities to decode. */\nconst ENTITY_MAP: Record<string, string> = {\n \"&amp;\": \"&\",\n \"&lt;\": \"<\",\n \"&gt;\": \">\",\n \"&quot;\": '\"',\n \"&#39;\": \"'\",\n \"&nbsp;\": \" \",\n};\n\nconst ENTITY_REGEX = /&amp;|&lt;|&gt;|&quot;|&#39;|&nbsp;/g;\n\n/**\n * Parse raw HTML string and return clean text.\n * Removes non-content elements, strips tags, decodes entities, collapses whitespace.\n * Returns empty string on empty/whitespace-only input.\n */\nexport function extractTextFromHtml(html: string): string {\n if (!html || !html.trim()) return \"\";\n\n let text = html;\n\n // 1. Remove stripped tag elements and their content\n for (const tag of STRIPPED_TAGS) {\n text = text.replace(buildTagStripRegex(tag), \"\");\n }\n\n // 2. Remove elements with role=\"navigation\" or role=\"banner\"\n text = text.replace(ROLE_STRIP_REGEX, \"\");\n\n // 3. Strip all remaining HTML tags\n text = text.replace(HTML_TAG_REGEX, \" \");\n\n // 4. Decode common HTML entities\n text = text.replace(ENTITY_REGEX, (match) => ENTITY_MAP[match] ?? match);\n\n // 5. Collapse consecutive whitespace into single spaces/newlines\n // First normalize newlines, then collapse blank lines, then collapse spaces\n text = text.replace(/\\r\\n/g, \"\\n\");\n text = text.replace(/[ \\t]+/g, \" \");\n text = text.replace(/\\n[ ]+/g, \"\\n\");\n text = text.replace(/[ ]+\\n/g, \"\\n\");\n text = text.replace(/\\n{2,}/g, \"\\n\");\n\n return text.trim();\n}\n\n/**\n * JavaScript source executed inside the browser context via page.evaluate().\n * Uses DOM APIs (document, HTMLElement, etc.) which are only available at runtime.\n * Accepts a single argument: the CSS selector string or null for full-page extraction.\n */\nconst BROWSER_EXTRACT_FN = `\n(sel) => {\n const TAGS = [\"script\", \"style\", \"nav\", \"footer\", \"header\", \"aside\"];\n const ROLES = [\"navigation\", \"banner\"];\n\n const root = sel ? document.querySelector(sel) : document.body;\n if (!root) return \"\";\n\n const clone = root.cloneNode(true);\n\n for (const tag of TAGS) {\n for (const el of clone.querySelectorAll(tag)) el.remove();\n }\n for (const role of ROLES) {\n for (const el of clone.querySelectorAll('[role=\"' + role + '\"]')) el.remove();\n }\n\n let text = clone.textContent || \"\";\n text = text.replace(/\\\\u00a0/g, \" \");\n text = text.replace(/[ \\\\t]+/g, \" \");\n text = text.replace(/\\\\n[ ]+/g, \"\\\\n\");\n text = text.replace(/[ ]+\\\\n/g, \"\\\\n\");\n text = text.replace(/\\\\n{2,}/g, \"\\\\n\");\n return text.trim();\n}\n`;\n\n/**\n * Extract clean text from a live Playwright Page (runs in browser context).\n * Optionally scoped to a CSS selector. Returns empty string on failure.\n */\nexport async function extractTextFromPage(\n page: Page,\n selector?: string,\n): Promise<string> {\n const text = await page.evaluate<string, string | null>(\n new Function(\"sel\", `return (${BROWSER_EXTRACT_FN})(sel)`) as (sel: string | null) => string,\n selector ?? null,\n );\n\n return text ?? \"\";\n}\n", "import { logDebug } from \"../../components/logger.js\";\nimport { logAndSwallow } from \"../../components/log-and-swallow.js\";\nimport * as net from \"node:net\";\n\nconst TAG = \"browser_ipc\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { BrowserAction, BrowserToolResult } from \"../../types/browser.js\";\nimport type { BrowserTool } from \"./browser-tool.js\";\nimport { abtarsHome } from \"../../paths.js\";\n\nconst LOG_PREFIX = \"[browser-ipc]\";\n\n/** Default socket path: ~/.abtars/browser.sock */\nexport function getDefaultSocketPath(): string {\n return path.join(abtarsHome(), \"browser-socket\", \"browser.sock\");\n}\n\n/**\n * Unix domain socket server that routes BrowserAction requests\n * to a BrowserTool instance, enabling session persistence across\n * CLI invocations.\n *\n * Protocol: client connects \u2192 sends one JSON line \u2192 receives one JSON line \u2192 connection closes.\n */\nexport class BrowserIpcServer {\n private _server: net.Server | null = null;\n private readonly _tool: BrowserTool;\n private readonly _socketPath: string;\n\n constructor(tool: BrowserTool, socketPath?: string) {\n this._tool = tool;\n this._socketPath = socketPath ?? getDefaultSocketPath();\n }\n\n get socketPath(): string {\n return this._socketPath;\n }\n\n get isListening(): boolean {\n return this._server?.listening ?? false;\n }\n\n /** Start listening. Removes stale socket file if present. */\n async start(): Promise<void> {\n // Ensure parent directory exists.\n const dir = path.dirname(this._socketPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n // Remove stale socket file from a previous run.\n this._removeSocketFile();\n\n return new Promise<void>((resolve, reject) => {\n const server = net.createServer({ allowHalfOpen: true }, (conn) => {\n this._handleConnection(conn);\n });\n\n server.on(\"error\", (err) => {\n console.error(`${LOG_PREFIX} Server error: ${err.message}`);\n reject(err);\n });\n\n server.listen(this._socketPath, () => {\n this._server = server;\n logDebug(\"browser\", `${LOG_PREFIX} Listening on ${this._socketPath}`);\n resolve();\n });\n });\n }\n\n /** Stop the server and remove the socket file. */\n async shutdown(): Promise<void> {\n return new Promise<void>((resolve) => {\n if (!this._server) {\n this._removeSocketFile();\n resolve();\n return;\n }\n\n this._server.close(() => {\n this._server = null;\n this._removeSocketFile();\n logDebug(\"browser\", `${LOG_PREFIX} Shut down`);\n resolve();\n });\n });\n }\n\n // \u2500\u2500 Connection handler \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private _handleConnection(conn: net.Socket): void {\n let data = \"\";\n\n conn.on(\"data\", (chunk) => {\n data += chunk.toString();\n });\n\n conn.on(\"end\", () => {\n void this._processRequest(data, conn);\n });\n\n conn.on(\"error\", (err) => {\n console.error(`${LOG_PREFIX} Connection error: ${err.message}`);\n });\n }\n\n private async _processRequest(\n raw: string,\n conn: net.Socket,\n ): Promise<void> {\n let result: BrowserToolResult;\n\n try {\n const action = JSON.parse(raw.trim()) as BrowserAction;\n result = await this._tool.execute(action);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n result = { success: false, error: `IPC parse/execute error: ${message}` };\n }\n\n conn.end(JSON.stringify(result) + \"\\n\");\n }\n\n // \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private _removeSocketFile(): void {\n try {\n if (fs.existsSync(this._socketPath)) {\n fs.unlinkSync(this._socketPath);\n }\n } catch (err) {\n logAndSwallow(TAG, \"unlink socket\", err);\n }\n }\n}\n", "import { getEnv } from \"../../components/env-schema.js\";\nimport { logAndSwallow } from \"../../components/log-and-swallow.js\";\n\nconst TAG = \"domain_allowlist\";\n/**\n * Validates URLs against a configurable allowlist of domain patterns.\n * Prevents the agent from navigating to arbitrary domains.\n *\n * Pattern matching:\n * - `*.example.com` \u2192 matches any subdomain of example.com\n * - `example.com` \u2192 exact match only\n * - Empty list \u2192 open mode (all domains allowed)\n */\nexport class DomainAllowlist {\n private readonly _patterns: string[];\n\n constructor(patterns: string[]) {\n this._patterns = patterns\n .map((p) => p.trim().toLowerCase())\n .filter((p) => p.length > 0);\n }\n\n /** Check if a URL's hostname matches the allowlist. Returns true if allowed. */\n isAllowed(url: string): boolean {\n if (this._patterns.length === 0) return true;\n\n let hostname: string;\n try {\n hostname = new URL(url).hostname.toLowerCase();\n } catch (err) {\n logAndSwallow(TAG, \"parse URL\", err);\n return false;\n }\n\n return this._patterns.some((pattern) => {\n if (pattern.startsWith(\"*.\")) {\n const suffix = pattern.slice(2); // e.g. \"example.com\"\n return hostname === suffix || hostname.endsWith(`.${suffix}`);\n }\n return hostname === pattern;\n });\n }\n\n /** Get the list of configured patterns (for error messages). */\n get patterns(): string[] {\n return [...this._patterns];\n }\n\n /** True if no patterns configured (open mode). */\n get isOpenMode(): boolean {\n return this._patterns.length === 0;\n }\n\n /** Create a DomainAllowlist from the BROWSER_ALLOWED_DOMAINS env var. */\n static fromEnv(): DomainAllowlist {\n const raw = getEnv().browserAllowedDomains;\n const patterns = raw.split(\",\").map((s) => s.trim()).filter((s) => s.length > 0);\n return new DomainAllowlist(patterns);\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["TAG", "path", "TAG", "LOG_PREFIX", "resolve", "TAG"]
7
+ }
@@ -0,0 +1,69 @@
1
+ import { createRequire as __bundleCreateRequire } from 'node:module'; import { fileURLToPath as __bundleFileURLToPath } from 'node:url'; import { dirname as __bundleDirname } from 'node:path'; const require = __bundleCreateRequire(import.meta.url); const __chunk_filename = __bundleFileURLToPath(import.meta.url); const __chunk_dirname = __bundleDirname(__chunk_filename);
2
+
3
+ // src/components/usage-tracker.ts
4
+ import { appendFileSync, readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ var buffer = [];
7
+ var usagePath = "";
8
+ function initUsageTracker(home) {
9
+ const stateDir = join(home, "state");
10
+ if (!existsSync(stateDir)) mkdirSync(stateDir, { recursive: true });
11
+ usagePath = join(stateDir, "usage.jsonl");
12
+ }
13
+ function recordUsage(model, inputTokens, outputTokens) {
14
+ if (!usagePath) return;
15
+ buffer.push({ ts: Date.now(), model, in: inputTokens, out: outputTokens });
16
+ if (buffer.length >= 100) flushUsage();
17
+ }
18
+ function flushUsage() {
19
+ if (!usagePath || buffer.length === 0) return;
20
+ const lines = buffer.map((e) => JSON.stringify(e)).join("\n") + "\n";
21
+ appendFileSync(usagePath, lines);
22
+ buffer = [];
23
+ }
24
+ function readUsage(since, costTable) {
25
+ const result = { inputTokens: 0, outputTokens: 0, cost: 0, byModel: /* @__PURE__ */ new Map() };
26
+ if (!usagePath) return result;
27
+ const entries = [...buffer];
28
+ if (existsSync(usagePath)) {
29
+ const raw = readFileSync(usagePath, "utf-8");
30
+ for (const line of raw.split("\n")) {
31
+ if (!line) continue;
32
+ try {
33
+ entries.push(JSON.parse(line));
34
+ } catch {
35
+ }
36
+ }
37
+ }
38
+ for (const e of entries) {
39
+ if (e.ts < since) continue;
40
+ result.inputTokens += e.in;
41
+ result.outputTokens += e.out;
42
+ const pricing = costTable.get(e.model);
43
+ const entryCost = pricing ? (e.in * pricing.input + e.out * pricing.output) / 1e6 : 0;
44
+ result.cost += entryCost;
45
+ const existing = result.byModel.get(e.model);
46
+ if (existing) {
47
+ existing.in += e.in;
48
+ existing.out += e.out;
49
+ existing.cost += entryCost;
50
+ } else {
51
+ result.byModel.set(e.model, { in: e.in, out: e.out, cost: entryCost });
52
+ }
53
+ }
54
+ return result;
55
+ }
56
+ function resetUsage() {
57
+ if (!usagePath) return;
58
+ buffer = [];
59
+ writeFileSync(usagePath, "");
60
+ }
61
+
62
+ export {
63
+ initUsageTracker,
64
+ recordUsage,
65
+ flushUsage,
66
+ readUsage,
67
+ resetUsage
68
+ };
69
+ //# sourceMappingURL=chunk-3E545J66.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/components/usage-tracker.ts"],
4
+ "sourcesContent": ["/**\n * usage-tracker.ts \u2014 append-only token usage log + aggregation.\n * Storage: ~/.abtars/state/usage.jsonl (one JSON line per prompt).\n */\nimport { appendFileSync, readFileSync, writeFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport interface UsageEntry {\n ts: number;\n model: string;\n in: number;\n out: number;\n}\n\nlet buffer: UsageEntry[] = [];\nlet usagePath = \"\";\n\nexport function initUsageTracker(home: string): void {\n const stateDir = join(home, \"state\");\n if (!existsSync(stateDir)) mkdirSync(stateDir, { recursive: true });\n usagePath = join(stateDir, \"usage.jsonl\");\n}\n\nexport function recordUsage(model: string, inputTokens: number, outputTokens: number): void {\n if (!usagePath) return;\n buffer.push({ ts: Date.now(), model, in: inputTokens, out: outputTokens });\n if (buffer.length >= 100) flushUsage();\n}\n\nexport function flushUsage(): void {\n if (!usagePath || buffer.length === 0) return;\n const lines = buffer.map(e => JSON.stringify(e)).join(\"\\n\") + \"\\n\";\n appendFileSync(usagePath, lines);\n buffer = [];\n}\n\nexport interface UsageSummary {\n inputTokens: number;\n outputTokens: number;\n cost: number;\n byModel: Map<string, { in: number; out: number; cost: number }>;\n}\n\nexport function readUsage(since: number, costTable: Map<string, { input: number; output: number }>): UsageSummary {\n const result: UsageSummary = { inputTokens: 0, outputTokens: 0, cost: 0, byModel: new Map() };\n if (!usagePath) return result;\n\n // Include unflushed buffer\n const entries: UsageEntry[] = [...buffer];\n\n if (existsSync(usagePath)) {\n const raw = readFileSync(usagePath, \"utf-8\");\n for (const line of raw.split(\"\\n\")) {\n if (!line) continue;\n try { entries.push(JSON.parse(line)); } catch { /* skip malformed */ }\n }\n }\n\n for (const e of entries) {\n if (e.ts < since) continue;\n result.inputTokens += e.in;\n result.outputTokens += e.out;\n const pricing = costTable.get(e.model);\n const entryCost = pricing\n ? (e.in * pricing.input + e.out * pricing.output) / 1_000_000\n : 0;\n result.cost += entryCost;\n\n const existing = result.byModel.get(e.model);\n if (existing) {\n existing.in += e.in;\n existing.out += e.out;\n existing.cost += entryCost;\n } else {\n result.byModel.set(e.model, { in: e.in, out: e.out, cost: entryCost });\n }\n }\n return result;\n}\n\nexport function resetUsage(): void {\n if (!usagePath) return;\n buffer = [];\n writeFileSync(usagePath, \"\");\n}\n"],
5
+ "mappings": ";;;AAIA,SAAS,gBAAgB,cAAc,eAAe,YAAY,iBAAiB;AACnF,SAAS,YAAY;AASrB,IAAI,SAAuB,CAAC;AAC5B,IAAI,YAAY;AAET,SAAS,iBAAiB,MAAoB;AACnD,QAAM,WAAW,KAAK,MAAM,OAAO;AACnC,MAAI,CAAC,WAAW,QAAQ,EAAG,WAAU,UAAU,EAAE,WAAW,KAAK,CAAC;AAClE,cAAY,KAAK,UAAU,aAAa;AAC1C;AAEO,SAAS,YAAY,OAAe,aAAqB,cAA4B;AAC1F,MAAI,CAAC,UAAW;AAChB,SAAO,KAAK,EAAE,IAAI,KAAK,IAAI,GAAG,OAAO,IAAI,aAAa,KAAK,aAAa,CAAC;AACzE,MAAI,OAAO,UAAU,IAAK,YAAW;AACvC;AAEO,SAAS,aAAmB;AACjC,MAAI,CAAC,aAAa,OAAO,WAAW,EAAG;AACvC,QAAM,QAAQ,OAAO,IAAI,OAAK,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI,IAAI;AAC9D,iBAAe,WAAW,KAAK;AAC/B,WAAS,CAAC;AACZ;AASO,SAAS,UAAU,OAAe,WAAyE;AAChH,QAAM,SAAuB,EAAE,aAAa,GAAG,cAAc,GAAG,MAAM,GAAG,SAAS,oBAAI,IAAI,EAAE;AAC5F,MAAI,CAAC,UAAW,QAAO;AAGvB,QAAM,UAAwB,CAAC,GAAG,MAAM;AAExC,MAAI,WAAW,SAAS,GAAG;AACzB,UAAM,MAAM,aAAa,WAAW,OAAO;AAC3C,eAAW,QAAQ,IAAI,MAAM,IAAI,GAAG;AAClC,UAAI,CAAC,KAAM;AACX,UAAI;AAAE,gBAAQ,KAAK,KAAK,MAAM,IAAI,CAAC;AAAA,MAAG,QAAQ;AAAA,MAAuB;AAAA,IACvE;AAAA,EACF;AAEA,aAAW,KAAK,SAAS;AACvB,QAAI,EAAE,KAAK,MAAO;AAClB,WAAO,eAAe,EAAE;AACxB,WAAO,gBAAgB,EAAE;AACzB,UAAM,UAAU,UAAU,IAAI,EAAE,KAAK;AACrC,UAAM,YAAY,WACb,EAAE,KAAK,QAAQ,QAAQ,EAAE,MAAM,QAAQ,UAAU,MAClD;AACJ,WAAO,QAAQ;AAEf,UAAM,WAAW,OAAO,QAAQ,IAAI,EAAE,KAAK;AAC3C,QAAI,UAAU;AACZ,eAAS,MAAM,EAAE;AACjB,eAAS,OAAO,EAAE;AAClB,eAAS,QAAQ;AAAA,IACnB,OAAO;AACL,aAAO,QAAQ,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,KAAK,MAAM,UAAU,CAAC;AAAA,IACvE;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,aAAmB;AACjC,MAAI,CAAC,UAAW;AAChB,WAAS,CAAC;AACV,gBAAc,WAAW,EAAE;AAC7B;",
6
+ "names": []
7
+ }