explorbot 0.0.1 → 0.0.5
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.
- package/README.md +80 -26
- package/bin/explorbot-cli.ts +679 -0
- package/boat/api-tester/src/ai/chief/styles.ts +15 -0
- package/boat/api-tester/src/ai/chief.ts +335 -0
- package/boat/api-tester/src/ai/curler-tools.ts +278 -0
- package/boat/api-tester/src/ai/curler.ts +306 -0
- package/boat/api-tester/src/api-client.ts +28 -0
- package/boat/api-tester/src/apibot.ts +203 -0
- package/boat/api-tester/src/cli.ts +301 -0
- package/boat/api-tester/src/config.ts +190 -0
- package/dist/bin/explorbot-cli.js +19 -98
- package/dist/boat/api-tester/bin/apibot-cli.js +0 -1
- package/dist/boat/api-tester/src/ai/chief/styles.js +0 -1
- package/dist/boat/api-tester/src/ai/chief.js +0 -1
- package/dist/boat/api-tester/src/ai/curler-tools.js +0 -1
- package/dist/boat/api-tester/src/ai/curler.js +0 -1
- package/dist/boat/api-tester/src/api-client.js +0 -1
- package/dist/boat/api-tester/src/apibot.js +0 -1
- package/dist/boat/api-tester/src/cli.js +0 -1
- package/dist/boat/api-tester/src/config.js +0 -1
- package/dist/src/action-result.js +0 -1
- package/dist/src/action.js +0 -1
- package/dist/src/activity.js +0 -1
- package/dist/src/ai/agent.js +0 -1
- package/dist/src/ai/bosun.js +0 -1
- package/dist/src/ai/captain/idle-mode.js +0 -1
- package/dist/src/ai/captain/mixin.js +0 -1
- package/dist/src/ai/captain/test-mode.js +0 -1
- package/dist/src/ai/captain/web-mode.js +0 -1
- package/dist/src/ai/captain.js +0 -1
- package/dist/src/ai/conversation.js +0 -1
- package/dist/src/ai/experience-compactor.js +0 -1
- package/dist/src/ai/fisherman-tools.js +0 -1
- package/dist/src/ai/fisherman.js +0 -1
- package/dist/src/ai/historian.js +0 -1
- package/dist/src/ai/navigator.js +0 -1
- package/dist/src/ai/pilot.js +0 -1
- package/dist/src/ai/planner/session-dedup.js +0 -1
- package/dist/src/ai/planner/styles.js +0 -1
- package/dist/src/ai/planner/subpages.js +0 -1
- package/dist/src/ai/planner.js +0 -1
- package/dist/src/ai/provider.js +0 -1
- package/dist/src/ai/quartermaster.js +0 -1
- package/dist/src/ai/researcher/cache.js +0 -1
- package/dist/src/ai/researcher/coordinates.js +0 -1
- package/dist/src/ai/researcher/deep-analysis.js +0 -1
- package/dist/src/ai/researcher/fingerprint-worker.js +0 -1
- package/dist/src/ai/researcher/focus.js +0 -1
- package/dist/src/ai/researcher/locators.js +0 -1
- package/dist/src/ai/researcher/mixin.js +0 -1
- package/dist/src/ai/researcher/parser.js +0 -1
- package/dist/src/ai/researcher/research-result.js +0 -1
- package/dist/src/ai/researcher.js +0 -1
- package/dist/src/ai/rules.js +0 -1
- package/dist/src/ai/task-agent.js +0 -1
- package/dist/src/ai/tester.js +0 -1
- package/dist/src/ai/tools.js +0 -1
- package/dist/src/api/api-client.js +0 -1
- package/dist/src/api/request-result.js +0 -1
- package/dist/src/api/request-store.js +0 -1
- package/dist/src/api/spec-reader.js +0 -1
- package/dist/src/api/xhr-capture.js +0 -1
- package/dist/src/browser-server.js +0 -1
- package/dist/src/command-handler.js +0 -1
- package/dist/src/commands/add-rule-command.js +0 -1
- package/dist/src/commands/base-command.js +0 -1
- package/dist/src/commands/clean-command.js +0 -1
- package/dist/src/commands/context-aria-command.js +0 -1
- package/dist/src/commands/context-command.js +0 -1
- package/dist/src/commands/context-data-command.js +0 -1
- package/dist/src/commands/context-experience-command.js +0 -1
- package/dist/src/commands/context-html-command.js +0 -1
- package/dist/src/commands/context-knowledge-command.js +0 -1
- package/dist/src/commands/debug-command.js +0 -1
- package/dist/src/commands/drill-command.js +0 -1
- package/dist/src/commands/exit-command.js +0 -1
- package/dist/src/commands/explore-command.js +2 -2
- package/dist/src/commands/freesail-command.js +0 -1
- package/dist/src/commands/help-command.js +0 -1
- package/dist/src/commands/index.js +0 -1
- package/dist/src/commands/init-command.js +115 -0
- package/dist/src/commands/knows-command.js +0 -1
- package/dist/src/commands/learn-command.js +0 -1
- package/dist/src/commands/navigate-command.js +0 -1
- package/dist/src/commands/path-command.js +0 -1
- package/dist/src/commands/plan-clear-command.js +0 -1
- package/dist/src/commands/plan-command.js +0 -1
- package/dist/src/commands/plan-edit-command.js +0 -1
- package/dist/src/commands/plan-load-command.js +0 -1
- package/dist/src/commands/plan-reload-command.js +0 -1
- package/dist/src/commands/plan-save-command.js +0 -1
- package/dist/src/commands/research-command.js +0 -1
- package/dist/src/commands/start-command.js +0 -1
- package/dist/src/commands/status-command.js +0 -1
- package/dist/src/commands/test-command.js +0 -1
- package/dist/src/components/ActivityPane.js +0 -1
- package/dist/src/components/AddKnowledge.js +0 -1
- package/dist/src/components/AddRule.js +0 -1
- package/dist/src/components/App.js +0 -1
- package/dist/src/components/Autocomplete.js +0 -1
- package/dist/src/components/InputPane.js +0 -1
- package/dist/src/components/InputReadline.js +0 -1
- package/dist/src/components/LogPane.js +0 -1
- package/dist/src/components/PlanEditor.js +0 -1
- package/dist/src/components/PlanPane.js +0 -1
- package/dist/src/components/SessionTimer.js +0 -1
- package/dist/src/components/StateTransitionPane.js +0 -1
- package/dist/src/components/StatusPane.js +0 -1
- package/dist/src/components/TaskPane.js +0 -1
- package/dist/src/components/Welcome.js +0 -1
- package/dist/src/components/WelcomeChecklist.js +0 -1
- package/dist/src/components/WelcomeCommands.js +0 -1
- package/dist/src/components/autocomplete-store.js +0 -1
- package/dist/src/components/parse-keypress.js +0 -1
- package/dist/src/config.js +0 -1
- package/dist/src/execution-controller.js +0 -1
- package/dist/src/experience-tracker.js +0 -1
- package/dist/src/explorbot.js +0 -1
- package/dist/src/explorer.js +0 -1
- package/dist/src/index.js +0 -1
- package/dist/src/knowledge-tracker.js +2 -2
- package/dist/src/observability.js +0 -1
- package/dist/src/reporter.js +0 -1
- package/dist/src/state-manager.js +0 -1
- package/dist/src/stats.js +0 -1
- package/dist/src/test-plan.js +0 -1
- package/dist/src/utils/aria.js +0 -1
- package/dist/src/utils/cli-name.js +16 -0
- package/dist/src/utils/code-extractor.js +0 -1
- package/dist/src/utils/context-formatter.js +0 -1
- package/dist/src/utils/error-page.js +0 -1
- package/dist/src/utils/expandable.js +0 -1
- package/dist/src/utils/hooks-runner.js +0 -1
- package/dist/src/utils/html-diff.js +0 -1
- package/dist/src/utils/html.js +0 -1
- package/dist/src/utils/logger.js +0 -1
- package/dist/src/utils/loop.js +0 -1
- package/dist/src/utils/markdown-parser.js +0 -1
- package/dist/src/utils/markdown-query.js +0 -1
- package/dist/src/utils/markdown-terminal.js +0 -1
- package/dist/src/utils/research-parser.js +0 -1
- package/dist/src/utils/retry.js +0 -1
- package/dist/src/utils/rules-loader.js +0 -1
- package/dist/src/utils/strings.js +0 -1
- package/dist/src/utils/test-plan-markdown.js +0 -1
- package/dist/src/utils/throttle.js +0 -1
- package/dist/src/utils/unique-names.js +0 -1
- package/dist/src/utils/url-matcher.js +0 -1
- package/dist/src/utils/web-element.js +0 -1
- package/dist/src/utils/xpath.js +0 -1
- package/package.json +27 -3
- package/src/action-result.ts +694 -0
- package/src/action.ts +445 -0
- package/src/activity.ts +111 -0
- package/src/ai/agent.ts +3 -0
- package/src/ai/bosun.ts +557 -0
- package/src/ai/captain/idle-mode.ts +116 -0
- package/src/ai/captain/mixin.ts +22 -0
- package/src/ai/captain/test-mode.ts +262 -0
- package/src/ai/captain/web-mode.ts +136 -0
- package/src/ai/captain.ts +504 -0
- package/src/ai/conversation.ts +205 -0
- package/src/ai/experience-compactor.ts +284 -0
- package/src/ai/fisherman-tools.ts +181 -0
- package/src/ai/fisherman.ts +223 -0
- package/src/ai/historian.ts +457 -0
- package/src/ai/navigator.ts +572 -0
- package/src/ai/pilot.ts +776 -0
- package/src/ai/planner/session-dedup.ts +35 -0
- package/src/ai/planner/styles.ts +17 -0
- package/src/ai/planner/subpages.ts +141 -0
- package/src/ai/planner.ts +536 -0
- package/src/ai/provider.ts +613 -0
- package/src/ai/quartermaster.ts +286 -0
- package/src/ai/researcher/cache.ts +103 -0
- package/src/ai/researcher/coordinates.ts +238 -0
- package/src/ai/researcher/deep-analysis.ts +415 -0
- package/src/ai/researcher/fingerprint-worker.ts +59 -0
- package/src/ai/researcher/focus.ts +42 -0
- package/src/ai/researcher/locators.ts +282 -0
- package/src/ai/researcher/mixin.ts +4 -0
- package/src/ai/researcher/parser.ts +186 -0
- package/src/ai/researcher/research-result.ts +115 -0
- package/src/ai/researcher.ts +857 -0
- package/src/ai/rules.ts +376 -0
- package/src/ai/task-agent.ts +141 -0
- package/src/ai/tester.ts +939 -0
- package/src/ai/tools.ts +1117 -0
- package/src/api/api-client.ts +109 -0
- package/src/api/request-result.ts +212 -0
- package/src/api/request-store.ts +130 -0
- package/src/api/spec-reader.ts +174 -0
- package/src/api/xhr-capture.ts +100 -0
- package/src/browser-server.ts +74 -0
- package/src/command-handler.ts +454 -0
- package/src/commands/add-rule-command.ts +63 -0
- package/src/commands/base-command.ts +27 -0
- package/src/commands/clean-command.ts +73 -0
- package/src/commands/context-aria-command.ts +22 -0
- package/src/commands/context-command.ts +67 -0
- package/src/commands/context-data-command.ts +30 -0
- package/src/commands/context-experience-command.ts +48 -0
- package/src/commands/context-html-command.ts +33 -0
- package/src/commands/context-knowledge-command.ts +43 -0
- package/src/commands/debug-command.ts +13 -0
- package/src/commands/drill-command.ts +34 -0
- package/src/commands/exit-command.ts +32 -0
- package/src/commands/explore-command.ts +129 -0
- package/src/commands/freesail-command.ts +95 -0
- package/src/commands/help-command.ts +8 -0
- package/src/commands/index.ts +69 -0
- package/src/commands/init-command.ts +128 -0
- package/src/commands/knows-command.ts +68 -0
- package/src/commands/learn-command.ts +44 -0
- package/src/commands/navigate-command.ts +18 -0
- package/src/commands/path-command.ts +83 -0
- package/src/commands/plan-clear-command.ts +14 -0
- package/src/commands/plan-command.ts +41 -0
- package/src/commands/plan-edit-command.ts +9 -0
- package/src/commands/plan-load-command.ts +18 -0
- package/src/commands/plan-reload-command.ts +28 -0
- package/src/commands/plan-save-command.ts +25 -0
- package/src/commands/research-command.ts +45 -0
- package/src/commands/start-command.ts +13 -0
- package/src/commands/status-command.tsx +23 -0
- package/src/commands/test-command.ts +84 -0
- package/src/components/ActivityPane.tsx +80 -0
- package/src/components/AddKnowledge.tsx +169 -0
- package/src/components/AddRule.tsx +174 -0
- package/src/components/App.tsx +377 -0
- package/src/components/Autocomplete.tsx +63 -0
- package/src/components/InputPane.tsx +259 -0
- package/src/components/InputReadline.tsx +704 -0
- package/src/components/LogPane.tsx +187 -0
- package/src/components/PlanEditor.tsx +150 -0
- package/src/components/PlanPane.tsx +71 -0
- package/src/components/SessionTimer.tsx +35 -0
- package/src/components/StateTransitionPane.tsx +149 -0
- package/src/components/StatusPane.tsx +62 -0
- package/src/components/TaskPane.tsx +119 -0
- package/src/components/Welcome.tsx +83 -0
- package/src/components/WelcomeChecklist.tsx +118 -0
- package/src/components/WelcomeCommands.tsx +102 -0
- package/src/components/autocomplete-store.ts +35 -0
- package/src/components/parse-keypress.ts +170 -0
- package/src/config.ts +490 -0
- package/src/execution-controller.ts +109 -0
- package/src/experience-tracker.ts +350 -0
- package/src/explorbot.ts +405 -0
- package/src/explorer.ts +713 -0
- package/src/index.tsx +62 -0
- package/src/knowledge-tracker.ts +230 -0
- package/src/observability.ts +150 -0
- package/src/reporter.ts +224 -0
- package/src/state-manager.ts +556 -0
- package/src/stats.ts +53 -0
- package/src/test-plan.ts +432 -0
- package/src/utils/aria.ts +629 -0
- package/src/utils/cli-name.ts +13 -0
- package/src/utils/code-extractor.ts +22 -0
- package/src/utils/context-formatter.ts +239 -0
- package/src/utils/error-page.ts +23 -0
- package/src/utils/expandable.ts +38 -0
- package/src/utils/hooks-runner.ts +79 -0
- package/src/utils/html-diff.ts +918 -0
- package/src/utils/html.ts +1316 -0
- package/src/utils/logger.ts +534 -0
- package/src/utils/loop.ts +176 -0
- package/src/utils/markdown-parser.ts +127 -0
- package/src/utils/markdown-query.ts +466 -0
- package/src/utils/markdown-terminal.ts +43 -0
- package/src/utils/research-parser.ts +11 -0
- package/src/utils/retry.ts +73 -0
- package/src/utils/rules-loader.ts +118 -0
- package/src/utils/strings.ts +13 -0
- package/src/utils/test-plan-markdown.ts +332 -0
- package/src/utils/throttle.ts +18 -0
- package/src/utils/unique-names.ts +14 -0
- package/src/utils/url-matcher.ts +45 -0
- package/src/utils/web-element.ts +145 -0
- package/src/utils/xpath.ts +129 -0
- package/dist/bin/explorbot-cli.js.map +0 -1
- package/dist/boat/api-tester/bin/apibot-cli.js.map +0 -1
- package/dist/boat/api-tester/example/apibot.config.js +0 -31
- package/dist/boat/api-tester/example/apibot.config.js.map +0 -1
- package/dist/boat/api-tester/src/ai/chief/styles.js.map +0 -1
- package/dist/boat/api-tester/src/ai/chief.js.map +0 -1
- package/dist/boat/api-tester/src/ai/curler-tools.js.map +0 -1
- package/dist/boat/api-tester/src/ai/curler.js.map +0 -1
- package/dist/boat/api-tester/src/api-client.js.map +0 -1
- package/dist/boat/api-tester/src/apibot.js.map +0 -1
- package/dist/boat/api-tester/src/cli.js.map +0 -1
- package/dist/boat/api-tester/src/config.js.map +0 -1
- package/dist/prompts/audit-rules.md +0 -124
- package/dist/src/action-result.js.map +0 -1
- package/dist/src/action.js.map +0 -1
- package/dist/src/activity.js.map +0 -1
- package/dist/src/ai/agent.js.map +0 -1
- package/dist/src/ai/bosun.js.map +0 -1
- package/dist/src/ai/captain/idle-mode.js.map +0 -1
- package/dist/src/ai/captain/mixin.js.map +0 -1
- package/dist/src/ai/captain/test-mode.js.map +0 -1
- package/dist/src/ai/captain/web-mode.js.map +0 -1
- package/dist/src/ai/captain.js.map +0 -1
- package/dist/src/ai/conversation.js.map +0 -1
- package/dist/src/ai/experience-compactor.js.map +0 -1
- package/dist/src/ai/fisherman-tools.js.map +0 -1
- package/dist/src/ai/fisherman.js.map +0 -1
- package/dist/src/ai/historian.js.map +0 -1
- package/dist/src/ai/navigator.js.map +0 -1
- package/dist/src/ai/pilot.js.map +0 -1
- package/dist/src/ai/planner/session-dedup.js.map +0 -1
- package/dist/src/ai/planner/styles.js.map +0 -1
- package/dist/src/ai/planner/subpages.js.map +0 -1
- package/dist/src/ai/planner.js.map +0 -1
- package/dist/src/ai/provider.js.map +0 -1
- package/dist/src/ai/quartermaster.js.map +0 -1
- package/dist/src/ai/researcher/cache.js.map +0 -1
- package/dist/src/ai/researcher/coordinates.js.map +0 -1
- package/dist/src/ai/researcher/deep-analysis.js.map +0 -1
- package/dist/src/ai/researcher/fingerprint-worker.js.map +0 -1
- package/dist/src/ai/researcher/focus.js.map +0 -1
- package/dist/src/ai/researcher/locators.js.map +0 -1
- package/dist/src/ai/researcher/mixin.js.map +0 -1
- package/dist/src/ai/researcher/parser.js.map +0 -1
- package/dist/src/ai/researcher/research-result.js.map +0 -1
- package/dist/src/ai/researcher.js.map +0 -1
- package/dist/src/ai/rules.js.map +0 -1
- package/dist/src/ai/task-agent.js.map +0 -1
- package/dist/src/ai/tester.js.map +0 -1
- package/dist/src/ai/tools.js.map +0 -1
- package/dist/src/api/api-client.js.map +0 -1
- package/dist/src/api/request-result.js.map +0 -1
- package/dist/src/api/request-store.js.map +0 -1
- package/dist/src/api/spec-reader.js.map +0 -1
- package/dist/src/api/xhr-capture.js.map +0 -1
- package/dist/src/browser-server.js.map +0 -1
- package/dist/src/command-handler.js.map +0 -1
- package/dist/src/commands/add-rule-command.js.map +0 -1
- package/dist/src/commands/base-command.js.map +0 -1
- package/dist/src/commands/clean-command.js.map +0 -1
- package/dist/src/commands/context-aria-command.js.map +0 -1
- package/dist/src/commands/context-command.js.map +0 -1
- package/dist/src/commands/context-data-command.js.map +0 -1
- package/dist/src/commands/context-experience-command.js.map +0 -1
- package/dist/src/commands/context-html-command.js.map +0 -1
- package/dist/src/commands/context-knowledge-command.js.map +0 -1
- package/dist/src/commands/debug-command.js.map +0 -1
- package/dist/src/commands/drill-command.js.map +0 -1
- package/dist/src/commands/exit-command.js.map +0 -1
- package/dist/src/commands/explore-command.js.map +0 -1
- package/dist/src/commands/freesail-command.js.map +0 -1
- package/dist/src/commands/help-command.js.map +0 -1
- package/dist/src/commands/index.js.map +0 -1
- package/dist/src/commands/knows-command.js.map +0 -1
- package/dist/src/commands/learn-command.js.map +0 -1
- package/dist/src/commands/navigate-command.js.map +0 -1
- package/dist/src/commands/path-command.js.map +0 -1
- package/dist/src/commands/plan-clear-command.js.map +0 -1
- package/dist/src/commands/plan-command.js.map +0 -1
- package/dist/src/commands/plan-edit-command.js.map +0 -1
- package/dist/src/commands/plan-load-command.js.map +0 -1
- package/dist/src/commands/plan-reload-command.js.map +0 -1
- package/dist/src/commands/plan-save-command.js.map +0 -1
- package/dist/src/commands/research-command.js.map +0 -1
- package/dist/src/commands/start-command.js.map +0 -1
- package/dist/src/commands/status-command.js.map +0 -1
- package/dist/src/commands/test-command.js.map +0 -1
- package/dist/src/components/ActivityPane.js.map +0 -1
- package/dist/src/components/AddKnowledge.js.map +0 -1
- package/dist/src/components/AddRule.js.map +0 -1
- package/dist/src/components/App.js.map +0 -1
- package/dist/src/components/Autocomplete.js.map +0 -1
- package/dist/src/components/InputPane.js.map +0 -1
- package/dist/src/components/InputReadline.js.map +0 -1
- package/dist/src/components/LogPane.js.map +0 -1
- package/dist/src/components/PlanEditor.js.map +0 -1
- package/dist/src/components/PlanPane.js.map +0 -1
- package/dist/src/components/SessionTimer.js.map +0 -1
- package/dist/src/components/StateTransitionPane.js.map +0 -1
- package/dist/src/components/StatusPane.js.map +0 -1
- package/dist/src/components/TaskPane.js.map +0 -1
- package/dist/src/components/Welcome.js.map +0 -1
- package/dist/src/components/WelcomeChecklist.js.map +0 -1
- package/dist/src/components/WelcomeCommands.js.map +0 -1
- package/dist/src/components/autocomplete-store.js.map +0 -1
- package/dist/src/components/parse-keypress.js.map +0 -1
- package/dist/src/config.js.map +0 -1
- package/dist/src/execution-controller.js.map +0 -1
- package/dist/src/experience-tracker.js.map +0 -1
- package/dist/src/explorbot.js.map +0 -1
- package/dist/src/explorer.js.map +0 -1
- package/dist/src/index.js.map +0 -1
- package/dist/src/knowledge-tracker.js.map +0 -1
- package/dist/src/observability.js.map +0 -1
- package/dist/src/reporter.js.map +0 -1
- package/dist/src/state-manager.js.map +0 -1
- package/dist/src/stats.js.map +0 -1
- package/dist/src/test-plan.js.map +0 -1
- package/dist/src/utils/aria.js.map +0 -1
- package/dist/src/utils/code-extractor.js.map +0 -1
- package/dist/src/utils/context-formatter.js.map +0 -1
- package/dist/src/utils/error-page.js.map +0 -1
- package/dist/src/utils/expandable.js.map +0 -1
- package/dist/src/utils/hooks-runner.js.map +0 -1
- package/dist/src/utils/html-diff.js.map +0 -1
- package/dist/src/utils/html.js.map +0 -1
- package/dist/src/utils/logger.js.map +0 -1
- package/dist/src/utils/loop.js.map +0 -1
- package/dist/src/utils/markdown-parser.js.map +0 -1
- package/dist/src/utils/markdown-query.js.map +0 -1
- package/dist/src/utils/markdown-terminal.js.map +0 -1
- package/dist/src/utils/research-parser.js.map +0 -1
- package/dist/src/utils/retry.js.map +0 -1
- package/dist/src/utils/rules-loader.js.map +0 -1
- package/dist/src/utils/strings.js.map +0 -1
- package/dist/src/utils/test-plan-markdown.js.map +0 -1
- package/dist/src/utils/throttle.js.map +0 -1
- package/dist/src/utils/unique-names.js.map +0 -1
- package/dist/src/utils/url-matcher.js.map +0 -1
- package/dist/src/utils/web-element.js.map +0 -1
- package/dist/src/utils/xpath.js.map +0 -1
- package/prompts/audit-rules.md +0 -124
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import dedent from 'dedent';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { AIProvider } from '../../../../src/ai/provider.ts';
|
|
4
|
+
import type { Reporter } from '../../../../src/reporter.ts';
|
|
5
|
+
import { type Test, TestResult } from '../../../../src/test-plan.ts';
|
|
6
|
+
import { createDebug, tag } from '../../../../src/utils/logger.ts';
|
|
7
|
+
import { loop } from '../../../../src/utils/loop.ts';
|
|
8
|
+
import type { ApiClient } from '../api-client.ts';
|
|
9
|
+
import type { RequestStore } from '../../../../src/api/request-store.ts';
|
|
10
|
+
import { createCurlerTools } from './curler-tools.ts';
|
|
11
|
+
|
|
12
|
+
const debugLog = createDebug('explorbot:curler');
|
|
13
|
+
|
|
14
|
+
const MAX_ITERATIONS = 10;
|
|
15
|
+
|
|
16
|
+
export class Curler {
|
|
17
|
+
private provider: AIProvider;
|
|
18
|
+
private apiClient: ApiClient;
|
|
19
|
+
private requestState: RequestStore;
|
|
20
|
+
private reporter: Reporter;
|
|
21
|
+
|
|
22
|
+
constructor(provider: AIProvider, apiClient: ApiClient, requestState: RequestStore, reporter: Reporter) {
|
|
23
|
+
this.provider = provider;
|
|
24
|
+
this.apiClient = apiClient;
|
|
25
|
+
this.requestState = requestState;
|
|
26
|
+
this.reporter = reporter;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async test(test: Test, opts?: { specDefinition?: string; baseEndpoint?: string; searchSpec?: (query: string) => string }): Promise<{ success: boolean }> {
|
|
30
|
+
tag('info').log(`Testing: ${test.scenario}`);
|
|
31
|
+
debugLog('Starting test:', test.scenario);
|
|
32
|
+
|
|
33
|
+
this.requestState.clear();
|
|
34
|
+
test.start();
|
|
35
|
+
await this.reporter.reportTestStart(test);
|
|
36
|
+
|
|
37
|
+
const conversation = this.provider.startConversation(this.buildSystemPrompt(), 'curler', this.provider.getAgenticModel('curler'));
|
|
38
|
+
const tools = createCurlerTools(this.apiClient, this.requestState, test, opts?.searchSpec);
|
|
39
|
+
|
|
40
|
+
const initialPrompt = this.buildTestPrompt(test, opts?.specDefinition, opts?.baseEndpoint);
|
|
41
|
+
conversation.addUserText(initialPrompt);
|
|
42
|
+
|
|
43
|
+
await loop(
|
|
44
|
+
async ({ stop, iteration }) => {
|
|
45
|
+
debugLog(`Iteration ${iteration}`);
|
|
46
|
+
|
|
47
|
+
if (iteration > 1) {
|
|
48
|
+
const requestLog = this.requestState.toLog();
|
|
49
|
+
const nextStep = dedent`
|
|
50
|
+
<request_log>
|
|
51
|
+
${requestLog || 'No requests made yet'}
|
|
52
|
+
</request_log>
|
|
53
|
+
|
|
54
|
+
<task>
|
|
55
|
+
Continue testing. Review the request log above and proceed with the next step.
|
|
56
|
+
</task>
|
|
57
|
+
|
|
58
|
+
<notes>
|
|
59
|
+
${test.notesToString() || 'No notes yet'}
|
|
60
|
+
</notes>
|
|
61
|
+
`;
|
|
62
|
+
conversation.addUserText(nextStep);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const result = await this.provider.invokeConversation(conversation, tools, {
|
|
66
|
+
maxToolRoundtrips: 5,
|
|
67
|
+
toolChoice: 'required',
|
|
68
|
+
agentName: 'curler',
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (!result) throw new Error('Failed to get response from provider');
|
|
72
|
+
|
|
73
|
+
const toolNames = result.toolExecutions?.map((e: any) => e.toolName) || [];
|
|
74
|
+
debugLog('Tool calls:', toolNames.join(', '));
|
|
75
|
+
|
|
76
|
+
if (test.hasFinished) {
|
|
77
|
+
stop();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (iteration >= MAX_ITERATIONS) {
|
|
82
|
+
tag('warning').log('Max iterations reached, running final review...');
|
|
83
|
+
stop();
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
maxAttempts: MAX_ITERATIONS,
|
|
88
|
+
observability: {
|
|
89
|
+
name: `curler: ${test.scenario}`,
|
|
90
|
+
agent: 'curler',
|
|
91
|
+
sessionId: test.sessionName,
|
|
92
|
+
metadata: {
|
|
93
|
+
input: {
|
|
94
|
+
scenario: test.scenario,
|
|
95
|
+
startUrl: test.startUrl,
|
|
96
|
+
expected: test.expected,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
catch: async ({ error, stop }) => {
|
|
101
|
+
tag('error').log(`Test execution error: ${error}`);
|
|
102
|
+
stop();
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
await this.finalReview(test);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
tag('error').log(`Final review failed: ${error}`);
|
|
111
|
+
}
|
|
112
|
+
this.finishTest(test);
|
|
113
|
+
const meta: Record<string, string | undefined> = {
|
|
114
|
+
endpoint: test.startUrl,
|
|
115
|
+
style: test.style,
|
|
116
|
+
sessionName: test.sessionName,
|
|
117
|
+
};
|
|
118
|
+
await this.reporter.reportTest(test, meta);
|
|
119
|
+
|
|
120
|
+
return { success: test.isSuccessful };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private finishTest(test: Test): void {
|
|
124
|
+
if (!test.hasFinished) {
|
|
125
|
+
test.finish(TestResult.FAILED);
|
|
126
|
+
}
|
|
127
|
+
tag('info').log(`Finished: ${test.scenario}`);
|
|
128
|
+
tag('multiline').log(test.getPrintableNotes().join('\n'));
|
|
129
|
+
|
|
130
|
+
if (test.isSuccessful) {
|
|
131
|
+
tag('success').log(`Passed: ${test.scenario}`);
|
|
132
|
+
} else if (test.isSkipped) {
|
|
133
|
+
tag('warning').log(`Skipped: ${test.scenario}`);
|
|
134
|
+
} else {
|
|
135
|
+
tag('error').log(`Failed: ${test.scenario}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private async finalReview(test: Test): Promise<void> {
|
|
140
|
+
const notes = test.notesToString() || 'No notes recorded.';
|
|
141
|
+
const requestLog = this.requestState.toLog() || 'No requests made.';
|
|
142
|
+
const hasFailedNotes = test.getCheckedNotes().some((n) => n.status === TestResult.FAILED);
|
|
143
|
+
const isUnfinished = !test.hasFinished;
|
|
144
|
+
|
|
145
|
+
if (!hasFailedNotes && !isUnfinished) return;
|
|
146
|
+
|
|
147
|
+
tag('info').log('Running final review...');
|
|
148
|
+
|
|
149
|
+
const schema = z.object({
|
|
150
|
+
summary: z.string().describe('One-line summary of test results'),
|
|
151
|
+
goalsAchieved: z.boolean().describe('Whether the main test goals were accomplished'),
|
|
152
|
+
failuresCritical: z.boolean().describe('Whether any assertion failures are critical enough to fail the test'),
|
|
153
|
+
details: z.string().describe('Brief explanation of what passed, what failed, and why failures are or are not critical'),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const model = this.provider.getAgenticModel('curler');
|
|
157
|
+
const response = await this.provider.generateObject(
|
|
158
|
+
[
|
|
159
|
+
{
|
|
160
|
+
role: 'system',
|
|
161
|
+
content: this.buildFinalReviewSystemPrompt(),
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
role: 'user',
|
|
165
|
+
content: dedent`
|
|
166
|
+
Scenario: ${test.scenario}
|
|
167
|
+
|
|
168
|
+
Expected outcomes:
|
|
169
|
+
${test.expected.map((e) => `- ${e}`).join('\n')}
|
|
170
|
+
|
|
171
|
+
<notes>
|
|
172
|
+
${notes}
|
|
173
|
+
</notes>
|
|
174
|
+
|
|
175
|
+
<request_log>
|
|
176
|
+
${requestLog}
|
|
177
|
+
</request_log>
|
|
178
|
+
|
|
179
|
+
Evaluate:
|
|
180
|
+
1. Were the expected outcomes achieved based on the request results?
|
|
181
|
+
2. Are any assertion failures critical (wrong status codes, missing core data) or minor (optional fields, cosmetic)?
|
|
182
|
+
3. Should the test pass or fail overall?
|
|
183
|
+
`,
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
schema,
|
|
187
|
+
model
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const result = response?.object;
|
|
191
|
+
if (!result) return;
|
|
192
|
+
|
|
193
|
+
test.summary = result.summary;
|
|
194
|
+
test.addNote(`Review: ${result.details}`);
|
|
195
|
+
|
|
196
|
+
if (result.goalsAchieved && !result.failuresCritical) {
|
|
197
|
+
test.addNote(result.summary, TestResult.PASSED);
|
|
198
|
+
test.finish(TestResult.PASSED);
|
|
199
|
+
} else {
|
|
200
|
+
test.addNote(result.summary, TestResult.FAILED);
|
|
201
|
+
test.finish(TestResult.FAILED);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private buildTestPrompt(test: Test, specDefinition?: string, baseEndpoint?: string): string {
|
|
206
|
+
let prompt = dedent`
|
|
207
|
+
<task>
|
|
208
|
+
SCENARIO: ${test.scenario}
|
|
209
|
+
|
|
210
|
+
EXPECTED OUTCOMES:
|
|
211
|
+
${test.expected.map((e) => `- ${e}`).join('\n')}
|
|
212
|
+
|
|
213
|
+
PLANNED STEPS:
|
|
214
|
+
${test.plannedSteps.map((s) => `- ${s}`).join('\n')}
|
|
215
|
+
|
|
216
|
+
ENDPOINT: ${test.startUrl}
|
|
217
|
+
|
|
218
|
+
Execute the test by making HTTP requests using the request tool.
|
|
219
|
+
Use verifyStructure and verifyData to check responses.
|
|
220
|
+
Use record to document findings.
|
|
221
|
+
Use finish when all goals are achieved.
|
|
222
|
+
Use stop only if the scenario is fundamentally impossible.
|
|
223
|
+
</task>
|
|
224
|
+
`;
|
|
225
|
+
|
|
226
|
+
if (specDefinition) {
|
|
227
|
+
let specBlock = `\n\n<api_spec>\n${specDefinition}\n</api_spec>`;
|
|
228
|
+
if (baseEndpoint) {
|
|
229
|
+
specBlock += dedent`
|
|
230
|
+
|
|
231
|
+
<path_mapping>
|
|
232
|
+
IMPORTANT: The spec shows absolute paths (e.g. /api/v2/{project_id}/suites) but the base URL is ${baseEndpoint}.
|
|
233
|
+
The request tool prepends the base URL automatically.
|
|
234
|
+
Use ONLY the relative path after the base prefix: e.g. /suites, /suites/{id} — NOT the full spec path.
|
|
235
|
+
</path_mapping>
|
|
236
|
+
`;
|
|
237
|
+
}
|
|
238
|
+
prompt += specBlock;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return prompt;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private buildSystemPrompt(): string {
|
|
245
|
+
return dedent`
|
|
246
|
+
<role>
|
|
247
|
+
You are a senior API test engineer. Execute HTTP requests to test API endpoints.
|
|
248
|
+
Validate responses against expectations.
|
|
249
|
+
</role>
|
|
250
|
+
|
|
251
|
+
<approach>
|
|
252
|
+
1. Use the request tool to make HTTP calls — response preview shows first 500 chars
|
|
253
|
+
2. If you need a related endpoint (e.g., to create prerequisite data), use schemaFor to discover it
|
|
254
|
+
3. Extract IDs and key values from the response preview to chain requests
|
|
255
|
+
4. After each request, use verifyStructure with a Zod schema to validate response shape
|
|
256
|
+
Example: schema = "z.object({ id: z.number(), name: z.string(), items: z.array(z.string()) })"
|
|
257
|
+
Use z.any() for fields you don't care about, z.optional() for nullable fields
|
|
258
|
+
5. Use verifyData with expect() assertions to check specific values
|
|
259
|
+
The "response" variable is the full parsed JSON body — use the structure from verifyStructure to access nested fields correctly
|
|
260
|
+
Example: "expect(response.name).toBe('Test Suite')"
|
|
261
|
+
Example: "expect(response.data.items).toHaveLength(3)"
|
|
262
|
+
Example: "expect(response.status).not.toBe('deleted')"
|
|
263
|
+
6. Use record to document findings and observations
|
|
264
|
+
7. Use finish when all test goals are achieved and verified
|
|
265
|
+
8. Use stop only if the scenario cannot be completed at all
|
|
266
|
+
</approach>
|
|
267
|
+
|
|
268
|
+
<rules>
|
|
269
|
+
- Always check HTTP status codes from the request tool result
|
|
270
|
+
- After each request, verify structure with a Zod schema matching the API spec
|
|
271
|
+
- Use verifyData with expect() for value assertions — "response" is the full parsed JSON, use the structure returned by verifyStructure to access the correct paths
|
|
272
|
+
- For CRUD tests: create first, extract ID from preview, then read/update/delete
|
|
273
|
+
- Chain requests logically — extract IDs from response preview
|
|
274
|
+
- Record important findings as you go
|
|
275
|
+
- Be precise about what you expect vs what you observe
|
|
276
|
+
- If a test requires data from another endpoint, use schemaFor to look it up before guessing
|
|
277
|
+
</rules>
|
|
278
|
+
`;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private buildFinalReviewSystemPrompt(): string {
|
|
282
|
+
return dedent`
|
|
283
|
+
You evaluate API test results.
|
|
284
|
+
Analyze notes and request log to determine if test goals were achieved.
|
|
285
|
+
Decide if assertion failures are critical (should fail the test) or minor (test still passes).
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
Status code rules:
|
|
289
|
+
- 500+ errors are always critical failures regardless of test type
|
|
290
|
+
- 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
|
|
291
|
+
|
|
292
|
+
For positive tests (creating/updating data):
|
|
293
|
+
- Focus on whether the expected data was actually saved correctly
|
|
294
|
+
- Never trust POST/PUT/PATCH response alone — a follow-up GET must confirm the data was persisted
|
|
295
|
+
- The real measure of success is the data state verified via GET, not the write response or status code
|
|
296
|
+
|
|
297
|
+
For negative tests (invalid input, unauthorized access):
|
|
298
|
+
- PUT and PATCH methods are usually the same so do not treat them differently.
|
|
299
|
+
- Verify the invalid data was NOT created — check with GET to ensure nothing was saved by accident
|
|
300
|
+
- Any 4xx response that correctly rejects the request is acceptable
|
|
301
|
+
|
|
302
|
+
Critical failures: 500+ errors, data not saved in positive tests, invalid data accidentally created in negative tests, broken CRUD operations.
|
|
303
|
+
Minor failures: optional fields missing, cosmetic differences, extra fields in response, different-than-expected 4xx code.
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ApiClient as BaseApiClient } from '../../../src/api/api-client.ts';
|
|
2
|
+
import type { HookFn } from './config.ts';
|
|
3
|
+
|
|
4
|
+
export class ApiClient extends BaseApiClient {
|
|
5
|
+
private bootstrapHook?: HookFn;
|
|
6
|
+
private teardownHook?: HookFn;
|
|
7
|
+
|
|
8
|
+
constructor(baseEndpoint: string, defaultHeaders: Record<string, string> = {}, hooks?: { bootstrap?: HookFn; teardown?: HookFn }) {
|
|
9
|
+
super(baseEndpoint, defaultHeaders);
|
|
10
|
+
this.bootstrapHook = hooks?.bootstrap;
|
|
11
|
+
this.teardownHook = hooks?.teardown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async bootstrap(): Promise<void> {
|
|
15
|
+
if (!this.bootstrapHook) return;
|
|
16
|
+
const ctx = { headers: this.getHeaders(), baseEndpoint: this.getBaseEndpoint() };
|
|
17
|
+
const result = await this.bootstrapHook(ctx);
|
|
18
|
+
if (result && typeof result === 'object') {
|
|
19
|
+
this.setHeaders(result);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async teardown(): Promise<void> {
|
|
24
|
+
if (!this.teardownHook) return;
|
|
25
|
+
const ctx = { headers: this.getHeaders(), baseEndpoint: this.getBaseEndpoint() };
|
|
26
|
+
await this.teardownHook(ctx);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { AIProvider } from '../../../src/ai/provider.ts';
|
|
4
|
+
import { Reporter } from '../../../src/reporter.ts';
|
|
5
|
+
import { Plan } from '../../../src/test-plan.ts';
|
|
6
|
+
import { setVerboseMode, tag } from '../../../src/utils/logger.ts';
|
|
7
|
+
import { Chief } from './ai/chief.ts';
|
|
8
|
+
import { Curler } from './ai/curler.ts';
|
|
9
|
+
import { ApiClient } from './api-client.ts';
|
|
10
|
+
import { type ApibotConfig, ApibotConfigParser } from './config.ts';
|
|
11
|
+
import { RequestStore } from '../../../src/api/request-store.ts';
|
|
12
|
+
import { extractEndpointDefinition, loadSpec, searchEndpoints, validateSpecs } from '../../../src/api/spec-reader.ts';
|
|
13
|
+
|
|
14
|
+
export class ApiBot {
|
|
15
|
+
private configParser: ApibotConfigParser;
|
|
16
|
+
private provider!: AIProvider;
|
|
17
|
+
private config!: ApibotConfig;
|
|
18
|
+
private agents: Record<string, any> = {};
|
|
19
|
+
private currentPlan?: Plan;
|
|
20
|
+
private apiClient!: ApiClient;
|
|
21
|
+
private requestState!: RequestStore;
|
|
22
|
+
private reporter!: Reporter;
|
|
23
|
+
private options: ApibotOptions;
|
|
24
|
+
private apiSpec: any;
|
|
25
|
+
|
|
26
|
+
constructor(options: ApibotOptions = {}) {
|
|
27
|
+
this.options = options;
|
|
28
|
+
this.configParser = ApibotConfigParser.getInstance();
|
|
29
|
+
if (this.options.verbose) {
|
|
30
|
+
process.env.DEBUG = 'apibot:*';
|
|
31
|
+
setVerboseMode(true);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async start(): Promise<void> {
|
|
36
|
+
this.config = await this.configParser.loadConfig({ config: this.options.config, path: this.options.path });
|
|
37
|
+
this.provider = new AIProvider(this.config.ai);
|
|
38
|
+
await this.provider.validateConnection();
|
|
39
|
+
|
|
40
|
+
this.apiClient = new ApiClient(this.config.api.baseEndpoint, this.config.api.headers || {}, {
|
|
41
|
+
bootstrap: this.config.api.bootstrap,
|
|
42
|
+
teardown: this.config.api.teardown,
|
|
43
|
+
});
|
|
44
|
+
await this.apiClient.bootstrap();
|
|
45
|
+
|
|
46
|
+
const outputDir = this.configParser.getOutputDir();
|
|
47
|
+
this.configParser.ensureDirectory(outputDir);
|
|
48
|
+
this.requestState = new RequestStore(outputDir);
|
|
49
|
+
this.reporter = new Reporter(this.config.reporter);
|
|
50
|
+
|
|
51
|
+
validateSpecs(this.config.api.spec);
|
|
52
|
+
this.apiSpec = await loadSpec(this.config.api.spec!, outputDir);
|
|
53
|
+
tag('info').log('OpenAPI spec loaded');
|
|
54
|
+
|
|
55
|
+
await this.healthCheck();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private async healthCheck(): Promise<void> {
|
|
59
|
+
const baseUrl = this.config.api.baseEndpoint;
|
|
60
|
+
const headers = this.config.api.headers || {};
|
|
61
|
+
const headerSummary = Object.entries(headers)
|
|
62
|
+
.map(([k, v]) => {
|
|
63
|
+
const lower = k.toLowerCase();
|
|
64
|
+
if (lower === 'authorization' || lower === 'x-api-key') return `${k}: ${v.slice(0, 12)}...`;
|
|
65
|
+
return `${k}: ${v}`;
|
|
66
|
+
})
|
|
67
|
+
.join(', ');
|
|
68
|
+
|
|
69
|
+
tag('info').log(`Connecting to ${baseUrl}`);
|
|
70
|
+
if (headerSummary) tag('info').log(`Headers: ${headerSummary}`);
|
|
71
|
+
|
|
72
|
+
const result = await this.apiClient.request({ method: 'GET', path: '/' });
|
|
73
|
+
|
|
74
|
+
if (result.error) {
|
|
75
|
+
tag('error').log(`Connection failed: ${result.error}`);
|
|
76
|
+
throw new Error(`Cannot connect to ${baseUrl}: ${result.error}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
tag('success').log(`Connected to ${baseUrl} (${result.status} ${result.statusText})`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async stop(): Promise<void> {
|
|
83
|
+
await this.reporter?.finishRun();
|
|
84
|
+
await this.apiClient?.teardown();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
createAgent<T>(factory: (deps: { ai: AIProvider; config: ApibotConfig; apiClient: ApiClient; requestState: RequestStore }) => T): T {
|
|
88
|
+
return factory({
|
|
89
|
+
ai: this.provider,
|
|
90
|
+
config: this.config,
|
|
91
|
+
apiClient: this.apiClient,
|
|
92
|
+
requestState: this.requestState,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
agentChief(): Chief {
|
|
97
|
+
return (this.agents.chief ||= this.createAgent(({ ai, config, apiClient }) => new Chief(ai, config, apiClient)));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
agentCurler(): Curler {
|
|
101
|
+
return (this.agents.curler ||= this.createAgent(({ ai, apiClient, requestState }) => new Curler(ai, apiClient, requestState, this.reporter)));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async plan(endpoint: string, opts: { style?: string; fresh?: boolean } = {}): Promise<Plan> {
|
|
105
|
+
if (opts.fresh) {
|
|
106
|
+
this.currentPlan = undefined;
|
|
107
|
+
this.agents.chief = undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const chief = this.agentChief();
|
|
111
|
+
const specDefinition = this.getEndpointDefinition(endpoint);
|
|
112
|
+
this.currentPlan = await chief.plan(endpoint, { style: opts.style, specDefinition });
|
|
113
|
+
const savedPath = this.savePlan();
|
|
114
|
+
if (savedPath) {
|
|
115
|
+
tag('info').log(`Plan saved to: ${path.relative(process.cwd(), savedPath)}`);
|
|
116
|
+
}
|
|
117
|
+
return this.currentPlan;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
loadPlan(filename: string): Plan {
|
|
121
|
+
const plansDir = this.configParser.getPlansDir();
|
|
122
|
+
let planPath = filename;
|
|
123
|
+
|
|
124
|
+
if (!path.isAbsolute(filename)) {
|
|
125
|
+
planPath = path.join(plansDir, filename);
|
|
126
|
+
if (!existsSync(planPath) && !filename.endsWith('.md')) {
|
|
127
|
+
planPath = path.join(plansDir, `${filename}.md`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!existsSync(planPath)) {
|
|
132
|
+
throw new Error(`Plan file not found: ${planPath}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.currentPlan = Plan.fromMarkdown(planPath);
|
|
136
|
+
return this.currentPlan;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
savePlan(filename?: string): string | null {
|
|
140
|
+
if (!this.currentPlan) return null;
|
|
141
|
+
|
|
142
|
+
const plansDir = this.configParser.getPlansDir();
|
|
143
|
+
if (!existsSync(plansDir)) {
|
|
144
|
+
mkdirSync(plansDir, { recursive: true });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const planFilename = filename || this.generatePlanFilename();
|
|
148
|
+
const planPath = path.join(plansDir, planFilename);
|
|
149
|
+
this.currentPlan.saveToMarkdown(planPath);
|
|
150
|
+
return planPath;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
getCurrentPlan(): Plan | undefined {
|
|
154
|
+
return this.currentPlan;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
getProvider(): AIProvider {
|
|
158
|
+
return this.provider;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
getConfig(): ApibotConfig {
|
|
162
|
+
return this.config;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
getConfigParser(): ApibotConfigParser {
|
|
166
|
+
return this.configParser;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
getRequestState(): RequestStore {
|
|
170
|
+
return this.requestState;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
getEndpointDefinition(endpoint: string): string {
|
|
174
|
+
return extractEndpointDefinition(this.apiSpec, endpoint, this.config.api.baseEndpoint);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
searchSpec(query: string): string {
|
|
178
|
+
return searchEndpoints(this.apiSpec, query, this.config.api.baseEndpoint);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
tryGetEndpointDefinition(endpoint: string): string | undefined {
|
|
182
|
+
try {
|
|
183
|
+
return this.getEndpointDefinition(endpoint);
|
|
184
|
+
} catch (e) {
|
|
185
|
+
tag('warning').log(e instanceof Error ? e.message : 'Could not extract spec for endpoint');
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private generatePlanFilename(): string {
|
|
191
|
+
const endpoint = this.currentPlan?.url || '/';
|
|
192
|
+
const sanitized = endpoint.replace(/^\//, '').replace(/[^a-zA-Z0-9]/g, '_') || 'root';
|
|
193
|
+
return `${sanitized.slice(0, 200)}.md`;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
interface ApibotOptions {
|
|
198
|
+
verbose?: boolean;
|
|
199
|
+
config?: string;
|
|
200
|
+
path?: string;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export type { ApibotOptions };
|