explorbot 0.0.1 → 0.1.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 (423) hide show
  1. package/README.md +80 -26
  2. package/bin/explorbot-cli.ts +680 -0
  3. package/boat/api-tester/src/ai/chief/styles.ts +15 -0
  4. package/boat/api-tester/src/ai/chief.ts +335 -0
  5. package/boat/api-tester/src/ai/curler-tools.ts +278 -0
  6. package/boat/api-tester/src/ai/curler.ts +306 -0
  7. package/boat/api-tester/src/api-client.ts +28 -0
  8. package/boat/api-tester/src/apibot.ts +203 -0
  9. package/boat/api-tester/src/cli.ts +301 -0
  10. package/boat/api-tester/src/config.ts +190 -0
  11. package/dist/bin/explorbot-cli.js +23 -101
  12. package/dist/boat/api-tester/bin/apibot-cli.js +0 -1
  13. package/dist/boat/api-tester/src/ai/chief/styles.js +0 -1
  14. package/dist/boat/api-tester/src/ai/chief.js +0 -1
  15. package/dist/boat/api-tester/src/ai/curler-tools.js +0 -1
  16. package/dist/boat/api-tester/src/ai/curler.js +0 -1
  17. package/dist/boat/api-tester/src/api-client.js +0 -1
  18. package/dist/boat/api-tester/src/apibot.js +0 -1
  19. package/dist/boat/api-tester/src/cli.js +0 -1
  20. package/dist/boat/api-tester/src/config.js +0 -1
  21. package/dist/src/action-result.js +0 -1
  22. package/dist/src/action.js +14 -12
  23. package/dist/src/activity.js +0 -1
  24. package/dist/src/ai/agent.js +0 -1
  25. package/dist/src/ai/bosun.js +0 -1
  26. package/dist/src/ai/captain/idle-mode.js +0 -1
  27. package/dist/src/ai/captain/mixin.js +0 -1
  28. package/dist/src/ai/captain/test-mode.js +0 -1
  29. package/dist/src/ai/captain/web-mode.js +0 -1
  30. package/dist/src/ai/captain.js +0 -1
  31. package/dist/src/ai/conversation.js +0 -1
  32. package/dist/src/ai/experience-compactor.js +0 -1
  33. package/dist/src/ai/fisherman-tools.js +0 -1
  34. package/dist/src/ai/fisherman.js +0 -1
  35. package/dist/src/ai/historian.js +0 -1
  36. package/dist/src/ai/navigator.js +0 -1
  37. package/dist/src/ai/pilot.js +0 -1
  38. package/dist/src/ai/planner/session-dedup.js +0 -1
  39. package/dist/src/ai/planner/styles.js +0 -1
  40. package/dist/src/ai/planner/subpages.js +42 -7
  41. package/dist/src/ai/planner.js +15 -4
  42. package/dist/src/ai/provider.js +0 -1
  43. package/dist/src/ai/quartermaster.js +0 -1
  44. package/dist/src/ai/researcher/cache.js +13 -9
  45. package/dist/src/ai/researcher/coordinates.js +4 -3
  46. package/dist/src/ai/researcher/deep-analysis.js +16 -20
  47. package/dist/src/ai/researcher/fingerprint-worker.js +0 -1
  48. package/dist/src/ai/researcher/focus.js +0 -1
  49. package/dist/src/ai/researcher/locators.js +1 -2
  50. package/dist/src/ai/researcher/mixin.js +0 -1
  51. package/dist/src/ai/researcher/parser.js +4 -4
  52. package/dist/src/ai/researcher/research-result.js +2 -1
  53. package/dist/src/ai/researcher.js +6 -6
  54. package/dist/src/ai/rules.js +0 -1
  55. package/dist/src/ai/task-agent.js +0 -1
  56. package/dist/src/ai/tester.js +0 -1
  57. package/dist/src/ai/tools.js +4 -1
  58. package/dist/src/api/api-client.js +0 -1
  59. package/dist/src/api/request-result.js +0 -1
  60. package/dist/src/api/request-store.js +0 -1
  61. package/dist/src/api/spec-reader.js +0 -1
  62. package/dist/src/api/xhr-capture.js +0 -1
  63. package/dist/src/browser-server.js +0 -1
  64. package/dist/src/command-handler.js +0 -1
  65. package/dist/src/commands/add-rule-command.js +0 -1
  66. package/dist/src/commands/base-command.js +0 -1
  67. package/dist/src/commands/clean-command.js +0 -1
  68. package/dist/src/commands/context-aria-command.js +0 -1
  69. package/dist/src/commands/context-command.js +2 -3
  70. package/dist/src/commands/context-data-command.js +0 -1
  71. package/dist/src/commands/context-experience-command.js +0 -1
  72. package/dist/src/commands/context-html-command.js +0 -1
  73. package/dist/src/commands/context-knowledge-command.js +0 -1
  74. package/dist/src/commands/debug-command.js +0 -1
  75. package/dist/src/commands/drill-command.js +0 -1
  76. package/dist/src/commands/exit-command.js +0 -1
  77. package/dist/src/commands/explore-command.js +3 -3
  78. package/dist/src/commands/freesail-command.js +0 -1
  79. package/dist/src/commands/help-command.js +0 -1
  80. package/dist/src/commands/index.js +0 -1
  81. package/dist/src/commands/init-command.js +117 -0
  82. package/dist/src/commands/knows-command.js +0 -1
  83. package/dist/src/commands/learn-command.js +0 -1
  84. package/dist/src/commands/navigate-command.js +0 -1
  85. package/dist/src/commands/path-command.js +0 -1
  86. package/dist/src/commands/plan-clear-command.js +0 -1
  87. package/dist/src/commands/plan-command.js +6 -2
  88. package/dist/src/commands/plan-edit-command.js +0 -1
  89. package/dist/src/commands/plan-load-command.js +0 -1
  90. package/dist/src/commands/plan-reload-command.js +0 -1
  91. package/dist/src/commands/plan-save-command.js +0 -1
  92. package/dist/src/commands/research-command.js +0 -1
  93. package/dist/src/commands/start-command.js +0 -1
  94. package/dist/src/commands/status-command.js +0 -1
  95. package/dist/src/commands/test-command.js +0 -1
  96. package/dist/src/components/ActivityPane.js +0 -1
  97. package/dist/src/components/AddKnowledge.js +0 -1
  98. package/dist/src/components/AddRule.js +0 -1
  99. package/dist/src/components/App.js +0 -1
  100. package/dist/src/components/Autocomplete.js +0 -1
  101. package/dist/src/components/InputPane.js +0 -1
  102. package/dist/src/components/InputReadline.js +0 -1
  103. package/dist/src/components/LogPane.js +0 -1
  104. package/dist/src/components/PlanEditor.js +0 -1
  105. package/dist/src/components/PlanPane.js +0 -1
  106. package/dist/src/components/SessionTimer.js +0 -1
  107. package/dist/src/components/StateTransitionPane.js +0 -1
  108. package/dist/src/components/StatusPane.js +0 -1
  109. package/dist/src/components/TaskPane.js +0 -1
  110. package/dist/src/components/Welcome.js +0 -1
  111. package/dist/src/components/WelcomeChecklist.js +0 -1
  112. package/dist/src/components/WelcomeCommands.js +0 -1
  113. package/dist/src/components/autocomplete-store.js +0 -1
  114. package/dist/src/components/parse-keypress.js +0 -1
  115. package/dist/src/config.js +0 -1
  116. package/dist/src/execution-controller.js +0 -1
  117. package/dist/src/experience-tracker.js +0 -1
  118. package/dist/src/explorbot.js +1 -2
  119. package/dist/src/explorer.js +58 -17
  120. package/dist/src/index.js +0 -1
  121. package/dist/src/knowledge-tracker.js +2 -2
  122. package/dist/src/observability.js +0 -1
  123. package/dist/src/reporter.js +0 -1
  124. package/dist/src/state-manager.js +0 -1
  125. package/dist/src/stats.js +0 -1
  126. package/dist/src/test-plan.js +0 -1
  127. package/dist/src/utils/aria.js +0 -1
  128. package/dist/src/utils/cli-name.js +16 -0
  129. package/dist/src/utils/code-extractor.js +0 -1
  130. package/dist/src/utils/context-formatter.js +0 -1
  131. package/dist/src/utils/error-page.js +0 -1
  132. package/dist/src/utils/expandable.js +0 -1
  133. package/dist/src/utils/hooks-runner.js +0 -1
  134. package/dist/src/utils/html-diff.js +0 -1
  135. package/dist/src/utils/html.js +0 -1
  136. package/dist/src/utils/logger.js +0 -1
  137. package/dist/src/utils/loop.js +0 -1
  138. package/dist/src/utils/markdown-parser.js +0 -1
  139. package/dist/src/utils/markdown-query.js +0 -1
  140. package/dist/src/utils/markdown-terminal.js +0 -1
  141. package/dist/src/utils/research-parser.js +0 -1
  142. package/dist/src/utils/retry.js +0 -1
  143. package/dist/src/utils/rules-loader.js +0 -1
  144. package/dist/src/utils/strings.js +0 -1
  145. package/dist/src/utils/test-plan-markdown.js +0 -1
  146. package/dist/src/utils/throttle.js +0 -1
  147. package/dist/src/utils/unique-names.js +0 -1
  148. package/dist/src/utils/url-matcher.js +0 -1
  149. package/dist/src/utils/web-element.js +6 -5
  150. package/dist/src/utils/xpath.js +0 -1
  151. package/package.json +28 -4
  152. package/src/action-result.ts +694 -0
  153. package/src/action.ts +449 -0
  154. package/src/activity.ts +111 -0
  155. package/src/ai/agent.ts +3 -0
  156. package/src/ai/bosun.ts +557 -0
  157. package/src/ai/captain/idle-mode.ts +116 -0
  158. package/src/ai/captain/mixin.ts +22 -0
  159. package/src/ai/captain/test-mode.ts +262 -0
  160. package/src/ai/captain/web-mode.ts +136 -0
  161. package/src/ai/captain.ts +504 -0
  162. package/src/ai/conversation.ts +205 -0
  163. package/src/ai/experience-compactor.ts +284 -0
  164. package/src/ai/fisherman-tools.ts +181 -0
  165. package/src/ai/fisherman.ts +223 -0
  166. package/src/ai/historian.ts +457 -0
  167. package/src/ai/navigator.ts +572 -0
  168. package/src/ai/pilot.ts +776 -0
  169. package/src/ai/planner/session-dedup.ts +35 -0
  170. package/src/ai/planner/styles.ts +17 -0
  171. package/src/ai/planner/subpages.ts +171 -0
  172. package/src/ai/planner.ts +549 -0
  173. package/src/ai/provider.ts +613 -0
  174. package/src/ai/quartermaster.ts +286 -0
  175. package/src/ai/researcher/cache.ts +109 -0
  176. package/src/ai/researcher/coordinates.ts +239 -0
  177. package/src/ai/researcher/deep-analysis.ts +412 -0
  178. package/src/ai/researcher/fingerprint-worker.ts +59 -0
  179. package/src/ai/researcher/focus.ts +42 -0
  180. package/src/ai/researcher/locators.ts +282 -0
  181. package/src/ai/researcher/mixin.ts +4 -0
  182. package/src/ai/researcher/parser.ts +186 -0
  183. package/src/ai/researcher/research-result.ts +116 -0
  184. package/src/ai/researcher.ts +858 -0
  185. package/src/ai/rules.ts +376 -0
  186. package/src/ai/task-agent.ts +141 -0
  187. package/src/ai/tester.ts +939 -0
  188. package/src/ai/tools.ts +1122 -0
  189. package/src/api/api-client.ts +109 -0
  190. package/src/api/request-result.ts +212 -0
  191. package/src/api/request-store.ts +130 -0
  192. package/src/api/spec-reader.ts +174 -0
  193. package/src/api/xhr-capture.ts +100 -0
  194. package/src/browser-server.ts +74 -0
  195. package/src/command-handler.ts +454 -0
  196. package/src/commands/add-rule-command.ts +63 -0
  197. package/src/commands/base-command.ts +27 -0
  198. package/src/commands/clean-command.ts +73 -0
  199. package/src/commands/context-aria-command.ts +22 -0
  200. package/src/commands/context-command.ts +67 -0
  201. package/src/commands/context-data-command.ts +30 -0
  202. package/src/commands/context-experience-command.ts +48 -0
  203. package/src/commands/context-html-command.ts +33 -0
  204. package/src/commands/context-knowledge-command.ts +43 -0
  205. package/src/commands/debug-command.ts +13 -0
  206. package/src/commands/drill-command.ts +34 -0
  207. package/src/commands/exit-command.ts +32 -0
  208. package/src/commands/explore-command.ts +129 -0
  209. package/src/commands/freesail-command.ts +95 -0
  210. package/src/commands/help-command.ts +8 -0
  211. package/src/commands/index.ts +69 -0
  212. package/src/commands/init-command.ts +131 -0
  213. package/src/commands/knows-command.ts +68 -0
  214. package/src/commands/learn-command.ts +44 -0
  215. package/src/commands/navigate-command.ts +18 -0
  216. package/src/commands/path-command.ts +83 -0
  217. package/src/commands/plan-clear-command.ts +14 -0
  218. package/src/commands/plan-command.ts +46 -0
  219. package/src/commands/plan-edit-command.ts +9 -0
  220. package/src/commands/plan-load-command.ts +18 -0
  221. package/src/commands/plan-reload-command.ts +28 -0
  222. package/src/commands/plan-save-command.ts +25 -0
  223. package/src/commands/research-command.ts +45 -0
  224. package/src/commands/start-command.ts +13 -0
  225. package/src/commands/status-command.tsx +23 -0
  226. package/src/commands/test-command.ts +84 -0
  227. package/src/components/ActivityPane.tsx +80 -0
  228. package/src/components/AddKnowledge.tsx +169 -0
  229. package/src/components/AddRule.tsx +174 -0
  230. package/src/components/App.tsx +377 -0
  231. package/src/components/Autocomplete.tsx +63 -0
  232. package/src/components/InputPane.tsx +259 -0
  233. package/src/components/InputReadline.tsx +704 -0
  234. package/src/components/LogPane.tsx +187 -0
  235. package/src/components/PlanEditor.tsx +150 -0
  236. package/src/components/PlanPane.tsx +71 -0
  237. package/src/components/SessionTimer.tsx +35 -0
  238. package/src/components/StateTransitionPane.tsx +149 -0
  239. package/src/components/StatusPane.tsx +62 -0
  240. package/src/components/TaskPane.tsx +119 -0
  241. package/src/components/Welcome.tsx +83 -0
  242. package/src/components/WelcomeChecklist.tsx +118 -0
  243. package/src/components/WelcomeCommands.tsx +102 -0
  244. package/src/components/autocomplete-store.ts +35 -0
  245. package/src/components/parse-keypress.ts +170 -0
  246. package/src/config.ts +491 -0
  247. package/src/execution-controller.ts +109 -0
  248. package/src/experience-tracker.ts +350 -0
  249. package/src/explorbot.ts +405 -0
  250. package/src/explorer.ts +760 -0
  251. package/src/index.tsx +62 -0
  252. package/src/knowledge-tracker.ts +230 -0
  253. package/src/observability.ts +150 -0
  254. package/src/reporter.ts +224 -0
  255. package/src/state-manager.ts +556 -0
  256. package/src/stats.ts +53 -0
  257. package/src/test-plan.ts +432 -0
  258. package/src/utils/aria.ts +629 -0
  259. package/src/utils/cli-name.ts +13 -0
  260. package/src/utils/code-extractor.ts +22 -0
  261. package/src/utils/context-formatter.ts +239 -0
  262. package/src/utils/error-page.ts +23 -0
  263. package/src/utils/expandable.ts +38 -0
  264. package/src/utils/hooks-runner.ts +79 -0
  265. package/src/utils/html-diff.ts +918 -0
  266. package/src/utils/html.ts +1316 -0
  267. package/src/utils/logger.ts +534 -0
  268. package/src/utils/loop.ts +176 -0
  269. package/src/utils/markdown-parser.ts +127 -0
  270. package/src/utils/markdown-query.ts +466 -0
  271. package/src/utils/markdown-terminal.ts +43 -0
  272. package/src/utils/research-parser.ts +11 -0
  273. package/src/utils/retry.ts +73 -0
  274. package/src/utils/rules-loader.ts +118 -0
  275. package/src/utils/strings.ts +13 -0
  276. package/src/utils/test-plan-markdown.ts +332 -0
  277. package/src/utils/throttle.ts +18 -0
  278. package/src/utils/unique-names.ts +14 -0
  279. package/src/utils/url-matcher.ts +45 -0
  280. package/src/utils/web-element.ts +147 -0
  281. package/src/utils/xpath.ts +129 -0
  282. package/dist/bin/explorbot-cli.js.map +0 -1
  283. package/dist/boat/api-tester/bin/apibot-cli.js.map +0 -1
  284. package/dist/boat/api-tester/example/apibot.config.js +0 -31
  285. package/dist/boat/api-tester/example/apibot.config.js.map +0 -1
  286. package/dist/boat/api-tester/src/ai/chief/styles.js.map +0 -1
  287. package/dist/boat/api-tester/src/ai/chief.js.map +0 -1
  288. package/dist/boat/api-tester/src/ai/curler-tools.js.map +0 -1
  289. package/dist/boat/api-tester/src/ai/curler.js.map +0 -1
  290. package/dist/boat/api-tester/src/api-client.js.map +0 -1
  291. package/dist/boat/api-tester/src/apibot.js.map +0 -1
  292. package/dist/boat/api-tester/src/cli.js.map +0 -1
  293. package/dist/boat/api-tester/src/config.js.map +0 -1
  294. package/dist/prompts/audit-rules.md +0 -124
  295. package/dist/src/action-result.js.map +0 -1
  296. package/dist/src/action.js.map +0 -1
  297. package/dist/src/activity.js.map +0 -1
  298. package/dist/src/ai/agent.js.map +0 -1
  299. package/dist/src/ai/bosun.js.map +0 -1
  300. package/dist/src/ai/captain/idle-mode.js.map +0 -1
  301. package/dist/src/ai/captain/mixin.js.map +0 -1
  302. package/dist/src/ai/captain/test-mode.js.map +0 -1
  303. package/dist/src/ai/captain/web-mode.js.map +0 -1
  304. package/dist/src/ai/captain.js.map +0 -1
  305. package/dist/src/ai/conversation.js.map +0 -1
  306. package/dist/src/ai/experience-compactor.js.map +0 -1
  307. package/dist/src/ai/fisherman-tools.js.map +0 -1
  308. package/dist/src/ai/fisherman.js.map +0 -1
  309. package/dist/src/ai/historian.js.map +0 -1
  310. package/dist/src/ai/navigator.js.map +0 -1
  311. package/dist/src/ai/pilot.js.map +0 -1
  312. package/dist/src/ai/planner/session-dedup.js.map +0 -1
  313. package/dist/src/ai/planner/styles.js.map +0 -1
  314. package/dist/src/ai/planner/subpages.js.map +0 -1
  315. package/dist/src/ai/planner.js.map +0 -1
  316. package/dist/src/ai/provider.js.map +0 -1
  317. package/dist/src/ai/quartermaster.js.map +0 -1
  318. package/dist/src/ai/researcher/cache.js.map +0 -1
  319. package/dist/src/ai/researcher/coordinates.js.map +0 -1
  320. package/dist/src/ai/researcher/deep-analysis.js.map +0 -1
  321. package/dist/src/ai/researcher/fingerprint-worker.js.map +0 -1
  322. package/dist/src/ai/researcher/focus.js.map +0 -1
  323. package/dist/src/ai/researcher/locators.js.map +0 -1
  324. package/dist/src/ai/researcher/mixin.js.map +0 -1
  325. package/dist/src/ai/researcher/parser.js.map +0 -1
  326. package/dist/src/ai/researcher/research-result.js.map +0 -1
  327. package/dist/src/ai/researcher.js.map +0 -1
  328. package/dist/src/ai/rules.js.map +0 -1
  329. package/dist/src/ai/task-agent.js.map +0 -1
  330. package/dist/src/ai/tester.js.map +0 -1
  331. package/dist/src/ai/tools.js.map +0 -1
  332. package/dist/src/api/api-client.js.map +0 -1
  333. package/dist/src/api/request-result.js.map +0 -1
  334. package/dist/src/api/request-store.js.map +0 -1
  335. package/dist/src/api/spec-reader.js.map +0 -1
  336. package/dist/src/api/xhr-capture.js.map +0 -1
  337. package/dist/src/browser-server.js.map +0 -1
  338. package/dist/src/command-handler.js.map +0 -1
  339. package/dist/src/commands/add-rule-command.js.map +0 -1
  340. package/dist/src/commands/base-command.js.map +0 -1
  341. package/dist/src/commands/clean-command.js.map +0 -1
  342. package/dist/src/commands/context-aria-command.js.map +0 -1
  343. package/dist/src/commands/context-command.js.map +0 -1
  344. package/dist/src/commands/context-data-command.js.map +0 -1
  345. package/dist/src/commands/context-experience-command.js.map +0 -1
  346. package/dist/src/commands/context-html-command.js.map +0 -1
  347. package/dist/src/commands/context-knowledge-command.js.map +0 -1
  348. package/dist/src/commands/debug-command.js.map +0 -1
  349. package/dist/src/commands/drill-command.js.map +0 -1
  350. package/dist/src/commands/exit-command.js.map +0 -1
  351. package/dist/src/commands/explore-command.js.map +0 -1
  352. package/dist/src/commands/freesail-command.js.map +0 -1
  353. package/dist/src/commands/help-command.js.map +0 -1
  354. package/dist/src/commands/index.js.map +0 -1
  355. package/dist/src/commands/knows-command.js.map +0 -1
  356. package/dist/src/commands/learn-command.js.map +0 -1
  357. package/dist/src/commands/navigate-command.js.map +0 -1
  358. package/dist/src/commands/path-command.js.map +0 -1
  359. package/dist/src/commands/plan-clear-command.js.map +0 -1
  360. package/dist/src/commands/plan-command.js.map +0 -1
  361. package/dist/src/commands/plan-edit-command.js.map +0 -1
  362. package/dist/src/commands/plan-load-command.js.map +0 -1
  363. package/dist/src/commands/plan-reload-command.js.map +0 -1
  364. package/dist/src/commands/plan-save-command.js.map +0 -1
  365. package/dist/src/commands/research-command.js.map +0 -1
  366. package/dist/src/commands/start-command.js.map +0 -1
  367. package/dist/src/commands/status-command.js.map +0 -1
  368. package/dist/src/commands/test-command.js.map +0 -1
  369. package/dist/src/components/ActivityPane.js.map +0 -1
  370. package/dist/src/components/AddKnowledge.js.map +0 -1
  371. package/dist/src/components/AddRule.js.map +0 -1
  372. package/dist/src/components/App.js.map +0 -1
  373. package/dist/src/components/Autocomplete.js.map +0 -1
  374. package/dist/src/components/InputPane.js.map +0 -1
  375. package/dist/src/components/InputReadline.js.map +0 -1
  376. package/dist/src/components/LogPane.js.map +0 -1
  377. package/dist/src/components/PlanEditor.js.map +0 -1
  378. package/dist/src/components/PlanPane.js.map +0 -1
  379. package/dist/src/components/SessionTimer.js.map +0 -1
  380. package/dist/src/components/StateTransitionPane.js.map +0 -1
  381. package/dist/src/components/StatusPane.js.map +0 -1
  382. package/dist/src/components/TaskPane.js.map +0 -1
  383. package/dist/src/components/Welcome.js.map +0 -1
  384. package/dist/src/components/WelcomeChecklist.js.map +0 -1
  385. package/dist/src/components/WelcomeCommands.js.map +0 -1
  386. package/dist/src/components/autocomplete-store.js.map +0 -1
  387. package/dist/src/components/parse-keypress.js.map +0 -1
  388. package/dist/src/config.js.map +0 -1
  389. package/dist/src/execution-controller.js.map +0 -1
  390. package/dist/src/experience-tracker.js.map +0 -1
  391. package/dist/src/explorbot.js.map +0 -1
  392. package/dist/src/explorer.js.map +0 -1
  393. package/dist/src/index.js.map +0 -1
  394. package/dist/src/knowledge-tracker.js.map +0 -1
  395. package/dist/src/observability.js.map +0 -1
  396. package/dist/src/reporter.js.map +0 -1
  397. package/dist/src/state-manager.js.map +0 -1
  398. package/dist/src/stats.js.map +0 -1
  399. package/dist/src/test-plan.js.map +0 -1
  400. package/dist/src/utils/aria.js.map +0 -1
  401. package/dist/src/utils/code-extractor.js.map +0 -1
  402. package/dist/src/utils/context-formatter.js.map +0 -1
  403. package/dist/src/utils/error-page.js.map +0 -1
  404. package/dist/src/utils/expandable.js.map +0 -1
  405. package/dist/src/utils/hooks-runner.js.map +0 -1
  406. package/dist/src/utils/html-diff.js.map +0 -1
  407. package/dist/src/utils/html.js.map +0 -1
  408. package/dist/src/utils/logger.js.map +0 -1
  409. package/dist/src/utils/loop.js.map +0 -1
  410. package/dist/src/utils/markdown-parser.js.map +0 -1
  411. package/dist/src/utils/markdown-query.js.map +0 -1
  412. package/dist/src/utils/markdown-terminal.js.map +0 -1
  413. package/dist/src/utils/research-parser.js.map +0 -1
  414. package/dist/src/utils/retry.js.map +0 -1
  415. package/dist/src/utils/rules-loader.js.map +0 -1
  416. package/dist/src/utils/strings.js.map +0 -1
  417. package/dist/src/utils/test-plan-markdown.js.map +0 -1
  418. package/dist/src/utils/throttle.js.map +0 -1
  419. package/dist/src/utils/unique-names.js.map +0 -1
  420. package/dist/src/utils/url-matcher.js.map +0 -1
  421. package/dist/src/utils/web-element.js.map +0 -1
  422. package/dist/src/utils/xpath.js.map +0 -1
  423. package/prompts/audit-rules.md +0 -124
@@ -0,0 +1,73 @@
1
+ import { createDebug, tag } from './logger.js';
2
+
3
+ const debugLog = createDebug('explorbot:retry');
4
+
5
+ const RATE_LIMIT_DELAYS = [10_000, 20_000, 30_000, 60_000, 90_000];
6
+ const RATE_LIMIT_MAX_ATTEMPTS = RATE_LIMIT_DELAYS.length + 1;
7
+
8
+ export interface RetryOptions {
9
+ maxAttempts?: number;
10
+ baseDelay?: number;
11
+ maxDelay?: number;
12
+ backoffMultiplier?: number;
13
+ retryCondition?: (error: Error) => boolean;
14
+ }
15
+
16
+ const defaultOptions: Required<RetryOptions> = {
17
+ maxAttempts: 3,
18
+ baseDelay: 1000,
19
+ maxDelay: 10000,
20
+ backoffMultiplier: 2,
21
+ retryCondition: (error: Error) => {
22
+ return error.constructor.name === 'AI_APICallError' || error.message.includes('schema') || error.message.includes('timeout') || error.message.includes('network') || error.message.includes('rate limit') || error.message.includes('Failed to generate JSON');
23
+ },
24
+ };
25
+
26
+ function isRateLimitError(error: Error): boolean {
27
+ return error.message.toLowerCase().includes('rate limit');
28
+ }
29
+
30
+ export async function withRetry<T>(operation: () => Promise<T>, options: RetryOptions = {}): Promise<T> {
31
+ const config = { ...defaultOptions, ...options };
32
+ let lastError: Error;
33
+ let rateLimitAttempt = 0;
34
+
35
+ for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
36
+ try {
37
+ if (attempt > 1) debugLog(`Attempt ${attempt}/${config.maxAttempts}`);
38
+ return await operation();
39
+ } catch (error) {
40
+ lastError = error instanceof Error ? error : new Error(String(error));
41
+
42
+ if (lastError.name === 'AbortError') {
43
+ throw lastError;
44
+ }
45
+
46
+ if (isRateLimitError(lastError) && rateLimitAttempt < RATE_LIMIT_DELAYS.length) {
47
+ const delay = RATE_LIMIT_DELAYS[rateLimitAttempt];
48
+ tag('warning').log(`Rate limit hit, waiting ${delay / 1000}s before retry (${rateLimitAttempt + 1}/${RATE_LIMIT_MAX_ATTEMPTS})...`);
49
+ await new Promise((resolve) => setTimeout(resolve, delay));
50
+ rateLimitAttempt++;
51
+ attempt--;
52
+ continue;
53
+ }
54
+
55
+ if (attempt === config.maxAttempts) {
56
+ debugLog(`All ${config.maxAttempts} attempts failed`);
57
+ throw lastError;
58
+ }
59
+
60
+ if (!config.retryCondition(lastError)) {
61
+ debugLog('Error does not meet retry condition, not retrying');
62
+ throw lastError;
63
+ }
64
+
65
+ const delay = Math.min(config.baseDelay * config.backoffMultiplier ** (attempt - 1), config.maxDelay);
66
+
67
+ debugLog(`Retrying in ${delay}ms. Error: ${lastError.message}`);
68
+ await new Promise((resolve) => setTimeout(resolve, delay));
69
+ }
70
+ }
71
+
72
+ throw lastError!;
73
+ }
@@ -0,0 +1,118 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { basename, dirname, join, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { tag } from './logger.ts';
5
+ import { matchesUrl } from './url-matcher.ts';
6
+
7
+ const BUILT_IN_DIR = resolve(dirname(fileURLToPath(import.meta.url)), '../../rules');
8
+
9
+ export class RulesLoader {
10
+ static loadRules(agentName: string, rulesConfig: RuleEntry[], currentUrl: string): string {
11
+ const parts: string[] = [];
12
+
13
+ for (const entry of rulesConfig) {
14
+ if (typeof entry === 'string') {
15
+ const content = loadFile(agentName, entry);
16
+ if (content) parts.push(content);
17
+ continue;
18
+ }
19
+
20
+ for (const [pattern, filename] of Object.entries(entry)) {
21
+ if (!matchesUrl(pattern, currentUrl)) continue;
22
+ const content = loadFile(agentName, filename);
23
+ if (content) parts.push(content);
24
+ }
25
+ }
26
+
27
+ return parts.join('\n\n');
28
+ }
29
+
30
+ static loadStyles(agentName: string, styleNames: string[]): Record<string, string> {
31
+ const styles: Record<string, string> = {};
32
+ for (const name of styleNames) {
33
+ styles[name] = loadStyleFile(agentName, name);
34
+ }
35
+ return styles;
36
+ }
37
+
38
+ static listStyleNames(agentName: string): string[] {
39
+ const names = new Set<string>();
40
+ const userDir = join(process.cwd(), 'rules', agentName, 'styles');
41
+ const builtInDir = join(BUILT_IN_DIR, agentName, 'styles');
42
+ for (const dir of [userDir, builtInDir]) {
43
+ if (!existsSync(dir)) continue;
44
+ for (const f of readdirSync(dir)) {
45
+ if (f.endsWith('.md')) names.add(basename(f, '.md'));
46
+ }
47
+ }
48
+ if (!names.size) {
49
+ throw new Error(`No planning styles found for agent "${agentName}". Expected .md files under rules/${agentName}/styles/ or bundled rules.`);
50
+ }
51
+ return [...names].sort();
52
+ }
53
+
54
+ static getActiveStyle(styles: Record<string, string>, iteration: number, override?: string): { name: string; approach: string } {
55
+ const names = Object.keys(styles);
56
+
57
+ if (override) {
58
+ const approach = styles[override];
59
+ if (!approach) throw new Error(`Unknown planning style: "${override}". Available: ${names.join(', ')}`);
60
+ return { name: override, approach };
61
+ }
62
+
63
+ const idx = iteration % names.length;
64
+ const name = names[idx];
65
+ return { name, approach: styles[name] };
66
+ }
67
+
68
+ static extractStyles(agentName: string, targetDir: string): string[] {
69
+ const sourceDir = join(BUILT_IN_DIR, agentName, 'styles');
70
+ if (!existsSync(sourceDir)) throw new Error(`No built-in styles found for agent: ${agentName}`);
71
+
72
+ mkdirSync(targetDir, { recursive: true });
73
+
74
+ const files = readdirSync(sourceDir)
75
+ .filter((f) => f.endsWith('.md'))
76
+ .sort();
77
+ const extracted: string[] = [];
78
+
79
+ for (const file of files) {
80
+ const target = join(targetDir, file);
81
+ if (existsSync(target)) {
82
+ tag('info').log(`Skipping ${file} (already exists)`);
83
+ continue;
84
+ }
85
+ writeFileSync(target, readFileSync(join(sourceDir, file), 'utf8'));
86
+ extracted.push(file);
87
+ tag('success').log(`Extracted ${file}`);
88
+ }
89
+
90
+ return extracted;
91
+ }
92
+ }
93
+
94
+ function loadFile(agentName: string, name: string, subdir?: string): string | undefined {
95
+ const file = `${name}.md`;
96
+ const segments = subdir ? [agentName, subdir, file] : [agentName, file];
97
+
98
+ const userPath = join(process.cwd(), 'rules', ...segments);
99
+ if (existsSync(userPath)) return readFileSync(userPath, 'utf8').trim();
100
+
101
+ const builtInPath = join(BUILT_IN_DIR, ...segments);
102
+ if (existsSync(builtInPath)) return readFileSync(builtInPath, 'utf8').trim();
103
+
104
+ return undefined;
105
+ }
106
+
107
+ function loadStyleFile(agentName: string, name: string): string {
108
+ const content = loadFile(agentName, name, 'styles');
109
+ if (content) return content;
110
+
111
+ const userPath = join(process.cwd(), 'rules', agentName, 'styles', `${name}.md`);
112
+ const builtInPath = join(BUILT_IN_DIR, agentName, 'styles', `${name}.md`);
113
+ throw new Error(`Style "${name}" not found for agent "${agentName}". Searched: ${userPath}, ${builtInPath}`);
114
+ }
115
+
116
+ type RuleEntry = string | Record<string, string>;
117
+
118
+ export type { RuleEntry };
@@ -0,0 +1,13 @@
1
+ export function truncateJson(input: any): string {
2
+ if (!input) return '';
3
+ const str = JSON.stringify(input);
4
+ return str.length <= 80 ? str : `${str.slice(0, 77)}...`;
5
+ }
6
+
7
+ export function sanitizeFilename(name: string): string {
8
+ return name
9
+ .toLowerCase()
10
+ .replace(/[^a-z0-9]+/g, '_')
11
+ .replace(/^_+|_+$/g, '')
12
+ .slice(0, 50);
13
+ }
@@ -0,0 +1,332 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { type Note, Plan, Test } from '../test-plan.ts';
3
+ import { mdq } from './markdown-query.ts';
4
+
5
+ const NOISE_PREFIXES = ['Test started', 'Finish requested:', 'Session name:'];
6
+
7
+ function isNoiseNote(message: string): boolean {
8
+ return NOISE_PREFIXES.some((prefix) => message.startsWith(prefix));
9
+ }
10
+
11
+ function getPilotVerdict(notes: Record<string, Note>): string | null {
12
+ for (const note of Object.values(notes)) {
13
+ if (note.status === 'passed' && note.message.startsWith('Pilot:')) {
14
+ return note.message.replace(/^Pilot:\s*/, '');
15
+ }
16
+ }
17
+ return null;
18
+ }
19
+
20
+ function formatBulletItem(text: string): string {
21
+ const lines = text.split('\n');
22
+ let result = `* ${lines[0]}\n`;
23
+ for (let i = 1; i < lines.length; i++) {
24
+ result += `${lines[i]}\n`;
25
+ }
26
+ return result;
27
+ }
28
+
29
+ function parseMultiLineBullet(lines: string[], i: number): { text: string; nextIndex: number } {
30
+ let text = lines[i].replace(/^\*\s+/, '');
31
+ let j = i + 1;
32
+ while (j < lines.length) {
33
+ const nextLine = lines[j];
34
+ const trimmedNext = nextLine.trim();
35
+ if (nextLine.match(/^\*\s+/)) break;
36
+ if (trimmedNext.startsWith('##') || trimmedNext.startsWith('<!--')) break;
37
+ if (nextLine.startsWith(' ')) {
38
+ text += `\n${nextLine}`;
39
+ j++;
40
+ } else {
41
+ break;
42
+ }
43
+ }
44
+ return { text, nextIndex: j - 1 };
45
+ }
46
+
47
+ function formatFailedNotes(notes: Record<string, Note>): string[] {
48
+ const lines: string[] = [];
49
+ for (const note of Object.values(notes)) {
50
+ if (!note.status) continue;
51
+ if (isNoiseNote(note.message)) continue;
52
+ if (note.message.startsWith('Pilot:')) continue;
53
+ if (note.status === 'passed') {
54
+ lines.push(` ${note.message}`);
55
+ } else if (note.status === 'failed') {
56
+ lines.push(` FAILED ${note.message}`);
57
+ }
58
+ }
59
+ return lines;
60
+ }
61
+
62
+ export function planToCompactAiContext(plan: Plan): string {
63
+ const allTests = plan.getAllTests();
64
+ const passed = allTests.filter((t) => t.result === 'passed');
65
+ const failed = allTests.filter((t) => t.result === 'failed');
66
+ const pending = allTests.filter((t) => !t.result);
67
+
68
+ const summaryParts: string[] = [];
69
+ if (passed.length > 0) summaryParts.push(`${passed.length} passed`);
70
+ if (failed.length > 0) summaryParts.push(`${failed.length} failed`);
71
+ if (pending.length > 0) summaryParts.push(`${pending.length} pending`);
72
+
73
+ let content = `${allTests.length} tests (${summaryParts.join(', ')})\n`;
74
+
75
+ if (passed.length > 0) {
76
+ content += '\n## Passed\n';
77
+ for (const test of passed) {
78
+ content += `- [${test.priority}]${test.style ? ` [${test.style}]` : ''} "${test.scenario}"\n`;
79
+ if (test.startUrl) content += ` url: ${test.startUrl}\n`;
80
+ const verdict = getPilotVerdict(test.notes);
81
+ if (verdict) content += ` Verdict: ${verdict}\n`;
82
+ }
83
+ }
84
+
85
+ if (failed.length > 0) {
86
+ content += '\n## Failed\n';
87
+ for (const test of failed) {
88
+ content += `- [${test.priority}]${test.style ? ` [${test.style}]` : ''} "${test.scenario}"\n`;
89
+ if (test.startUrl) content += ` url: ${test.startUrl}\n`;
90
+ const noteLines = formatFailedNotes(test.notes);
91
+ content += noteLines.join('\n');
92
+ if (noteLines.length > 0) content += '\n';
93
+ }
94
+ }
95
+
96
+ if (pending.length > 0) {
97
+ content += '\n## Pending\n';
98
+ for (const test of pending) {
99
+ content += `- [${test.priority}] "${test.scenario}"\n`;
100
+ }
101
+ }
102
+
103
+ return content;
104
+ }
105
+
106
+ export function parsePlanFromMarkdown(filePath: string): Plan {
107
+ const plans = parsePlansFromMarkdown(filePath);
108
+ if (plans.length === 0) return new Plan('');
109
+ if (plans.length === 1) return plans[0];
110
+
111
+ const main = plans[0];
112
+ for (let i = 1; i < plans.length; i++) {
113
+ for (const test of plans[i].tests) {
114
+ main.addTest(test);
115
+ }
116
+ }
117
+ return main;
118
+ }
119
+
120
+ export function parsePlansFromMarkdown(filePath: string): Plan[] {
121
+ const content = readFileSync(filePath, 'utf-8');
122
+ const lines = content.split('\n');
123
+
124
+ const plans: Plan[] = [];
125
+ let currentPlan: Plan | null = null;
126
+ let currentTest: Test | null = null;
127
+ let inRequirements = false;
128
+ let inSteps = false;
129
+ let inExpected = false;
130
+ let priority: 'critical' | 'important' | 'high' | 'normal' | 'low' = 'normal';
131
+
132
+ for (let i = 0; i < lines.length; i++) {
133
+ const line = lines[i].trim();
134
+
135
+ if (line.startsWith('<!-- suite -->')) {
136
+ currentTest = null;
137
+ inRequirements = false;
138
+ inSteps = false;
139
+ inExpected = false;
140
+ priority = 'normal';
141
+ const title = lines[i + 1]?.replace(/^#\s+/, '') || '';
142
+ currentPlan = new Plan(title);
143
+ plans.push(currentPlan);
144
+ i++;
145
+ continue;
146
+ }
147
+
148
+ if (!currentPlan) continue;
149
+
150
+ if (line.startsWith('<!-- test')) {
151
+ currentTest = null;
152
+ const priorityMatch = line.match(/priority:\s*(\w+)/);
153
+ priority = (priorityMatch?.[1] as 'critical' | 'important' | 'high' | 'normal' | 'low') || 'normal';
154
+ continue;
155
+ }
156
+
157
+ if (line.startsWith('# ') && currentTest === null) {
158
+ const scenario = line.replace(/^#\s+/, '');
159
+ currentTest = new Test(scenario, priority, [], '');
160
+ currentPlan.addTest(currentTest);
161
+ inRequirements = false;
162
+ inSteps = false;
163
+ inExpected = false;
164
+ continue;
165
+ }
166
+
167
+ if (currentTest && line === '## Requirements') {
168
+ inRequirements = true;
169
+ inSteps = false;
170
+ inExpected = false;
171
+ continue;
172
+ }
173
+
174
+ if (currentTest && line === '## Steps') {
175
+ inRequirements = false;
176
+ inSteps = true;
177
+ inExpected = false;
178
+ continue;
179
+ }
180
+
181
+ if (currentTest && line === '## Expected') {
182
+ inRequirements = false;
183
+ inSteps = false;
184
+ inExpected = true;
185
+ continue;
186
+ }
187
+
188
+ if (currentTest && inRequirements && line && !line.startsWith('##')) {
189
+ currentTest.startUrl = line;
190
+ continue;
191
+ }
192
+
193
+ if (currentTest && inSteps && line.startsWith('* ')) {
194
+ const { text, nextIndex } = parseMultiLineBullet(lines, i);
195
+ currentTest.plannedSteps.push(text);
196
+ i = nextIndex;
197
+ continue;
198
+ }
199
+
200
+ if (currentTest && inExpected && line.startsWith('* ')) {
201
+ const { text, nextIndex } = parseMultiLineBullet(lines, i);
202
+ currentTest.expected.push(text);
203
+ i = nextIndex;
204
+ }
205
+ }
206
+
207
+ for (const plan of plans) {
208
+ if (plan.url) continue;
209
+ const suiteStart = content.indexOf(`# ${plan.title}`);
210
+ if (suiteStart === -1) continue;
211
+ const nextSuite = content.indexOf('<!-- suite -->', suiteStart + 1);
212
+ const suiteContent = nextSuite === -1 ? content.slice(suiteStart) : content.slice(suiteStart, nextSuite);
213
+ const firstItem = mdq(suiteContent).query('section[0] item[0]').text().trim();
214
+ const urlMatch = firstItem.match(/^URL:\s*(.+)/);
215
+ if (urlMatch) {
216
+ plan.url = urlMatch[1].replace(/\*\*|`|\*|_|~~?/g, '').trim();
217
+ for (const test of plan.tests) {
218
+ if (!test.startUrl) test.startUrl = plan.url;
219
+ }
220
+ }
221
+ }
222
+
223
+ return plans;
224
+ }
225
+
226
+ function formatPlanSuite(plan: Plan): string {
227
+ let content = `<!-- suite -->\n# ${plan.title}\n\n`;
228
+
229
+ if (plan.url) {
230
+ content += `### Prerequisite\n\n* URL: ${plan.url}\n\n`;
231
+ }
232
+
233
+ if (plan.iteration > 0) {
234
+ content += `<!-- plan updated on ${new Date().toISOString()} -->\n\n`;
235
+ }
236
+
237
+ for (const test of plan.tests) {
238
+ content += `<!-- test\npriority: ${test.priority}\n-->\n`;
239
+ content += `# ${test.scenario}\n\n`;
240
+ content += '## Requirements\n';
241
+ content += `${test.startUrl || 'Current page'}\n\n`;
242
+
243
+ if (test.plannedSteps.length > 0) {
244
+ content += '## Steps\n';
245
+ for (const step of test.plannedSteps) {
246
+ content += formatBulletItem(step);
247
+ }
248
+ content += '\n';
249
+ }
250
+
251
+ content += '## Expected\n';
252
+
253
+ for (const expectation of test.expected) {
254
+ content += formatBulletItem(expectation);
255
+ }
256
+
257
+ content += '\n';
258
+ }
259
+
260
+ return content;
261
+ }
262
+
263
+ export function savePlanToMarkdown(plan: Plan, filePath: string): void {
264
+ writeFileSync(filePath, formatPlanSuite(plan), 'utf-8');
265
+ }
266
+
267
+ export function savePlansToMarkdown(plans: Plan[], filePath: string): void {
268
+ const content = plans.map((plan) => formatPlanSuite(plan)).join('\n');
269
+ writeFileSync(filePath, content, 'utf-8');
270
+ }
271
+
272
+ export function planToAiContext(plan: Plan, options?: { skipSteps?: boolean }): string {
273
+ let content = `# Test Plan: ${plan.title}\n\n`;
274
+
275
+ if (plan.url) {
276
+ content += `**URL:** ${plan.url}\n\n`;
277
+ }
278
+
279
+ content += `**Total Tests:** ${plan.tests.length}\n`;
280
+ content += `**Status:** ${plan.isComplete ? 'Complete' : 'In Progress'}\n\n`;
281
+
282
+ for (let i = 0; i < plan.tests.length; i++) {
283
+ const test = plan.tests[i];
284
+ content += `## Test ${i + 1}: ${test.scenario}\n\n`;
285
+ content += `**Priority:** ${test.priority}\n`;
286
+ content += `**Status:** ${test.status}\n`;
287
+ if (test.result) {
288
+ content += `**Result:** ${test.result}\n`;
289
+ }
290
+ content += '\n';
291
+
292
+ if (test.startUrl) {
293
+ content += `**Start URL:** ${test.startUrl}\n\n`;
294
+ }
295
+
296
+ if (test.plannedSteps.length > 0) {
297
+ content += '**Planned Steps:**\n';
298
+ for (const step of test.plannedSteps) {
299
+ content += `- ${step}\n`;
300
+ }
301
+ content += '\n';
302
+ }
303
+
304
+ if (test.expected.length > 0) {
305
+ content += '**Expected Outcomes:**\n';
306
+ for (const expectation of test.expected) {
307
+ content += `- ${expectation}\n`;
308
+ }
309
+ content += '\n';
310
+ }
311
+
312
+ if (!options?.skipSteps && Object.keys(test.steps).length > 0) {
313
+ content += '**Steps:**\n';
314
+ for (const step of Object.values(test.steps)) {
315
+ content += `- ${step.text}\n`;
316
+ }
317
+ content += '\n';
318
+ }
319
+
320
+ if (Object.keys(test.notes).length > 0) {
321
+ content += '**Notes:**\n';
322
+ for (const note of test.getPrintableNotes()) {
323
+ content += `${note}\n`;
324
+ }
325
+ content += '\n';
326
+ }
327
+
328
+ content += '---\n\n';
329
+ }
330
+
331
+ return content;
332
+ }
@@ -0,0 +1,18 @@
1
+ const DEFAULT_INTERVAL_SECONDS = 30;
2
+
3
+ const lastExecutionByKey = new Map<string, number>();
4
+
5
+ export async function throttle<T>(fn: () => Promise<T> | T, intervalSeconds = DEFAULT_INTERVAL_SECONDS): Promise<T | undefined> {
6
+ const key = fn.toString();
7
+ const now = Date.now();
8
+ const lastExecution = lastExecutionByKey.get(key);
9
+ if (lastExecution !== undefined && now - lastExecution < intervalSeconds * 1000) {
10
+ return undefined;
11
+ }
12
+ lastExecutionByKey.set(key, now);
13
+ return await fn();
14
+ }
15
+
16
+ export function __clearThrottleCacheForTests(): void {
17
+ lastExecutionByKey.clear();
18
+ }
@@ -0,0 +1,14 @@
1
+ import { adjectives, animals, colors, uniqueNamesGenerator } from 'unique-names-generator';
2
+
3
+ const nameConfig = {
4
+ dictionaries: [adjectives, adjectives, colors],
5
+ separator: '',
6
+ length: 3,
7
+ style: 'capital',
8
+ };
9
+
10
+ export function uniqSessionName(): string {
11
+ const name = uniqueNamesGenerator(nameConfig);
12
+ const randomNum = Math.floor(Math.random() * 999);
13
+ return `${name}${randomNum}`;
14
+ }
@@ -0,0 +1,45 @@
1
+ import micromatch from 'micromatch';
2
+
3
+ export function matchesUrl(pattern: string, path: string): boolean {
4
+ if (pattern === '*') return true;
5
+ const norm = (s: string) => s?.replace(/\/+$/, '').toLowerCase();
6
+ if (norm(pattern) === norm(path)) return true;
7
+
8
+ if (pattern.endsWith('/*')) {
9
+ const base = pattern.slice(0, -2).replace(/\/+$/, '');
10
+ const normPath = path.replace(/\/+$/, '');
11
+ if (normPath === base || path.startsWith(`${base}/`)) return true;
12
+ }
13
+
14
+ if (pattern.startsWith('^')) {
15
+ try {
16
+ return new RegExp(pattern.slice(1)).test(path);
17
+ } catch {
18
+ return false;
19
+ }
20
+ }
21
+
22
+ if (pattern.startsWith('~') && pattern.endsWith('~') && pattern.length > 2) {
23
+ try {
24
+ return new RegExp(pattern.slice(1, -1)).test(path);
25
+ } catch {
26
+ return false;
27
+ }
28
+ }
29
+
30
+ try {
31
+ return micromatch.isMatch(path, pattern);
32
+ } catch {
33
+ return false;
34
+ }
35
+ }
36
+
37
+ export function extractStatePath(url: string): string {
38
+ if (url.startsWith('/')) return url;
39
+ try {
40
+ const urlObj = new URL(url);
41
+ return urlObj.pathname + urlObj.hash;
42
+ } catch {
43
+ return url;
44
+ }
45
+ }