@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,108 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { DEFAULT_JEVITATE_REPO, fileDraft, } from "@jevitate/domain";
|
|
4
|
+
import { resolveDataDir } from "./data-dir.js";
|
|
5
|
+
export class FilingConfigError extends Error {
|
|
6
|
+
code = "E_FILING_CONFIG";
|
|
7
|
+
}
|
|
8
|
+
const REPO = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
9
|
+
/** `owner/name`, or a config error — a malformed repo is never silently used. */
|
|
10
|
+
export function assertRepo(repo, where) {
|
|
11
|
+
if (!REPO.test(repo))
|
|
12
|
+
throw new FilingConfigError(`${where}: ${JSON.stringify(repo)} is not an owner/name repo`);
|
|
13
|
+
return repo;
|
|
14
|
+
}
|
|
15
|
+
/** Reads `~/.jevitate/filing.json`; a missing file is "no filing config". Anything malformed fails closed. */
|
|
16
|
+
export function loadFilingFileConfig(path = resolveDataDir(["filing.json"])) {
|
|
17
|
+
let raw;
|
|
18
|
+
try {
|
|
19
|
+
raw = readFileSync(path, "utf8");
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
if (typeof e === "object" && e !== null && "code" in e && e.code === "ENOENT")
|
|
23
|
+
return {};
|
|
24
|
+
throw new FilingConfigError(`cannot read ${path}: ${e instanceof Error ? e.message : String(e)}`);
|
|
25
|
+
}
|
|
26
|
+
let parsed;
|
|
27
|
+
try {
|
|
28
|
+
parsed = JSON.parse(raw);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
throw new FilingConfigError(`${path} is not valid JSON`);
|
|
32
|
+
}
|
|
33
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
34
|
+
throw new FilingConfigError(`${path} must be a JSON object`);
|
|
35
|
+
}
|
|
36
|
+
const o = parsed;
|
|
37
|
+
const out = {};
|
|
38
|
+
if (o.enabled !== undefined) {
|
|
39
|
+
if (typeof o.enabled !== "boolean")
|
|
40
|
+
throw new FilingConfigError(`${path}: enabled must be a boolean`);
|
|
41
|
+
out.enabled = o.enabled;
|
|
42
|
+
}
|
|
43
|
+
if (o.jevitateRepo !== undefined) {
|
|
44
|
+
if (typeof o.jevitateRepo !== "string")
|
|
45
|
+
throw new FilingConfigError(`${path}: jevitateRepo must be a string`);
|
|
46
|
+
out.jevitateRepo = assertRepo(o.jevitateRepo, `${path} jevitateRepo`);
|
|
47
|
+
}
|
|
48
|
+
if (o.targets !== undefined) {
|
|
49
|
+
if (o.targets === null || typeof o.targets !== "object" || Array.isArray(o.targets)) {
|
|
50
|
+
throw new FilingConfigError(`${path}: targets must be an object keyed by origin`);
|
|
51
|
+
}
|
|
52
|
+
const targets = {};
|
|
53
|
+
for (const [origin, v] of Object.entries(o.targets)) {
|
|
54
|
+
const repo = v !== null && typeof v === "object" && "repo" in v ? v.repo : undefined;
|
|
55
|
+
if (typeof repo !== "string")
|
|
56
|
+
throw new FilingConfigError(`${path}: targets[${origin}].repo must be a string`);
|
|
57
|
+
targets[origin] = { repo: assertRepo(repo, `${path} targets[${origin}]`) };
|
|
58
|
+
}
|
|
59
|
+
out.targets = targets;
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
/** Flags win over the file; the target repo is looked up by the run's origin. */
|
|
64
|
+
export function resolveFilingConfig(file, flags, targetOrigin) {
|
|
65
|
+
const targetRepo = flags.issueRepo ?? file.targets?.[targetOrigin]?.repo;
|
|
66
|
+
return {
|
|
67
|
+
enabled: flags.fileIssues ?? file.enabled ?? false,
|
|
68
|
+
jevitateRepo: assertRepo(flags.jevitateRepo ?? file.jevitateRepo ?? DEFAULT_JEVITATE_REPO, "jevitate repo"),
|
|
69
|
+
...(targetRepo === undefined ? {} : { targetRepo: assertRepo(targetRepo, "issue repo") }),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** `<dir>/<stem>.json` → `<dir>/<stem>.issues/`. */
|
|
73
|
+
export function issuesDirFor(recordingPath) {
|
|
74
|
+
return recordingPath.endsWith(".json") ? `${recordingPath.slice(0, -".json".length)}.issues` : `${recordingPath}.issues`;
|
|
75
|
+
}
|
|
76
|
+
/** Writes each draft as `<fingerprint>.md` (title heading + body) next to the Recording. */
|
|
77
|
+
export function writeIssueDrafts(recordingPath, drafts) {
|
|
78
|
+
if (drafts.length === 0)
|
|
79
|
+
return [];
|
|
80
|
+
const dir = issuesDirFor(recordingPath);
|
|
81
|
+
mkdirSync(dir, { recursive: true });
|
|
82
|
+
return drafts.map((d) => {
|
|
83
|
+
const path = join(dir, `${d.fingerprint}.md`);
|
|
84
|
+
writeFileSync(path, `# ${d.title}\n\n${d.body}\n`, "utf8");
|
|
85
|
+
return { fingerprint: d.fingerprint, title: d.title, attribution: d.attribution, targets: d.targets, labels: d.labels, path };
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Writes every draft, then files them per the config. The filer is created ONLY when filing is
|
|
90
|
+
* enabled (so a disabled run never even looks for `gh` or a token).
|
|
91
|
+
*/
|
|
92
|
+
export async function processIssueDrafts(recordingPath, drafts, config, makeFiler, nowIso) {
|
|
93
|
+
const written = writeIssueDrafts(recordingPath, drafts);
|
|
94
|
+
const filing = [];
|
|
95
|
+
const filer = config.enabled && drafts.length > 0 ? makeFiler() : null;
|
|
96
|
+
for (const d of drafts) {
|
|
97
|
+
const outcomes = filer === null
|
|
98
|
+
? d.targets.map((target) => ({
|
|
99
|
+
target,
|
|
100
|
+
status: "draft-only",
|
|
101
|
+
reason: config.enabled ? "no filer" : "filing is disabled",
|
|
102
|
+
}))
|
|
103
|
+
: await fileDraft(filer, d, config, nowIso);
|
|
104
|
+
filing.push({ fingerprint: d.fingerprint, outcomes });
|
|
105
|
+
}
|
|
106
|
+
return { drafts: written, filing };
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=findings-filing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findings-filing.js","sourceRoot":"","sources":["../src/findings-filing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-derived request auth for code-issued HTTP calls (mission fixtures, #140/#144) — the
|
|
3
|
+
* "#135-style" helper: authenticate a request exactly as the app's own page would, from the run's
|
|
4
|
+
* `--storage-state` (a bearer token the SPA keeps in localStorage, or the session cookies) or from a
|
|
5
|
+
* `--secret-field` binding. No browser is needed: the storageState JSON already holds what the page
|
|
6
|
+
* would read.
|
|
7
|
+
*
|
|
8
|
+
* Every value this module returns is a credential. Callers put it on the wire and NOWHERE else — it
|
|
9
|
+
* never reaches a model, a log line or an artifact (fixture step logs never include request headers).
|
|
10
|
+
*
|
|
11
|
+
* NOTE (coordination): #135 (authenticated invariant probes) needs the same thing; if it lands its
|
|
12
|
+
* own helper, fold one into the other.
|
|
13
|
+
*/
|
|
14
|
+
export type RequestAuth =
|
|
15
|
+
/** `<header>: <scheme> <localStorage[key]>` for the request's origin (default `Authorization: Bearer`). */
|
|
16
|
+
{
|
|
17
|
+
readonly from: "localStorage";
|
|
18
|
+
readonly key: string;
|
|
19
|
+
readonly scheme?: string;
|
|
20
|
+
readonly header?: string;
|
|
21
|
+
}
|
|
22
|
+
/** The storageState cookies that match the request URL, as a `Cookie` header. */
|
|
23
|
+
| {
|
|
24
|
+
readonly from: "cookies";
|
|
25
|
+
}
|
|
26
|
+
/** `<header>: <scheme> <value>` from a `--secret-field` binding, named by its env variable. */
|
|
27
|
+
| {
|
|
28
|
+
readonly from: "secretField";
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly scheme?: string;
|
|
31
|
+
readonly header?: string;
|
|
32
|
+
};
|
|
33
|
+
export interface AuthSources {
|
|
34
|
+
/** Path of the run's Playwright storageState JSON (`--storage-state`). */
|
|
35
|
+
readonly storageStatePath?: string;
|
|
36
|
+
/** `--secret-field` values by their env-variable name. */
|
|
37
|
+
readonly secretFields?: Readonly<Record<string, string>>;
|
|
38
|
+
}
|
|
39
|
+
export declare class FixtureAuthError extends Error {
|
|
40
|
+
constructor(message: string);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A storageState file's `localStorage[key]` for `origin` (or null) — the same lookup `authHeaders`
|
|
44
|
+
* does for its `localStorage` source, exposed on its own for #173: an observer's cross-actor probe
|
|
45
|
+
* authenticates from its OWN storageState file this way, with no browser and no navigation needed.
|
|
46
|
+
*/
|
|
47
|
+
export declare function localStorageValue(storageStatePath: string, origin: string, key: string): string | null;
|
|
48
|
+
/**
|
|
49
|
+
* The header(s) `auth` adds to a request to `url`. Throws `FixtureAuthError` (naming the source,
|
|
50
|
+
* never a value) when the source is missing — a fixture never silently runs unauthenticated.
|
|
51
|
+
*/
|
|
52
|
+
export declare function authHeaders(auth: RequestAuth, url: string, sources: AuthSources): Record<string, string>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
export class FixtureAuthError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "FixtureAuthError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
function readStorageState(path) {
|
|
9
|
+
let raw;
|
|
10
|
+
try {
|
|
11
|
+
raw = JSON.parse(readFileSync(path, "utf8"));
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
// The message names the file only: its contents are credentials.
|
|
15
|
+
throw new FixtureAuthError(`cannot read storage state ${path}: ${e instanceof SyntaxError ? "not valid JSON" : "unreadable"}`);
|
|
16
|
+
}
|
|
17
|
+
const o = (raw ?? {});
|
|
18
|
+
const cookies = Array.isArray(o.cookies) ? o.cookies.filter((c) => typeof c?.name === "string" && typeof c.value === "string" && typeof c.domain === "string") : [];
|
|
19
|
+
const origins = Array.isArray(o.origins)
|
|
20
|
+
? o.origins.filter((x) => typeof x?.origin === "string" && Array.isArray(x.localStorage))
|
|
21
|
+
: [];
|
|
22
|
+
return { cookies, origins };
|
|
23
|
+
}
|
|
24
|
+
const LOOPBACK = new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
25
|
+
function cookieMatches(c, url, nowSec) {
|
|
26
|
+
const domain = c.domain.startsWith(".") ? c.domain.slice(1) : c.domain;
|
|
27
|
+
const host = url.hostname;
|
|
28
|
+
if (host !== domain && !(c.domain.startsWith(".") && host.endsWith(`.${domain}`)))
|
|
29
|
+
return false;
|
|
30
|
+
const path = c.path ?? "/";
|
|
31
|
+
if (!url.pathname.startsWith(path))
|
|
32
|
+
return false;
|
|
33
|
+
if (c.secure === true && url.protocol !== "https:" && !LOOPBACK.has(host))
|
|
34
|
+
return false;
|
|
35
|
+
if (typeof c.expires === "number" && c.expires > 0 && c.expires < nowSec)
|
|
36
|
+
return false;
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
function withScheme(scheme, value, fallback) {
|
|
40
|
+
const s = scheme ?? fallback;
|
|
41
|
+
return s === "" ? value : `${s} ${value}`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A storageState file's `localStorage[key]` for `origin` (or null) — the same lookup `authHeaders`
|
|
45
|
+
* does for its `localStorage` source, exposed on its own for #173: an observer's cross-actor probe
|
|
46
|
+
* authenticates from its OWN storageState file this way, with no browser and no navigation needed.
|
|
47
|
+
*/
|
|
48
|
+
export function localStorageValue(storageStatePath, origin, key) {
|
|
49
|
+
const state = readStorageState(storageStatePath);
|
|
50
|
+
return state.origins.find((o) => o.origin === origin)?.localStorage.find((i) => i.name === key)?.value ?? null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The header(s) `auth` adds to a request to `url`. Throws `FixtureAuthError` (naming the source,
|
|
54
|
+
* never a value) when the source is missing — a fixture never silently runs unauthenticated.
|
|
55
|
+
*/
|
|
56
|
+
export function authHeaders(auth, url, sources) {
|
|
57
|
+
const target = new URL(url);
|
|
58
|
+
if (auth.from === "secretField") {
|
|
59
|
+
const value = sources.secretFields?.[auth.name];
|
|
60
|
+
if (value === undefined || value === "") {
|
|
61
|
+
throw new FixtureAuthError(`auth needs the --secret-field bound to env:${auth.name}, which this run does not have`);
|
|
62
|
+
}
|
|
63
|
+
return { [auth.header ?? "authorization"]: withScheme(auth.scheme, value, "Bearer") };
|
|
64
|
+
}
|
|
65
|
+
if (sources.storageStatePath === undefined) {
|
|
66
|
+
throw new FixtureAuthError(`auth from ${auth.from} needs --storage-state`);
|
|
67
|
+
}
|
|
68
|
+
const state = readStorageState(sources.storageStatePath);
|
|
69
|
+
if (auth.from === "cookies") {
|
|
70
|
+
const nowSec = Date.now() / 1000;
|
|
71
|
+
const jar = state.cookies.filter((c) => cookieMatches(c, target, nowSec));
|
|
72
|
+
if (jar.length === 0)
|
|
73
|
+
throw new FixtureAuthError(`the storage state has no cookie for ${target.origin}`);
|
|
74
|
+
return { cookie: jar.map((c) => `${c.name}=${c.value}`).join("; ") };
|
|
75
|
+
}
|
|
76
|
+
const entry = state.origins.find((o) => o.origin === target.origin)?.localStorage.find((i) => i.name === auth.key);
|
|
77
|
+
if (entry === undefined || entry.value === "") {
|
|
78
|
+
throw new FixtureAuthError(`the storage state has no localStorage "${auth.key}" for ${target.origin}`);
|
|
79
|
+
}
|
|
80
|
+
return { [auth.header ?? "authorization"]: withScheme(auth.scheme, entry.value, "Bearer") };
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=fixture-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixture-auth.js","sourceRoot":"","sources":["../src/fixture-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { MissionFailure } from "@jevitate/domain";
|
|
3
|
+
import type { SecretField } from "@jevitate/explore";
|
|
4
|
+
import { FixtureSetupError, MissionFixtures, UnboundSetupRefError, type FixtureBindings, type FixtureRecord, type FixtureSpec } from "./mission-fixtures.js";
|
|
5
|
+
/**
|
|
6
|
+
* The CLI side of mission fixtures (#140/#144): the shared flags, building the lifecycle from them
|
|
7
|
+
* (or from a target's `fixtures` in `~/.jevitate/targets.json`), the static `${setup.x}` checks that
|
|
8
|
+
* run before any browser or request, and the `inconclusive` result a failed setup ends a run with.
|
|
9
|
+
*/
|
|
10
|
+
export interface FixtureFlags {
|
|
11
|
+
readonly fixtures?: string;
|
|
12
|
+
readonly before?: string;
|
|
13
|
+
readonly after?: string;
|
|
14
|
+
readonly allowShellHooks?: boolean;
|
|
15
|
+
readonly hookTimeoutMs?: string;
|
|
16
|
+
}
|
|
17
|
+
/** Adds `--fixtures`, `--before`, `--after`, `--allow-shell-hooks` and `--hook-timeout-ms`. */
|
|
18
|
+
export declare function withFixtureFlags(cmd: Command): Command;
|
|
19
|
+
export interface FixtureContext {
|
|
20
|
+
readonly allowlist: readonly string[];
|
|
21
|
+
readonly baseUrl: string;
|
|
22
|
+
readonly storageState?: string;
|
|
23
|
+
readonly secretFields?: readonly SecretField[];
|
|
24
|
+
readonly secrets?: readonly string[];
|
|
25
|
+
/** A target's own fixtures file (`~/.jevitate/targets.json`), used when `--fixtures` is absent. */
|
|
26
|
+
readonly targetFixtures?: string;
|
|
27
|
+
/** A spec already validated elsewhere (a persisted mission result), used when no file is given. */
|
|
28
|
+
readonly spec?: FixtureSpec;
|
|
29
|
+
}
|
|
30
|
+
/** The run's fixture lifecycle, or `undefined` when none was asked for. Throws `FixtureSpecError`. */
|
|
31
|
+
export declare function buildMissionFixtures(flags: FixtureFlags, ctx: FixtureContext): MissionFixtures | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Before any browser or request: every `${setup.x}` in `texts` must be an output the fixture
|
|
34
|
+
* declares (unless a `--before` hook may bind it), and a run without fixtures may not reference one.
|
|
35
|
+
*/
|
|
36
|
+
export declare function checkSetupRefs(texts: Readonly<Record<string, string | readonly string[] | undefined>>, fx: MissionFixtures | undefined): void;
|
|
37
|
+
/** Throws unless a `${setup.*}` reference in `url` leaves its origin fixed (the allowlist is decided before setup runs). */
|
|
38
|
+
export declare function checkUrlRefOrigin(url: string): void;
|
|
39
|
+
/** The `${setup.*}`-bearing strings of an invariants spec, keyed `--invariants <path>`, for {@link checkSetupRefs}. */
|
|
40
|
+
export declare function invariantSetupTexts(spec: unknown): Record<string, string>;
|
|
41
|
+
/**
|
|
42
|
+
* `${setup.x}` in an invariants spec (#187) — probe paths, `deniedAs.open`, capture routes — bound
|
|
43
|
+
* once setup ran. Origin-fixed like a fixture step: a bound value that moves a string off the origin
|
|
44
|
+
* it resolved to with the reference unfilled (`//evil.test/…`) is refused, never probed.
|
|
45
|
+
*/
|
|
46
|
+
export declare function substituteSpecSetupRefs<T>(spec: T, b: FixtureBindings, baseUrl: string): T;
|
|
47
|
+
/** The typed result a run ends with when its fixture could not be set up: `inconclusive`, a configuration error. */
|
|
48
|
+
export interface FixtureSetupFailedResult {
|
|
49
|
+
readonly outcome: "inconclusive";
|
|
50
|
+
readonly reason: string;
|
|
51
|
+
readonly failure: MissionFailure;
|
|
52
|
+
readonly attribution: "configuration";
|
|
53
|
+
readonly exitCode: 2;
|
|
54
|
+
readonly fixtures: FixtureRecord;
|
|
55
|
+
}
|
|
56
|
+
export declare function fixtureSetupFailedResult(err: FixtureSetupError | UnboundSetupRefError, fx: MissionFixtures): FixtureSetupFailedResult;
|
|
57
|
+
/**
|
|
58
|
+
* Regression capture (#144): every reproduce/minimize replay restores + re-runs the fixture the
|
|
59
|
+
* failing Recording was made from. The spec is `--fixtures`, else the one saved in `--result`
|
|
60
|
+
* (re-validated against that mission's allowlist); shell hooks must be re-supplied (and match the
|
|
61
|
+
* mission's by hash). A Recording that carries a fixture identity is never replayed without one.
|
|
62
|
+
*/
|
|
63
|
+
export declare function regressionFixtures(flags: FixtureFlags, recording: {
|
|
64
|
+
readonly site: string;
|
|
65
|
+
readonly fixture?: {
|
|
66
|
+
readonly identity: string;
|
|
67
|
+
};
|
|
68
|
+
}, missionResult: unknown,
|
|
69
|
+
/** `--storage-state` (#129): wins over the one recorded with the mission. */
|
|
70
|
+
storageState?: string): MissionFixtures | undefined;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { positiveIntArg } from "./cli-args.js";
|
|
2
|
+
import { FixtureSetupError, FixtureSpecError, MissionFixtures, SETUP_REF, UnboundSetupRefError, hookHash, loadFixtureFile, parseFixtureSpec, referencedNames, substituteSetupRefs, } from "./mission-fixtures.js";
|
|
3
|
+
/** Adds `--fixtures`, `--before`, `--after`, `--allow-shell-hooks` and `--hook-timeout-ms`. */
|
|
4
|
+
export function withFixtureFlags(cmd) {
|
|
5
|
+
return cmd
|
|
6
|
+
.option("--fixtures <file>", "mission fixtures JSON {setup:[...], restore:[...]} (#140/#144): HTTP steps to an --allow origin, authenticated from " +
|
|
7
|
+
"--storage-state/--secret-field, run before the mission and restored after it — and around every replay. Outputs bind as ${setup.<name>}")
|
|
8
|
+
.option("--before <cmd>", "operator shell hook run before the mission and every replay (needs --allow-shell-hooks); may print {vars, secret}")
|
|
9
|
+
.option("--after <cmd>", "operator shell hook run after the mission and every replay (needs --allow-shell-hooks)")
|
|
10
|
+
.option("--allow-shell-hooks", "opt in to running --before/--after (operator commands; never model-chosen)", false)
|
|
11
|
+
.option("--hook-timeout-ms <ms>", "timeout for each --before/--after hook (default 60000; the process group is killed)", positiveIntArg);
|
|
12
|
+
}
|
|
13
|
+
function hooksOf(flags) {
|
|
14
|
+
if (flags.before === undefined && flags.after === undefined)
|
|
15
|
+
return undefined;
|
|
16
|
+
const timeoutMs = flags.hookTimeoutMs === undefined ? undefined : Number(flags.hookTimeoutMs);
|
|
17
|
+
if (timeoutMs !== undefined && !(Number.isInteger(timeoutMs) && timeoutMs > 0)) {
|
|
18
|
+
throw new FixtureSpecError("--hook-timeout-ms must be a positive integer");
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
...(flags.before === undefined ? {} : { before: flags.before }),
|
|
22
|
+
...(flags.after === undefined ? {} : { after: flags.after }),
|
|
23
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** The run's fixture lifecycle, or `undefined` when none was asked for. Throws `FixtureSpecError`. */
|
|
27
|
+
export function buildMissionFixtures(flags, ctx) {
|
|
28
|
+
const hooks = hooksOf(flags);
|
|
29
|
+
const file = flags.fixtures ?? ctx.targetFixtures;
|
|
30
|
+
const bounds = { allowlist: ctx.allowlist, baseUrl: ctx.baseUrl };
|
|
31
|
+
const spec = file !== undefined ? loadFixtureFile(file, bounds, { openRefs: hooks?.before !== undefined }) : ctx.spec;
|
|
32
|
+
if (spec === undefined && hooks === undefined)
|
|
33
|
+
return undefined;
|
|
34
|
+
const secretFields = Object.fromEntries((ctx.secretFields ?? []).filter((f) => f.kind === "value").map((f) => [f.name, f.secret]));
|
|
35
|
+
return new MissionFixtures({
|
|
36
|
+
...bounds,
|
|
37
|
+
...(spec === undefined ? {} : { spec }),
|
|
38
|
+
...(hooks === undefined ? {} : { hooks }),
|
|
39
|
+
allowShellHooks: flags.allowShellHooks === true,
|
|
40
|
+
auth: { ...(ctx.storageState === undefined ? {} : { storageStatePath: ctx.storageState }), secretFields },
|
|
41
|
+
...(ctx.secrets === undefined ? {} : { secrets: ctx.secrets }),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Before any browser or request: every `${setup.x}` in `texts` must be an output the fixture
|
|
46
|
+
* declares (unless a `--before` hook may bind it), and a run without fixtures may not reference one.
|
|
47
|
+
*/
|
|
48
|
+
export function checkSetupRefs(texts, fx) {
|
|
49
|
+
// `null`: a `--before` hook may bind more names at run time (still checked when substituted).
|
|
50
|
+
const declared = fx === undefined ? new Set() : fx.declaredOutputs();
|
|
51
|
+
const secret = fx?.declaredSecretOutputs() ?? new Set();
|
|
52
|
+
for (const [where, v] of Object.entries(texts)) {
|
|
53
|
+
for (const text of v === undefined ? [] : typeof v === "string" ? [v] : v) {
|
|
54
|
+
for (const name of referencedNames(text)) {
|
|
55
|
+
if (fx === undefined)
|
|
56
|
+
throw new UnboundSetupRefError(`${where} references \${setup.${name}} but the run has no --fixtures/--before`);
|
|
57
|
+
if (secret.has(name)) {
|
|
58
|
+
throw new UnboundSetupRefError(`${where} references the secret output \${setup.${name}}; secret outputs stay inside the fixture's own requests`);
|
|
59
|
+
}
|
|
60
|
+
if (declared !== null && !declared.has(name))
|
|
61
|
+
throw new UnboundSetupRefError(`${where} references \${setup.${name}}, which the fixture does not output`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Throws unless a `${setup.*}` reference in `url` leaves its origin fixed (the allowlist is decided before setup runs). */
|
|
67
|
+
export function checkUrlRefOrigin(url) {
|
|
68
|
+
if (!url.includes("${setup."))
|
|
69
|
+
return;
|
|
70
|
+
try {
|
|
71
|
+
if (new URL(url.replace(SETUP_REF, "0")).origin !== new URL(url.replace(SETUP_REF, "x.evil.test")).origin)
|
|
72
|
+
throw new Error("origin");
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
throw new UnboundSetupRefError("--url: a ${setup.*} reference may fill the path or query, never the origin — put it after a `/` (e.g. `http://host:8093/${setup.path}` with a path value that has no leading `/`, or `http://host:8093/projects/${setup.id}`)");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Every string inside an invariants spec (#187), with the JSON path it sits at. */
|
|
79
|
+
function specStrings(v, path, out) {
|
|
80
|
+
if (typeof v === "string")
|
|
81
|
+
out.push([path, v]);
|
|
82
|
+
else if (Array.isArray(v))
|
|
83
|
+
v.forEach((x, i) => specStrings(x, `${path}[${i}]`, out));
|
|
84
|
+
else if (v !== null && typeof v === "object")
|
|
85
|
+
for (const [k, x] of Object.entries(v))
|
|
86
|
+
specStrings(x, path === "" ? k : `${path}.${k}`, out);
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
/** The `${setup.*}`-bearing strings of an invariants spec, keyed `--invariants <path>`, for {@link checkSetupRefs}. */
|
|
90
|
+
export function invariantSetupTexts(spec) {
|
|
91
|
+
return Object.fromEntries(specStrings(spec, "", []).filter(([, s]) => s.includes("${setup.")).map(([p, s]) => [`--invariants ${p}`, s]));
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* `${setup.x}` in an invariants spec (#187) — probe paths, `deniedAs.open`, capture routes — bound
|
|
95
|
+
* once setup ran. Origin-fixed like a fixture step: a bound value that moves a string off the origin
|
|
96
|
+
* it resolved to with the reference unfilled (`//evil.test/…`) is refused, never probed.
|
|
97
|
+
*/
|
|
98
|
+
export function substituteSpecSetupRefs(spec, b, baseUrl) {
|
|
99
|
+
const walk = (v, path) => {
|
|
100
|
+
if (typeof v === "string") {
|
|
101
|
+
if (!v.includes("${setup."))
|
|
102
|
+
return v;
|
|
103
|
+
const where = `--invariants ${path}`;
|
|
104
|
+
const out = substituteSetupRefs(v, b, { where });
|
|
105
|
+
let before = null;
|
|
106
|
+
let after = null;
|
|
107
|
+
try {
|
|
108
|
+
before = new URL(v.replace(SETUP_REF, "0"), baseUrl).origin;
|
|
109
|
+
after = new URL(out, baseUrl).origin;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// not a URL — nothing to keep on an origin
|
|
113
|
+
}
|
|
114
|
+
if (before !== after)
|
|
115
|
+
throw new UnboundSetupRefError(`${where}: a \${setup.*} value may fill the path or query, never the origin`);
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
if (Array.isArray(v))
|
|
119
|
+
return v.map((x, i) => walk(x, `${path}[${i}]`));
|
|
120
|
+
if (v !== null && typeof v === "object")
|
|
121
|
+
return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, walk(x, path === "" ? k : `${path}.${k}`)]));
|
|
122
|
+
return v;
|
|
123
|
+
};
|
|
124
|
+
return walk(spec, "");
|
|
125
|
+
}
|
|
126
|
+
export function fixtureSetupFailedResult(err, fx) {
|
|
127
|
+
const reason = err instanceof FixtureSetupError ? err.message : `fixture setup failed: ${err.message}`;
|
|
128
|
+
return {
|
|
129
|
+
outcome: "inconclusive",
|
|
130
|
+
reason,
|
|
131
|
+
failure: { kind: "configuration", message: reason },
|
|
132
|
+
attribution: "configuration",
|
|
133
|
+
exitCode: 2,
|
|
134
|
+
fixtures: fx.record(),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Regression capture (#144): every reproduce/minimize replay restores + re-runs the fixture the
|
|
139
|
+
* failing Recording was made from. The spec is `--fixtures`, else the one saved in `--result`
|
|
140
|
+
* (re-validated against that mission's allowlist); shell hooks must be re-supplied (and match the
|
|
141
|
+
* mission's by hash). A Recording that carries a fixture identity is never replayed without one.
|
|
142
|
+
*/
|
|
143
|
+
export function regressionFixtures(flags, recording, missionResult,
|
|
144
|
+
/** `--storage-state` (#129): wins over the one recorded with the mission. */
|
|
145
|
+
storageState) {
|
|
146
|
+
const result = isObject(missionResult) && isObject(missionResult.result) ? missionResult.result : undefined;
|
|
147
|
+
const target = isObject(result?.target) ? result.target : undefined;
|
|
148
|
+
const saved = isObject(result?.fixtures) ? result.fixtures : undefined;
|
|
149
|
+
const allowlist = Array.isArray(target?.allowlist) ? target.allowlist.filter((a) => typeof a === "string") : [recording.site];
|
|
150
|
+
const baseUrl = typeof target?.seedUrl === "string" ? target.seedUrl : recording.site;
|
|
151
|
+
const savedHooks = isObject(saved?.hooks) ? saved.hooks : {};
|
|
152
|
+
if ((savedHooks.before !== undefined || savedHooks.after !== undefined) &&
|
|
153
|
+
((flags.before === undefined ? undefined : hookHash(flags.before)) !== savedHooks.before ||
|
|
154
|
+
(flags.after === undefined ? undefined : hookHash(flags.after)) !== savedHooks.after)) {
|
|
155
|
+
throw new FixtureSpecError("the mission ran with --before/--after shell hooks: re-supply the SAME commands with --allow-shell-hooks");
|
|
156
|
+
}
|
|
157
|
+
const spec = flags.fixtures === undefined && saved?.spec !== undefined
|
|
158
|
+
? parseFixtureSpec(saved.spec, { allowlist, baseUrl }, { openRefs: flags.before !== undefined })
|
|
159
|
+
: undefined;
|
|
160
|
+
const fx = buildMissionFixtures(flags, {
|
|
161
|
+
allowlist,
|
|
162
|
+
baseUrl,
|
|
163
|
+
...(storageState !== undefined
|
|
164
|
+
? { storageState }
|
|
165
|
+
: typeof target?.storageStatePath === "string"
|
|
166
|
+
? { storageState: target.storageStatePath }
|
|
167
|
+
: {}),
|
|
168
|
+
...(spec === undefined ? {} : { spec }),
|
|
169
|
+
});
|
|
170
|
+
if (fx === undefined && recording.fixture !== undefined) {
|
|
171
|
+
throw new FixtureSpecError(`this Recording started from fixture ${recording.fixture.identity}: pass --fixtures (or --result with its saved fixture) so every replay restores the same state`);
|
|
172
|
+
}
|
|
173
|
+
return fx;
|
|
174
|
+
}
|
|
175
|
+
function isObject(v) {
|
|
176
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=fixture-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixture-cli.js","sourceRoot":"","sources":["../src/fixture-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type CredentialStore, type RetryDeps } from "@jevitate/ai-core";
|
|
2
|
+
import type { IssueFilerPort, IssueRef, NewIssue } from "@jevitate/domain";
|
|
3
|
+
/**
|
|
4
|
+
* The GitHub adapter for the domain's `IssueFilerPort` (owner ruling 3). It prefers the `gh` CLI
|
|
5
|
+
* when it is installed (the user's own authenticated session); otherwise it uses the REST API with
|
|
6
|
+
* `GITHUB_TOKEN` from jevitate's credential store. The token goes ONLY into the Authorization
|
|
7
|
+
* header — never into a message, a log line, an error, or an issue — and any error text is
|
|
8
|
+
* scrubbed of it before it leaves this module.
|
|
9
|
+
*
|
|
10
|
+
* Both transports are injectable so tests exercise the exact commands/requests without ever
|
|
11
|
+
* talking to GitHub.
|
|
12
|
+
*/
|
|
13
|
+
export interface ExecResult {
|
|
14
|
+
readonly code: number;
|
|
15
|
+
readonly stdout: string;
|
|
16
|
+
readonly stderr: string;
|
|
17
|
+
}
|
|
18
|
+
/** Runs a command with optional stdin. No shell — arguments are passed verbatim (portable). */
|
|
19
|
+
export type Exec = (cmd: string, args: readonly string[], stdin?: string) => Promise<ExecResult>;
|
|
20
|
+
export type FetchLike = (url: string, init: {
|
|
21
|
+
method: string;
|
|
22
|
+
headers: Record<string, string>;
|
|
23
|
+
body?: string;
|
|
24
|
+
}) => Promise<{
|
|
25
|
+
readonly status: number;
|
|
26
|
+
text(): Promise<string>;
|
|
27
|
+
}>;
|
|
28
|
+
export declare const defaultExec: Exec;
|
|
29
|
+
export interface GitHubIssueFilerDeps {
|
|
30
|
+
readonly store: CredentialStore;
|
|
31
|
+
readonly exec?: Exec;
|
|
32
|
+
readonly fetch?: FetchLike;
|
|
33
|
+
readonly apiBase?: string;
|
|
34
|
+
/** Backoff for transient REST failures (injected clock in tests). */
|
|
35
|
+
readonly retry?: RetryDeps;
|
|
36
|
+
}
|
|
37
|
+
export declare class GitHubFilingError extends Error {
|
|
38
|
+
readonly code: "E_ISSUE_FILING";
|
|
39
|
+
/** The HTTP status, for a REST failure (drives the transient-retry decision). */
|
|
40
|
+
readonly status?: number;
|
|
41
|
+
constructor(message: string, status?: number);
|
|
42
|
+
}
|
|
43
|
+
export declare class GitHubIssueFiler implements IssueFilerPort {
|
|
44
|
+
#private;
|
|
45
|
+
constructor(deps: GitHubIssueFilerDeps);
|
|
46
|
+
/** `gh` when it is installed and runnable, else REST (which needs `GITHUB_TOKEN`). */
|
|
47
|
+
mode(): Promise<"gh" | "rest">;
|
|
48
|
+
findOpenByMarker(repo: string, marker: string): Promise<IssueRef | null>;
|
|
49
|
+
/**
|
|
50
|
+
* Creating an issue is NOT idempotent, so it is never blindly retried: a "failed" create (a 502,
|
|
51
|
+
* a dropped connection) may well have persisted. After a transient failure the fingerprint
|
|
52
|
+
* search runs again — if the issue now exists it IS the one this call created and is returned;
|
|
53
|
+
* only if it is still absent is the create tried again, on the backoff schedule. A body without a
|
|
54
|
+
* fingerprint marker cannot be checked, so it gets exactly one attempt. Same for both transports.
|
|
55
|
+
*/
|
|
56
|
+
create(repo: string, issue: NewIssue): Promise<IssueRef>;
|
|
57
|
+
/**
|
|
58
|
+
* An occurrence comment is not idempotent either: it gets exactly one attempt (a duplicate
|
|
59
|
+
* comment is worse than a missing one — the draft on disk keeps the occurrence either way).
|
|
60
|
+
*/
|
|
61
|
+
comment(repo: string, number: number, body: string): Promise<IssueRef>;
|
|
62
|
+
}
|