@jevitate/cli 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/dist/ai-cli.d.ts +31 -11
- package/dist/ai-cli.js +131 -88
- package/dist/ai-cli.js.map +1 -1
- package/dist/author-journey-cli.d.ts +4 -0
- package/dist/author-journey-cli.js +111 -0
- package/dist/author-journey-cli.js.map +1 -0
- package/dist/bin.js +60349 -16477
- package/dist/bin.js.map +1 -1
- package/dist/browser-run-options.d.ts +89 -0
- package/dist/browser-run-options.js +175 -0
- package/dist/browser-run-options.js.map +1 -0
- package/dist/build-info.generated.d.ts +2 -0
- package/dist/build-info.generated.js +10 -0
- package/dist/build-info.generated.js.map +1 -0
- package/dist/campaign-api.d.ts +151 -0
- package/dist/campaign-api.js +446 -0
- package/dist/campaign-api.js.map +1 -0
- package/dist/campaign-cli.d.ts +6 -0
- package/dist/campaign-cli.js +66 -0
- package/dist/campaign-cli.js.map +1 -0
- package/dist/check-api.d.ts +5 -0
- package/dist/check-api.js +234 -0
- package/dist/check-api.js.map +1 -0
- package/dist/check-budget.d.ts +26 -0
- package/dist/check-budget.js +90 -0
- package/dist/check-budget.js.map +1 -0
- package/dist/check-cli.d.ts +31 -0
- package/dist/check-cli.js +109 -0
- package/dist/check-cli.js.map +1 -0
- package/dist/check-execute.d.ts +29 -0
- package/dist/check-execute.js +420 -0
- package/dist/check-execute.js.map +1 -0
- package/dist/check-plan.d.ts +118 -0
- package/dist/check-plan.js +516 -0
- package/dist/check-plan.js.map +1 -0
- package/dist/check-suite.d.ts +184 -0
- package/dist/check-suite.js +540 -0
- package/dist/check-suite.js.map +1 -0
- package/dist/check-types.d.ts +181 -0
- package/dist/check-types.js +35 -0
- package/dist/check-types.js.map +1 -0
- package/dist/cli-args.d.ts +26 -0
- package/dist/cli-args.js +50 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli-output.d.ts +106 -0
- package/dist/cli-output.js +563 -0
- package/dist/cli-output.js.map +1 -0
- package/dist/cli-reference.d.ts +3 -0
- package/dist/cli-reference.js +81 -0
- package/dist/cli-reference.js.map +1 -0
- package/dist/cli-refusal.d.ts +12 -0
- package/dist/cli-refusal.js +52 -0
- package/dist/cli-refusal.js.map +1 -0
- package/dist/cli-shared.d.ts +340 -0
- package/dist/cli-shared.js +537 -0
- package/dist/cli-shared.js.map +1 -0
- package/dist/cli-surface.d.ts +33 -0
- package/dist/cli-surface.js +48 -0
- package/dist/cli-surface.js.map +1 -0
- package/dist/conversation-options.d.ts +15 -0
- package/dist/conversation-options.js +10 -0
- package/dist/conversation-options.js.map +1 -0
- package/dist/credentials-file.d.ts +15 -0
- package/dist/credentials-file.js +56 -0
- package/dist/credentials-file.js.map +1 -0
- package/dist/data-dir.d.ts +1 -1
- package/dist/data-dir.js +1 -1
- package/dist/defect-evidence.d.ts +141 -0
- package/dist/defect-evidence.js +505 -0
- package/dist/defect-evidence.js.map +1 -0
- package/dist/demo-aspect-api.d.ts +209 -0
- package/dist/demo-aspect-api.js +398 -0
- package/dist/demo-aspect-api.js.map +1 -0
- package/dist/demo-aspect-cli.d.ts +3 -0
- package/dist/demo-aspect-cli.js +243 -0
- package/dist/demo-aspect-cli.js.map +1 -0
- package/dist/demo-capture.d.ts +142 -0
- package/dist/demo-capture.js +537 -0
- package/dist/demo-capture.js.map +1 -0
- package/dist/engine.d.ts +35 -0
- package/dist/engine.js +25 -0
- package/dist/engine.js.map +1 -0
- package/dist/environments.d.ts +155 -0
- package/dist/environments.js +364 -0
- package/dist/environments.js.map +1 -0
- package/dist/exit-codes.d.ts +46 -0
- package/dist/exit-codes.js +91 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/explore-adversarial.d.ts +144 -0
- package/dist/explore-adversarial.js +227 -0
- package/dist/explore-adversarial.js.map +1 -0
- package/dist/explore-api.d.ts +7 -187
- package/dist/explore-api.js +7 -301
- package/dist/explore-api.js.map +1 -1
- package/dist/explore-author.d.ts +79 -0
- package/dist/explore-author.js +86 -0
- package/dist/explore-author.js.map +1 -0
- package/dist/explore-cli.d.ts +7 -0
- package/dist/explore-cli.js +1199 -0
- package/dist/explore-cli.js.map +1 -0
- package/dist/explore-coverage.d.ts +173 -0
- package/dist/explore-coverage.js +246 -0
- package/dist/explore-coverage.js.map +1 -0
- package/dist/explore-feature.d.ts +125 -0
- package/dist/explore-feature.js +218 -0
- package/dist/explore-feature.js.map +1 -0
- package/dist/explore-goal.d.ts +271 -0
- package/dist/explore-goal.js +417 -0
- package/dist/explore-goal.js.map +1 -0
- package/dist/explore-shared.d.ts +177 -0
- package/dist/explore-shared.js +149 -0
- package/dist/explore-shared.js.map +1 -0
- package/dist/explore-specs.d.ts +53 -0
- package/dist/explore-specs.js +333 -0
- package/dist/explore-specs.js.map +1 -0
- package/dist/findings-filing.d.ts +64 -0
- package/dist/findings-filing.js +108 -0
- package/dist/findings-filing.js.map +1 -0
- package/dist/fixture-auth.d.ts +63 -0
- package/dist/fixture-auth.js +95 -0
- package/dist/fixture-auth.js.map +1 -0
- package/dist/fixture-cli.d.ts +99 -0
- package/dist/fixture-cli.js +292 -0
- package/dist/fixture-cli.js.map +1 -0
- package/dist/github-issue-filer.d.ts +62 -0
- package/dist/github-issue-filer.js +189 -0
- package/dist/github-issue-filer.js.map +1 -0
- package/dist/host-health-run.d.ts +25 -0
- package/dist/host-health-run.js +33 -0
- package/dist/host-health-run.js.map +1 -0
- package/dist/http-5xx-outcome.d.ts +6 -0
- package/dist/http-5xx-outcome.js +39 -0
- package/dist/http-5xx-outcome.js.map +1 -0
- package/dist/inbox-cli.d.ts +7 -0
- package/dist/inbox-cli.js +197 -0
- package/dist/inbox-cli.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/init-cli.d.ts +4 -0
- package/dist/init-cli.js +156 -0
- package/dist/init-cli.js.map +1 -0
- package/dist/init-keys.d.ts +30 -2
- package/dist/init-keys.js +19 -5
- package/dist/init-keys.js.map +1 -1
- package/dist/init-next-steps.d.ts +35 -0
- package/dist/init-next-steps.js +79 -0
- package/dist/init-next-steps.js.map +1 -0
- package/dist/invariants-file.d.ts +34 -0
- package/dist/invariants-file.js +68 -0
- package/dist/invariants-file.js.map +1 -0
- package/dist/invariants-validate.d.ts +30 -0
- package/dist/invariants-validate.js +105 -0
- package/dist/invariants-validate.js.map +1 -0
- package/dist/journey-annotate-api.d.ts +76 -0
- package/dist/journey-annotate-api.js +269 -0
- package/dist/journey-annotate-api.js.map +1 -0
- package/dist/journey-annotate-cli.d.ts +9 -0
- package/dist/journey-annotate-cli.js +181 -0
- package/dist/journey-annotate-cli.js.map +1 -0
- package/dist/journey-api.d.ts +104 -2
- package/dist/journey-api.js +163 -20
- package/dist/journey-api.js.map +1 -1
- package/dist/journey-cli.d.ts +4 -0
- package/dist/journey-cli.js +411 -0
- package/dist/journey-cli.js.map +1 -0
- package/dist/journey-demo-api.d.ts +114 -0
- package/dist/journey-demo-api.js +405 -0
- package/dist/journey-demo-api.js.map +1 -0
- package/dist/journey-demo-cli.d.ts +10 -0
- package/dist/journey-demo-cli.js +158 -0
- package/dist/journey-demo-cli.js.map +1 -0
- package/dist/journey-prefix.d.ts +194 -0
- package/dist/journey-prefix.js +279 -0
- package/dist/journey-prefix.js.map +1 -0
- package/dist/key-report.d.ts +49 -0
- package/dist/key-report.js +101 -0
- package/dist/key-report.js.map +1 -0
- package/dist/kill-signal.d.ts +193 -0
- package/dist/kill-signal.js +283 -0
- package/dist/kill-signal.js.map +1 -0
- package/dist/launch-armed.d.ts +37 -0
- package/dist/launch-armed.js +31 -0
- package/dist/launch-armed.js.map +1 -0
- package/dist/ledger-api.d.ts +106 -0
- package/dist/ledger-api.js +239 -0
- package/dist/ledger-api.js.map +1 -0
- package/dist/ledger-cli.d.ts +18 -0
- package/dist/ledger-cli.js +107 -0
- package/dist/ledger-cli.js.map +1 -0
- package/dist/load-api.d.ts +29 -11
- package/dist/load-api.js +23 -15
- package/dist/load-api.js.map +1 -1
- package/dist/load-cli.d.ts +4 -0
- package/dist/load-cli.js +97 -0
- package/dist/load-cli.js.map +1 -0
- package/dist/log-correlation.d.ts +200 -0
- package/dist/log-correlation.js +394 -0
- package/dist/log-correlation.js.map +1 -0
- package/dist/log-lines.d.ts +131 -0
- package/dist/log-lines.js +389 -0
- package/dist/log-lines.js.map +1 -0
- package/dist/log-sources.d.ts +71 -0
- package/dist/log-sources.js +236 -0
- package/dist/log-sources.js.map +1 -0
- package/dist/log-trace.d.ts +80 -0
- package/dist/log-trace.js +210 -0
- package/dist/log-trace.js.map +1 -0
- package/dist/logs-cli.d.ts +3 -0
- package/dist/logs-cli.js +51 -0
- package/dist/logs-cli.js.map +1 -0
- package/dist/logs-retention.d.ts +28 -0
- package/dist/logs-retention.js +85 -0
- package/dist/logs-retention.js.map +1 -0
- package/dist/masked-input.d.ts +22 -0
- package/dist/masked-input.js +99 -0
- package/dist/masked-input.js.map +1 -0
- package/dist/mcp-api.d.ts +91 -1
- package/dist/mcp-api.js +514 -18
- package/dist/mcp-api.js.map +1 -1
- package/dist/mcp-args.d.ts +50 -0
- package/dist/mcp-args.js +164 -0
- package/dist/mcp-args.js.map +1 -0
- package/dist/mcp-cli-bridge.d.ts +34 -0
- package/dist/mcp-cli-bridge.js +62 -0
- package/dist/mcp-cli-bridge.js.map +1 -0
- package/dist/mcp-cli-runner.d.ts +29 -0
- package/dist/mcp-cli-runner.js +44 -0
- package/dist/mcp-cli-runner.js.map +1 -0
- package/dist/mcp-cli-tools.d.ts +129 -0
- package/dist/mcp-cli-tools.js +737 -0
- package/dist/mcp-cli-tools.js.map +1 -0
- package/dist/mcp-paths.d.ts +28 -0
- package/dist/mcp-paths.js +76 -0
- package/dist/mcp-paths.js.map +1 -0
- package/dist/mission-actors.d.ts +40 -0
- package/dist/mission-actors.js +72 -0
- package/dist/mission-actors.js.map +1 -0
- package/dist/mission-api.d.ts +35 -0
- package/dist/mission-api.js +60 -3
- package/dist/mission-api.js.map +1 -1
- package/dist/mission-cli.d.ts +4 -0
- package/dist/mission-cli.js +248 -0
- package/dist/mission-cli.js.map +1 -0
- package/dist/mission-exit.d.ts +22 -0
- package/dist/mission-exit.js +26 -0
- package/dist/mission-exit.js.map +1 -0
- package/dist/mission-fixtures.d.ts +234 -0
- package/dist/mission-fixtures.js +862 -0
- package/dist/mission-fixtures.js.map +1 -0
- package/dist/mission-journal.d.ts +60 -0
- package/dist/mission-journal.js +115 -0
- package/dist/mission-journal.js.map +1 -0
- package/dist/mission-queue-cli.d.ts +10 -0
- package/dist/mission-queue-cli.js +198 -0
- package/dist/mission-queue-cli.js.map +1 -0
- package/dist/mission-queue-runner.d.ts +126 -0
- package/dist/mission-queue-runner.js +406 -0
- package/dist/mission-queue-runner.js.map +1 -0
- package/dist/multi-run-cli.d.ts +37 -0
- package/dist/multi-run-cli.js +116 -0
- package/dist/multi-run-cli.js.map +1 -0
- package/dist/multi-run.d.ts +339 -0
- package/dist/multi-run.js +619 -0
- package/dist/multi-run.js.map +1 -0
- package/dist/openrouter-call.d.ts +22 -0
- package/dist/openrouter-call.js +64 -0
- package/dist/openrouter-call.js.map +1 -0
- package/dist/profile-cli.d.ts +4 -0
- package/dist/profile-cli.js +57 -0
- package/dist/profile-cli.js.map +1 -0
- package/dist/program.d.ts +3 -75
- package/dist/program.js +123 -1921
- package/dist/program.js.map +1 -1
- package/dist/project-dir.d.ts +96 -0
- package/dist/project-dir.js +221 -0
- package/dist/project-dir.js.map +1 -0
- package/dist/record-api.d.ts +13 -7
- package/dist/record-api.js +44 -30
- package/dist/record-api.js.map +1 -1
- package/dist/record-cli.d.ts +4 -0
- package/dist/record-cli.js +76 -0
- package/dist/record-cli.js.map +1 -0
- package/dist/recording-cli.d.ts +4 -0
- package/dist/recording-cli.js +248 -0
- package/dist/recording-cli.js.map +1 -0
- package/dist/regression-api.d.ts +107 -0
- package/dist/regression-api.js +387 -6
- package/dist/regression-api.js.map +1 -1
- package/dist/regression-cli.d.ts +4 -0
- package/dist/regression-cli.js +284 -0
- package/dist/regression-cli.js.map +1 -0
- package/dist/report-api.d.ts +146 -0
- package/dist/report-api.js +431 -0
- package/dist/report-api.js.map +1 -0
- package/dist/report-cli.d.ts +12 -0
- package/dist/report-cli.js +147 -0
- package/dist/report-cli.js.map +1 -0
- package/dist/resource-preflight.d.ts +87 -0
- package/dist/resource-preflight.js +160 -0
- package/dist/resource-preflight.js.map +1 -0
- package/dist/result-schema.d.ts +22 -0
- package/dist/result-schema.js +28 -0
- package/dist/result-schema.js.map +1 -0
- package/dist/run-index.d.ts +29 -0
- package/dist/run-index.js +70 -0
- package/dist/run-index.js.map +1 -0
- package/dist/run-key-preflight.d.ts +42 -0
- package/dist/run-key-preflight.js +82 -0
- package/dist/run-key-preflight.js.map +1 -0
- package/dist/run-screenshots.d.ts +123 -0
- package/dist/run-screenshots.js +235 -0
- package/dist/run-screenshots.js.map +1 -0
- package/dist/runtime-env.d.ts +8 -0
- package/dist/runtime-env.js +23 -0
- package/dist/runtime-env.js.map +1 -0
- package/dist/secret-args.d.ts +23 -0
- package/dist/secret-args.js +45 -0
- package/dist/secret-args.js.map +1 -0
- package/dist/serve-cli.d.ts +4 -0
- package/dist/serve-cli.js +95 -0
- package/dist/serve-cli.js.map +1 -0
- package/dist/server-log-verify.d.ts +36 -0
- package/dist/server-log-verify.js +120 -0
- package/dist/server-log-verify.js.map +1 -0
- package/dist/session-check.d.ts +14 -0
- package/dist/session-check.js +44 -0
- package/dist/session-check.js.map +1 -0
- package/dist/site-cli.d.ts +4 -0
- package/dist/site-cli.js +127 -0
- package/dist/site-cli.js.map +1 -0
- package/dist/site-gate-cli.d.ts +28 -0
- package/dist/site-gate-cli.js +59 -0
- package/dist/site-gate-cli.js.map +1 -0
- package/dist/source-api.js +20 -4
- package/dist/source-api.js.map +1 -1
- package/dist/source-cli.d.ts +4 -0
- package/dist/source-cli.js +284 -0
- package/dist/source-cli.js.map +1 -0
- package/dist/source-run-api.d.ts +27 -1
- package/dist/source-run-api.js +22 -11
- package/dist/source-run-api.js.map +1 -1
- package/dist/storage-state-snapshot.d.ts +50 -0
- package/dist/storage-state-snapshot.js +87 -0
- package/dist/storage-state-snapshot.js.map +1 -0
- package/dist/suite-explore-options.d.ts +321 -0
- package/dist/suite-explore-options.js +162 -0
- package/dist/suite-explore-options.js.map +1 -0
- package/dist/target-config.d.ts +115 -0
- package/dist/target-config.js +225 -0
- package/dist/target-config.js.map +1 -0
- package/dist/transcript-file.d.ts +12 -0
- package/dist/transcript-file.js +23 -0
- package/dist/transcript-file.js.map +1 -0
- package/dist/type-fixture-file.d.ts +9 -0
- package/dist/type-fixture-file.js +48 -0
- package/dist/type-fixture-file.js.map +1 -0
- package/dist/ui-api.d.ts +3 -0
- package/dist/ui-api.js +11 -4
- package/dist/ui-api.js.map +1 -1
- package/dist/usability-capture.d.ts +43 -0
- package/dist/usability-capture.js +327 -0
- package/dist/usability-capture.js.map +1 -0
- package/dist/usage-config.d.ts +36 -0
- package/dist/usage-config.js +114 -0
- package/dist/usage-config.js.map +1 -0
- package/dist/ux-api.d.ts +3 -75
- package/dist/ux-api.js +3 -220
- package/dist/ux-api.js.map +1 -1
- package/dist/ux-claim-probe.d.ts +45 -0
- package/dist/ux-claim-probe.js +386 -0
- package/dist/ux-claim-probe.js.map +1 -0
- package/dist/ux-cli.d.ts +4 -0
- package/dist/ux-cli.js +109 -0
- package/dist/ux-cli.js.map +1 -0
- package/dist/ux-config.d.ts +29 -0
- package/dist/ux-config.js +103 -0
- package/dist/ux-config.js.map +1 -0
- package/dist/ux-evidence.d.ts +54 -0
- package/dist/ux-evidence.js +197 -0
- package/dist/ux-evidence.js.map +1 -0
- package/dist/ux-product.d.ts +14 -0
- package/dist/ux-product.js +39 -0
- package/dist/ux-product.js.map +1 -0
- package/dist/ux-review.d.ts +116 -0
- package/dist/ux-review.js +171 -0
- package/dist/ux-review.js.map +1 -0
- package/dist/ux-usability.d.ts +263 -0
- package/dist/ux-usability.js +580 -0
- package/dist/ux-usability.js.map +1 -0
- package/dist/verify-fix-api.d.ts +194 -0
- package/dist/verify-fix-api.js +506 -0
- package/dist/verify-fix-api.js.map +1 -0
- package/dist/verify-fix-cli.d.ts +4 -0
- package/dist/verify-fix-cli.js +115 -0
- package/dist/verify-fix-cli.js.map +1 -0
- package/package.json +23 -18
- package/skills/jevitate-ci-check/SKILL.md +77 -0
- package/skills/jevitate-demo/SKILL.md +61 -0
- package/skills/jevitate-explore/SKILL.md +96 -87
- package/skills/jevitate-getting-started/SKILL.md +89 -0
- package/skills/jevitate-load-test/SKILL.md +11 -2
- package/skills/jevitate-mission-scope/SKILL.md +19 -11
- package/skills/jevitate-record/SKILL.md +23 -5
- package/skills/jevitate-run-journey/SKILL.md +61 -17
- package/skills/jevitate-sources/SKILL.md +57 -14
- package/skills/jevitate-test-campaign/SKILL.md +203 -0
- package/skills/jevitate-ux-review/SKILL.md +106 -16
- package/skills/jevitate-verify-fix/SKILL.md +76 -0
package/dist/mcp-api.js
CHANGED
|
@@ -1,13 +1,39 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { SiteGateRefusedError } from "@jevitate/runtime";
|
|
3
|
+
import { withSiteGate } from "./site-gate-cli.js";
|
|
2
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
5
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
-
import { ALLOWED_TOOLS, FORBIDDEN_TOOLS, findCapabilities, queueExploration as facadeQueueExploration, aiGenerateText as facadeAiGenerateText, facadeListIncoming, facadeGetCommand, facadeGetThread, facadeQueueAction, facadeQueueRetrieval, facadeApproveAction, facadeCancelCommand, facadeGetSiteHealth, } from "@jevitate/mcp-facade";
|
|
6
|
+
import { ALLOWED_TOOLS, FORBIDDEN_TOOLS, findCapabilities, queueExploration as facadeQueueExploration, aiGenerateText as facadeAiGenerateText, facadeListIncoming, facadeGetCommand, facadeGetThread, facadeQueueAction, facadeQueueRetrieval, facadeApproveAction, facadeCancelCommand, facadeGetSiteHealth, isMissionResultId, isQueuedMissionId, missionResultFileName, missionStatus, parseResultOutcome, } from "@jevitate/mcp-facade";
|
|
5
7
|
import { FsJourneyStore, JourneyRegistry } from "@jevitate/journey";
|
|
6
|
-
import { FsMissionTargetStore, MissionTargetRegistry, FsMissionQueueStore, } from "@jevitate/missions";
|
|
8
|
+
import { FsMissionTargetStore, MissionTargetRegistry, FsMissionQueueStore, MISSION_STRATEGIES, QUEUED_SCREENSHOT_MODES, } from "@jevitate/missions";
|
|
7
9
|
import { FsInboxStore } from "@jevitate/inbox";
|
|
8
|
-
import { envCredentialStore, } from "@jevitate/ai-core";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
10
|
+
import { ALL_CREDENTIAL_KEYS, envCredentialStore, } from "@jevitate/ai-core";
|
|
11
|
+
import { existsSync } from "node:fs";
|
|
12
|
+
import { access, readFile } from "node:fs/promises";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { ParamValidationError } from "@jevitate/journey";
|
|
15
|
+
import { safeRunPolicy as defaultRunPolicy } from "@jevitate/domain";
|
|
16
|
+
import { JourneyRequiresAuthError, UnknownJourneyError, runJourneyProgrammatically } from "./journey-api.js";
|
|
17
|
+
import { runVerifyFix } from "./verify-fix-api.js";
|
|
18
|
+
import { ExtensionMismatchError } from "./browser-run-options.js";
|
|
19
|
+
import { browserRunFromFlags, emulationFromFlags } from "./cli-shared.js";
|
|
20
|
+
import { environmentFromFlags, isEnvironmentError } from "./environments.js";
|
|
21
|
+
import { buildMissionFixtures, checkSetupRefs } from "./fixture-cli.js";
|
|
22
|
+
import { FixtureSetupError, FixtureSpecError, UnboundSetupRefError } from "./mission-fixtures.js";
|
|
23
|
+
import { parseScreenshotsArg } from "./run-screenshots.js";
|
|
24
|
+
import { makeExploreSelfHealer } from "./self-heal-adapter.js";
|
|
25
|
+
import { McpArgError, argErrorBody, optBool, optEnum, optInt, optNamedSessions, optPath, optExtensions, optRecordVideo, optScreenshots, optString, optStringArray, optStringMap, optViewport } from "./mcp-args.js";
|
|
26
|
+
import { defaultMcpPathRoots } from "./mcp-paths.js";
|
|
27
|
+
import { CLI_TOOL_SPECS, cliToolInputSchema, runCliTool } from "./mcp-cli-tools.js";
|
|
28
|
+
import { currentEngineInfo } from "./engine.js";
|
|
29
|
+
import { loadTargetsFile } from "./target-config.js";
|
|
30
|
+
import { resourcePreflight } from "./resource-preflight.js";
|
|
31
|
+
/** #205: a native tool's `maxBrowsers` / `maxBrowserMemory` (MiB) arguments, typed like the CLI flags. */
|
|
32
|
+
function governanceArgs(args) {
|
|
33
|
+
const maxBrowsers = optInt(args, "maxBrowsers", 1);
|
|
34
|
+
const maxBrowserMemory = optInt(args, "maxBrowserMemory", 1);
|
|
35
|
+
return { ...(maxBrowsers === undefined ? {} : { maxBrowsers }), ...(maxBrowserMemory === undefined ? {} : { maxBrowserMemory }) };
|
|
36
|
+
}
|
|
11
37
|
const ALLOWED = new Set(ALLOWED_TOOLS);
|
|
12
38
|
const FORBIDDEN = new Set(FORBIDDEN_TOOLS);
|
|
13
39
|
/**
|
|
@@ -33,9 +59,17 @@ function errorResult(value) {
|
|
|
33
59
|
* SDK error (which can echo request internals verbatim) never surfaces a key
|
|
34
60
|
* through the MCP result. The message returned to the caller is generic and
|
|
35
61
|
* key-free; this is belt-and-braces on top of the gateway's outbound guard. */
|
|
62
|
+
/** #217: a persisted goal result's own ending (`result.goalOutcome`), when present. */
|
|
63
|
+
function goalOutcomeOf(file) {
|
|
64
|
+
const result = file.result;
|
|
65
|
+
return result !== null && typeof result === "object" && "goalOutcome" in result ? result.goalOutcome : undefined;
|
|
66
|
+
}
|
|
67
|
+
/** A model-backed option was asked for without usable gateways/keys (typed `setup_required`). */
|
|
68
|
+
class SetupRequired extends Error {
|
|
69
|
+
}
|
|
36
70
|
function redactCredentials(message, store) {
|
|
37
71
|
let out = message;
|
|
38
|
-
for (const key of
|
|
72
|
+
for (const key of ALL_CREDENTIAL_KEYS) {
|
|
39
73
|
const value = store.read(key);
|
|
40
74
|
if (value)
|
|
41
75
|
out = out.split(value).join("***REDACTED***");
|
|
@@ -57,7 +91,22 @@ export function buildMcpTools(deps) {
|
|
|
57
91
|
const findCaps = deps.findCapabilities ??
|
|
58
92
|
((query) => findCapabilities(new JourneyRegistry(new FsJourneyStore(deps.journeysDir)), query));
|
|
59
93
|
const runJourney = deps.runJourney ??
|
|
60
|
-
((id, params) =>
|
|
94
|
+
((id, params, storageState, o) => withSiteGate(deps.sitePolicyDbPath, (siteGate) => runJourneyProgrammatically({
|
|
95
|
+
dir: deps.journeysDir,
|
|
96
|
+
id,
|
|
97
|
+
params,
|
|
98
|
+
policy: o?.policy ?? defaultRunPolicy(),
|
|
99
|
+
...(storageState !== undefined ? { storageState } : {}),
|
|
100
|
+
...(siteGate === undefined ? {} : { siteGate }),
|
|
101
|
+
...(o?.selfHealer === undefined ? {} : { selfHealer: o.selfHealer }),
|
|
102
|
+
...(o?.environment === undefined ? {} : { environment: o.environment }),
|
|
103
|
+
...(o?.browser === undefined ? {} : { browser: o.browser }),
|
|
104
|
+
...(o?.emulation === undefined ? {} : { emulation: o.emulation }),
|
|
105
|
+
...(o?.screenshots === undefined ? {} : { screenshots: o.screenshots }),
|
|
106
|
+
...(o?.fixtures === undefined ? {} : { fixtures: o.fixtures }),
|
|
107
|
+
...(o?.actionDeltas === true ? { actionDeltas: true } : {}),
|
|
108
|
+
})));
|
|
109
|
+
const pathRoots = deps.pathRoots ?? defaultMcpPathRoots();
|
|
61
110
|
// queue_exploration: enqueue over the SAME promoted fs store `jevitate
|
|
62
111
|
// mission target` writes. The store is built lazily inside the closure so
|
|
63
112
|
// constructing the tool set never touches disk; a missing dir is a config
|
|
@@ -91,7 +140,7 @@ export function buildMcpTools(deps) {
|
|
|
91
140
|
if (!deps.inboxDir) {
|
|
92
141
|
throw new Error("inbox tools require inboxDir to be configured");
|
|
93
142
|
}
|
|
94
|
-
return new FsInboxStore(deps.inboxDir);
|
|
143
|
+
return new FsInboxStore(deps.inboxDir, deps.engine ?? currentEngineInfo());
|
|
95
144
|
};
|
|
96
145
|
function inboxHandler(fn) {
|
|
97
146
|
return async (args) => {
|
|
@@ -131,19 +180,391 @@ export function buildMcpTools(deps) {
|
|
|
131
180
|
},
|
|
132
181
|
};
|
|
133
182
|
const queueCommonRequired = ["run", "journey", "step", "reason", "agent"];
|
|
183
|
+
// get_mission_result: reads a persisted typed mission result by ID only (the artifact stem the
|
|
184
|
+
// CLI wrote) — never a caller-supplied path. The status carries the CLI exit code, and a run that
|
|
185
|
+
// itself broke (inconclusive/crashed) is an MCP error result, so it can never read as a pass.
|
|
186
|
+
//
|
|
187
|
+
// The id is either a result stem (`explore-<stamp>`, `usability-<stamp>.recording`, …) or a
|
|
188
|
+
// `queue_exploration` missionId (#117): the latter resolves through the mission's queue record —
|
|
189
|
+
// `queued`/`running` is reported as such (not an error, not a result), `failed` as an error, and
|
|
190
|
+
// `done` reads the result its drain wrote. Both shapes are closed regexes; nothing caller-shaped
|
|
191
|
+
// ever reaches a path.
|
|
192
|
+
const resolveResultId = async (id, tool) => {
|
|
193
|
+
if (isMissionResultId(id))
|
|
194
|
+
return { resultId: id };
|
|
195
|
+
if (!isQueuedMissionId(id)) {
|
|
196
|
+
return {
|
|
197
|
+
response: errorResult({
|
|
198
|
+
error: "invalid_args",
|
|
199
|
+
message: `${tool} requires a mission result 'id' (explore-|coverage-|exploratory-|adversarial-|feature-|usability-<stamp>) or a queue_exploration missionId`,
|
|
200
|
+
}),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (!deps.missionQueueDir) {
|
|
204
|
+
return { response: errorResult({ error: "not_configured", message: `${tool} requires missionQueueDir to resolve a missionId` }) };
|
|
205
|
+
}
|
|
206
|
+
let mission;
|
|
207
|
+
try {
|
|
208
|
+
mission = await new FsMissionQueueStore(deps.missionQueueDir).get(id);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return { response: errorResult({ error: "corrupt_mission", id }) };
|
|
212
|
+
}
|
|
213
|
+
if (mission === null)
|
|
214
|
+
return { response: errorResult({ error: "not_found", id }) };
|
|
215
|
+
if (mission.status === "queued" || mission.status === "running") {
|
|
216
|
+
// Not finished: neither a pass nor a failure — the agent polls again later.
|
|
217
|
+
const pending = {
|
|
218
|
+
id,
|
|
219
|
+
missionId: id,
|
|
220
|
+
status: mission.status,
|
|
221
|
+
pending: true,
|
|
222
|
+
enqueuedAtIso: mission.enqueuedAtIso,
|
|
223
|
+
...(mission.startedAtIso === undefined ? {} : { startedAtIso: mission.startedAtIso }),
|
|
224
|
+
};
|
|
225
|
+
return { response: jsonResult(pending), pending };
|
|
226
|
+
}
|
|
227
|
+
if (mission.status === "failed") {
|
|
228
|
+
return { response: errorResult({ id, missionId: id, status: "failed", isError: true, error: mission.error ?? "mission failed" }) };
|
|
229
|
+
}
|
|
230
|
+
if (!isMissionResultId(mission.resultId))
|
|
231
|
+
return { response: errorResult({ error: "corrupt_mission", id }) };
|
|
232
|
+
return { resultId: mission.resultId, missionId: id };
|
|
233
|
+
};
|
|
234
|
+
/** The dirs a result may live in: recordings, and (usability reviews) the UX reports dir. */
|
|
235
|
+
const resultPathsFor = (resultId) => {
|
|
236
|
+
const file = missionResultFileName(resultId);
|
|
237
|
+
const dirs = deps.resultDirsFor?.(resultId) ?? [deps.recordingsDir, ...(resultId.startsWith("usability-") ? [deps.uxReportsDir] : [])];
|
|
238
|
+
return dirs.filter((d) => d !== undefined).map((d) => join(d, file));
|
|
239
|
+
};
|
|
240
|
+
const getMissionResult = async (args) => {
|
|
241
|
+
if (!deps.recordingsDir) {
|
|
242
|
+
return errorResult({ error: "not_configured", message: "get_mission_result requires recordingsDir" });
|
|
243
|
+
}
|
|
244
|
+
const ref = await resolveResultId(args.id, "get_mission_result");
|
|
245
|
+
if ("response" in ref)
|
|
246
|
+
return ref.response;
|
|
247
|
+
const ids = { id: args.id, ...(ref.missionId === undefined ? {} : { missionId: ref.missionId, resultId: ref.resultId }) };
|
|
248
|
+
let raw;
|
|
249
|
+
for (const path of resultPathsFor(ref.resultId)) {
|
|
250
|
+
try {
|
|
251
|
+
raw = await readFile(path, "utf8");
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// Try the next candidate dir.
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (raw === undefined) {
|
|
259
|
+
return errorResult({ error: "not_found", ...ids });
|
|
260
|
+
}
|
|
261
|
+
let parsed;
|
|
262
|
+
try {
|
|
263
|
+
parsed = JSON.parse(raw);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return errorResult({ error: "corrupt_result", ...ids });
|
|
267
|
+
}
|
|
268
|
+
// #217: `missionOutcome` is canonical; a goal run's own ending rides beside it as `goalOutcome`.
|
|
269
|
+
const parsedOutcome = parsed !== null && typeof parsed === "object" && "missionOutcome" in parsed
|
|
270
|
+
? parseResultOutcome(parsed.missionOutcome, goalOutcomeOf(parsed))
|
|
271
|
+
: null;
|
|
272
|
+
if (parsedOutcome === null) {
|
|
273
|
+
return errorResult({ error: "corrupt_result", ...ids });
|
|
274
|
+
}
|
|
275
|
+
const status = {
|
|
276
|
+
...missionStatus(parsedOutcome.outcome),
|
|
277
|
+
...(parsedOutcome.goalOutcome === undefined ? {} : { goalOutcome: parsedOutcome.goalOutcome }),
|
|
278
|
+
};
|
|
279
|
+
const result = parsed.result ?? null;
|
|
280
|
+
// An adversarial run's coverage is surfaced next to the status: an `inconclusive` run says what
|
|
281
|
+
// it did and did not exercise, so an agent can tell "found nothing" from "tried nothing".
|
|
282
|
+
const coverage = result !== null && typeof result === "object" && "coverage" in result ? result.coverage : undefined;
|
|
283
|
+
const body = { ...ids, ...status, ...(coverage === undefined ? {} : { coverage }), result };
|
|
284
|
+
return status.isError ? errorResult(body) : jsonResult(body);
|
|
285
|
+
};
|
|
286
|
+
// verify_fix: replays a persisted finding by (result id, fingerprint) — never a caller-supplied
|
|
287
|
+
// recording or path (invariant #5 analogue). Passes only if the defect signal is absent; an
|
|
288
|
+
// unreachable replay is `inconclusive` and returned as an error result, never as "fixed".
|
|
289
|
+
// #142 follow-up: a `cmd:` server-log source may only be re-run when the OPERATOR opted in for
|
|
290
|
+
// this origin in ~/.jevitate/targets.json (`allowLogCmd: true`) — never via an MCP argument.
|
|
291
|
+
const verifyFixImpl = deps.verifyFix ??
|
|
292
|
+
((a) => runVerifyFix({
|
|
293
|
+
targets: loadTargetsFile(deps.targetsConfigPath),
|
|
294
|
+
resultPath: a.resultPath,
|
|
295
|
+
fingerprint: a.fingerprint,
|
|
296
|
+
...(a.storageState === undefined ? {} : { storageState: a.storageState }),
|
|
297
|
+
...(a.browser === undefined ? {} : { browser: a.browser }),
|
|
298
|
+
...(a.replays === undefined ? {} : { replays: a.replays }),
|
|
299
|
+
...(a.invariantFiles === undefined ? {} : { invariantFiles: a.invariantFiles }),
|
|
300
|
+
...(a.fixtureFlags === undefined ? {} : { fixtureFlags: a.fixtureFlags }),
|
|
301
|
+
...(a.emulation === undefined ? {} : { emulation: a.emulation }),
|
|
302
|
+
...(a.allowEmulationOverride === undefined ? {} : { allowEmulationOverride: a.allowEmulationOverride }),
|
|
303
|
+
...(a.screenshots === undefined ? {} : { screenshots: a.screenshots }),
|
|
304
|
+
...(a.actionDeltas === true ? { actionDeltas: true } : {}),
|
|
305
|
+
}));
|
|
306
|
+
/** #255: verify_fix's CLI-parity replay options, validated like `verify-fix`'s flags (typed, before any browser). */
|
|
307
|
+
const verifyFixOptions = (args) => {
|
|
308
|
+
const storageState = optPath(args, "storageState", pathRoots, { session: true });
|
|
309
|
+
if (storageState !== undefined && !existsSync(storageState))
|
|
310
|
+
throw new McpArgError(`storage state not found: ${storageState}`);
|
|
311
|
+
const replays = optInt(args, "replays", 1);
|
|
312
|
+
const invariantFiles = (optStringArray(args, "invariants") ?? []).map((f, i) => optPath({ [`invariants[${i}]`]: f }, `invariants[${i}]`, pathRoots));
|
|
313
|
+
const fixtures = optPath(args, "fixtures", pathRoots);
|
|
314
|
+
const fixtureIdentity = optNamedSessions(args, "fixtureIdentity", pathRoots);
|
|
315
|
+
const allowEmulationOverride = optBool(args, "allowEmulationOverride");
|
|
316
|
+
const headed = optBool(args, "headed");
|
|
317
|
+
const slowMo = optInt(args, "slowMo", 0);
|
|
318
|
+
const recordVideo = optRecordVideo(args, pathRoots);
|
|
319
|
+
const shots = optScreenshots(args, pathRoots);
|
|
320
|
+
const viewport = optViewport(args);
|
|
321
|
+
const device = optString(args, "device");
|
|
322
|
+
const extension = optExtensions(args, pathRoots);
|
|
323
|
+
const governance = governanceArgs(args);
|
|
324
|
+
const actionDeltas = optBool(args, "actionDeltas");
|
|
325
|
+
try {
|
|
326
|
+
const browser = browserRunFromFlags({ browserArg: [], ...governance, ...(extension === undefined ? {} : { extension }), ...(headed === undefined ? {} : { headed }), ...(slowMo === undefined ? {} : { slowMo }), ...(recordVideo === undefined ? {} : { recordVideo }) });
|
|
327
|
+
const screenshots = parseScreenshotsArg(shots);
|
|
328
|
+
const emulation = emulationFromFlags({ ...(viewport === undefined ? {} : { viewport }), ...(device === undefined ? {} : { device }) });
|
|
329
|
+
return {
|
|
330
|
+
...(storageState === undefined ? {} : { storageState }),
|
|
331
|
+
...(replays === undefined ? {} : { replays }),
|
|
332
|
+
...(invariantFiles.length === 0 ? {} : { invariantFiles }),
|
|
333
|
+
...(fixtures === undefined && fixtureIdentity === undefined
|
|
334
|
+
? {}
|
|
335
|
+
: { fixtureFlags: { ...(fixtures === undefined ? {} : { fixtures }), ...(fixtureIdentity === undefined ? {} : { fixtureIdentity }) } }),
|
|
336
|
+
...(allowEmulationOverride === undefined ? {} : { allowEmulationOverride }),
|
|
337
|
+
...(browser === undefined ? {} : { browser }),
|
|
338
|
+
...(screenshots === undefined ? {} : { screenshots }),
|
|
339
|
+
...(emulation === undefined ? {} : { emulation }),
|
|
340
|
+
...(actionDeltas === true ? { actionDeltas: true } : {}),
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
throw new McpArgError(err instanceof Error ? err.message : String(err));
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
const verifyFixTool = async (args) => {
|
|
348
|
+
if (!deps.recordingsDir) {
|
|
349
|
+
return errorResult({ error: "not_configured", message: "verify_fix requires recordingsDir" });
|
|
350
|
+
}
|
|
351
|
+
if (!(isMissionResultId(args.id) || isQueuedMissionId(args.id)) ||
|
|
352
|
+
typeof args.fingerprint !== "string" ||
|
|
353
|
+
!/^[0-9a-f]{16}$/.test(args.fingerprint)) {
|
|
354
|
+
return errorResult({ error: "invalid_args", message: "verify_fix requires a mission result 'id' (or missionId) and a 16-hex 'fingerprint'" });
|
|
355
|
+
}
|
|
356
|
+
let options;
|
|
357
|
+
try {
|
|
358
|
+
options = verifyFixOptions(args);
|
|
359
|
+
}
|
|
360
|
+
catch (err) {
|
|
361
|
+
const body = argErrorBody(err);
|
|
362
|
+
if (body === undefined)
|
|
363
|
+
throw err;
|
|
364
|
+
return errorResult(body);
|
|
365
|
+
}
|
|
366
|
+
// #205: the same pre-run checks as `jevitate verify-fix` (orphan sweep, starved-host refusal).
|
|
367
|
+
const starved = await resourcePreflight({});
|
|
368
|
+
if (starved !== null)
|
|
369
|
+
return errorResult({ error: "refused", code: starved.error?.code, message: starved.error?.message });
|
|
370
|
+
const ref = await resolveResultId(args.id, "verify_fix");
|
|
371
|
+
if ("response" in ref) {
|
|
372
|
+
// A mission still queued/running has nothing to verify yet: never a pass.
|
|
373
|
+
return ref.pending === undefined ? ref.response : errorResult({ error: "not_ready", ...ref.pending });
|
|
374
|
+
}
|
|
375
|
+
// The first candidate that exists (usability results may sit in the UX reports dir).
|
|
376
|
+
const candidates = resultPathsFor(ref.resultId);
|
|
377
|
+
let resultPath = candidates[0];
|
|
378
|
+
for (const path of candidates) {
|
|
379
|
+
try {
|
|
380
|
+
await access(path);
|
|
381
|
+
resultPath = path;
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
catch {
|
|
385
|
+
// Try the next candidate dir.
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
try {
|
|
389
|
+
const report = await verifyFixImpl({ ...options, resultPath, fingerprint: args.fingerprint });
|
|
390
|
+
const body = {
|
|
391
|
+
id: args.id,
|
|
392
|
+
...(ref.missionId === undefined ? {} : { missionId: ref.missionId, resultId: ref.resultId }),
|
|
393
|
+
status: report.verdict,
|
|
394
|
+
...report,
|
|
395
|
+
};
|
|
396
|
+
// Neither is a pass: `inconclusive` proved nothing either way, `intermittent` (#74) means the
|
|
397
|
+
// signal fired on SOME but not all fresh-context replays — never trustworthy as "fixed".
|
|
398
|
+
return report.verdict === "inconclusive" || report.verdict === "intermittent" ? errorResult(body) : jsonResult(body);
|
|
399
|
+
}
|
|
400
|
+
catch (err) {
|
|
401
|
+
return errorResult({ error: "verify_fix_refused", message: err instanceof Error ? err.message : String(err) });
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
/**
|
|
405
|
+
* #255: `run_journey`'s CLI-parity options, resolved exactly as `journey run` resolves its flags
|
|
406
|
+
* (the same helpers: environmentFromFlags, browserRunFromFlags, parseScreenshotsArg,
|
|
407
|
+
* emulationFromFlags, buildMissionFixtures) — every refusal typed, before any browser opens.
|
|
408
|
+
*/
|
|
409
|
+
const resolveJourneyRun = async (args) => {
|
|
410
|
+
// Params keep their historical leniency for a non-object (treated as none); a non-string VALUE is refused.
|
|
411
|
+
const params = args.params && typeof args.params === "object" && !Array.isArray(args.params) ? (optStringMap(args, "params") ?? {}) : {};
|
|
412
|
+
const envName = optString(args, "env");
|
|
413
|
+
const baseUrl = optString(args, "baseUrl");
|
|
414
|
+
const environment = environmentFromFlags({ ...(envName === undefined ? {} : { env: envName }), ...(baseUrl === undefined ? {} : { baseUrl }) }, { ...(deps.environmentsFile === undefined ? {} : { environmentsFile: deps.environmentsFile }), ...(deps.targetsConfigPath === undefined ? {} : { targetsFile: deps.targetsConfigPath }) });
|
|
415
|
+
// --storage-state wins; else the environment's own session (~/.jevitate/targets.json[<origin>]).
|
|
416
|
+
const storageState = optPath(args, "storageState", pathRoots, { session: true }) ?? environment?.storageState;
|
|
417
|
+
if (args.storageState !== undefined && storageState !== undefined && !existsSync(storageState))
|
|
418
|
+
throw new McpArgError(`storage state not found: ${storageState}`);
|
|
419
|
+
const headed = optBool(args, "headed");
|
|
420
|
+
const slowMo = optInt(args, "slowMo", 0);
|
|
421
|
+
const recordVideo = optRecordVideo(args, pathRoots);
|
|
422
|
+
const extension = optExtensions(args, pathRoots);
|
|
423
|
+
const governance = governanceArgs(args);
|
|
424
|
+
let browser;
|
|
425
|
+
let screenshots;
|
|
426
|
+
let emulation;
|
|
427
|
+
try {
|
|
428
|
+
browser = browserRunFromFlags({ browserArg: [], ...governance, ...(extension === undefined ? {} : { extension }), ...(headed === undefined ? {} : { headed }), ...(slowMo === undefined ? {} : { slowMo }), ...(recordVideo === undefined ? {} : { recordVideo }) });
|
|
429
|
+
screenshots = parseScreenshotsArg(optScreenshots(args, pathRoots));
|
|
430
|
+
const viewport = optViewport(args);
|
|
431
|
+
const device = optString(args, "device");
|
|
432
|
+
emulation = emulationFromFlags({ ...(viewport === undefined ? {} : { viewport }), ...(device === undefined ? {} : { device }) });
|
|
433
|
+
}
|
|
434
|
+
catch (err) {
|
|
435
|
+
if (err instanceof McpArgError || argErrorBody(err) !== undefined)
|
|
436
|
+
throw err;
|
|
437
|
+
throw new McpArgError(err instanceof Error ? err.message : String(err));
|
|
438
|
+
}
|
|
439
|
+
const fixturesFile = optPath(args, "fixtures", pathRoots);
|
|
440
|
+
const fixtureIdentity = optNamedSessions(args, "fixtureIdentity", pathRoots);
|
|
441
|
+
// The environment's hooks apply as on the CLI; they need --allow-shell-hooks, which MCP never sets.
|
|
442
|
+
const fixtureFlags = {
|
|
443
|
+
...(fixturesFile === undefined ? {} : { fixtures: fixturesFile }),
|
|
444
|
+
...(fixtureIdentity === undefined ? {} : { fixtureIdentity }),
|
|
445
|
+
...(environment?.hooks?.before === undefined ? {} : { before: environment.hooks.before }),
|
|
446
|
+
...(environment?.hooks?.after === undefined ? {} : { after: environment.hooks.after }),
|
|
447
|
+
};
|
|
448
|
+
const fixtures = (site) => {
|
|
449
|
+
const fx = buildMissionFixtures(fixtureFlags, {
|
|
450
|
+
allowlist: environment === undefined ? [site] : environment.allowedOrigins,
|
|
451
|
+
baseUrl: site,
|
|
452
|
+
...(storageState !== undefined ? { storageState } : {}),
|
|
453
|
+
...(environment?.fixtures === undefined ? {} : { targetFixtures: environment.fixtures }),
|
|
454
|
+
});
|
|
455
|
+
checkSetupRefs({ "--param": Object.values(params) }, fx);
|
|
456
|
+
return fx;
|
|
457
|
+
};
|
|
458
|
+
const selfHeal = optEnum(args, "selfHeal", ["fail-closed", "hybrid", "full"]) ?? "fail-closed";
|
|
459
|
+
const real = optBool(args, "real") ?? false;
|
|
460
|
+
const fakeAi = optBool(args, "fakeAi") ?? false;
|
|
461
|
+
let selfHealer;
|
|
462
|
+
let policy = defaultRunPolicy();
|
|
463
|
+
let usage;
|
|
464
|
+
if (selfHeal !== "fail-closed") {
|
|
465
|
+
if (deps.selfHealGateways === undefined)
|
|
466
|
+
throw new SetupRequired("run_journey selfHeal needs the model gateways, which this server was not given");
|
|
467
|
+
let judge;
|
|
468
|
+
let gen;
|
|
469
|
+
try {
|
|
470
|
+
({ judge, gen, usage } = await deps.selfHealGateways({ real, fakeAi }));
|
|
471
|
+
}
|
|
472
|
+
catch (err) {
|
|
473
|
+
throw new SetupRequired(redactCredentials(err instanceof Error ? err.message : String(err), credentialStore));
|
|
474
|
+
}
|
|
475
|
+
selfHealer = makeExploreSelfHealer(judge, gen);
|
|
476
|
+
policy = { ...policy, selfHeal: { mode: selfHeal } };
|
|
477
|
+
}
|
|
478
|
+
return {
|
|
479
|
+
params,
|
|
480
|
+
...(storageState === undefined ? {} : { storageState }),
|
|
481
|
+
options: {
|
|
482
|
+
policy,
|
|
483
|
+
fixtures,
|
|
484
|
+
...(selfHealer === undefined ? {} : { selfHealer }),
|
|
485
|
+
...(environment === undefined ? {} : { environment }),
|
|
486
|
+
...(browser === undefined ? {} : { browser }),
|
|
487
|
+
...(emulation === undefined ? {} : { emulation }),
|
|
488
|
+
...(screenshots === undefined ? {} : { screenshots }),
|
|
489
|
+
...(optBool(args, "actionDeltas") === true ? { actionDeltas: true } : {}),
|
|
490
|
+
},
|
|
491
|
+
...(usage === undefined ? {} : { usage }),
|
|
492
|
+
};
|
|
493
|
+
};
|
|
134
494
|
const wired = {
|
|
495
|
+
verify_fix: {
|
|
496
|
+
description: "Replay a finding's reproduction (by mission result id — or a finished queue_exploration missionId — + fingerprint) N times in fresh browsers (default 3) — `jevitate verify-fix`. status: fixed (signal absent on every replay) | still-reproduces | intermittent (fired on some but not all replays — never a pass) | inconclusive (replay could not reach the step — never a pass). " +
|
|
497
|
+
"#255 (same as the CLI flags): 'replays' (>= 1); 'recordVideo' (true or a directory: the before/after evidence pair — `evidence.before` is the run's own clip, `evidence.after` a captioned replay — plus videoPaths); 'screenshots'; 'headed'/'slowMo'; " +
|
|
498
|
+
"'storageState' (a path inside the project or ~/.jevitate, never a repo's .jevitate/; overrides the mission's session); 'viewport' {width,height} or 'device' (refused when it differs from the finding's recorded emulation unless 'allowEmulationOverride'); " +
|
|
499
|
+
"'invariants' (invariant file paths re-checking a declared-invariant defect); 'fixtures' (a fixtures file overriding the mission's saved one); 'fixtureIdentity' (#243: 'name=<storageState path>' entries re-binding a fixture step's auth.identity; default: the identities the mission recorded); 'extension' (#256: unpacked extension directories — refused unless the same build the finding was recorded with); 'actionDeltas' (#303, opt-in: the defect step's replayed delta vs the recorded one, as evidence on each attempt); 'maxBrowsers'/'maxBrowserMemory' (#205: machine-wide browser cap, browser memory ceiling in MiB). Operator-only settings (shell hooks, cmd: log sources, re-sending paid/destructive hang writes) come from targets.json, never an argument.",
|
|
500
|
+
inputSchema: {
|
|
501
|
+
type: "object",
|
|
502
|
+
properties: {
|
|
503
|
+
id: { type: "string" },
|
|
504
|
+
fingerprint: { type: "string" },
|
|
505
|
+
replays: { type: "integer", minimum: 1 },
|
|
506
|
+
actionDeltas: { type: "boolean" },
|
|
507
|
+
recordVideo: { type: ["boolean", "string"] },
|
|
508
|
+
screenshots: { type: ["boolean", "string"] },
|
|
509
|
+
headed: { type: "boolean" },
|
|
510
|
+
slowMo: { type: "integer", minimum: 0 },
|
|
511
|
+
storageState: { type: "string" },
|
|
512
|
+
viewport: { type: "object", properties: { width: { type: "integer" }, height: { type: "integer" } }, required: ["width", "height"] },
|
|
513
|
+
device: { type: "string" },
|
|
514
|
+
allowEmulationOverride: { type: "boolean" },
|
|
515
|
+
invariants: { type: "array", items: { type: "string" } },
|
|
516
|
+
fixtures: { type: "string" },
|
|
517
|
+
fixtureIdentity: { type: "array", items: { type: "string" } },
|
|
518
|
+
extension: { type: "array", items: { type: "string" } },
|
|
519
|
+
maxBrowsers: { type: "integer", minimum: 1 },
|
|
520
|
+
maxBrowserMemory: { type: "integer", minimum: 1 },
|
|
521
|
+
},
|
|
522
|
+
required: ["id", "fingerprint"],
|
|
523
|
+
},
|
|
524
|
+
handler: verifyFixTool,
|
|
525
|
+
},
|
|
526
|
+
get_mission_result: {
|
|
527
|
+
description: "Read a mission's TYPED result by id: a result stem (explore-|coverage-|exploratory-|adversarial-|feature-|usability-<stamp>, e.g. adversarial-2026-09-23T00-00-00-000Z) or a queue_exploration missionId. A queued mission reports status queued | running (pending: true — poll again; `jevitate mission run` drains the queue) or failed (an error: it could not run). A finished one: status is clean | defects-found | hang | intermittent | inconclusive | crashed, with the matching CLI exit code. A broken run (inconclusive/crashed) is returned as an error result — never a pass. An adversarial result carries `coverage` (target controls exercised/total, forms submitted, strategies applied vs found nothing, out-of-scope steps): a run below its coverage thresholds is inconclusive, never clean.",
|
|
528
|
+
inputSchema: { type: "object", properties: { id: { type: "string" } }, required: ["id"] },
|
|
529
|
+
handler: getMissionResult,
|
|
530
|
+
},
|
|
135
531
|
find_capabilities: {
|
|
136
532
|
description: "Find promoted Journeys (capabilities) whose metadata matches a query.",
|
|
137
533
|
inputSchema: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
|
138
534
|
handler: async (args) => jsonResult(await findCaps(typeof args.query === "string" ? args.query : "")),
|
|
139
535
|
},
|
|
140
536
|
run_journey: {
|
|
141
|
-
description: "Run a PUBLISHED Journey by id with string params (fail-closed policy)
|
|
537
|
+
description: "Run a PUBLISHED Journey by id with string params (fail-closed policy) — `jevitate journey run`. Never accepts inline steps. " +
|
|
538
|
+
"Optional 'storageState': a PATH (on the machine running this MCP server; inside the project or ~/.jevitate, never a repo's .jevitate/) to a Playwright storageState " +
|
|
539
|
+
"JSON file, for a Journey authored behind a login (#118) — the file's contents are read only by the " +
|
|
540
|
+
"server's own browser, never returned or logged. A Journey that declares metadata.requiresAuth refuses " +
|
|
541
|
+
"with a clear error when no storageState is given. #255 (same as the CLI flags): 'env' (a named environment in .jevitate/environments.json; its session from ~/.jevitate/targets.json) and/or 'baseUrl'; " +
|
|
542
|
+
"'headed' (needs a display) and 'slowMo'; 'recordVideo' (true, or a directory) → videoPaths; 'screenshots' (true | screens | steps | mode:<dir> | <dir>) → screenshotPaths; " +
|
|
543
|
+
"'viewport' {width,height} or 'device' (mutually exclusive); 'fixtures' (a fixtures JSON path: setup before, restore after; 'fixtureIdentity' (#243) 'name=<storageState path>' entries name who a step with auth.identity authenticates as; an environment's shell hooks are never run over MCP — they refuse as the CLI does without --allow-shell-hooks); " +
|
|
544
|
+
"'extension' (#256: unpacked extension directories inside the project or ~/.jevitate; a Journey recorded with extensions needs the same build); 'selfHeal' fail-closed (default) | hybrid | full with 'real' or 'fakeAi' (a write step never self-heals); 'actionDeltas' (#303, opt-in: each replayed step's action delta, compared with the recorded one → actionDeltas); 'maxBrowsers'/'maxBrowserMemory' (#205: machine-wide browser cap, browser memory ceiling in MiB). A site-policy refusal is {error: throttled, retryAfter}.",
|
|
142
545
|
inputSchema: {
|
|
143
546
|
type: "object",
|
|
144
547
|
properties: {
|
|
145
548
|
id: { type: "string" },
|
|
146
549
|
params: { type: "object", additionalProperties: { type: "string" } },
|
|
550
|
+
storageState: { type: "string" },
|
|
551
|
+
env: { type: "string" },
|
|
552
|
+
baseUrl: { type: "string" },
|
|
553
|
+
headed: { type: "boolean" },
|
|
554
|
+
slowMo: { type: "integer", minimum: 0 },
|
|
555
|
+
recordVideo: { type: ["boolean", "string"] },
|
|
556
|
+
screenshots: { type: ["boolean", "string"] },
|
|
557
|
+
viewport: { type: "object", properties: { width: { type: "integer" }, height: { type: "integer" } }, required: ["width", "height"] },
|
|
558
|
+
device: { type: "string" },
|
|
559
|
+
fixtures: { type: "string" },
|
|
560
|
+
fixtureIdentity: { type: "array", items: { type: "string" } },
|
|
561
|
+
selfHeal: { type: "string", enum: ["fail-closed", "hybrid", "full"] },
|
|
562
|
+
real: { type: "boolean" },
|
|
563
|
+
fakeAi: { type: "boolean" },
|
|
564
|
+
extension: { type: "array", items: { type: "string" } },
|
|
565
|
+
maxBrowsers: { type: "integer", minimum: 1 },
|
|
566
|
+
maxBrowserMemory: { type: "integer", minimum: 1 },
|
|
567
|
+
actionDeltas: { type: "boolean" },
|
|
147
568
|
},
|
|
148
569
|
required: ["id"],
|
|
149
570
|
},
|
|
@@ -151,16 +572,55 @@ export function buildMcpTools(deps) {
|
|
|
151
572
|
if (typeof args.id !== "string" || args.id.length === 0) {
|
|
152
573
|
return errorResult({ error: "invalid_args", message: "run_journey requires a non-empty string 'id'" });
|
|
153
574
|
}
|
|
154
|
-
// Invariant #5: only id + params
|
|
155
|
-
// `steps`/`recording` in the arguments is deliberately ignored.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
575
|
+
// Invariant #5: only id + params (+ a storageState PATH and the run options below) are
|
|
576
|
+
// threaded through — any inline `steps`/`recording` in the arguments is deliberately ignored.
|
|
577
|
+
let resolved;
|
|
578
|
+
try {
|
|
579
|
+
resolved = await resolveJourneyRun(args);
|
|
580
|
+
}
|
|
581
|
+
catch (err) {
|
|
582
|
+
const body = argErrorBody(err) ?? (isEnvironmentError(err) ? { error: "invalid_args", code: err.code, message: err.message } : undefined);
|
|
583
|
+
if (body !== undefined)
|
|
584
|
+
return errorResult(body);
|
|
585
|
+
if (err instanceof SetupRequired)
|
|
586
|
+
return errorResult({ error: "setup_required", message: err.message });
|
|
587
|
+
throw err;
|
|
588
|
+
}
|
|
589
|
+
// #205: the same pre-run checks as `jevitate journey run` (orphan sweep, starved-host refusal).
|
|
590
|
+
const starved = await resourcePreflight({});
|
|
591
|
+
if (starved !== null)
|
|
592
|
+
return errorResult({ error: "refused", code: starved.error?.code, message: starved.error?.message });
|
|
593
|
+
try {
|
|
594
|
+
const result = await runJourney(args.id, resolved.params, resolved.storageState, resolved.options);
|
|
595
|
+
// #163: a self-healing run's model usage lands on its result, as on the CLI.
|
|
596
|
+
return jsonResult(resolved.usage === undefined || result === null || typeof result !== "object" ? result : { ...result, usage: resolved.usage.snapshot() });
|
|
597
|
+
}
|
|
598
|
+
catch (err) {
|
|
599
|
+
// A site-policy refusal (throttle, budget, quiet hours) is an answer the agent acts on — when to retry.
|
|
600
|
+
if (err instanceof SiteGateRefusedError) {
|
|
601
|
+
return errorResult({ error: "throttled", reason: err.reason, retryAfter: err.retryAfter, message: err.message });
|
|
602
|
+
}
|
|
603
|
+
if (err instanceof UnknownJourneyError)
|
|
604
|
+
return errorResult({ error: "not_found", id: args.id, message: err.message });
|
|
605
|
+
if (err instanceof JourneyRequiresAuthError)
|
|
606
|
+
return errorResult({ error: "invalid_args", code: "E_JOURNEY_REQUIRES_AUTH", message: err.message });
|
|
607
|
+
if (err instanceof ExtensionMismatchError)
|
|
608
|
+
return errorResult({ error: "invalid_args", code: err.code, message: err.message });
|
|
609
|
+
if (err instanceof ParamValidationError)
|
|
610
|
+
return errorResult({ error: "invalid_args", code: "E_INVALID_PARAMS", message: err.message });
|
|
611
|
+
if (isEnvironmentError(err) || err instanceof FixtureSpecError || err instanceof UnboundSetupRefError) {
|
|
612
|
+
return errorResult({ error: "invalid_args", code: err.code, message: err.message });
|
|
613
|
+
}
|
|
614
|
+
// Never run on unknown state: inconclusive (exit 2) — proves nothing, so an error result.
|
|
615
|
+
if (err instanceof FixtureSetupError) {
|
|
616
|
+
return errorResult({ outcome: "inconclusive", reason: err.message, failure: { kind: "configuration", message: err.message }, attribution: "configuration", exitCode: 2 });
|
|
617
|
+
}
|
|
618
|
+
throw err;
|
|
619
|
+
}
|
|
160
620
|
},
|
|
161
621
|
},
|
|
162
622
|
queue_exploration: {
|
|
163
|
-
description: "Enqueue an exploration mission against a PROMOTED target. Never runs anything — only queues. Refuses unknown/unpromoted targets
|
|
623
|
+
description: "Enqueue an exploration mission against a PROMOTED target. Never runs anything — only queues; `jevitate mission run` drains the queue, and get_mission_result {id: missionId} reports its status/result. strategy: goal-based (goal|feature|route + successAssertion) | coverage | exploratory (novelty-first coverage) | adversarial (optional in-scope route glob) | feature (feature name, optional route glob); a usability review is not queueable — use run_exploration {strategy: usability}. The target's authorized origin plus its declared apiOrigins are the only reachable origins. Refuses unknown/unpromoted targets, over-ceiling budgets and invalid declared `invariants` (an optional closed spec checked around every action; probes GET/HEAD on the target origin only). Optional 'viewport' ({width,height}) or 'device' (a Playwright devices registry name, e.g. \"iPhone 13\") — mutually exclusive (#149); default: Playwright's own default viewport. An unknown device is refused before any browser opens. #255: 'recordVideo' / 'evidenceVideo' (booleans) and 'screenshots' (screens | steps) write media next to the result (listed in it) — a queued request never names a path; 'persona' names a persona in the operator's ~/.jevitate/targets.json for the target's origin (its session, never the caller's). Same as `jevitate mission queue`.",
|
|
164
624
|
inputSchema: {
|
|
165
625
|
type: "object",
|
|
166
626
|
properties: {
|
|
@@ -169,7 +629,7 @@ export function buildMcpTools(deps) {
|
|
|
169
629
|
feature: { type: "string" },
|
|
170
630
|
route: { type: "string" },
|
|
171
631
|
successAssertion: { type: "object" },
|
|
172
|
-
strategy: { type: "string" },
|
|
632
|
+
strategy: { type: "string", enum: [...MISSION_STRATEGIES] },
|
|
173
633
|
budget: {
|
|
174
634
|
type: "object",
|
|
175
635
|
properties: {
|
|
@@ -178,6 +638,22 @@ export function buildMcpTools(deps) {
|
|
|
178
638
|
maxCandidates: { type: "number" },
|
|
179
639
|
},
|
|
180
640
|
},
|
|
641
|
+
// App-declared invariants (#86), inline only: a closed schema (observe: dom|network|probe,
|
|
642
|
+
// invariants: require|never|always). Probes are GET/HEAD on the target's own origin; a spec
|
|
643
|
+
// that does not validate refuses the enqueue.
|
|
644
|
+
invariants: { type: "object" },
|
|
645
|
+
// Per-mission viewport/device emulation (#149) — mutually exclusive.
|
|
646
|
+
viewport: {
|
|
647
|
+
type: "object",
|
|
648
|
+
properties: { width: { type: "number" }, height: { type: "number" } },
|
|
649
|
+
required: ["width", "height"],
|
|
650
|
+
},
|
|
651
|
+
device: { type: "string" },
|
|
652
|
+
// #255: media next to the result (a mode or a boolean — never a path) and a persona NAME.
|
|
653
|
+
recordVideo: { type: "boolean" },
|
|
654
|
+
screenshots: { type: "string", enum: [...QUEUED_SCREENSHOT_MODES] },
|
|
655
|
+
evidenceVideo: { type: "boolean" },
|
|
656
|
+
persona: { type: "string" },
|
|
181
657
|
},
|
|
182
658
|
required: ["target"],
|
|
183
659
|
},
|
|
@@ -293,6 +769,19 @@ export function buildMcpTools(deps) {
|
|
|
293
769
|
handler: inboxHandler((store) => facadeGetSiteHealth(store)),
|
|
294
770
|
},
|
|
295
771
|
};
|
|
772
|
+
// #255: every CLI command without a hand-written tool above — mirrored exactly, run in process.
|
|
773
|
+
for (const spec of CLI_TOOL_SPECS) {
|
|
774
|
+
if (wired[spec.name] !== undefined)
|
|
775
|
+
throw new Error(`MCP tool '${spec.name}' is defined twice`);
|
|
776
|
+
wired[spec.name] = {
|
|
777
|
+
description: spec.description,
|
|
778
|
+
inputSchema: cliToolInputSchema(spec),
|
|
779
|
+
handler: async (args) => {
|
|
780
|
+
const { isError, body } = await runCliTool(spec, args, { runCli: deps.runCli, roots: pathRoots, redact: (text) => redactCredentials(text, credentialStore) });
|
|
781
|
+
return isError ? errorResult(body) : jsonResult(body);
|
|
782
|
+
},
|
|
783
|
+
};
|
|
784
|
+
}
|
|
296
785
|
const notImplemented = (name) => async () => errorResult({
|
|
297
786
|
error: "not_implemented",
|
|
298
787
|
tool: name,
|
|
@@ -319,7 +808,14 @@ export function buildMcpTools(deps) {
|
|
|
319
808
|
export function createMcpServer(deps) {
|
|
320
809
|
const tools = buildMcpTools(deps);
|
|
321
810
|
const byName = new Map(tools.map((t) => [t.name, t]));
|
|
322
|
-
|
|
811
|
+
// The real build identity (#112), never a placeholder: `version` is the published semver, and the
|
|
812
|
+
// description names the commit/build time so an MCP client can tie a session to a build.
|
|
813
|
+
const engine = deps.engine ?? currentEngineInfo();
|
|
814
|
+
const server = new Server({
|
|
815
|
+
name: "jevitate",
|
|
816
|
+
version: engine.version,
|
|
817
|
+
description: `jevitate ${engine.version} (commit ${engine.commit}, built ${engine.builtAt})`,
|
|
818
|
+
}, { capabilities: { tools: {} } });
|
|
323
819
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
324
820
|
tools: tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })),
|
|
325
821
|
}));
|