@jevitate/cli 0.1.0 → 0.2.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.js +40 -30
- package/dist/ai-cli.js.map +1 -1
- package/dist/author-journey-cli.d.ts +4 -0
- package/dist/author-journey-cli.js +109 -0
- package/dist/author-journey-cli.js.map +1 -0
- package/dist/bin.js +58077 -22454
- package/dist/bin.js.map +1 -1
- package/dist/browser-run-options.d.ts +58 -0
- package/dist/browser-run-options.js +106 -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/check-api.d.ts +5 -0
- package/dist/check-api.js +233 -0
- package/dist/check-api.js.map +1 -0
- package/dist/check-budget.d.ts +26 -0
- package/dist/check-budget.js +89 -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 +25 -0
- package/dist/check-execute.js +385 -0
- package/dist/check-execute.js.map +1 -0
- package/dist/check-plan.d.ts +113 -0
- package/dist/check-plan.js +448 -0
- package/dist/check-plan.js.map +1 -0
- package/dist/check-suite.d.ts +171 -0
- package/dist/check-suite.js +515 -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 +102 -0
- package/dist/cli-output.js +516 -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 +330 -0
- package/dist/cli-shared.js +492 -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 +502 -0
- package/dist/defect-evidence.js.map +1 -0
- package/dist/demo-aspect-api.d.ts +209 -0
- package/dist/demo-aspect-api.js +397 -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 +241 -0
- package/dist/demo-aspect-cli.js.map +1 -0
- package/dist/demo-capture.d.ts +108 -0
- package/dist/demo-capture.js +438 -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 +137 -0
- package/dist/explore-adversarial.js +218 -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 +963 -0
- package/dist/explore-cli.js.map +1 -0
- package/dist/explore-coverage.d.ts +158 -0
- package/dist/explore-coverage.js +240 -0
- package/dist/explore-coverage.js.map +1 -0
- package/dist/explore-feature.d.ts +127 -0
- package/dist/explore-feature.js +220 -0
- package/dist/explore-feature.js.map +1 -0
- package/dist/explore-goal.d.ts +253 -0
- package/dist/explore-goal.js +374 -0
- package/dist/explore-goal.js.map +1 -0
- package/dist/explore-shared.d.ts +152 -0
- package/dist/explore-shared.js +129 -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 +52 -0
- package/dist/fixture-auth.js +82 -0
- package/dist/fixture-auth.js.map +1 -0
- package/dist/fixture-cli.d.ts +70 -0
- package/dist/fixture-cli.js +178 -0
- package/dist/fixture-cli.js.map +1 -0
- package/dist/github-issue-filer.d.ts +62 -0
- package/dist/github-issue-filer.js +188 -0
- package/dist/github-issue-filer.js.map +1 -0
- package/dist/host-health-run.d.ts +24 -0
- package/dist/host-health-run.js +27 -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 +127 -0
- package/dist/init-cli.js.map +1 -0
- package/dist/init-keys.d.ts +19 -2
- package/dist/init-keys.js +15 -4
- 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 +256 -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 +179 -0
- package/dist/journey-annotate-cli.js.map +1 -0
- package/dist/journey-api.d.ts +79 -2
- package/dist/journey-api.js +136 -19
- package/dist/journey-api.js.map +1 -1
- package/dist/journey-cli.d.ts +4 -0
- package/dist/journey-cli.js +370 -0
- package/dist/journey-cli.js.map +1 -0
- package/dist/journey-demo-api.d.ts +112 -0
- package/dist/journey-demo-api.js +392 -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 +155 -0
- package/dist/journey-demo-cli.js.map +1 -0
- package/dist/kill-signal.d.ts +193 -0
- package/dist/kill-signal.js +284 -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 +237 -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 +147 -0
- package/dist/log-correlation.js +325 -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 +235 -0
- package/dist/log-sources.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 +84 -0
- package/dist/logs-retention.js.map +1 -0
- package/dist/mcp-api.d.ts +89 -1
- package/dist/mcp-api.js +472 -18
- package/dist/mcp-api.js.map +1 -1
- package/dist/mcp-args.d.ts +38 -0
- package/dist/mcp-args.js +131 -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 +125 -0
- package/dist/mcp-cli-tools.js +680 -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 +58 -2
- package/dist/mission-api.js.map +1 -1
- package/dist/mission-cli.d.ts +4 -0
- package/dist/mission-cli.js +247 -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 +225 -0
- package/dist/mission-fixtures.js +828 -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 +397 -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 +110 -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 +63 -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 +119 -1921
- package/dist/program.js.map +1 -1
- package/dist/project-dir.d.ts +96 -0
- package/dist/project-dir.js +220 -0
- package/dist/project-dir.js.map +1 -0
- package/dist/record-api.d.ts +10 -6
- package/dist/record-api.js +39 -28
- package/dist/record-api.js.map +1 -1
- package/dist/record-cli.d.ts +4 -0
- package/dist/record-cli.js +64 -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 +101 -0
- package/dist/regression-api.js +384 -5
- package/dist/regression-api.js.map +1 -1
- package/dist/regression-cli.d.ts +4 -0
- package/dist/regression-cli.js +232 -0
- package/dist/regression-cli.js.map +1 -0
- package/dist/report-api.d.ts +146 -0
- package/dist/report-api.js +430 -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/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-screenshots.d.ts +123 -0
- package/dist/run-screenshots.js +229 -0
- package/dist/run-screenshots.js.map +1 -0
- package/dist/runtime-env.d.ts +8 -0
- package/dist/runtime-env.js +22 -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 +34 -0
- package/dist/server-log-verify.js +115 -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 +58 -0
- package/dist/site-gate-cli.js.map +1 -0
- package/dist/source-api.js +18 -3
- 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 +289 -0
- package/dist/suite-explore-options.js +140 -0
- package/dist/suite-explore-options.js.map +1 -0
- package/dist/target-config.d.ts +109 -0
- package/dist/target-config.js +216 -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/ui-api.d.ts +3 -0
- package/dist/ui-api.js +7 -1
- package/dist/ui-api.js.map +1 -1
- package/dist/usability-capture.d.ts +43 -0
- package/dist/usability-capture.js +326 -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-cli.d.ts +4 -0
- package/dist/ux-cli.js +102 -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-review.d.ts +112 -0
- package/dist/ux-review.js +151 -0
- package/dist/ux-review.js.map +1 -0
- package/dist/ux-usability.d.ts +238 -0
- package/dist/ux-usability.js +494 -0
- package/dist/ux-usability.js.map +1 -0
- package/dist/verify-fix-api.d.ts +173 -0
- package/dist/verify-fix-api.js +454 -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 +110 -0
- package/dist/verify-fix-cli.js.map +1 -0
- package/package.json +19 -14
- package/skills/jevitate-ci-check/SKILL.md +77 -0
- package/skills/jevitate-demo/SKILL.md +61 -0
- package/skills/jevitate-explore/SKILL.md +88 -87
- package/skills/jevitate-getting-started/SKILL.md +88 -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-ux-review/SKILL.md +106 -16
- package/skills/jevitate-verify-fix/SKILL.md +76 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { type CliDeps } from "./cli-shared.js";
|
|
3
|
+
/** Registers the long-running servers: `jevitate mcp` and `jevitate ui`. */
|
|
4
|
+
export declare function registerServeCommands(program: Command, deps: CliDeps, buildProgram: (deps: CliDeps) => Command): void;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { envCredentialStore, OpenRouterGenerationGateway } from "@jevitate/ai-core";
|
|
2
|
+
import { loadLocalCredentials } from "./credentials-file.js";
|
|
3
|
+
import { fail } from "./envelope.js";
|
|
4
|
+
import { startMcpServer } from "./mcp-api.js";
|
|
5
|
+
import { realOpenRouterCall } from "./openrouter-call.js";
|
|
6
|
+
import { startUiServer } from "./ui-api.js";
|
|
7
|
+
import { intArg } from "./cli-args.js";
|
|
8
|
+
import { renderPrintConfig } from "./init-mcp.js";
|
|
9
|
+
import { mcpToolDeps } from "./mcp-cli-bridge.js";
|
|
10
|
+
import { makeInProcessCliRunner } from "./mcp-cli-runner.js";
|
|
11
|
+
import { resolveJourneysDir, resolveInboxDir, emitJson, DEFAULT_EXPLORE_CATALOG, DEFAULT_EXPLORE_CONSTRAINTS, } from "./cli-shared.js";
|
|
12
|
+
/** Registers the long-running servers: `jevitate mcp` and `jevitate ui`. */
|
|
13
|
+
export function registerServeCommands(program, deps, buildProgram) {
|
|
14
|
+
// Additive: `jevitate mcp` (Ticket #20) — start an MCP stdio server that
|
|
15
|
+
// exposes ONLY `@jevitate/mcp-facade`'s allowlisted tools (never the raw
|
|
16
|
+
// browser primitives in FORBIDDEN_TOOLS). This is the subcommand form of the
|
|
17
|
+
// MCP server (single-bundle deployment — no separate published package).
|
|
18
|
+
// The server owns stdin/stdout as the MCP protocol channel, so on success it
|
|
19
|
+
// blocks and writes NOTHING to stdout; only a setup failure (before the
|
|
20
|
+
// transport connects) emits a JSON envelope.
|
|
21
|
+
program
|
|
22
|
+
.command("mcp")
|
|
23
|
+
.description("start an MCP stdio server exposing only the allowlisted Jevitate tools")
|
|
24
|
+
.option("--dir <path>", "journeys directory (default: the repo's .jevitate/journeys; outside a repo ~/.jevitate/journeys)")
|
|
25
|
+
.option("--print-config <harness>", "print the config snippet to register `jevitate mcp` in a harness (claude | cursor | codex | json) and exit — prints only, writes nothing")
|
|
26
|
+
.action(async function () {
|
|
27
|
+
const { dir, printConfig } = this.opts();
|
|
28
|
+
// `--print-config <harness>` is the universal escape hatch: render the
|
|
29
|
+
// exact registration snippet and exit WITHOUT starting the server (safe:
|
|
30
|
+
// no writes, no stdio takeover). An unknown harness is a fail envelope.
|
|
31
|
+
if (printConfig !== undefined) {
|
|
32
|
+
const harness = printConfig;
|
|
33
|
+
if (harness !== "claude" && harness !== "cursor" && harness !== "codex" && harness !== "json") {
|
|
34
|
+
emitJson(program, fail("E_MCP_PRINT_CONFIG", `--print-config must be one of claude | cursor | codex | json (got '${printConfig}')`));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
program.configureOutput().writeOut?.(`${renderPrintConfig(harness)}\n`);
|
|
38
|
+
process.exitCode = 0;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
// Credential store + generation gateway for the allowlisted
|
|
43
|
+
// `ai_generate_text` tool. The gateway is the REAL OpenRouter adapter:
|
|
44
|
+
// the key is read only inside it (Authorization header only), every
|
|
45
|
+
// outbound payload passes the never-to-model guard, and the facade's
|
|
46
|
+
// preflight returns a typed `setup_required` when the key is absent —
|
|
47
|
+
// so no `--real/--fake` flag is needed for the non-interactive server.
|
|
48
|
+
const aiStore = envCredentialStore(deps.ai?.env ?? process.env, deps.ai?.localConfig ?? loadLocalCredentials());
|
|
49
|
+
const generationGateway = deps.ai?.gateway ??
|
|
50
|
+
new OpenRouterGenerationGateway({
|
|
51
|
+
store: aiStore,
|
|
52
|
+
catalog: deps.ai?.catalog ?? DEFAULT_EXPLORE_CATALOG,
|
|
53
|
+
constraints: deps.ai?.constraints ?? DEFAULT_EXPLORE_CONSTRAINTS,
|
|
54
|
+
call: await realOpenRouterCall(),
|
|
55
|
+
});
|
|
56
|
+
// #254: the same default stores the CLI's `inbox`/`mission queue`/`mission result` read (mcpToolDeps).
|
|
57
|
+
// #255: the CLI-mirroring tools run the SAME program in process, over the same journeys dir.
|
|
58
|
+
const journeysDir = resolveJourneysDir(deps, dir);
|
|
59
|
+
const runCli = makeInProcessCliRunner(() => buildProgram({ ...deps, journeysDir }));
|
|
60
|
+
await startMcpServer(mcpToolDeps(deps, { journeysDir, credentialStore: aiStore, generationGateway, runCli }));
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
emitJson(program, fail("E_MCP_SERVE", String(err instanceof Error ? err.message : err)));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
// Additive: `jevitate ui` (Task 8) — starts the local, loopback-only HTTP
|
|
67
|
+
// HITL approval dashboard (ui-api.ts's `startUiServer`). Resolves the SAME
|
|
68
|
+
// inbox dir `jevitate mcp`'s inbox tools serve (resolveInboxDir), so the
|
|
69
|
+
// two commands agree on where approvals/handbacks/reviews live. On success
|
|
70
|
+
// it prints the bound URL (carrying the capability token) and stays alive —
|
|
71
|
+
// the open HTTP server keeps the process running, the same way `mcp`'s open
|
|
72
|
+
// stdio transport does.
|
|
73
|
+
program
|
|
74
|
+
.command("ui")
|
|
75
|
+
.description("start the local HITL approval dashboard (loopback-only HTTP server)")
|
|
76
|
+
.option("--port <n>", "explicit port (fails on conflict; default 4180, retries on conflict)", intArg({ min: 0, max: 65535 }))
|
|
77
|
+
.option("--no-open", "do not open the dashboard URL in the default browser")
|
|
78
|
+
.option("--inbox-dir <path>", "inbox store directory (default: ~/.jevitate/inbox — same dir `jevitate mcp` serves)")
|
|
79
|
+
.action(async function () {
|
|
80
|
+
const o = this.opts();
|
|
81
|
+
try {
|
|
82
|
+
const start = deps.ui?.startUiServer ?? startUiServer;
|
|
83
|
+
const handle = await start({
|
|
84
|
+
inboxDir: resolveInboxDir(deps, o.inboxDir),
|
|
85
|
+
open: o.open ?? true,
|
|
86
|
+
...(o.port !== undefined ? { port: Number(o.port) } : {}),
|
|
87
|
+
});
|
|
88
|
+
program.configureOutput().writeOut?.(`${handle.url}\n`);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
emitJson(program, fail("E_UI_SERVE", String(err instanceof Error ? err.message : err)));
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=serve-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve-cli.js","sourceRoot":"","sources":["../src/serve-cli.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAmB,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAEL,kBAAkB,EAClB,eAAe,EACf,QAAQ,EACR,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAEzB,4EAA4E;AAC5E,MAAM,UAAU,qBAAqB,CAAC,OAAgB,EAAE,IAAa,EAAE,YAAwC;IAC7G,yEAAyE;IACzE,yEAAyE;IACzE,6EAA6E;IAC7E,yEAAyE;IACzE,6EAA6E;IAC7E,wEAAwE;IACxE,6CAA6C;IAC7C,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,wEAAwE,CAAC;SACrF,MAAM,CAAC,cAAc,EAAE,kGAAkG,CAAC;SAC1H,MAAM,CACL,0BAA0B,EAC1B,0IAA0I,CAC3I;SACA,MAAM,CAAC,KAAK;QACX,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,IAAI,EAA0C,CAAC;QAEjF,uEAAuE;QACvE,yEAAyE;QACzE,wEAAwE;QACxE,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,WAAyB,CAAC;YAC1C,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;gBAC9F,QAAQ,CACN,OAAO,EACP,IAAI,CAAC,oBAAoB,EAAE,sEAAsE,WAAW,IAAI,CAAC,CAClH,CAAC;gBACF,OAAO;YACT,CAAC;YACD,OAAO,CAAC,eAAe,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACxE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,4DAA4D;YAC5D,uEAAuE;YACvE,oEAAoE;YACpE,qEAAqE;YACrE,sEAAsE;YACtE,uEAAuE;YACvE,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,WAAW,IAAI,oBAAoB,EAAE,CAAC,CAAC;YAChH,MAAM,iBAAiB,GACrB,IAAI,CAAC,EAAE,EAAE,OAAO;gBAChB,IAAI,2BAA2B,CAAC;oBAC9B,KAAK,EAAE,OAAO;oBACd,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,IAAI,uBAAuB;oBACpD,WAAW,EAAE,IAAI,CAAC,EAAE,EAAE,WAAW,IAAI,2BAA2B;oBAChE,IAAI,EAAE,MAAM,kBAAkB,EAAE;iBACjC,CAAC,CAAC;YACL,uGAAuG;YACvG,6FAA6F;YAC7F,MAAM,WAAW,GAAG,kBAAkB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAClD,MAAM,MAAM,GAAG,sBAAsB,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;YACpF,MAAM,cAAc,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QAChH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC3F,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,2EAA2E;IAC3E,4EAA4E;IAC5E,4EAA4E;IAC5E,wBAAwB;IACxB,OAAO;SACJ,OAAO,CAAC,IAAI,CAAC;SACb,WAAW,CAAC,qEAAqE,CAAC;SAClF,MAAM,CAAC,YAAY,EAAE,sEAAsE,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;SAC5H,MAAM,CAAC,WAAW,EAAE,sDAAsD,CAAC;SAC3E,MAAM,CAAC,oBAAoB,EAAE,qFAAqF,CAAC;SACnH,MAAM,CAAC,KAAK;QACX,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAwD,CAAC;QAC5E,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,EAAE,aAAa,IAAI,aAAa,CAAC;YACtD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC;gBACzB,QAAQ,EAAE,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC;gBAC3C,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI;gBACpB,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC1D,CAAC,CAAC;YACH,OAAO,CAAC,eAAe,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1F,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Recording } from "@jevitate/recording";
|
|
2
|
+
import { type VerifyFixResult, type VerifySession } from "@jevitate/explore";
|
|
3
|
+
/**
|
|
4
|
+
* `verify-fix` for a `server-log` defect (#142): NOT a re-check of `@jevitate/explore`'s
|
|
5
|
+
* `verifyFix` (which looks for a fingerprint among DOM/console/network signals — a server log line
|
|
6
|
+
* is none of those). Instead, this replays the defect's recorded steps in a fresh browser session
|
|
7
|
+
* AND re-opens the SAME log sources for the SAME drain window, and asks whether a line matching
|
|
8
|
+
* the SAME matcher and the SAME normalized message reappears. Kept entirely in the CLI layer (new
|
|
9
|
+
* file) — `packages/explore/src/verify-fix.ts` is untouched.
|
|
10
|
+
*
|
|
11
|
+
* Mirrors `verifyFix`'s own replay discipline (#74): `replays` fresh-context attempts, only ones
|
|
12
|
+
* that actually reached the defect's step count as evidence, and the pure `verifyReplayVerdict`
|
|
13
|
+
* rule (fired on every ran attempt ⇒ still-reproduces, none ⇒ fixed, a mix ⇒ intermittent — never
|
|
14
|
+
* "fixed" off one clean replay).
|
|
15
|
+
*/
|
|
16
|
+
export interface VerifyServerLogParams {
|
|
17
|
+
readonly recording: Recording;
|
|
18
|
+
/** Flat index of the last Recording step to replay (the defect's `repro.recordingStepIndex`). */
|
|
19
|
+
readonly recordingStepIndex: number;
|
|
20
|
+
readonly fingerprint: string;
|
|
21
|
+
/** The defect's persisted log sources (raw `--log-source` specs) and matcher (raw `--log-defect`). */
|
|
22
|
+
readonly sources: readonly string[];
|
|
23
|
+
readonly matcher: string;
|
|
24
|
+
readonly normalizedMessage: string;
|
|
25
|
+
readonly drainMs: number;
|
|
26
|
+
readonly allowLogCmd: boolean;
|
|
27
|
+
/** Opens a FRESH browser session (never the one the defect was found in). */
|
|
28
|
+
readonly openSession: () => Promise<VerifySession>;
|
|
29
|
+
readonly settleCeilingMs?: number;
|
|
30
|
+
readonly targetTimeoutMs?: number;
|
|
31
|
+
/** Fresh-context replays (#74). Default `DEFAULT_VERIFY_REPLAYS` (mirrors `@jevitate/explore`). */
|
|
32
|
+
readonly replays?: number;
|
|
33
|
+
}
|
|
34
|
+
export declare function verifyServerLogDefect(params: VerifyServerLogParams): Promise<VerifyFixResult>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { RecordingInterpreter } from "@jevitate/interpreter";
|
|
2
|
+
import { perceive, verifyReplayVerdict } from "@jevitate/explore";
|
|
3
|
+
import { closeLogSources, openLogSources, parseLogSourceSpecs } from "./log-sources.js";
|
|
4
|
+
import { matchesLogDefect, normalizeLogMessage, parseLogDefectSpec, parseLogLine } from "./log-lines.js";
|
|
5
|
+
const DEFAULT_REPLAYS = 3;
|
|
6
|
+
function firstLine(e) {
|
|
7
|
+
return e instanceof Error ? e.message.split("\n")[0] ?? e.message : String(e);
|
|
8
|
+
}
|
|
9
|
+
function sleep(ms) {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
const t = setTimeout(resolve, ms);
|
|
12
|
+
t.unref?.();
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async function runOneServerLogReplay(params, sourceSpecs) {
|
|
16
|
+
const lines = [];
|
|
17
|
+
const handles = openLogSources(sourceSpecs, {
|
|
18
|
+
onLine: (raw, arrivalEpochMs) => {
|
|
19
|
+
if (lines.length >= 5_000)
|
|
20
|
+
return;
|
|
21
|
+
lines.push(parseLogLine(raw, arrivalEpochMs, ""));
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
let session;
|
|
25
|
+
try {
|
|
26
|
+
session = await params.openSession();
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
await closeLogSources(handles);
|
|
30
|
+
return {
|
|
31
|
+
ran: false,
|
|
32
|
+
fired: false,
|
|
33
|
+
detail: `could not open a fresh session: ${firstLine(e)}`,
|
|
34
|
+
replay: { outcome: "failed", at: -1, error: firstLine(e) },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const interpreter = new RecordingInterpreter(params.targetTimeoutMs === undefined ? {} : { targetTimeoutMs: params.targetTimeoutMs });
|
|
39
|
+
const outcome = await interpreter.runToCheckpoint(session.actor, params.recording, params.recordingStepIndex);
|
|
40
|
+
await perceive(session.page, {
|
|
41
|
+
...(params.settleCeilingMs === undefined ? {} : { renderWaitMs: params.settleCeilingMs }),
|
|
42
|
+
}).catch(() => undefined);
|
|
43
|
+
// Hold the log sources open for the SAME drain window the original run used, so async backend
|
|
44
|
+
// work that settles late is caught here too — never blocking anything but this one attempt.
|
|
45
|
+
await sleep(params.drainMs);
|
|
46
|
+
await closeLogSources(handles);
|
|
47
|
+
const matcher = parseLogDefectSpec(params.matcher);
|
|
48
|
+
const fired = lines.some((l) => matchesLogDefect(l, matcher) && normalizeLogMessage(l.message) === params.normalizedMessage);
|
|
49
|
+
if (outcome.outcome !== "completed") {
|
|
50
|
+
const replay = outcome.outcome === "failed"
|
|
51
|
+
? { outcome: "failed", at: outcome.at, error: outcome.error.split("\n")[0] ?? outcome.error, ...(outcome.reason === undefined ? {} : { reason: outcome.reason }) }
|
|
52
|
+
: { outcome: "failed", at: outcome.at, error: "replay paused for a human hand-back" };
|
|
53
|
+
const mismatch = outcome.outcome === "failed" && outcome.reason !== undefined;
|
|
54
|
+
return {
|
|
55
|
+
ran: false,
|
|
56
|
+
fired,
|
|
57
|
+
replay,
|
|
58
|
+
detail: mismatch
|
|
59
|
+
? `replay stopped at step ${outcome.at}: ${outcome.reason} — the recorded path was not reproduced, so this proves nothing`
|
|
60
|
+
: `replay could not reach step ${params.recordingStepIndex} (failed at ${outcome.at}); the log source was not re-checked against the intended step`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
ran: true,
|
|
65
|
+
fired,
|
|
66
|
+
replay: { outcome: "completed" },
|
|
67
|
+
detail: fired
|
|
68
|
+
? "a matching server log line reappeared during the drain window"
|
|
69
|
+
: "replay reached the defect's step and no matching server log line appeared within the drain window",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
await closeLogSources(handles).catch(() => undefined);
|
|
74
|
+
return { ran: false, fired: false, detail: `replay failed: ${firstLine(e)}`, replay: { outcome: "failed", at: -1, error: firstLine(e) } };
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
await session.close().catch(() => undefined);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export async function verifyServerLogDefect(params) {
|
|
81
|
+
const base = { fingerprint: params.fingerprint };
|
|
82
|
+
let sourceSpecs;
|
|
83
|
+
try {
|
|
84
|
+
sourceSpecs = parseLogSourceSpecs(params.sources, params.allowLogCmd);
|
|
85
|
+
parseLogDefectSpec(params.matcher); // validated once up front, fail closed
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
return {
|
|
89
|
+
...base,
|
|
90
|
+
verdict: "inconclusive",
|
|
91
|
+
observedFingerprints: [],
|
|
92
|
+
replay: { outcome: "failed", at: -1, error: "not replayed" },
|
|
93
|
+
reason: `the defect's log source(s)/matcher no longer parse: ${firstLine(e)}`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const replays = params.replays ?? DEFAULT_REPLAYS;
|
|
97
|
+
if (!Number.isInteger(replays) || replays < 1)
|
|
98
|
+
throw new Error(`verifyServerLogDefect: replays must be >= 1, got ${replays}`);
|
|
99
|
+
const runs = [];
|
|
100
|
+
for (let i = 0; i < replays; i++)
|
|
101
|
+
runs.push(await runOneServerLogReplay(params, sourceSpecs));
|
|
102
|
+
const last = runs[runs.length - 1];
|
|
103
|
+
const verdict = verifyReplayVerdict(runs);
|
|
104
|
+
const ran = runs.filter((r) => r.ran).length;
|
|
105
|
+
const fired = runs.filter((r) => r.ran && r.fired).length;
|
|
106
|
+
const reason = verdict === "inconclusive"
|
|
107
|
+
? last.detail
|
|
108
|
+
: verdict === "still-reproduces"
|
|
109
|
+
? `the matching server log line reappeared on all ${fired}/${ran} replay(s) that ran`
|
|
110
|
+
: verdict === "fixed"
|
|
111
|
+
? `no matching server log line appeared on any of the ${ran}/${runs.length} replay(s) that ran`
|
|
112
|
+
: `the matching server log line appeared on ${fired}/${ran} replay(s) that ran — intermittent on the current code, never reported as fixed`;
|
|
113
|
+
return { ...base, verdict, observedFingerprints: fired > 0 ? [params.fingerprint] : [], replay: last.replay, reason, attempts: runs.map((r) => ({ ran: r.ran, fired: r.fired, detail: r.detail })) };
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=server-log-verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-log-verify.js","sourceRoot":"","sources":["../src/server-log-verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #213: was a run's `--storage-state` (a persona's) session honoured? A dead session (an expired or
|
|
3
|
+
* bogus cookie) does not fail the run by itself: the app shows its sign-in form, and a goal run's
|
|
4
|
+
* model may simply sign in on its own — the run then passes, silently testing as whoever it logged
|
|
5
|
+
* in as, not the persona. Code-decided from the FIRST page the run decided on (its seed landing):
|
|
6
|
+
* a login-like URL (the #82 pattern), or a sign-in form — a visible password field — on it.
|
|
7
|
+
*
|
|
8
|
+
* Returns why the session was not honoured, or undefined (no storage state given, or the first page
|
|
9
|
+
* was not a sign-in page). Never a verdict about the app: a warning on the result and the summary.
|
|
10
|
+
*/
|
|
11
|
+
export declare function sessionLostReason(data: {
|
|
12
|
+
readonly target?: unknown;
|
|
13
|
+
readonly transcript?: unknown;
|
|
14
|
+
}): string | undefined;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
import { isLoginLikeUrl } from "@jevitate/explore";
|
|
3
|
+
/**
|
|
4
|
+
* #213: was a run's `--storage-state` (a persona's) session honoured? A dead session (an expired or
|
|
5
|
+
* bogus cookie) does not fail the run by itself: the app shows its sign-in form, and a goal run's
|
|
6
|
+
* model may simply sign in on its own — the run then passes, silently testing as whoever it logged
|
|
7
|
+
* in as, not the persona. Code-decided from the FIRST page the run decided on (its seed landing):
|
|
8
|
+
* a login-like URL (the #82 pattern), or a sign-in form — a visible password field — on it.
|
|
9
|
+
*
|
|
10
|
+
* Returns why the session was not honoured, or undefined (no storage state given, or the first page
|
|
11
|
+
* was not a sign-in page). Never a verdict about the app: a warning on the result and the summary.
|
|
12
|
+
*/
|
|
13
|
+
export function sessionLostReason(data) {
|
|
14
|
+
const target = isRecord(data.target) ? data.target : undefined;
|
|
15
|
+
const state = typeof target?.storageStatePath === "string" ? target.storageStatePath : undefined;
|
|
16
|
+
if (state === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
const first = Array.isArray(data.transcript) ? data.transcript.find(isRecord) : undefined;
|
|
19
|
+
if (first === undefined)
|
|
20
|
+
return undefined;
|
|
21
|
+
const url = typeof first.url === "string" ? first.url : "";
|
|
22
|
+
const controls = Array.isArray(first.controls) ? first.controls.filter((c) => typeof c === "string") : [];
|
|
23
|
+
const loginUrl = url !== "" && isLoginLikeUrl(url);
|
|
24
|
+
const passwordField = controls.some((c) => PASSWORD_CONTROL.test(c));
|
|
25
|
+
if (!loginUrl && !passwordField)
|
|
26
|
+
return undefined;
|
|
27
|
+
const where = pathOf(url);
|
|
28
|
+
const what = loginUrl ? `the first page was a sign-in page (${where})` : `the first page (${where}) showed a sign-in form (a password field)`;
|
|
29
|
+
return `the session in ${basename(state)} was not honoured — ${what}; the run did not start signed in as that session (re-save the storage state, or check its cookie is still valid)`;
|
|
30
|
+
}
|
|
31
|
+
/** A password field's control identity (`textbox "Password"`, `textbox "Passcode"`, …). */
|
|
32
|
+
const PASSWORD_CONTROL = /^(?:textbox|searchbox)\s+"[^"]*\b(?:password|passcode|passwort|mot de passe)\b[^"]*"$/i;
|
|
33
|
+
function pathOf(url) {
|
|
34
|
+
try {
|
|
35
|
+
return new URL(url).pathname;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return url === "" ? "?" : url;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function isRecord(v) {
|
|
42
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=session-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-check.js","sourceRoot":"","sources":["../src/session-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAkE;IAClG,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/D,MAAM,KAAK,GAAG,OAAO,MAAM,EAAE,gBAAgB,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC;IACjG,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1F,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,GAAG,GAAG,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvH,MAAM,QAAQ,GAAG,GAAG,KAAK,EAAE,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC;IACnD,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,IAAI,CAAC,QAAQ,IAAI,CAAC,aAAa;QAAE,OAAO,SAAS,CAAC;IAClD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,sCAAsC,KAAK,GAAG,CAAC,CAAC,CAAC,mBAAmB,KAAK,4CAA4C,CAAC;IAC9I,OAAO,kBAAkB,QAAQ,CAAC,KAAK,CAAC,uBAAuB,IAAI,mHAAmH,CAAC;AACzL,CAAC;AAED,2FAA2F;AAC3F,MAAM,gBAAgB,GAAG,wFAAwF,CAAC;AAElH,SAAS,MAAM,CAAC,GAAW;IACzB,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAChC,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,CAAU;IAC1B,OAAO,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC"}
|
package/dist/site-cli.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { SitePolicySchema, simulateTiming } from "@jevitate/domain";
|
|
3
|
+
import { ok, fail } from "./envelope.js";
|
|
4
|
+
import { sitePolicyKey } from "./site-gate-cli.js";
|
|
5
|
+
import { resolveDbPath, withSitePolicyRepository, parsePlannedScript, emitJson } from "./cli-shared.js";
|
|
6
|
+
/** Registers `jevitate site`: `policy get|set` and `simulate`. */
|
|
7
|
+
export function registerSiteCommands(program, deps) {
|
|
8
|
+
const site = program
|
|
9
|
+
.command("site")
|
|
10
|
+
.description("per-site policies for Journey runs: human-like pacing, throttles, run budgets and quiet hours");
|
|
11
|
+
const sitePolicy = site
|
|
12
|
+
.command("policy")
|
|
13
|
+
.description("read or set a site's policy (the site is the Journey's origin, e.g. https://app.example.com)");
|
|
14
|
+
sitePolicy
|
|
15
|
+
.command("get <site>")
|
|
16
|
+
.description("print the policy for a site (an origin) and account")
|
|
17
|
+
.option("--account <account>", "account id", "primary")
|
|
18
|
+
.option("--db <path>", "sqlite db path")
|
|
19
|
+
.option("--json", "emit a JSON envelope")
|
|
20
|
+
.action(async function (siteId) {
|
|
21
|
+
const { account, db, json } = this.opts();
|
|
22
|
+
try {
|
|
23
|
+
const dbPath = resolveDbPath(deps, db);
|
|
24
|
+
const policy = await withSitePolicyRepository(dbPath, (repository) => repository.get(sitePolicyKey(siteId), account));
|
|
25
|
+
const envelope = ok(policy);
|
|
26
|
+
if (json) {
|
|
27
|
+
emitJson(program, envelope);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
if (policy) {
|
|
31
|
+
program.configureOutput().writeOut?.(`policy for '${siteId}' (version ${policy.version}): ${JSON.stringify(policy)}\n`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
program.configureOutput().writeOut?.(`no policy configured for '${siteId}' (account '${account}')\n`);
|
|
35
|
+
}
|
|
36
|
+
process.exitCode = 0;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
emitJson(program, fail("E_SITE_POLICY_GET", String(err)));
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
sitePolicy
|
|
44
|
+
.command("set <site>")
|
|
45
|
+
.description("set the policy for a site (an origin): Journey runs there are paced, throttled, budgeted and kept out of quiet hours " +
|
|
46
|
+
"(journey run, source run, check, MCP run_journey); load run applies the pacing only")
|
|
47
|
+
.requiredOption("--file <path>", "path to a policy JSON file")
|
|
48
|
+
.option("--account <account>", "account id", "primary")
|
|
49
|
+
.option("--db <path>", "sqlite db path")
|
|
50
|
+
.option("--json", "emit a JSON envelope")
|
|
51
|
+
.action(async function (siteId) {
|
|
52
|
+
const { account, db, file, json } = this.opts();
|
|
53
|
+
let policy;
|
|
54
|
+
try {
|
|
55
|
+
const raw = await readFile(file, "utf8");
|
|
56
|
+
policy = SitePolicySchema.parse(JSON.parse(raw));
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
emitJson(program, fail("E_INVALID_POLICY", String(err)));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const dbPath = resolveDbPath(deps, db);
|
|
64
|
+
// The policy is stored (and reported) under the site's origin: a page URL names its origin.
|
|
65
|
+
const site = sitePolicyKey(siteId);
|
|
66
|
+
await withSitePolicyRepository(dbPath, (repository) => repository.set(site, account, policy));
|
|
67
|
+
const envelope = ok({ site, account, version: policy.version });
|
|
68
|
+
if (json) {
|
|
69
|
+
emitJson(program, envelope);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
program.configureOutput().writeOut?.(`policy for '${site}' (account '${account}') set to version ${policy.version}\n`);
|
|
73
|
+
process.exitCode = 0;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
emitJson(program, fail("E_SITE_POLICY_SET", String(err)));
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
site
|
|
81
|
+
.command("simulate <site>")
|
|
82
|
+
.description("estimate, offline, how long a planned step script takes under a site's pacing policy")
|
|
83
|
+
.requiredOption("--script <path>", "path to a planned-step script JSON file")
|
|
84
|
+
.option("--seed <n>", "deterministic RNG seed", "0")
|
|
85
|
+
.option("--account <account>", "account id", "primary")
|
|
86
|
+
.option("--db <path>", "sqlite db path")
|
|
87
|
+
.option("--json", "emit a JSON envelope")
|
|
88
|
+
.action(async function (siteId) {
|
|
89
|
+
const { account, db, script, seed, json } = this.opts();
|
|
90
|
+
let plannedScript;
|
|
91
|
+
try {
|
|
92
|
+
const raw = await readFile(script, "utf8");
|
|
93
|
+
plannedScript = parsePlannedScript(raw);
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
emitJson(program, fail("E_INVALID_SCRIPT", String(err)));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const seedNum = Number(seed);
|
|
100
|
+
if (!Number.isFinite(seedNum)) {
|
|
101
|
+
emitJson(program, fail("E_INVALID_SEED", `--seed must be a finite number, got ${JSON.stringify(seed)}`));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const dbPath = resolveDbPath(deps, db);
|
|
106
|
+
const policy = await withSitePolicyRepository(dbPath, (repository) => repository.get(sitePolicyKey(siteId), account));
|
|
107
|
+
const interaction = policy?.interaction ?? {};
|
|
108
|
+
const profile = simulateTiming(interaction, seedNum, plannedScript);
|
|
109
|
+
const envelope = ok(profile);
|
|
110
|
+
if (json) {
|
|
111
|
+
emitJson(program, envelope);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const out = program.configureOutput().writeOut;
|
|
115
|
+
for (const step of profile.steps) {
|
|
116
|
+
out?.(`${step.kind} '${step.label}': ${step.delayMs}ms\n`);
|
|
117
|
+
}
|
|
118
|
+
out?.(`totalMs: ${profile.totalMs}\n`);
|
|
119
|
+
process.exitCode = 0;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
emitJson(program, fail("E_SITE_SIMULATE", String(err)));
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=site-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site-cli.js","sourceRoot":"","sources":["../src/site-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAqC,MAAM,kBAAkB,CAAC;AACvG,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAgB,aAAa,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEtH,kEAAkE;AAClE,MAAM,UAAU,oBAAoB,CAAC,OAAgB,EAAE,IAAa;IAClE,MAAM,IAAI,GAAG,OAAO;SACjB,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,+FAA+F,CAAC,CAAC;IAChH,MAAM,UAAU,GAAG,IAAI;SACpB,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,8FAA8F,CAAC,CAAC;IAE/G,UAAU;SACP,OAAO,CAAC,YAAY,CAAC;SACrB,WAAW,CAAC,qDAAqD,CAAC;SAClE,MAAM,CAAC,qBAAqB,EAAE,YAAY,EAAE,SAAS,CAAC;SACtD,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC;SACvC,MAAM,CAAC,QAAQ,EAAE,sBAAsB,CAAC;SACxC,MAAM,CAAC,KAAK,WAA0B,MAAc;QACnD,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,EAAoD,CAAC;QAC5F,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACvC,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;YACtH,MAAM,QAAQ,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC;YAC5B,IAAI,IAAI,EAAE,CAAC;gBACT,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACN,IAAI,MAAM,EAAE,CAAC;oBACX,OAAO,CAAC,eAAe,EAAE,CAAC,QAAQ,EAAE,CAClC,eAAe,MAAM,cAAc,MAAM,CAAC,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAClF,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,eAAe,EAAE,CAAC,QAAQ,EAAE,CAClC,6BAA6B,MAAM,eAAe,OAAO,MAAM,CAChE,CAAC;gBACJ,CAAC;gBACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,UAAU;SACP,OAAO,CAAC,YAAY,CAAC;SACrB,WAAW,CACV,uHAAuH;QACrH,qFAAqF,CACxF;SACA,cAAc,CAAC,eAAe,EAAE,4BAA4B,CAAC;SAC7D,MAAM,CAAC,qBAAqB,EAAE,YAAY,EAAE,SAAS,CAAC;SACtD,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC;SACvC,MAAM,CAAC,QAAQ,EAAE,sBAAsB,CAAC;SACxC,MAAM,CAAC,KAAK,WAA0B,MAAc;QACnD,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,EAKzC,CAAC;QACL,IAAI,MAAkB,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACzC,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACzD,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACvC,4FAA4F;YAC5F,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;YACnC,MAAM,wBAAwB,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;YAC9F,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;YAChE,IAAI,IAAI,EAAE,CAAC;gBACT,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,eAAe,EAAE,CAAC,QAAQ,EAAE,CAClC,eAAe,IAAI,eAAe,OAAO,qBAAqB,MAAM,CAAC,OAAO,IAAI,CACjF,CAAC;gBACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,IAAI;SACD,OAAO,CAAC,iBAAiB,CAAC;SAC1B,WAAW,CAAC,sFAAsF,CAAC;SACnG,cAAc,CAAC,iBAAiB,EAAE,yCAAyC,CAAC;SAC5E,MAAM,CAAC,YAAY,EAAE,wBAAwB,EAAE,GAAG,CAAC;SACnD,MAAM,CAAC,qBAAqB,EAAE,YAAY,EAAE,SAAS,CAAC;SACtD,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC;SACvC,MAAM,CAAC,QAAQ,EAAE,sBAAsB,CAAC;SACxC,MAAM,CAAC,KAAK,WAA0B,MAAc;QACnD,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,EAMjD,CAAC;QACL,IAAI,aAA4B,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAC3C,aAAa,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACzD,OAAO;QACT,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,uCAAuC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YACzG,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACvC,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;YACtH,MAAM,WAAW,GAAG,MAAM,EAAE,WAAW,IAAI,EAAE,CAAC;YAC9C,MAAM,OAAO,GAAG,cAAc,CAAC,WAAW,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;YACpE,MAAM,QAAQ,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC;YAC7B,IAAI,IAAI,EAAE,CAAC;gBACT,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,QAAQ,CAAC;gBAC/C,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;oBACjC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,OAAO,MAAM,CAAC,CAAC;gBAC7D,CAAC;gBACD,GAAG,EAAE,CAAC,YAAY,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC;gBACvC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Recording } from "@jevitate/recording";
|
|
2
|
+
import type { Ability } from "@jevitate/screenplay";
|
|
3
|
+
import { type SiteGateDeps } from "@jevitate/runtime";
|
|
4
|
+
/**
|
|
5
|
+
* The site-policy gate's repositories (`jevitate site policy set`), opened from the policy database
|
|
6
|
+
* for the duration of `fn`. No database file means no policy was ever set: `fn` gets `undefined`,
|
|
7
|
+
* and nothing is created on disk.
|
|
8
|
+
*/
|
|
9
|
+
export declare function withSiteGate<T>(dbPath: string | undefined, fn: (deps: SiteGateDeps | undefined) => Promise<T>): Promise<T>;
|
|
10
|
+
/** A site policy's key: the origin of a URL (`https://app.example.com/login` → `https://app.example.com`); any other id as given. */
|
|
11
|
+
export declare function sitePolicyKey(site: string): string;
|
|
12
|
+
/** What a gated Journey run gives its actor, and records once it has run. */
|
|
13
|
+
export interface JourneyGate {
|
|
14
|
+
/** The pacing ability (none when no policy declares pacing), spread into `whoCan(...)`. */
|
|
15
|
+
readonly abilities: Ability[];
|
|
16
|
+
/** Records the run against the site's throttle class; call once the run executed, pass or fail. */
|
|
17
|
+
readonly done: () => Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The site-policy gate for one Journey run on `recording.site`'s origin — before any fixture or
|
|
21
|
+
* browser. Throws `SiteGateRefusedError` (quiet hours, min interval, budget) with when to retry.
|
|
22
|
+
* `enforceLimits: false` (a load run) applies only the pacing.
|
|
23
|
+
*/
|
|
24
|
+
export declare function gateJourney(siteGate: SiteGateDeps | undefined, recording: Recording, opts: {
|
|
25
|
+
readonly account?: string;
|
|
26
|
+
readonly enforceLimits: boolean;
|
|
27
|
+
readonly runId?: string;
|
|
28
|
+
}): Promise<JourneyGate>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { openDatabase, migrateToLatest, SqliteActivityRepository, SqliteBudgetRepository, SqliteSitePolicyRepository } from "@jevitate/storage-sqlite";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { SiteGateRefusedError, enterSiteGate, throttleClassOf } from "@jevitate/runtime";
|
|
5
|
+
/**
|
|
6
|
+
* The site-policy gate's repositories (`jevitate site policy set`), opened from the policy database
|
|
7
|
+
* for the duration of `fn`. No database file means no policy was ever set: `fn` gets `undefined`,
|
|
8
|
+
* and nothing is created on disk.
|
|
9
|
+
*/
|
|
10
|
+
export async function withSiteGate(dbPath, fn) {
|
|
11
|
+
if (dbPath === undefined || !existsSync(dbPath))
|
|
12
|
+
return fn(undefined);
|
|
13
|
+
const db = openDatabase(dbPath);
|
|
14
|
+
try {
|
|
15
|
+
await migrateToLatest(db);
|
|
16
|
+
const clock = { nowIso: () => new Date().toISOString(), monotonicMs: () => Date.now() };
|
|
17
|
+
return await fn({
|
|
18
|
+
policies: new SqliteSitePolicyRepository(db, clock),
|
|
19
|
+
budgets: new SqliteBudgetRepository(db),
|
|
20
|
+
activity: new SqliteActivityRepository(db),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
await db.destroy();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** A site policy's key: the origin of a URL (`https://app.example.com/login` → `https://app.example.com`); any other id as given. */
|
|
28
|
+
export function sitePolicyKey(site) {
|
|
29
|
+
try {
|
|
30
|
+
const u = new URL(site);
|
|
31
|
+
return u.origin === "null" ? site : u.origin;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return site;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const UNGATED = { abilities: [], done: async () => undefined };
|
|
38
|
+
/**
|
|
39
|
+
* The site-policy gate for one Journey run on `recording.site`'s origin — before any fixture or
|
|
40
|
+
* browser. Throws `SiteGateRefusedError` (quiet hours, min interval, budget) with when to retry.
|
|
41
|
+
* `enforceLimits: false` (a load run) applies only the pacing.
|
|
42
|
+
*/
|
|
43
|
+
export async function gateJourney(siteGate, recording, opts) {
|
|
44
|
+
if (siteGate === undefined)
|
|
45
|
+
return UNGATED;
|
|
46
|
+
const site = sitePolicyKey(recording.site);
|
|
47
|
+
const gate = await enterSiteGate(siteGate, {
|
|
48
|
+
site,
|
|
49
|
+
account: opts.account ?? "primary",
|
|
50
|
+
throttleClass: throttleClassOf(recording),
|
|
51
|
+
runId: opts.runId ?? randomUUID(),
|
|
52
|
+
enforceLimits: opts.enforceLimits,
|
|
53
|
+
});
|
|
54
|
+
if (!gate.ok)
|
|
55
|
+
throw new SiteGateRefusedError(site, gate.reason, gate.retryAfter);
|
|
56
|
+
return { abilities: gate.pace === null ? [] : [gate.pace], done: gate.done };
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=site-gate-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site-gate-cli.js","sourceRoot":"","sources":["../src/site-gate-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AACvJ,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,eAAe,EAAqB,MAAM,mBAAmB,CAAC;AAE5G;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAI,MAA0B,EAAE,EAAkD;IAClH,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC;IACtE,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC;QACH,MAAM,eAAe,CAAC,EAAE,CAAC,CAAC;QAC1B,MAAM,KAAK,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QACxF,OAAO,MAAM,EAAE,CAAC;YACd,QAAQ,EAAE,IAAI,0BAA0B,CAAC,EAAE,EAAE,KAAK,CAAC;YACnD,OAAO,EAAE,IAAI,sBAAsB,CAAC,EAAE,CAAC;YACvC,QAAQ,EAAE,IAAI,wBAAwB,CAAC,EAAE,CAAC;SAC3C,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC;IACrB,CAAC;AACH,CAAC;AAED,qIAAqI;AACrI,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;QACxB,OAAO,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAUD,MAAM,OAAO,GAAgB,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS,EAAE,CAAC;AAE5E;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAkC,EAClC,SAAoB,EACpB,IAA6F;IAE7F,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC;IAC3C,MAAM,IAAI,GAAG,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE;QACzC,IAAI;QACJ,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,SAAS;QAClC,aAAa,EAAE,eAAe,CAAC,SAAS,CAAC;QACzC,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,UAAU,EAAE;QACjC,aAAa,EAAE,IAAI,CAAC,aAAa;KAClC,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,IAAI,oBAAoB,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACjF,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;AAC/E,CAAC"}
|
package/dist/source-api.js
CHANGED
|
@@ -30,6 +30,15 @@ export class NoDeclaredOriginsError extends Error {
|
|
|
30
30
|
this.name = "NoDeclaredOriginsError";
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
/** Parses `site`'s origin, or `null` if it isn't a valid absolute URL. */
|
|
34
|
+
function originOf(site) {
|
|
35
|
+
try {
|
|
36
|
+
return new URL(site).origin;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
33
42
|
function mgrOf(deps) {
|
|
34
43
|
return new GitSourceManager(deps.sourcesDir, deps.git);
|
|
35
44
|
}
|
|
@@ -171,9 +180,15 @@ export async function publishJourneyToSource(deps, req) {
|
|
|
171
180
|
if (!journey.metadata.promoted) {
|
|
172
181
|
throw new NotPromotedError(`journey '${req.id}' is not promoted; promote it before publishing`);
|
|
173
182
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
183
|
+
// #125: `explore-author-journey` writes RELATIVE `navigate` urls (the recorded steps are
|
|
184
|
+
// relative to `recording.site`), so `collectNavigateOrigins` — which only resolves ABSOLUTE
|
|
185
|
+
// navigate urls — finds nothing to derive from. Fall back to `recording.site` itself: it's
|
|
186
|
+
// the origin the whole Recording was authored against, and exactly what an explore-authored
|
|
187
|
+
// Journey's relative navigates implicitly stay within.
|
|
188
|
+
const navigateOrigins = collectNavigateOrigins(journey.recording);
|
|
189
|
+
const siteOrigin = originOf(journey.recording.site);
|
|
190
|
+
const derivedOrigins = navigateOrigins.length > 0 ? navigateOrigins : siteOrigin ? [siteOrigin] : [];
|
|
191
|
+
const declaredOrigins = req.declareOrigins && req.declareOrigins.length > 0 ? req.declareOrigins : derivedOrigins;
|
|
177
192
|
if (declaredOrigins.length === 0) {
|
|
178
193
|
throw new NoDeclaredOriginsError(`journey '${req.id}' declares no origins and navigates to no absolute origin; pass --declare-origin <origin>`);
|
|
179
194
|
}
|