explorbot 0.0.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 (329) hide show
  1. package/LICENSE +94 -0
  2. package/README.md +267 -0
  3. package/assets/sample-files/sample.docx +0 -0
  4. package/assets/sample-files/sample.mp3 +0 -0
  5. package/assets/sample-files/sample.mp4 +0 -0
  6. package/assets/sample-files/sample.pdf +21 -0
  7. package/assets/sample-files/sample.png +0 -0
  8. package/assets/sample-files/sample.xlsx +0 -0
  9. package/assets/sample-files/sample.zip +0 -0
  10. package/dist/assets/sample-files/sample.docx +0 -0
  11. package/dist/assets/sample-files/sample.mp3 +0 -0
  12. package/dist/assets/sample-files/sample.mp4 +0 -0
  13. package/dist/assets/sample-files/sample.pdf +21 -0
  14. package/dist/assets/sample-files/sample.png +0 -0
  15. package/dist/assets/sample-files/sample.xlsx +0 -0
  16. package/dist/assets/sample-files/sample.zip +0 -0
  17. package/dist/bin/explorbot-cli.js +683 -0
  18. package/dist/bin/explorbot-cli.js.map +1 -0
  19. package/dist/boat/api-tester/bin/apibot-cli.js +5 -0
  20. package/dist/boat/api-tester/bin/apibot-cli.js.map +1 -0
  21. package/dist/boat/api-tester/example/apibot.config.js +31 -0
  22. package/dist/boat/api-tester/example/apibot.config.js.map +1 -0
  23. package/dist/boat/api-tester/src/ai/chief/styles.js +13 -0
  24. package/dist/boat/api-tester/src/ai/chief/styles.js.map +1 -0
  25. package/dist/boat/api-tester/src/ai/chief.js +301 -0
  26. package/dist/boat/api-tester/src/ai/chief.js.map +1 -0
  27. package/dist/boat/api-tester/src/ai/curler-tools.js +263 -0
  28. package/dist/boat/api-tester/src/ai/curler-tools.js.map +1 -0
  29. package/dist/boat/api-tester/src/ai/curler.js +271 -0
  30. package/dist/boat/api-tester/src/ai/curler.js.map +1 -0
  31. package/dist/boat/api-tester/src/api-client.js +26 -0
  32. package/dist/boat/api-tester/src/api-client.js.map +1 -0
  33. package/dist/boat/api-tester/src/apibot.js +166 -0
  34. package/dist/boat/api-tester/src/apibot.js.map +1 -0
  35. package/dist/boat/api-tester/src/cli.js +262 -0
  36. package/dist/boat/api-tester/src/cli.js.map +1 -0
  37. package/dist/boat/api-tester/src/config.js +159 -0
  38. package/dist/boat/api-tester/src/config.js.map +1 -0
  39. package/dist/prompts/audit-rules.md +124 -0
  40. package/dist/rules/chief/general.md +11 -0
  41. package/dist/rules/chief/styles/curious.md +12 -0
  42. package/dist/rules/chief/styles/hacker.md +19 -0
  43. package/dist/rules/chief/styles/normal.md +11 -0
  44. package/dist/rules/chief/styles/psycho.md +17 -0
  45. package/dist/rules/navigator/multiple-locator.md +47 -0
  46. package/dist/rules/navigator/output.md +69 -0
  47. package/dist/rules/navigator/verification-actions.md +122 -0
  48. package/dist/rules/navigator/verification-output.md +53 -0
  49. package/dist/rules/planner/styles/curious.md +39 -0
  50. package/dist/rules/planner/styles/normal.md +21 -0
  51. package/dist/rules/planner/styles/psycho.md +14 -0
  52. package/dist/rules/researcher/list-element.md +11 -0
  53. package/dist/rules/researcher/screenshot-ui-map.md +30 -0
  54. package/dist/rules/researcher/section-ui-map.md +18 -0
  55. package/dist/rules/researcher/ui-map-table.md +18 -0
  56. package/dist/src/action-result.js +574 -0
  57. package/dist/src/action-result.js.map +1 -0
  58. package/dist/src/action.js +388 -0
  59. package/dist/src/action.js.map +1 -0
  60. package/dist/src/activity.js +86 -0
  61. package/dist/src/activity.js.map +1 -0
  62. package/dist/src/ai/agent.js +2 -0
  63. package/dist/src/ai/agent.js.map +1 -0
  64. package/dist/src/ai/bosun.js +443 -0
  65. package/dist/src/ai/bosun.js.map +1 -0
  66. package/dist/src/ai/captain/idle-mode.js +102 -0
  67. package/dist/src/ai/captain/idle-mode.js.map +1 -0
  68. package/dist/src/ai/captain/mixin.js +11 -0
  69. package/dist/src/ai/captain/mixin.js.map +1 -0
  70. package/dist/src/ai/captain/test-mode.js +251 -0
  71. package/dist/src/ai/captain/test-mode.js.map +1 -0
  72. package/dist/src/ai/captain/web-mode.js +124 -0
  73. package/dist/src/ai/captain/web-mode.js.map +1 -0
  74. package/dist/src/ai/captain.js +442 -0
  75. package/dist/src/ai/captain.js.map +1 -0
  76. package/dist/src/ai/conversation.js +176 -0
  77. package/dist/src/ai/conversation.js.map +1 -0
  78. package/dist/src/ai/experience-compactor.js +232 -0
  79. package/dist/src/ai/experience-compactor.js.map +1 -0
  80. package/dist/src/ai/fisherman-tools.js +154 -0
  81. package/dist/src/ai/fisherman-tools.js.map +1 -0
  82. package/dist/src/ai/fisherman.js +184 -0
  83. package/dist/src/ai/fisherman.js.map +1 -0
  84. package/dist/src/ai/historian.js +384 -0
  85. package/dist/src/ai/historian.js.map +1 -0
  86. package/dist/src/ai/navigator.js +493 -0
  87. package/dist/src/ai/navigator.js.map +1 -0
  88. package/dist/src/ai/pilot.js +684 -0
  89. package/dist/src/ai/pilot.js.map +1 -0
  90. package/dist/src/ai/planner/session-dedup.js +28 -0
  91. package/dist/src/ai/planner/session-dedup.js.map +1 -0
  92. package/dist/src/ai/planner/styles.js +15 -0
  93. package/dist/src/ai/planner/styles.js.map +1 -0
  94. package/dist/src/ai/planner/subpages.js +118 -0
  95. package/dist/src/ai/planner/subpages.js.map +1 -0
  96. package/dist/src/ai/planner.js +486 -0
  97. package/dist/src/ai/planner.js.map +1 -0
  98. package/dist/src/ai/provider.js +540 -0
  99. package/dist/src/ai/provider.js.map +1 -0
  100. package/dist/src/ai/quartermaster.js +210 -0
  101. package/dist/src/ai/quartermaster.js.map +1 -0
  102. package/dist/src/ai/researcher/cache.js +95 -0
  103. package/dist/src/ai/researcher/cache.js.map +1 -0
  104. package/dist/src/ai/researcher/coordinates.js +210 -0
  105. package/dist/src/ai/researcher/coordinates.js.map +1 -0
  106. package/dist/src/ai/researcher/deep-analysis.js +364 -0
  107. package/dist/src/ai/researcher/deep-analysis.js.map +1 -0
  108. package/dist/src/ai/researcher/fingerprint-worker.js +46 -0
  109. package/dist/src/ai/researcher/fingerprint-worker.js.map +1 -0
  110. package/dist/src/ai/researcher/focus.js +37 -0
  111. package/dist/src/ai/researcher/focus.js.map +1 -0
  112. package/dist/src/ai/researcher/locators.js +242 -0
  113. package/dist/src/ai/researcher/locators.js.map +1 -0
  114. package/dist/src/ai/researcher/mixin.js +3 -0
  115. package/dist/src/ai/researcher/mixin.js.map +1 -0
  116. package/dist/src/ai/researcher/parser.js +160 -0
  117. package/dist/src/ai/researcher/parser.js.map +1 -0
  118. package/dist/src/ai/researcher/research-result.js +110 -0
  119. package/dist/src/ai/researcher/research-result.js.map +1 -0
  120. package/dist/src/ai/researcher.js +776 -0
  121. package/dist/src/ai/researcher.js.map +1 -0
  122. package/dist/src/ai/rules.js +368 -0
  123. package/dist/src/ai/rules.js.map +1 -0
  124. package/dist/src/ai/task-agent.js +110 -0
  125. package/dist/src/ai/task-agent.js.map +1 -0
  126. package/dist/src/ai/tester.js +840 -0
  127. package/dist/src/ai/tester.js.map +1 -0
  128. package/dist/src/ai/tools.js +980 -0
  129. package/dist/src/ai/tools.js.map +1 -0
  130. package/dist/src/api/api-client.js +91 -0
  131. package/dist/src/api/api-client.js.map +1 -0
  132. package/dist/src/api/request-result.js +177 -0
  133. package/dist/src/api/request-result.js.map +1 -0
  134. package/dist/src/api/request-store.js +109 -0
  135. package/dist/src/api/request-store.js.map +1 -0
  136. package/dist/src/api/spec-reader.js +148 -0
  137. package/dist/src/api/spec-reader.js.map +1 -0
  138. package/dist/src/api/xhr-capture.js +91 -0
  139. package/dist/src/api/xhr-capture.js.map +1 -0
  140. package/dist/src/browser-server.js +67 -0
  141. package/dist/src/browser-server.js.map +1 -0
  142. package/dist/src/command-handler.js +363 -0
  143. package/dist/src/command-handler.js.map +1 -0
  144. package/dist/src/commands/add-rule-command.js +52 -0
  145. package/dist/src/commands/add-rule-command.js.map +1 -0
  146. package/dist/src/commands/base-command.js +14 -0
  147. package/dist/src/commands/base-command.js.map +1 -0
  148. package/dist/src/commands/clean-command.js +67 -0
  149. package/dist/src/commands/clean-command.js.map +1 -0
  150. package/dist/src/commands/context-aria-command.js +18 -0
  151. package/dist/src/commands/context-aria-command.js.map +1 -0
  152. package/dist/src/commands/context-command.js +57 -0
  153. package/dist/src/commands/context-command.js.map +1 -0
  154. package/dist/src/commands/context-data-command.js +25 -0
  155. package/dist/src/commands/context-data-command.js.map +1 -0
  156. package/dist/src/commands/context-experience-command.js +41 -0
  157. package/dist/src/commands/context-experience-command.js.map +1 -0
  158. package/dist/src/commands/context-html-command.js +26 -0
  159. package/dist/src/commands/context-html-command.js.map +1 -0
  160. package/dist/src/commands/context-knowledge-command.js +36 -0
  161. package/dist/src/commands/context-knowledge-command.js.map +1 -0
  162. package/dist/src/commands/debug-command.js +12 -0
  163. package/dist/src/commands/debug-command.js.map +1 -0
  164. package/dist/src/commands/drill-command.js +29 -0
  165. package/dist/src/commands/drill-command.js.map +1 -0
  166. package/dist/src/commands/exit-command.js +26 -0
  167. package/dist/src/commands/exit-command.js.map +1 -0
  168. package/dist/src/commands/explore-command.js +124 -0
  169. package/dist/src/commands/explore-command.js.map +1 -0
  170. package/dist/src/commands/freesail-command.js +84 -0
  171. package/dist/src/commands/freesail-command.js.map +1 -0
  172. package/dist/src/commands/help-command.js +7 -0
  173. package/dist/src/commands/help-command.js.map +1 -0
  174. package/dist/src/commands/index.js +63 -0
  175. package/dist/src/commands/index.js.map +1 -0
  176. package/dist/src/commands/knows-command.js +54 -0
  177. package/dist/src/commands/knows-command.js.map +1 -0
  178. package/dist/src/commands/learn-command.js +35 -0
  179. package/dist/src/commands/learn-command.js.map +1 -0
  180. package/dist/src/commands/navigate-command.js +16 -0
  181. package/dist/src/commands/navigate-command.js.map +1 -0
  182. package/dist/src/commands/path-command.js +70 -0
  183. package/dist/src/commands/path-command.js.map +1 -0
  184. package/dist/src/commands/plan-clear-command.js +13 -0
  185. package/dist/src/commands/plan-clear-command.js.map +1 -0
  186. package/dist/src/commands/plan-command.js +36 -0
  187. package/dist/src/commands/plan-command.js.map +1 -0
  188. package/dist/src/commands/plan-edit-command.js +8 -0
  189. package/dist/src/commands/plan-edit-command.js.map +1 -0
  190. package/dist/src/commands/plan-load-command.js +16 -0
  191. package/dist/src/commands/plan-load-command.js.map +1 -0
  192. package/dist/src/commands/plan-reload-command.js +23 -0
  193. package/dist/src/commands/plan-reload-command.js.map +1 -0
  194. package/dist/src/commands/plan-save-command.js +22 -0
  195. package/dist/src/commands/plan-save-command.js.map +1 -0
  196. package/dist/src/commands/research-command.js +38 -0
  197. package/dist/src/commands/research-command.js.map +1 -0
  198. package/dist/src/commands/start-command.js +12 -0
  199. package/dist/src/commands/start-command.js.map +1 -0
  200. package/dist/src/commands/status-command.js +19 -0
  201. package/dist/src/commands/status-command.js.map +1 -0
  202. package/dist/src/commands/test-command.js +85 -0
  203. package/dist/src/commands/test-command.js.map +1 -0
  204. package/dist/src/components/ActivityPane.js +55 -0
  205. package/dist/src/components/ActivityPane.js.map +1 -0
  206. package/dist/src/components/AddKnowledge.js +122 -0
  207. package/dist/src/components/AddKnowledge.js.map +1 -0
  208. package/dist/src/components/AddRule.js +117 -0
  209. package/dist/src/components/AddRule.js.map +1 -0
  210. package/dist/src/components/App.js +313 -0
  211. package/dist/src/components/App.js.map +1 -0
  212. package/dist/src/components/Autocomplete.js +43 -0
  213. package/dist/src/components/Autocomplete.js.map +1 -0
  214. package/dist/src/components/InputPane.js +207 -0
  215. package/dist/src/components/InputPane.js.map +1 -0
  216. package/dist/src/components/InputReadline.js +598 -0
  217. package/dist/src/components/InputReadline.js.map +1 -0
  218. package/dist/src/components/LogPane.js +123 -0
  219. package/dist/src/components/LogPane.js.map +1 -0
  220. package/dist/src/components/PlanEditor.js +126 -0
  221. package/dist/src/components/PlanEditor.js.map +1 -0
  222. package/dist/src/components/PlanPane.js +51 -0
  223. package/dist/src/components/PlanPane.js.map +1 -0
  224. package/dist/src/components/SessionTimer.js +26 -0
  225. package/dist/src/components/SessionTimer.js.map +1 -0
  226. package/dist/src/components/StateTransitionPane.js +107 -0
  227. package/dist/src/components/StateTransitionPane.js.map +1 -0
  228. package/dist/src/components/StatusPane.js +37 -0
  229. package/dist/src/components/StatusPane.js.map +1 -0
  230. package/dist/src/components/TaskPane.js +96 -0
  231. package/dist/src/components/TaskPane.js.map +1 -0
  232. package/dist/src/components/Welcome.js +52 -0
  233. package/dist/src/components/Welcome.js.map +1 -0
  234. package/dist/src/components/WelcomeChecklist.js +96 -0
  235. package/dist/src/components/WelcomeChecklist.js.map +1 -0
  236. package/dist/src/components/WelcomeCommands.js +61 -0
  237. package/dist/src/components/WelcomeCommands.js.map +1 -0
  238. package/dist/src/components/autocomplete-store.js +22 -0
  239. package/dist/src/components/autocomplete-store.js.map +1 -0
  240. package/dist/src/components/parse-keypress.js +174 -0
  241. package/dist/src/components/parse-keypress.js.map +1 -0
  242. package/dist/src/config.js +249 -0
  243. package/dist/src/config.js.map +1 -0
  244. package/dist/src/execution-controller.js +92 -0
  245. package/dist/src/execution-controller.js.map +1 -0
  246. package/dist/src/experience-tracker.js +294 -0
  247. package/dist/src/experience-tracker.js.map +1 -0
  248. package/dist/src/explorbot.js +348 -0
  249. package/dist/src/explorbot.js.map +1 -0
  250. package/dist/src/explorer.js +611 -0
  251. package/dist/src/explorer.js.map +1 -0
  252. package/dist/src/index.js +56 -0
  253. package/dist/src/index.js.map +1 -0
  254. package/dist/src/knowledge-tracker.js +184 -0
  255. package/dist/src/knowledge-tracker.js.map +1 -0
  256. package/dist/src/observability.js +126 -0
  257. package/dist/src/observability.js.map +1 -0
  258. package/dist/src/reporter.js +185 -0
  259. package/dist/src/reporter.js.map +1 -0
  260. package/dist/src/state-manager.js +427 -0
  261. package/dist/src/state-manager.js.map +1 -0
  262. package/dist/src/stats.js +44 -0
  263. package/dist/src/stats.js.map +1 -0
  264. package/dist/src/test-plan.js +343 -0
  265. package/dist/src/test-plan.js.map +1 -0
  266. package/dist/src/utils/aria.js +588 -0
  267. package/dist/src/utils/aria.js.map +1 -0
  268. package/dist/src/utils/code-extractor.js +21 -0
  269. package/dist/src/utils/code-extractor.js.map +1 -0
  270. package/dist/src/utils/context-formatter.js +205 -0
  271. package/dist/src/utils/context-formatter.js.map +1 -0
  272. package/dist/src/utils/error-page.js +19 -0
  273. package/dist/src/utils/error-page.js.map +1 -0
  274. package/dist/src/utils/expandable.js +35 -0
  275. package/dist/src/utils/expandable.js.map +1 -0
  276. package/dist/src/utils/hooks-runner.js +77 -0
  277. package/dist/src/utils/hooks-runner.js.map +1 -0
  278. package/dist/src/utils/html-diff.js +734 -0
  279. package/dist/src/utils/html-diff.js.map +1 -0
  280. package/dist/src/utils/html.js +1163 -0
  281. package/dist/src/utils/html.js.map +1 -0
  282. package/dist/src/utils/logger.js +465 -0
  283. package/dist/src/utils/logger.js.map +1 -0
  284. package/dist/src/utils/loop.js +126 -0
  285. package/dist/src/utils/loop.js.map +1 -0
  286. package/dist/src/utils/markdown-parser.js +117 -0
  287. package/dist/src/utils/markdown-parser.js.map +1 -0
  288. package/dist/src/utils/markdown-query.js +393 -0
  289. package/dist/src/utils/markdown-query.js.map +1 -0
  290. package/dist/src/utils/markdown-terminal.js +40 -0
  291. package/dist/src/utils/markdown-terminal.js.map +1 -0
  292. package/dist/src/utils/research-parser.js +2 -0
  293. package/dist/src/utils/research-parser.js.map +1 -0
  294. package/dist/src/utils/retry.js +55 -0
  295. package/dist/src/utils/retry.js.map +1 -0
  296. package/dist/src/utils/rules-loader.js +104 -0
  297. package/dist/src/utils/rules-loader.js.map +1 -0
  298. package/dist/src/utils/strings.js +14 -0
  299. package/dist/src/utils/strings.js.map +1 -0
  300. package/dist/src/utils/test-plan-markdown.js +301 -0
  301. package/dist/src/utils/test-plan-markdown.js.map +1 -0
  302. package/dist/src/utils/throttle.js +16 -0
  303. package/dist/src/utils/throttle.js.map +1 -0
  304. package/dist/src/utils/unique-names.js +13 -0
  305. package/dist/src/utils/unique-names.js.map +1 -0
  306. package/dist/src/utils/url-matcher.js +48 -0
  307. package/dist/src/utils/url-matcher.js.map +1 -0
  308. package/dist/src/utils/web-element.js +131 -0
  309. package/dist/src/utils/web-element.js.map +1 -0
  310. package/dist/src/utils/xpath.js +110 -0
  311. package/dist/src/utils/xpath.js.map +1 -0
  312. package/package.json +119 -0
  313. package/prompts/audit-rules.md +124 -0
  314. package/rules/chief/general.md +11 -0
  315. package/rules/chief/styles/curious.md +12 -0
  316. package/rules/chief/styles/hacker.md +19 -0
  317. package/rules/chief/styles/normal.md +11 -0
  318. package/rules/chief/styles/psycho.md +17 -0
  319. package/rules/navigator/multiple-locator.md +47 -0
  320. package/rules/navigator/output.md +69 -0
  321. package/rules/navigator/verification-actions.md +122 -0
  322. package/rules/navigator/verification-output.md +53 -0
  323. package/rules/planner/styles/curious.md +39 -0
  324. package/rules/planner/styles/normal.md +21 -0
  325. package/rules/planner/styles/psycho.md +14 -0
  326. package/rules/researcher/list-element.md +11 -0
  327. package/rules/researcher/screenshot-ui-map.md +30 -0
  328. package/rules/researcher/section-ui-map.md +18 -0
  329. package/rules/researcher/ui-map-table.md +18 -0
@@ -0,0 +1,263 @@
1
+ import { tool } from 'ai';
2
+ import { expect } from 'expect';
3
+ import { readFileSync } from 'node:fs';
4
+ import dedent from 'dedent';
5
+ import { z } from 'zod';
6
+ import { TestResult } from "../../../../src/test-plan.js";
7
+ import { tag } from "../../../../src/utils/logger.js";
8
+ const readResponseData = (responseFile) => {
9
+ return JSON.parse(readFileSync(responseFile, 'utf8'));
10
+ };
11
+ function summarizeStructure(value, depth = 0) {
12
+ if (depth > 2)
13
+ return '...';
14
+ if (Array.isArray(value)) {
15
+ if (value.length === 0)
16
+ return '[]';
17
+ return `[${summarizeStructure(value[0], depth + 1)}]`;
18
+ }
19
+ if (value && typeof value === 'object') {
20
+ const entries = Object.keys(value).slice(0, 10);
21
+ const parts = entries.map((k) => `${k}: ${summarizeStructure(value[k], depth + 1)}`);
22
+ if (Object.keys(value).length > 10)
23
+ parts.push('...');
24
+ return `{ ${parts.join(', ')} }`;
25
+ }
26
+ return typeof value;
27
+ }
28
+ export function createCurlerTools(apiClient, requestState, test, searchSpec) {
29
+ const commitVerification = (label, passed, failDetail) => {
30
+ const activeNote = test.startNote(label);
31
+ if (passed) {
32
+ tag('success').log(`${label} passed`);
33
+ activeNote.commit(TestResult.PASSED);
34
+ }
35
+ else {
36
+ tag('warning').log(`${label} failed — ${failDetail}`);
37
+ activeNote.commit(TestResult.FAILED);
38
+ }
39
+ };
40
+ return {
41
+ request: tool({
42
+ description: dedent `
43
+ Make an HTTP request to the API endpoint.
44
+ Returns status, timing, and a preview of the response body (first 500 chars).
45
+ Full response is saved to responseFile — use verifyStructure and verifyData to check it.
46
+ `,
47
+ inputSchema: z.object({
48
+ method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']).describe('HTTP method'),
49
+ path: z.string().describe('API path (e.g., /users, /users/1)'),
50
+ headers: z.record(z.string(), z.string()).optional().describe('Additional headers'),
51
+ body: z.any().optional().describe('Request body (JSON object or string)'),
52
+ queryParams: z.record(z.string(), z.string()).optional().describe('Query parameters'),
53
+ }),
54
+ execute: async (input) => {
55
+ tag('step').log(`${input.method} ${input.path}`);
56
+ const activeNote = test.startNote(`${input.method} ${input.path}`);
57
+ const result = await apiClient.request({
58
+ method: input.method,
59
+ path: input.path,
60
+ headers: input.headers,
61
+ body: input.body,
62
+ queryParams: input.queryParams,
63
+ });
64
+ requestState.addRequest(result);
65
+ if (result.error) {
66
+ tag('error').log(`${input.method} ${input.path} > Network error: ${result.error}`);
67
+ activeNote.commit(TestResult.FAILED);
68
+ return {
69
+ success: false,
70
+ error: result.error,
71
+ curlCommand: result.toCurlCommand(),
72
+ };
73
+ }
74
+ const statusLine = `${result.status} ${result.statusText}`;
75
+ const bodyString = input.body ? (typeof input.body === 'string' ? input.body : JSON.stringify(input.body, null, 2)) : undefined;
76
+ test.addStep(`${input.method} ${input.path} > ${statusLine} (${result.timing}ms)`, result.timing, undefined, undefined, bodyString, [result.responseFile]);
77
+ if (result.status >= 400) {
78
+ tag('error').log(`${input.method} ${input.path} > ${statusLine} (${result.timing}ms)`);
79
+ activeNote.commit(TestResult.FAILED);
80
+ }
81
+ else {
82
+ tag('success').log(`${input.method} ${input.path} > ${statusLine} (${result.timing}ms)`);
83
+ activeNote.commit(TestResult.PASSED);
84
+ }
85
+ if (bodyString) {
86
+ tag('multiline').log(`Request body:\n${bodyString}`);
87
+ }
88
+ tag('multiline').log(`Response body:\n${result.rawResponseBody}`);
89
+ return {
90
+ success: true,
91
+ status: result.status,
92
+ statusText: result.statusText,
93
+ timing: result.timing,
94
+ responsePreview: result.rawResponseBody.substring(0, 500),
95
+ responseFile: result.responseFile,
96
+ curlCommand: result.toCurlCommand(),
97
+ };
98
+ },
99
+ }),
100
+ ...(searchSpec
101
+ ? {
102
+ schemaFor: tool({
103
+ description: dedent `
104
+ Search the API spec for endpoints matching a keyword.
105
+ Returns endpoint paths, HTTP methods, and schema definitions.
106
+ Use to discover related endpoints when you need to set up test data or find dependencies.
107
+ Pass "*" to list all available endpoints (paths and methods only).
108
+ `,
109
+ inputSchema: z.object({
110
+ query: z.string().describe('Keyword to search for in endpoint paths (e.g., "comments", "users"). Use "*" to list all endpoints.'),
111
+ }),
112
+ execute: async ({ query }) => {
113
+ tag('step').log(`Schema lookup: ${query}`);
114
+ return { result: searchSpec(query) };
115
+ },
116
+ }),
117
+ }
118
+ : {}),
119
+ verifyStructure: tool({
120
+ description: dedent `
121
+ Verify response JSON structure using a Zod schema.
122
+ Write a JS expression that returns a Zod schema (z is available).
123
+ Example schema: "z.object({ id: z.number(), name: z.string(), items: z.array(z.object({ title: z.string() })) })"
124
+ Returns validation errors if the response doesn't match.
125
+ On success, returns a "structure" field showing the actual response shape — use it to write correct verifyData assertions.
126
+ `,
127
+ inputSchema: z.object({
128
+ responseFile: z.string().describe('Path to the response JSON file'),
129
+ schema: z.string().describe('JS expression returning a Zod schema, e.g. "z.object({ id: z.number(), name: z.string() })"'),
130
+ }),
131
+ execute: async ({ responseFile, schema: schemaCode }) => {
132
+ let data;
133
+ try {
134
+ data = readResponseData(responseFile);
135
+ }
136
+ catch (e) {
137
+ commitVerification('Structure check: failed to read response', false, e.message);
138
+ return { passed: false, errors: [e.message] };
139
+ }
140
+ try {
141
+ const schemaObj = new Function('z', `return ${schemaCode}`)(z);
142
+ const result = schemaObj.safeParse(data);
143
+ if (result.success) {
144
+ commitVerification('Structure check: ✓ schema valid', true, '');
145
+ const structure = summarizeStructure(data);
146
+ return { passed: true, errors: [], structure };
147
+ }
148
+ const errors = result.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`);
149
+ commitVerification(`Structure check: ✗ ${errors.join(', ')}`, false, errors.join('; '));
150
+ return { passed: false, errors };
151
+ }
152
+ catch (e) {
153
+ commitVerification(`Structure check: schema error — ${e.message}`, false, e.message);
154
+ return { passed: false, errors: [e.message] };
155
+ }
156
+ },
157
+ }),
158
+ verifyData: tool({
159
+ description: dedent `
160
+ Verify specific values in the response JSON using expect() assertions.
161
+ Each assertion is a JS expression with "response" (full parsed JSON body) and "expect" (Jest expect) available.
162
+ "response" is the entire parsed JSON — access nested fields accordingly.
163
+ Example assertions:
164
+ - "expect(response.name).toBe('Test Suite')"
165
+ - "expect(response.items).toHaveLength(3)"
166
+ - "expect(response).toHaveProperty('id')"
167
+ - "expect(response.count).toBeGreaterThan(0)"
168
+ - "expect(response.data.tags).toContain('urgent')"
169
+ - "expect(response).toMatchObject({ status: 'active' })"
170
+ `,
171
+ inputSchema: z.object({
172
+ responseFile: z.string().describe('Path to the response JSON file'),
173
+ assertions: z.array(z.string()).describe('JS expressions using expect(response) — e.g. "expect(response.id).toBe(1)"'),
174
+ }),
175
+ execute: async ({ responseFile, assertions }) => {
176
+ let response;
177
+ try {
178
+ response = readResponseData(responseFile);
179
+ }
180
+ catch (e) {
181
+ commitVerification('Data check: failed to read response', false, e.message);
182
+ return { passed: false, results: [{ code: '<read file>', passed: false, error: e.message }] };
183
+ }
184
+ const results = [];
185
+ for (const code of assertions) {
186
+ try {
187
+ new Function('expect', 'response', code)(expect, response);
188
+ results.push({ code, passed: true });
189
+ }
190
+ catch (e) {
191
+ results.push({ code, passed: false, error: e.message });
192
+ }
193
+ }
194
+ const passed = results.every((r) => r.passed);
195
+ const okResults = results.filter((r) => r.passed);
196
+ const failedResults = results.filter((r) => !r.passed);
197
+ let label = 'Data check:';
198
+ if (okResults.length > 0)
199
+ label += ` ✓ ${okResults.map((r) => r.code).join(', ')}`;
200
+ if (failedResults.length > 0)
201
+ label += ` ✗ ${failedResults.map((f) => `${f.code}: ${f.error}`).join(', ')}`;
202
+ commitVerification(label, passed, failedResults.map((f) => `${f.code}: ${f.error}`).join('; '));
203
+ return { passed, results };
204
+ },
205
+ }),
206
+ record: tool({
207
+ description: dedent `
208
+ Record a note or observation during testing.
209
+ Use status "success" for achieved outcomes, "fail" for failures.
210
+ `,
211
+ inputSchema: z.object({
212
+ note: z.string().describe('Observation or finding'),
213
+ status: z.enum(['success', 'fail']).optional().describe('Outcome status'),
214
+ }),
215
+ execute: async ({ note, status }) => {
216
+ let mappedStatus = null;
217
+ if (status === 'success')
218
+ mappedStatus = TestResult.PASSED;
219
+ else if (status === 'fail')
220
+ mappedStatus = TestResult.FAILED;
221
+ test.addNote(note, mappedStatus);
222
+ if (status === 'success') {
223
+ tag('success').log(`${note}`);
224
+ }
225
+ else if (status === 'fail') {
226
+ tag('warning').log(`${note}`);
227
+ }
228
+ return { recorded: true };
229
+ },
230
+ }),
231
+ finish: tool({
232
+ description: dedent `
233
+ Mark the test as complete. Use when all test goals are achieved.
234
+ `,
235
+ inputSchema: z.object({
236
+ summary: z.string().describe('Summary of what was tested and results'),
237
+ }),
238
+ execute: async ({ summary }) => {
239
+ test.summary = summary;
240
+ test.addNote(`Test complete: ${summary}`, TestResult.PASSED);
241
+ test.finish(TestResult.PASSED);
242
+ tag('success').log(`Test finished: ${summary}`);
243
+ return { finished: true };
244
+ },
245
+ }),
246
+ stop: tool({
247
+ description: dedent `
248
+ Abort the test. Use when the scenario cannot be completed.
249
+ `,
250
+ inputSchema: z.object({
251
+ reason: z.string().describe('Why the test cannot continue'),
252
+ }),
253
+ execute: async ({ reason }) => {
254
+ test.summary = `Test stopped: ${reason}`;
255
+ test.addNote(`Test stopped: ${reason}`, TestResult.FAILED);
256
+ test.finish(TestResult.FAILED);
257
+ tag('warning').log(`Test stopped: ${reason}`);
258
+ return { stopped: true };
259
+ },
260
+ }),
261
+ };
262
+ }
263
+ //# sourceMappingURL=curler-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"curler-tools.js","sourceRoot":"","sources":["../../../../../boat/api-tester/src/ai/curler-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,271 @@
1
+ import dedent from 'dedent';
2
+ import { z } from 'zod';
3
+ import { TestResult } from "../../../../src/test-plan.js";
4
+ import { createDebug, tag } from "../../../../src/utils/logger.js";
5
+ import { loop } from "../../../../src/utils/loop.js";
6
+ import { createCurlerTools } from "./curler-tools.js";
7
+ const debugLog = createDebug('explorbot:curler');
8
+ const MAX_ITERATIONS = 10;
9
+ export class Curler {
10
+ provider;
11
+ apiClient;
12
+ requestState;
13
+ reporter;
14
+ constructor(provider, apiClient, requestState, reporter) {
15
+ this.provider = provider;
16
+ this.apiClient = apiClient;
17
+ this.requestState = requestState;
18
+ this.reporter = reporter;
19
+ }
20
+ async test(test, opts) {
21
+ tag('info').log(`Testing: ${test.scenario}`);
22
+ debugLog('Starting test:', test.scenario);
23
+ this.requestState.clear();
24
+ test.start();
25
+ await this.reporter.reportTestStart(test);
26
+ const conversation = this.provider.startConversation(this.buildSystemPrompt(), 'curler', this.provider.getAgenticModel('curler'));
27
+ const tools = createCurlerTools(this.apiClient, this.requestState, test, opts?.searchSpec);
28
+ const initialPrompt = this.buildTestPrompt(test, opts?.specDefinition, opts?.baseEndpoint);
29
+ conversation.addUserText(initialPrompt);
30
+ await loop(async ({ stop, iteration }) => {
31
+ debugLog(`Iteration ${iteration}`);
32
+ if (iteration > 1) {
33
+ const requestLog = this.requestState.toLog();
34
+ const nextStep = dedent `
35
+ <request_log>
36
+ ${requestLog || 'No requests made yet'}
37
+ </request_log>
38
+
39
+ <task>
40
+ Continue testing. Review the request log above and proceed with the next step.
41
+ </task>
42
+
43
+ <notes>
44
+ ${test.notesToString() || 'No notes yet'}
45
+ </notes>
46
+ `;
47
+ conversation.addUserText(nextStep);
48
+ }
49
+ const result = await this.provider.invokeConversation(conversation, tools, {
50
+ maxToolRoundtrips: 5,
51
+ toolChoice: 'required',
52
+ agentName: 'curler',
53
+ });
54
+ if (!result)
55
+ throw new Error('Failed to get response from provider');
56
+ const toolNames = result.toolExecutions?.map((e) => e.toolName) || [];
57
+ debugLog('Tool calls:', toolNames.join(', '));
58
+ if (test.hasFinished) {
59
+ stop();
60
+ return;
61
+ }
62
+ if (iteration >= MAX_ITERATIONS) {
63
+ tag('warning').log('Max iterations reached, running final review...');
64
+ stop();
65
+ }
66
+ }, {
67
+ maxAttempts: MAX_ITERATIONS,
68
+ observability: {
69
+ name: `curler: ${test.scenario}`,
70
+ agent: 'curler',
71
+ sessionId: test.sessionName,
72
+ metadata: {
73
+ input: {
74
+ scenario: test.scenario,
75
+ startUrl: test.startUrl,
76
+ expected: test.expected,
77
+ },
78
+ },
79
+ },
80
+ catch: async ({ error, stop }) => {
81
+ tag('error').log(`Test execution error: ${error}`);
82
+ stop();
83
+ },
84
+ });
85
+ try {
86
+ await this.finalReview(test);
87
+ }
88
+ catch (error) {
89
+ tag('error').log(`Final review failed: ${error}`);
90
+ }
91
+ this.finishTest(test);
92
+ const meta = {
93
+ endpoint: test.startUrl,
94
+ style: test.style,
95
+ sessionName: test.sessionName,
96
+ };
97
+ await this.reporter.reportTest(test, meta);
98
+ return { success: test.isSuccessful };
99
+ }
100
+ finishTest(test) {
101
+ if (!test.hasFinished) {
102
+ test.finish(TestResult.FAILED);
103
+ }
104
+ tag('info').log(`Finished: ${test.scenario}`);
105
+ tag('multiline').log(test.getPrintableNotes().join('\n'));
106
+ if (test.isSuccessful) {
107
+ tag('success').log(`Passed: ${test.scenario}`);
108
+ }
109
+ else if (test.isSkipped) {
110
+ tag('warning').log(`Skipped: ${test.scenario}`);
111
+ }
112
+ else {
113
+ tag('error').log(`Failed: ${test.scenario}`);
114
+ }
115
+ }
116
+ async finalReview(test) {
117
+ const notes = test.notesToString() || 'No notes recorded.';
118
+ const requestLog = this.requestState.toLog() || 'No requests made.';
119
+ const hasFailedNotes = test.getCheckedNotes().some((n) => n.status === TestResult.FAILED);
120
+ const isUnfinished = !test.hasFinished;
121
+ if (!hasFailedNotes && !isUnfinished)
122
+ return;
123
+ tag('info').log('Running final review...');
124
+ const schema = z.object({
125
+ summary: z.string().describe('One-line summary of test results'),
126
+ goalsAchieved: z.boolean().describe('Whether the main test goals were accomplished'),
127
+ failuresCritical: z.boolean().describe('Whether any assertion failures are critical enough to fail the test'),
128
+ details: z.string().describe('Brief explanation of what passed, what failed, and why failures are or are not critical'),
129
+ });
130
+ const model = this.provider.getAgenticModel('curler');
131
+ const response = await this.provider.generateObject([
132
+ {
133
+ role: 'system',
134
+ content: this.buildFinalReviewSystemPrompt(),
135
+ },
136
+ {
137
+ role: 'user',
138
+ content: dedent `
139
+ Scenario: ${test.scenario}
140
+
141
+ Expected outcomes:
142
+ ${test.expected.map((e) => `- ${e}`).join('\n')}
143
+
144
+ <notes>
145
+ ${notes}
146
+ </notes>
147
+
148
+ <request_log>
149
+ ${requestLog}
150
+ </request_log>
151
+
152
+ Evaluate:
153
+ 1. Were the expected outcomes achieved based on the request results?
154
+ 2. Are any assertion failures critical (wrong status codes, missing core data) or minor (optional fields, cosmetic)?
155
+ 3. Should the test pass or fail overall?
156
+ `,
157
+ },
158
+ ], schema, model);
159
+ const result = response?.object;
160
+ if (!result)
161
+ return;
162
+ test.summary = result.summary;
163
+ test.addNote(`Review: ${result.details}`);
164
+ if (result.goalsAchieved && !result.failuresCritical) {
165
+ test.addNote(result.summary, TestResult.PASSED);
166
+ test.finish(TestResult.PASSED);
167
+ }
168
+ else {
169
+ test.addNote(result.summary, TestResult.FAILED);
170
+ test.finish(TestResult.FAILED);
171
+ }
172
+ }
173
+ buildTestPrompt(test, specDefinition, baseEndpoint) {
174
+ let prompt = dedent `
175
+ <task>
176
+ SCENARIO: ${test.scenario}
177
+
178
+ EXPECTED OUTCOMES:
179
+ ${test.expected.map((e) => `- ${e}`).join('\n')}
180
+
181
+ PLANNED STEPS:
182
+ ${test.plannedSteps.map((s) => `- ${s}`).join('\n')}
183
+
184
+ ENDPOINT: ${test.startUrl}
185
+
186
+ Execute the test by making HTTP requests using the request tool.
187
+ Use verifyStructure and verifyData to check responses.
188
+ Use record to document findings.
189
+ Use finish when all goals are achieved.
190
+ Use stop only if the scenario is fundamentally impossible.
191
+ </task>
192
+ `;
193
+ if (specDefinition) {
194
+ let specBlock = `\n\n<api_spec>\n${specDefinition}\n</api_spec>`;
195
+ if (baseEndpoint) {
196
+ specBlock += dedent `
197
+
198
+ <path_mapping>
199
+ IMPORTANT: The spec shows absolute paths (e.g. /api/v2/{project_id}/suites) but the base URL is ${baseEndpoint}.
200
+ The request tool prepends the base URL automatically.
201
+ Use ONLY the relative path after the base prefix: e.g. /suites, /suites/{id} — NOT the full spec path.
202
+ </path_mapping>
203
+ `;
204
+ }
205
+ prompt += specBlock;
206
+ }
207
+ return prompt;
208
+ }
209
+ buildSystemPrompt() {
210
+ return dedent `
211
+ <role>
212
+ You are a senior API test engineer. Execute HTTP requests to test API endpoints.
213
+ Validate responses against expectations.
214
+ </role>
215
+
216
+ <approach>
217
+ 1. Use the request tool to make HTTP calls — response preview shows first 500 chars
218
+ 2. If you need a related endpoint (e.g., to create prerequisite data), use schemaFor to discover it
219
+ 3. Extract IDs and key values from the response preview to chain requests
220
+ 4. After each request, use verifyStructure with a Zod schema to validate response shape
221
+ Example: schema = "z.object({ id: z.number(), name: z.string(), items: z.array(z.string()) })"
222
+ Use z.any() for fields you don't care about, z.optional() for nullable fields
223
+ 5. Use verifyData with expect() assertions to check specific values
224
+ The "response" variable is the full parsed JSON body — use the structure from verifyStructure to access nested fields correctly
225
+ Example: "expect(response.name).toBe('Test Suite')"
226
+ Example: "expect(response.data.items).toHaveLength(3)"
227
+ Example: "expect(response.status).not.toBe('deleted')"
228
+ 6. Use record to document findings and observations
229
+ 7. Use finish when all test goals are achieved and verified
230
+ 8. Use stop only if the scenario cannot be completed at all
231
+ </approach>
232
+
233
+ <rules>
234
+ - Always check HTTP status codes from the request tool result
235
+ - After each request, verify structure with a Zod schema matching the API spec
236
+ - Use verifyData with expect() for value assertions — "response" is the full parsed JSON, use the structure returned by verifyStructure to access the correct paths
237
+ - For CRUD tests: create first, extract ID from preview, then read/update/delete
238
+ - Chain requests logically — extract IDs from response preview
239
+ - Record important findings as you go
240
+ - Be precise about what you expect vs what you observe
241
+ - If a test requires data from another endpoint, use schemaFor to look it up before guessing
242
+ </rules>
243
+ `;
244
+ }
245
+ buildFinalReviewSystemPrompt() {
246
+ return dedent `
247
+ You evaluate API test results.
248
+ Analyze notes and request log to determine if test goals were achieved.
249
+ Decide if assertion failures are critical (should fail the test) or minor (test still passes).
250
+
251
+
252
+ Status code rules:
253
+ - 500+ errors are always critical failures regardless of test type
254
+ - 4xx codes (400, 403, 404, 412, 422, etc.) are NOT failures by themselves — different APIs return different 4xx codes for similar situations, so do not be strict about the exact 4xx code
255
+
256
+ For positive tests (creating/updating data):
257
+ - Focus on whether the expected data was actually saved correctly
258
+ - Never trust POST/PUT/PATCH response alone — a follow-up GET must confirm the data was persisted
259
+ - The real measure of success is the data state verified via GET, not the write response or status code
260
+
261
+ For negative tests (invalid input, unauthorized access):
262
+ - PUT and PATCH methods are usually the same so do not treat them differently.
263
+ - Verify the invalid data was NOT created — check with GET to ensure nothing was saved by accident
264
+ - Any 4xx response that correctly rejects the request is acceptable
265
+
266
+ Critical failures: 500+ errors, data not saved in positive tests, invalid data accidentally created in negative tests, broken CRUD operations.
267
+ Minor failures: optional fields missing, cosmetic differences, extra fields in response, different-than-expected 4xx code.
268
+ `;
269
+ }
270
+ }
271
+ //# sourceMappingURL=curler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"curler.js","sourceRoot":"","sources":["../../../../../boat/api-tester/src/ai/curler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { ApiClient as BaseApiClient } from "../../../src/api/api-client.js";
2
+ export class ApiClient extends BaseApiClient {
3
+ bootstrapHook;
4
+ teardownHook;
5
+ constructor(baseEndpoint, defaultHeaders = {}, hooks) {
6
+ super(baseEndpoint, defaultHeaders);
7
+ this.bootstrapHook = hooks?.bootstrap;
8
+ this.teardownHook = hooks?.teardown;
9
+ }
10
+ async bootstrap() {
11
+ if (!this.bootstrapHook)
12
+ return;
13
+ const ctx = { headers: this.getHeaders(), baseEndpoint: this.getBaseEndpoint() };
14
+ const result = await this.bootstrapHook(ctx);
15
+ if (result && typeof result === 'object') {
16
+ this.setHeaders(result);
17
+ }
18
+ }
19
+ async teardown() {
20
+ if (!this.teardownHook)
21
+ return;
22
+ const ctx = { headers: this.getHeaders(), baseEndpoint: this.getBaseEndpoint() };
23
+ await this.teardownHook(ctx);
24
+ }
25
+ }
26
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../../../boat/api-tester/src/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG5E,MAAM,OAAO,SAAU,SAAQ,aAAa;IAClC,aAAa,CAAU;IACvB,YAAY,CAAU;IAE9B,YAAY,YAAoB,EAAE,iBAAyC,EAAE,EAAE,KAAiD;QAC9H,KAAK,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;QACpC,IAAI,CAAC,aAAa,GAAG,KAAK,EAAE,SAAS,CAAC;QACtC,IAAI,CAAC,YAAY,GAAG,KAAK,EAAE,QAAQ,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,SAAS;QACb,IAAI,CAAC,IAAI,CAAC,aAAa;YAAE,OAAO;QAChC,MAAM,GAAG,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;QACjF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QAC7C,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACzC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO;QAC/B,MAAM,GAAG,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;QACjF,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;CACF"}