@jevitate/cli 0.6.0 → 0.8.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/dist/ai-cli.d.ts +3 -1
- package/dist/ai-cli.js +60 -16
- package/dist/ai-cli.js.map +1 -1
- package/dist/approval-provenance.d.ts +120 -0
- package/dist/approval-provenance.js +300 -0
- package/dist/approval-provenance.js.map +1 -0
- package/dist/author-journey-cli.js +11 -18
- package/dist/author-journey-cli.js.map +1 -1
- package/dist/bin.js +29012 -20685
- package/dist/build-info.generated.js +2 -2
- package/dist/builtin-analyzers.d.ts +7 -0
- package/dist/builtin-analyzers.js +19 -0
- package/dist/builtin-analyzers.js.map +1 -0
- package/dist/campaign-cli.d.ts +6 -0
- package/dist/campaign-cli.js +9 -4
- package/dist/campaign-cli.js.map +1 -1
- package/dist/catalog-analysis.d.ts +70 -0
- package/dist/catalog-analysis.js +507 -0
- package/dist/catalog-analysis.js.map +1 -0
- package/dist/catalog-api.d.ts +73 -0
- package/dist/catalog-api.js +97 -0
- package/dist/catalog-api.js.map +1 -0
- package/dist/catalog-cli.d.ts +12 -0
- package/dist/catalog-cli.js +224 -0
- package/dist/catalog-cli.js.map +1 -0
- package/dist/catalog-review.d.ts +29 -0
- package/dist/catalog-review.js +290 -0
- package/dist/catalog-review.js.map +1 -0
- package/dist/catalog.d.ts +133 -0
- package/dist/catalog.js +352 -0
- package/dist/catalog.js.map +1 -0
- package/dist/check-api.js +24 -2
- package/dist/check-api.js.map +1 -1
- package/dist/check-cli.d.ts +3 -0
- package/dist/check-cli.js +18 -4
- package/dist/check-cli.js.map +1 -1
- package/dist/check-execute.js +25 -9
- package/dist/check-execute.js.map +1 -1
- package/dist/check-plan.js +4 -1
- package/dist/check-plan.js.map +1 -1
- package/dist/check-suite.js +6 -3
- package/dist/check-suite.js.map +1 -1
- package/dist/check-types.d.ts +12 -1
- package/dist/check-types.js.map +1 -1
- package/dist/cli-output.js +86 -4
- package/dist/cli-output.js.map +1 -1
- package/dist/cli-shared.d.ts +22 -2
- package/dist/cli-shared.js +34 -5
- package/dist/cli-shared.js.map +1 -1
- package/dist/demo-aspect-api.d.ts +21 -5
- package/dist/demo-aspect-api.js +30 -1
- package/dist/demo-aspect-api.js.map +1 -1
- package/dist/demo-aspect-cli.js +64 -5
- package/dist/demo-aspect-cli.js.map +1 -1
- package/dist/environments.js +6 -1
- package/dist/environments.js.map +1 -1
- package/dist/exit-codes.js +4 -0
- package/dist/exit-codes.js.map +1 -1
- package/dist/explore-adversarial.d.ts +3 -1
- package/dist/explore-adversarial.js +3 -3
- package/dist/explore-adversarial.js.map +1 -1
- package/dist/explore-author.js +5 -2
- package/dist/explore-author.js.map +1 -1
- package/dist/explore-cli.js +162 -13
- package/dist/explore-cli.js.map +1 -1
- package/dist/explore-coverage.d.ts +6 -2
- package/dist/explore-coverage.js +5 -4
- package/dist/explore-coverage.js.map +1 -1
- package/dist/explore-feature.d.ts +3 -1
- package/dist/explore-feature.js +2 -2
- package/dist/explore-feature.js.map +1 -1
- package/dist/explore-goal.d.ts +37 -6
- package/dist/explore-goal.js +70 -52
- package/dist/explore-goal.js.map +1 -1
- package/dist/explore-shared.d.ts +12 -7
- package/dist/explore-shared.js +9 -1
- package/dist/explore-shared.js.map +1 -1
- package/dist/goal-run-flags.d.ts +2 -0
- package/dist/goal-run-flags.js +8 -1
- package/dist/goal-run-flags.js.map +1 -1
- package/dist/http-5xx-outcome.d.ts +8 -3
- package/dist/http-5xx-outcome.js +5 -15
- package/dist/http-5xx-outcome.js.map +1 -1
- package/dist/init-cli.d.ts +7 -0
- package/dist/init-cli.js +79 -9
- package/dist/init-cli.js.map +1 -1
- package/dist/init-codeowners.d.ts +39 -0
- package/dist/init-codeowners.js +91 -0
- package/dist/init-codeowners.js.map +1 -0
- package/dist/init-keys.d.ts +3 -1
- package/dist/init-keys.js +20 -8
- package/dist/init-keys.js.map +1 -1
- package/dist/init-skills.d.ts +85 -7
- package/dist/init-skills.js +303 -36
- package/dist/init-skills.js.map +1 -1
- package/dist/jev-advisor.d.ts +72 -0
- package/dist/jev-advisor.js +172 -0
- package/dist/jev-advisor.js.map +1 -0
- package/dist/journey-api.d.ts +104 -5
- package/dist/journey-api.js +165 -23
- package/dist/journey-api.js.map +1 -1
- package/dist/journey-cli.d.ts +1 -1
- package/dist/journey-cli.js +320 -13
- package/dist/journey-cli.js.map +1 -1
- package/dist/journey-demo-api.d.ts +7 -1
- package/dist/journey-demo-api.js +8 -3
- package/dist/journey-demo-api.js.map +1 -1
- package/dist/journey-demo-cli.js +3 -0
- package/dist/journey-demo-cli.js.map +1 -1
- package/dist/journey-lint-sarif.d.ts +19 -0
- package/dist/journey-lint-sarif.js +48 -0
- package/dist/journey-lint-sarif.js.map +1 -0
- package/dist/journey-network-checks.d.ts +45 -9
- package/dist/journey-network-checks.js +117 -30
- package/dist/journey-network-checks.js.map +1 -1
- package/dist/journey-prefix.d.ts +24 -15
- package/dist/journey-prefix.js +53 -7
- package/dist/journey-prefix.js.map +1 -1
- package/dist/journey-review-api.d.ts +19 -0
- package/dist/journey-review-api.js +46 -0
- package/dist/journey-review-api.js.map +1 -0
- package/dist/journey-review-store.d.ts +16 -0
- package/dist/journey-review-store.js +95 -0
- package/dist/journey-review-store.js.map +1 -0
- package/dist/journey-review.d.ts +47 -0
- package/dist/journey-review.js +499 -0
- package/dist/journey-review.js.map +1 -0
- package/dist/journey-verify.d.ts +61 -0
- package/dist/journey-verify.js +144 -0
- package/dist/journey-verify.js.map +1 -0
- package/dist/key-report.d.ts +20 -5
- package/dist/key-report.js +32 -9
- package/dist/key-report.js.map +1 -1
- package/dist/kill-signal.js +10 -2
- package/dist/kill-signal.js.map +1 -1
- package/dist/load-api.js +9 -3
- package/dist/load-api.js.map +1 -1
- package/dist/load-cli.js +4 -2
- package/dist/load-cli.js.map +1 -1
- package/dist/log-classes.d.ts +73 -0
- package/dist/log-classes.js +167 -0
- package/dist/log-classes.js.map +1 -0
- package/dist/log-correlation.d.ts +29 -10
- package/dist/log-correlation.js +44 -21
- package/dist/log-correlation.js.map +1 -1
- package/dist/login-cli.d.ts +15 -0
- package/dist/login-cli.js +116 -0
- package/dist/login-cli.js.map +1 -0
- package/dist/logs-cli.js +3 -1
- package/dist/logs-cli.js.map +1 -1
- package/dist/mcp-api.d.ts +1 -0
- package/dist/mcp-api.js +61 -12
- package/dist/mcp-api.js.map +1 -1
- package/dist/mcp-cli-runner.d.ts +2 -0
- package/dist/mcp-cli-runner.js +3 -1
- package/dist/mcp-cli-runner.js.map +1 -1
- package/dist/mcp-cli-tools.d.ts +6 -0
- package/dist/mcp-cli-tools.js +182 -23
- package/dist/mcp-cli-tools.js.map +1 -1
- package/dist/mission-cli.js +8 -4
- package/dist/mission-cli.js.map +1 -1
- package/dist/mission-journal.d.ts +4 -1
- package/dist/mission-journal.js +9 -3
- package/dist/mission-journal.js.map +1 -1
- package/dist/mission-queue-cli.js +21 -2
- package/dist/mission-queue-cli.js.map +1 -1
- package/dist/mission-queue-runner.d.ts +6 -0
- package/dist/mission-queue-runner.js +33 -3
- package/dist/mission-queue-runner.js.map +1 -1
- package/dist/multi-run-cli.d.ts +7 -1
- package/dist/multi-run-cli.js +10 -3
- package/dist/multi-run-cli.js.map +1 -1
- package/dist/multi-run.d.ts +38 -3
- package/dist/multi-run.js +115 -23
- package/dist/multi-run.js.map +1 -1
- package/dist/persona-login.d.ts +199 -0
- package/dist/persona-login.js +574 -0
- package/dist/persona-login.js.map +1 -0
- package/dist/pre-approval.d.ts +98 -0
- package/dist/pre-approval.js +175 -0
- package/dist/pre-approval.js.map +1 -0
- package/dist/program.js +10 -0
- package/dist/program.js.map +1 -1
- package/dist/project-dir.d.ts +2 -1
- package/dist/project-dir.js +3 -1
- package/dist/project-dir.js.map +1 -1
- package/dist/promotions.d.ts +15 -0
- package/dist/promotions.js +52 -0
- package/dist/promotions.js.map +1 -0
- package/dist/readiness.d.ts +29 -0
- package/dist/readiness.js +291 -0
- package/dist/readiness.js.map +1 -0
- package/dist/regression-cli.js +4 -2
- package/dist/regression-cli.js.map +1 -1
- package/dist/report-api.d.ts +24 -2
- package/dist/report-api.js +33 -5
- package/dist/report-api.js.map +1 -1
- package/dist/report-cli.js +8 -1
- package/dist/report-cli.js.map +1 -1
- package/dist/result-schema.d.ts +12 -0
- package/dist/result-schema.js +8 -0
- package/dist/result-schema.js.map +1 -1
- package/dist/run-index.d.ts +4 -2
- package/dist/run-index.js +3 -1
- package/dist/run-index.js.map +1 -1
- package/dist/run-key-preflight.d.ts +2 -1
- package/dist/run-key-preflight.js +3 -2
- package/dist/run-key-preflight.js.map +1 -1
- package/dist/run-metadata.d.ts +26 -0
- package/dist/run-metadata.js +41 -0
- package/dist/run-metadata.js.map +1 -0
- package/dist/run-tags-cli.d.ts +12 -0
- package/dist/run-tags-cli.js +35 -0
- package/dist/run-tags-cli.js.map +1 -0
- package/dist/self-heal-adapter.js +3 -1
- package/dist/self-heal-adapter.js.map +1 -1
- package/dist/site-cli.d.ts +1 -1
- package/dist/site-cli.js +24 -1
- package/dist/site-cli.js.map +1 -1
- package/dist/source-cli.js +4 -2
- package/dist/source-cli.js.map +1 -1
- package/dist/source-run-api.js +17 -7
- package/dist/source-run-api.js.map +1 -1
- package/dist/suite-explore-options.d.ts +14 -0
- package/dist/suite-explore-options.js +7 -0
- package/dist/suite-explore-options.js.map +1 -1
- package/dist/sweep-api.d.ts +266 -0
- package/dist/sweep-api.js +828 -0
- package/dist/sweep-api.js.map +1 -0
- package/dist/sweep-cli.d.ts +9 -0
- package/dist/sweep-cli.js +115 -0
- package/dist/sweep-cli.js.map +1 -0
- package/dist/target-config.d.ts +5 -2
- package/dist/target-config.js +15 -0
- package/dist/target-config.js.map +1 -1
- package/dist/usability-capture.js +6 -0
- package/dist/usability-capture.js.map +1 -1
- package/dist/ux-cli.js +3 -1
- package/dist/ux-cli.js.map +1 -1
- package/dist/ux-usability.d.ts +9 -3
- package/dist/ux-usability.js +6 -4
- package/dist/ux-usability.js.map +1 -1
- package/dist/verify-fix-cli.js +4 -2
- package/dist/verify-fix-cli.js.map +1 -1
- package/package.json +13 -13
- package/skills/jevitate-demo/SKILL.md +5 -2
- package/skills/jevitate-explore/SKILL.md +6 -2
- package/skills/jevitate-getting-started/SKILL.md +12 -0
- package/skills/jevitate-run-journey/SKILL.md +17 -4
- package/skills/jevitate-test-campaign/SKILL.md +26 -5
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { contentHash } from "@jevitate/domain";
|
|
4
|
+
import { JevJudgmentGateway, JevProviderError, MissingCredentialError, RetryingJudgmentPort, UsageTracker, envCredentialStore, jevProviderOverride, realJevClientCall, resolveJevRoute, } from "@jevitate/ai-core";
|
|
5
|
+
import { GatewaySelectionError, keyPreflightOpts } from "./cli-shared.js";
|
|
6
|
+
import { loadLocalCredentials } from "./credentials-file.js";
|
|
7
|
+
import { preflightRunKeys } from "./run-key-preflight.js";
|
|
8
|
+
import { resolveUsagePricing } from "./usage-config.js";
|
|
9
|
+
/**
|
|
10
|
+
* #434/#435 — the advisory Jev layer of the review sheets and the pre-approval pipeline: one
|
|
11
|
+
* `JudgmentPort` (the existing judgment gateway, TypeSafe or OpenRouter key, #429) behind a cache
|
|
12
|
+
* keyed by the content hash of what is asked, plus the usage of every call actually made.
|
|
13
|
+
*
|
|
14
|
+
* Model use follows the CLI's `--real` convention: the layer runs only on a command given `--real`
|
|
15
|
+
* (review sheets, approvals, `catalog analyze`; MCP `real: true`). Unlike a run, a missing judgment
|
|
16
|
+
* key is not a refusal here — the deterministic layer stands on its own, so the sheet says
|
|
17
|
+
* "skipped: no judgment key". Without `--real` it says "skipped: pass --real".
|
|
18
|
+
*
|
|
19
|
+
* The cache (`<project>/.jevitate/cache/jev/<sha256>.json`, gitignored) holds ANSWERS only — a kind,
|
|
20
|
+
* a value, a probability — keyed by `sha256(state + questions)`. The state is built from catalog
|
|
21
|
+
* text (job stories, persona descriptions, a Journey's review-sheet lines, which never carry a
|
|
22
|
+
* secret value), and it is never written: nothing secret is cached. A re-review of unchanged
|
|
23
|
+
* content asks nothing new. An unreadable entry is a miss (it is re-asked and rewritten).
|
|
24
|
+
*/
|
|
25
|
+
/** Bumped when the questions' meaning changes in a way their text does not show. */
|
|
26
|
+
export const JEV_CACHE_VERSION = 1;
|
|
27
|
+
/** Why the Jev layer did not run. */
|
|
28
|
+
export const JEV_SKIPPED_PASS_REAL = "pass --real to ask Jev (needs a judgment key)";
|
|
29
|
+
export const JEV_SKIPPED_NO_KEY = "no judgment key — set TYPESAFE_API_KEY or OPENROUTER_API_KEY (jevitate ai setup)";
|
|
30
|
+
function validAnswer(q, a) {
|
|
31
|
+
if (a === null || typeof a !== "object")
|
|
32
|
+
return false;
|
|
33
|
+
const o = a;
|
|
34
|
+
if (o.kind !== q.kind)
|
|
35
|
+
return false;
|
|
36
|
+
if (q.kind === "choice")
|
|
37
|
+
return typeof o.value === "string" && q.options.includes(o.value) && typeof o.confidence === "number" && Number.isFinite(o.confidence);
|
|
38
|
+
if (q.kind === "noul")
|
|
39
|
+
return typeof o.value === "boolean" && typeof o.probability === "number" && Number.isFinite(o.probability);
|
|
40
|
+
return typeof o.value === "number" && Number.isFinite(o.value);
|
|
41
|
+
}
|
|
42
|
+
function validAnswers(questions, answers) {
|
|
43
|
+
if (answers === null || typeof answers !== "object")
|
|
44
|
+
return false;
|
|
45
|
+
const rec = answers;
|
|
46
|
+
return Object.entries(questions).every(([name, q]) => validAnswer(q, rec[name]));
|
|
47
|
+
}
|
|
48
|
+
/** #434/#435: the judgment port behind a memory + on-disk answer cache, counting what it asks. */
|
|
49
|
+
export class CachedJevAdvisor {
|
|
50
|
+
judge;
|
|
51
|
+
cacheDir;
|
|
52
|
+
usage;
|
|
53
|
+
#memory = new Map();
|
|
54
|
+
#asked = 0;
|
|
55
|
+
#cached = 0;
|
|
56
|
+
constructor(judge,
|
|
57
|
+
/** The answer cache directory; null: memory only (outside a project). */
|
|
58
|
+
cacheDir, usage) {
|
|
59
|
+
this.judge = judge;
|
|
60
|
+
this.cacheDir = cacheDir;
|
|
61
|
+
this.usage = usage;
|
|
62
|
+
}
|
|
63
|
+
async ask(state, questions) {
|
|
64
|
+
const key = contentHash({ v: JEV_CACHE_VERSION, state, questions });
|
|
65
|
+
const hit = this.#memory.get(key) ?? (await this.#read(key, questions));
|
|
66
|
+
if (hit !== undefined) {
|
|
67
|
+
this.#memory.set(key, hit);
|
|
68
|
+
this.#cached += Object.keys(questions).length;
|
|
69
|
+
return { answers: hit, cached: true };
|
|
70
|
+
}
|
|
71
|
+
this.#asked += Object.keys(questions).length;
|
|
72
|
+
const answers = await this.judge.systemOne({ state, questions });
|
|
73
|
+
if (!validAnswers(questions, answers))
|
|
74
|
+
throw new Error("the judgment answers do not match the questions asked");
|
|
75
|
+
this.#memory.set(key, answers);
|
|
76
|
+
await this.#write(key, answers);
|
|
77
|
+
return { answers, cached: false };
|
|
78
|
+
}
|
|
79
|
+
layer() {
|
|
80
|
+
const usage = this.usage.snapshot();
|
|
81
|
+
const { priceSource, missing, ...counts } = usage;
|
|
82
|
+
return {
|
|
83
|
+
status: "ran",
|
|
84
|
+
asked: this.#asked,
|
|
85
|
+
cached: this.#cached,
|
|
86
|
+
...(usage.judgments + usage.generations === 0
|
|
87
|
+
? {}
|
|
88
|
+
: { usage: { ...counts, ...(priceSource === undefined ? {} : { priceSource: [...priceSource] }), ...(missing === undefined ? {} : { missing: [...missing] }) } }),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async #read(key, questions) {
|
|
92
|
+
if (this.cacheDir === null)
|
|
93
|
+
return undefined;
|
|
94
|
+
let raw;
|
|
95
|
+
try {
|
|
96
|
+
raw = await readFile(join(this.cacheDir, `${key}.json`), "utf8");
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
if (typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT")
|
|
100
|
+
return undefined;
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
let parsed;
|
|
104
|
+
try {
|
|
105
|
+
parsed = JSON.parse(raw);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return undefined; // a torn entry is a miss: re-asked and rewritten
|
|
109
|
+
}
|
|
110
|
+
const answers = parsed?.version === JEV_CACHE_VERSION ? parsed.answers : undefined;
|
|
111
|
+
return validAnswers(questions, answers) ? answers : undefined;
|
|
112
|
+
}
|
|
113
|
+
async #write(key, answers) {
|
|
114
|
+
if (this.cacheDir === null)
|
|
115
|
+
return;
|
|
116
|
+
await mkdir(this.cacheDir, { recursive: true, mode: 0o700 });
|
|
117
|
+
const file = join(this.cacheDir, `${key}.json`);
|
|
118
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
119
|
+
await writeFile(tmp, `${JSON.stringify({ version: JEV_CACHE_VERSION, answers })}\n`, { mode: 0o600 });
|
|
120
|
+
await rename(tmp, file);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** The answer cache directory of a project data dir (`<.jevitate>/cache/jev`), or null outside a project. */
|
|
124
|
+
export function jevCacheDir(catalogDir) {
|
|
125
|
+
return catalogDir === null ? null : join(catalogDir, "cache", "jev");
|
|
126
|
+
}
|
|
127
|
+
/** The Jev layer's report when it did not run. */
|
|
128
|
+
export function skippedLayer(reason) {
|
|
129
|
+
return { status: "skipped", reason, asked: 0, cached: 0 };
|
|
130
|
+
}
|
|
131
|
+
export function jevLayerOf(setup) {
|
|
132
|
+
if (setup === undefined)
|
|
133
|
+
return skippedLayer(JEV_SKIPPED_PASS_REAL);
|
|
134
|
+
return "advisor" in setup ? setup.advisor.layer() : skippedLayer(setup.skipped);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* #434/#435: the Jev layer of one command. Without `--real`: skipped ("pass --real"). With it: an
|
|
138
|
+
* injected judge (tests) wins; else the live judgment gateway on the resolved Jev route
|
|
139
|
+
* (`--jev-provider`, else TypeSafe then OpenRouter) after the startup key check — or, when no
|
|
140
|
+
* judgment key is configured, skipped ("no judgment key"): the deterministic layer still runs.
|
|
141
|
+
* An unknown `--jev-provider` is refused (`GatewaySelectionError`), never ignored.
|
|
142
|
+
*/
|
|
143
|
+
export async function buildJevSetup(deps, opts) {
|
|
144
|
+
if (opts.real !== true)
|
|
145
|
+
return { skipped: JEV_SKIPPED_PASS_REAL };
|
|
146
|
+
const env = deps.explore?.env ?? process.env;
|
|
147
|
+
const usage = deps.explore?.usage ?? new UsageTracker(resolveUsagePricing(env));
|
|
148
|
+
if (deps.explore?.judge !== undefined)
|
|
149
|
+
return { advisor: new CachedJevAdvisor(deps.explore.judge, opts.cacheDir, usage) };
|
|
150
|
+
let provider;
|
|
151
|
+
try {
|
|
152
|
+
provider = jevProviderOverride(env, opts.jevProvider);
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
if (err instanceof JevProviderError)
|
|
156
|
+
throw new GatewaySelectionError(err.message);
|
|
157
|
+
throw err;
|
|
158
|
+
}
|
|
159
|
+
const store = envCredentialStore(env, deps.explore?.localConfig ?? loadLocalCredentials());
|
|
160
|
+
try {
|
|
161
|
+
resolveJevRoute(store, provider);
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
if (err instanceof MissingCredentialError)
|
|
165
|
+
return { skipped: JEV_SKIPPED_NO_KEY };
|
|
166
|
+
throw err;
|
|
167
|
+
}
|
|
168
|
+
await preflightRunKeys(["judgment"], store, { ...keyPreflightOpts(deps), ...(provider === undefined ? {} : { jevProvider: provider }) });
|
|
169
|
+
const judge = new RetryingJudgmentPort(new JevJudgmentGateway(store, await realJevClientCall(undefined, usage), provider));
|
|
170
|
+
return { advisor: new CachedJevAdvisor(judge, opts.cacheDir, usage) };
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=jev-advisor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jev-advisor.js","sourceRoot":"","sources":["../src/jev-advisor.ts"],"names":[],"mappings":"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"}
|
package/dist/journey-api.d.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import { type Journey } from "@jevitate/journey";
|
|
1
|
+
import { type Journey, type JourneyLintFinding } from "@jevitate/journey";
|
|
2
|
+
import { type ApprovalConfirm } from "./approval-provenance.js";
|
|
3
|
+
import type { JevSetup } from "./jev-advisor.js";
|
|
2
4
|
import { type RunPolicy } from "@jevitate/domain";
|
|
3
5
|
import { type BrowserPort, type BrowserSession, type EmulationSpec } from "@jevitate/playwright";
|
|
4
6
|
import { type BrowserRunOptions } from "./browser-run-options.js";
|
|
5
7
|
import { RecordingInterpreter, type StepObserver } from "@jevitate/interpreter";
|
|
6
|
-
import { type ReplayDeltaSummary } from "@jevitate/explore";
|
|
8
|
+
import { type BlockedWrite, type ReplayDeltaSummary } from "@jevitate/explore";
|
|
7
9
|
import { SecretPixelMask } from "./demo-capture.js";
|
|
8
10
|
import { type ScreenshotsResult, type ScreenshotsSpec } from "./run-screenshots.js";
|
|
9
11
|
import { type JourneyRunResult, type SelfHealer, type SiteGateDeps } from "@jevitate/runtime";
|
|
10
12
|
import { type FixtureRecord, type MissionFixtures } from "./mission-fixtures.js";
|
|
11
13
|
import { type ResolvedJourneyEnvironment } from "./environments.js";
|
|
14
|
+
import { type OutcomeCheckFailure } from "./journey-network-checks.js";
|
|
12
15
|
/**
|
|
13
16
|
* Distinct from `@jevitate/journey`'s `ParamValidationError` so CLI/API callers
|
|
14
17
|
* can tell "no such journey" apart from "params didn't match the journey's
|
|
@@ -23,6 +26,25 @@ export declare class UnknownJourneyError extends Error {
|
|
|
23
26
|
*/
|
|
24
27
|
export declare class JourneyRequiresAuthError extends Error {
|
|
25
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* #401: the Journey's assertions cannot prove its outcome (the `lintJourney` errors). `promote`
|
|
31
|
+
* refuses unless the reviewer accepts it with a non-empty `--accept-weak <reason>`.
|
|
32
|
+
*/
|
|
33
|
+
/** #432: `journey promote --reviewed-hash`: the Journey changed after the reviewer's sheet was produced. */
|
|
34
|
+
export declare class StaleReviewError extends Error {
|
|
35
|
+
readonly code = "E_JOURNEY_REVIEW_STALE";
|
|
36
|
+
}
|
|
37
|
+
export declare class WeakJourneyError extends Error {
|
|
38
|
+
readonly findings: readonly JourneyLintFinding[];
|
|
39
|
+
constructor(message: string, findings?: readonly JourneyLintFinding[]);
|
|
40
|
+
}
|
|
41
|
+
/** #401: the assertion-strength lint's verdict for one Journey (`journey lint`). */
|
|
42
|
+
export interface JourneyLintResult {
|
|
43
|
+
id: string;
|
|
44
|
+
findings: JourneyLintFinding[];
|
|
45
|
+
errors: number;
|
|
46
|
+
warnings: number;
|
|
47
|
+
}
|
|
26
48
|
export interface RunJourneyProgrammaticallyOptions {
|
|
27
49
|
/** Directory a `FsJourneyStore` reads Journey JSON files from. */
|
|
28
50
|
dir: string;
|
|
@@ -108,12 +130,55 @@ export interface RunJourneyProgrammaticallyOptions {
|
|
|
108
130
|
*/
|
|
109
131
|
actionDeltas?: boolean;
|
|
110
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* #402: how a mutation-proof replay (`journey verify --mutate`) differs from `journey run` — a
|
|
135
|
+
* separate argument, never a surface option: no other caller passes it.
|
|
136
|
+
*/
|
|
137
|
+
export interface MutationReplayOptions {
|
|
138
|
+
/**
|
|
139
|
+
* #402 `journey verify --mutate`: replay a MUTATED copy of the Journey. Applied after params are
|
|
140
|
+
* validated against the stored Journey (a mutation may stop a step reading a param), and never to
|
|
141
|
+
* an anchored prefix. Indices must be kept: a mutation changes a step, never removes one.
|
|
142
|
+
*/
|
|
143
|
+
mutateJourney?: (journey: Journey) => Journey;
|
|
144
|
+
/** #402: leave this flat step (0-based) out of the replay — `RecordingInterpreter`'s `skipStep`. */
|
|
145
|
+
skipStep?: (index: number) => boolean;
|
|
146
|
+
/**
|
|
147
|
+
* #402: ABORT the write requests (the #110 classifier, first-party only — `ReadOnlyGuard`) that
|
|
148
|
+
* start while this flat step (0-based) runs, until the network settles after it. Never answered,
|
|
149
|
+
* never sent; what was blocked comes back as `blockedWrites`.
|
|
150
|
+
*/
|
|
151
|
+
blockWritesAtStep?: number;
|
|
152
|
+
/** #402: also return the outcome checks' structured failures (`outcomeFailures`). */
|
|
153
|
+
structuredFailures?: boolean;
|
|
154
|
+
}
|
|
155
|
+
/** #402: what a mutation-proof replay returns beyond `journey run`'s result. */
|
|
156
|
+
export interface MutationReplayFields {
|
|
157
|
+
/** The step-request / end-state checks that failed (`structuredFailures`). */
|
|
158
|
+
outcomeFailures?: OutcomeCheckFailure[];
|
|
159
|
+
/** The writes aborted in `blockWritesAtStep`'s window. */
|
|
160
|
+
blockedWrites?: BlockedWrite[];
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* #246: a secret parameter's value (declared `secret: true`, or a credential-like name) never comes
|
|
164
|
+
* back in a run's output — the interpreter's vars start as the params, so the value is masked there.
|
|
165
|
+
*/
|
|
166
|
+
export declare function redactSecretParams<T>(result: T, journey: Journey, params: Record<string, string>): T;
|
|
167
|
+
/** Every string in `result` (deeply) with each secret, in each of its URL forms, masked. */
|
|
168
|
+
export declare function redactSecretValues<T>(result: T, secrets: readonly string[]): T;
|
|
111
169
|
/**
|
|
112
170
|
* #293: the params a Journey PREFIX runs with — a `--param` the whole Journey does not take is
|
|
113
171
|
* refused (`ParamValidationError`), one only a later step uses is dropped; the prefix's own required
|
|
114
172
|
* params are then checked by `validateParams` as usual.
|
|
115
173
|
*/
|
|
116
174
|
export declare function prefixParams(full: Journey, prefix: Journey, params: Record<string, string>): Record<string, string>;
|
|
175
|
+
/**
|
|
176
|
+
* #401: the assertion-strength lint (#401) for one Journey by id — an unknown id is refused,
|
|
177
|
+
* exactly as `journey run`/`promote` refuse one (`UnknownJourneyError`). Pure read; no browser.
|
|
178
|
+
*/
|
|
179
|
+
export declare function lintJourneyById(dir: string, id: string, opts?: {
|
|
180
|
+
readRequests?: readonly string[];
|
|
181
|
+
}): Promise<JourneyLintResult>;
|
|
117
182
|
/**
|
|
118
183
|
* Promotes a local Journey (#124, mirrors `promoteMissionTarget` in
|
|
119
184
|
* `mission-api.ts`) so it becomes discoverable via `journey find`/MCP
|
|
@@ -121,8 +186,40 @@ export declare function prefixParams(full: Journey, prefix: Journey, params: Rec
|
|
|
121
186
|
* same as `mission target promote`. An unknown id is refused with
|
|
122
187
|
* `UnknownJourneyError` (never silently created). Returns the persisted,
|
|
123
188
|
* now-promoted Journey.
|
|
189
|
+
*
|
|
190
|
+
* #401: lints first — a Journey whose assertions cannot prove its outcome is refused
|
|
191
|
+
* (`WeakJourneyError`) unless the reviewer accepts it with a non-empty `--accept-weak <reason>`,
|
|
192
|
+
* which is recorded on the Journey (`metadata.acceptedWeak`). Warnings never block.
|
|
193
|
+
*
|
|
194
|
+
* #432: records the approval (`metadata.approval`: the review hash, when, the waiver) and keeps a
|
|
195
|
+
* snapshot of the approved Journey (`.approved/<id>.json`). Given `reviewedHash` (what the reviewer's
|
|
196
|
+
* sheet showed), a Journey that changed since is refused (`StaleReviewError`) before anything else.
|
|
197
|
+
*
|
|
198
|
+
* #433: then the catalog gate (`journeyCatalogGate`): a Journey linking a job/persona that is not
|
|
199
|
+
* approved is refused (`UnvettedLinksError`) unless `acceptUnvetted` (recorded in
|
|
200
|
+
* `approval.waivers`), and the shared pre-approval findings are acknowledged (`acceptFindings`,
|
|
201
|
+
* recorded in `approval.acceptedFindings`) — the same pipeline `persona|job approve` and
|
|
202
|
+
* `demo approve` use. `catalogDir` (default: the project's `.jevitate/`) holds personas/jobs.
|
|
124
203
|
*/
|
|
125
|
-
export
|
|
204
|
+
export interface PromoteJourneyOptions {
|
|
205
|
+
acceptWeak?: string;
|
|
206
|
+
reviewedHash?: string;
|
|
207
|
+
acceptUnvetted?: string;
|
|
208
|
+
acceptFindings?: string;
|
|
209
|
+
/** The catalog's directory; undefined: the project's `.jevitate/` (none outside a project). */
|
|
210
|
+
catalogDir?: string | null;
|
|
211
|
+
/** Which approval path asks (`demo approve` promotes through here too). */
|
|
212
|
+
action?: "journey promote" | "demo approve";
|
|
213
|
+
/** #434/#435: the advisory Jev layer of the pre-approval readiness and analysis (`--real`), or why it is skipped. */
|
|
214
|
+
jev?: JevSetup;
|
|
215
|
+
/**
|
|
216
|
+
* #437: confirms the approval (after every gate, before anything is written) and returns its
|
|
217
|
+
* provenance — the CLI's `makeApprovalConfirm` (a typed confirmation on a TTY, the escape hatch,
|
|
218
|
+
* or the MCP channel). Omitted: recorded as `non-interactive` (`programmaticProvenance`).
|
|
219
|
+
*/
|
|
220
|
+
confirm?: ApprovalConfirm;
|
|
221
|
+
}
|
|
222
|
+
export declare function promoteJourney(dir: string, id: string, opts?: PromoteJourneyOptions): Promise<Journey>;
|
|
126
223
|
/**
|
|
127
224
|
* The one programmatic surface for "run this published Journey by id" —
|
|
128
225
|
* used by BOTH the CLI `journey run` action and external callers. Builds
|
|
@@ -132,8 +229,10 @@ export declare function promoteJourney(dir: string, id: string): Promise<Journey
|
|
|
132
229
|
* `ParamValidationError`, from `@jevitate/journey`) BEFORE any browser is
|
|
133
230
|
* launched, then builds the real Actor + `JourneyRunner` and runs.
|
|
134
231
|
*/
|
|
135
|
-
export declare function runJourneyProgrammatically(opts: RunJourneyProgrammaticallyOptions): Promise<JourneyRunResult & {
|
|
232
|
+
export declare function runJourneyProgrammatically(opts: RunJourneyProgrammaticallyOptions, mutation?: MutationReplayOptions): Promise<JourneyRunResult & {
|
|
136
233
|
fixtures?: FixtureRecord;
|
|
137
234
|
videoPaths?: string[];
|
|
138
235
|
actionDeltas?: ReplayDeltaSummary;
|
|
139
|
-
} & Partial<ScreenshotsResult
|
|
236
|
+
} & Partial<ScreenshotsResult> & MutationReplayFields>;
|
|
237
|
+
/** `err` with every secret (and its URL-encoded forms) masked in its message and stack — same object, same class. */
|
|
238
|
+
export declare function redactErrorSecrets(err: unknown, secrets: readonly string[]): unknown;
|
package/dist/journey-api.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import { FsJourneyStore, JourneyRegistry, ParamValidationError, deriveParamSchema, describeStep, flatJourneySteps, journeyPrefix, secretParamValues, validateParams } from "@jevitate/journey";
|
|
1
|
+
import { FsJourneyStore, JourneyRegistry, ParamValidationError, deriveParamSchema, describeStep, flatJourneySteps, journeyPrefix, lintJourney, secretParamValues, validateParams } from "@jevitate/journey";
|
|
2
|
+
import { programmaticProvenance } from "./approval-provenance.js";
|
|
3
|
+
import { journeyReviewHash } from "./journey-review.js";
|
|
4
|
+
import { writeApprovedSnapshot } from "./journey-review-store.js";
|
|
5
|
+
import { journeyCatalogGate, resolveCatalogDir } from "./catalog-api.js";
|
|
2
6
|
import { redactText } from "@jevitate/ai-core";
|
|
3
7
|
import { safeRunPolicy, clock } from "@jevitate/domain";
|
|
4
8
|
import { join } from "node:path";
|
|
@@ -8,7 +12,8 @@ import { artifactStamp } from "./mission-journal.js";
|
|
|
8
12
|
import { logsDirFor } from "./project-dir.js";
|
|
9
13
|
import { CastActor, BrowseTheWeb } from "@jevitate/screenplay";
|
|
10
14
|
import { RecordingInterpreter } from "@jevitate/interpreter";
|
|
11
|
-
import { ReplayDeltas, replayDeltaSummary } from "@jevitate/explore";
|
|
15
|
+
import { ReadOnlyGuard, ReplayDeltas, monitorFor, replayDeltaSummary } from "@jevitate/explore";
|
|
16
|
+
import { writeClassifier } from "@jevitate/recording";
|
|
12
17
|
import { BrowseTheWebToken } from "@jevitate/screenplay";
|
|
13
18
|
import { SecretPixelMask, maskingPort } from "./demo-capture.js";
|
|
14
19
|
import { RunScreenshots, composeObservers, screenshotObserver, screenshotsDirFor } from "./run-screenshots.js";
|
|
@@ -16,7 +21,7 @@ import { JourneyRunner } from "@jevitate/runtime";
|
|
|
16
21
|
import { gateJourney } from "./site-gate-cli.js";
|
|
17
22
|
import { substituteSetupRefs } from "./mission-fixtures.js";
|
|
18
23
|
import { applyJourneyEnvironment } from "./environments.js";
|
|
19
|
-
import {
|
|
24
|
+
import { JourneyOutcomeChecks } from "./journey-network-checks.js";
|
|
20
25
|
/**
|
|
21
26
|
* Distinct from `@jevitate/journey`'s `ParamValidationError` so CLI/API callers
|
|
22
27
|
* can tell "no such journey" apart from "params didn't match the journey's
|
|
@@ -31,12 +36,30 @@ export class UnknownJourneyError extends Error {
|
|
|
31
36
|
*/
|
|
32
37
|
export class JourneyRequiresAuthError extends Error {
|
|
33
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* #401: the Journey's assertions cannot prove its outcome (the `lintJourney` errors). `promote`
|
|
41
|
+
* refuses unless the reviewer accepts it with a non-empty `--accept-weak <reason>`.
|
|
42
|
+
*/
|
|
43
|
+
/** #432: `journey promote --reviewed-hash`: the Journey changed after the reviewer's sheet was produced. */
|
|
44
|
+
export class StaleReviewError extends Error {
|
|
45
|
+
code = "E_JOURNEY_REVIEW_STALE";
|
|
46
|
+
}
|
|
47
|
+
export class WeakJourneyError extends Error {
|
|
48
|
+
findings;
|
|
49
|
+
constructor(message, findings = []) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.findings = findings;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
34
54
|
/**
|
|
35
55
|
* #246: a secret parameter's value (declared `secret: true`, or a credential-like name) never comes
|
|
36
56
|
* back in a run's output — the interpreter's vars start as the params, so the value is masked there.
|
|
37
57
|
*/
|
|
38
|
-
function redactSecretParams(result, journey, params) {
|
|
39
|
-
|
|
58
|
+
export function redactSecretParams(result, journey, params) {
|
|
59
|
+
return redactSecretValues(result, secretParamValues(journey, params));
|
|
60
|
+
}
|
|
61
|
+
/** Every string in `result` (deeply) with each secret, in each of its URL forms, masked. */
|
|
62
|
+
export function redactSecretValues(result, secrets) {
|
|
40
63
|
if (secrets.length === 0)
|
|
41
64
|
return result;
|
|
42
65
|
const scrub = (v) => typeof v === "string"
|
|
@@ -62,23 +85,82 @@ export function prefixParams(full, prefix, params) {
|
|
|
62
85
|
return Object.fromEntries(Object.entries(params).filter(([k]) => needed.includes(k)));
|
|
63
86
|
}
|
|
64
87
|
/**
|
|
65
|
-
*
|
|
66
|
-
* `
|
|
67
|
-
* `find_capabilities` and runnable via `run_journey` — a human-approval gate,
|
|
68
|
-
* same as `mission target promote`. An unknown id is refused with
|
|
69
|
-
* `UnknownJourneyError` (never silently created). Returns the persisted,
|
|
70
|
-
* now-promoted Journey.
|
|
88
|
+
* #401: the assertion-strength lint (#401) for one Journey by id — an unknown id is refused,
|
|
89
|
+
* exactly as `journey run`/`promote` refuse one (`UnknownJourneyError`). Pure read; no browser.
|
|
71
90
|
*/
|
|
72
|
-
export async function
|
|
91
|
+
export async function lintJourneyById(dir, id, opts = {}) {
|
|
92
|
+
const registry = new JourneyRegistry(new FsJourneyStore(dir));
|
|
93
|
+
const journey = await registry.get(id);
|
|
94
|
+
if (!journey) {
|
|
95
|
+
throw new UnknownJourneyError(`unknown journey '${id}'`);
|
|
96
|
+
}
|
|
97
|
+
const findings = lintJourney(journey, opts.readRequests === undefined ? {} : { readRequests: opts.readRequests });
|
|
98
|
+
return {
|
|
99
|
+
id,
|
|
100
|
+
findings,
|
|
101
|
+
errors: findings.filter((f) => f.level === "error").length,
|
|
102
|
+
warnings: findings.filter((f) => f.level === "warning").length,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export async function promoteJourney(dir, id, opts = {}) {
|
|
73
106
|
const store = new FsJourneyStore(dir);
|
|
74
107
|
const registry = new JourneyRegistry(store);
|
|
75
108
|
const existing = await registry.get(id);
|
|
76
109
|
if (!existing) {
|
|
77
110
|
throw new UnknownJourneyError(`unknown journey '${id}'`);
|
|
78
111
|
}
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
112
|
+
// #432: approval binds to what the reviewer read — refused when the Journey changed since.
|
|
113
|
+
const contentHash = journeyReviewHash(existing);
|
|
114
|
+
if (opts.reviewedHash !== undefined && opts.reviewedHash.trim().toLowerCase() !== contentHash) {
|
|
115
|
+
throw new StaleReviewError(`journey '${id}' changed after its review sheet was produced (reviewed ${opts.reviewedHash.trim()}, now ${contentHash}) — review it again: jevitate journey review ${id}`);
|
|
116
|
+
}
|
|
117
|
+
const errors = lintJourney(existing).filter((f) => f.level === "error");
|
|
118
|
+
const reason = opts.acceptWeak?.trim() ?? "";
|
|
119
|
+
if (errors.length > 0 && reason === "") {
|
|
120
|
+
throw new WeakJourneyError([
|
|
121
|
+
`journey '${id}' has ${errors.length} assertion-strength error(s):`,
|
|
122
|
+
...errors.map((f) => f.message),
|
|
123
|
+
"strengthen the Journey or pass --accept-weak <reason>",
|
|
124
|
+
].join("\n"), errors);
|
|
125
|
+
}
|
|
126
|
+
const acceptedWeak = errors.length > 0 ? { reason, rules: [...new Set(errors.map((f) => f.rule))] } : undefined;
|
|
127
|
+
const gate = await journeyCatalogGate(existing, {
|
|
128
|
+
catalogDir: opts.catalogDir === undefined ? resolveCatalogDir(undefined) : opts.catalogDir,
|
|
129
|
+
journeysDir: dir,
|
|
130
|
+
action: opts.action ?? "journey promote",
|
|
131
|
+
...(opts.acceptUnvetted === undefined ? {} : { acceptUnvetted: opts.acceptUnvetted }),
|
|
132
|
+
...(opts.acceptFindings === undefined ? {} : { acceptFindings: opts.acceptFindings }),
|
|
133
|
+
...(opts.jev === undefined ? {} : { jev: opts.jev }),
|
|
134
|
+
});
|
|
135
|
+
// #437: the person confirms the approval and each waiver given with it (or it is refused) — then it is recorded with how it was made.
|
|
136
|
+
const provenance = opts.confirm === undefined
|
|
137
|
+
? programmaticProvenance()
|
|
138
|
+
: await opts.confirm({
|
|
139
|
+
kind: opts.action === "demo approve" ? "demo" : "journey",
|
|
140
|
+
id,
|
|
141
|
+
contentHash,
|
|
142
|
+
waivers: [
|
|
143
|
+
...(acceptedWeak === undefined ? [] : [{ flag: "--accept-weak", reason: acceptedWeak.reason, detail: acceptedWeak.rules.join(", ") }]),
|
|
144
|
+
...(gate.waivers ?? []).map((w) => ({ flag: "--accept-unvetted", reason: w.reason, detail: w.items.join(", ") })),
|
|
145
|
+
...(gate.acceptedFindings === undefined ? [] : [{ flag: "--accept-findings", reason: gate.acceptedFindings.reason, detail: gate.acceptedFindings.findings.join(", ") }]),
|
|
146
|
+
],
|
|
147
|
+
});
|
|
148
|
+
const approval = {
|
|
149
|
+
contentHash,
|
|
150
|
+
at: clock.nowIso(),
|
|
151
|
+
provenance,
|
|
152
|
+
...(acceptedWeak === undefined ? {} : { acceptedWeak: { ...acceptedWeak, provenance } }),
|
|
153
|
+
...(gate.waivers === undefined ? {} : { waivers: gate.waivers.map((w) => ({ ...w, provenance })) }),
|
|
154
|
+
...(gate.acceptedFindings === undefined ? {} : { acceptedFindings: { ...gate.acceptedFindings, provenance } }),
|
|
155
|
+
};
|
|
156
|
+
const promoted = {
|
|
157
|
+
...existing,
|
|
158
|
+
metadata: { ...existing.metadata, promoted: true, ...(acceptedWeak === undefined ? {} : { acceptedWeak }), approval },
|
|
159
|
+
};
|
|
160
|
+
await store.put(promoted);
|
|
161
|
+
// #432: the Journey as approved — the next review diffs against it ("change since last approval").
|
|
162
|
+
await writeApprovedSnapshot(dir, promoted);
|
|
163
|
+
return (await registry.get(id)) ?? promoted;
|
|
82
164
|
}
|
|
83
165
|
/**
|
|
84
166
|
* The one programmatic surface for "run this published Journey by id" —
|
|
@@ -89,7 +171,7 @@ export async function promoteJourney(dir, id) {
|
|
|
89
171
|
* `ParamValidationError`, from `@jevitate/journey`) BEFORE any browser is
|
|
90
172
|
* launched, then builds the real Actor + `JourneyRunner` and runs.
|
|
91
173
|
*/
|
|
92
|
-
export async function runJourneyProgrammatically(opts) {
|
|
174
|
+
export async function runJourneyProgrammatically(opts, mutation = {}) {
|
|
93
175
|
const store = new FsJourneyStore(opts.dir);
|
|
94
176
|
const registry = new JourneyRegistry(store);
|
|
95
177
|
const stored = await registry.get(opts.id);
|
|
@@ -113,6 +195,8 @@ export async function runJourneyProgrammatically(opts) {
|
|
|
113
195
|
// the cost (or risk) of opening a browser.
|
|
114
196
|
const inputParams = journey === full ? opts.params : prefixParams(full, journey, opts.params);
|
|
115
197
|
validateParams(deriveParamSchema(journey.recording), inputParams);
|
|
198
|
+
// #402: the mutated copy replays (and is judged); the stored Journey still names, redacts and gates.
|
|
199
|
+
const replayed = mutation.mutateJourney === undefined || journey !== full ? journey : mutation.mutateJourney(journey);
|
|
116
200
|
const policy = opts.policy ?? safeRunPolicy();
|
|
117
201
|
// The site policy (`jevitate site policy set <origin>`): pacing, throttles, budgets, quiet hours —
|
|
118
202
|
// decided before any fixture or browser; a refusal says why and when to retry.
|
|
@@ -169,33 +253,91 @@ export async function runJourneyProgrammatically(opts) {
|
|
|
169
253
|
try {
|
|
170
254
|
const actor = CastActor.named("cli-runner").whoCan(new BrowseTheWeb(session, allowedOrigins), ...gate.abilities);
|
|
171
255
|
const replayDeltas = opts.actionDeltas === true ? new ReplayDeltas({ secrets, recorded: flat.map((f) => f.recorded) }) : undefined;
|
|
172
|
-
|
|
173
|
-
|
|
256
|
+
// #322/#400: a full replay (never an anchored prefix) must also satisfy the Journey's end state
|
|
257
|
+
// and each step's request expectations.
|
|
258
|
+
const outcomeChecks = new JourneyOutcomeChecks(session.page, journey === full ? replayed : undefined, { secrets });
|
|
259
|
+
const blocker = mutation.blockWritesAtStep === undefined ? undefined : await stepWriteBlocker(session.page, mutation.blockWritesAtStep, allowedOrigins);
|
|
260
|
+
const observer = composeObservers(outcomeChecks.observer(), blocker?.observer, replayDeltas?.observer(), opts.observer, shots === undefined ? undefined : screenshotObserver(shots, (a) => a.ability(BrowseTheWebToken).session.page, whatOf));
|
|
261
|
+
const skipStep = mutation.skipStep;
|
|
262
|
+
const interpreter = opts.interpreter ??
|
|
263
|
+
(opts.observer === undefined && shots === undefined && replayDeltas === undefined && outcomeChecks.observer() === undefined && blocker === undefined && skipStep === undefined
|
|
264
|
+
? new RecordingInterpreter()
|
|
265
|
+
: new RecordingInterpreter({ observer, ...(skipStep === undefined ? {} : { skipStep: (i) => skipStep(i) }) }));
|
|
174
266
|
const runner = new JourneyRunner(actor, interpreter, undefined, undefined, opts.selfHealer);
|
|
175
267
|
let result;
|
|
176
268
|
try {
|
|
177
|
-
|
|
178
|
-
const networkChecks = journey === full ? journey.metadata.networkChecks : undefined;
|
|
179
|
-
result = redactSecretParams(await withNetworkChecks(session.page, networkChecks, () => runner.run({ journey, params, policy })), journey, params);
|
|
269
|
+
result = redactSecretParams(await outcomeChecks.run(actor, () => runner.run({ journey: replayed, params, policy })), journey, params);
|
|
180
270
|
}
|
|
181
271
|
finally {
|
|
182
272
|
await gate.done();
|
|
273
|
+
await blocker?.guard.disarm();
|
|
183
274
|
}
|
|
275
|
+
const mutationFields = {
|
|
276
|
+
...(mutation.structuredFailures === true ? { outcomeFailures: redactSecretParams([...outcomeChecks.lastFailures], journey, params) } : {}),
|
|
277
|
+
...(blocker === undefined ? {} : { blockedWrites: blocker.guard.drain() }),
|
|
278
|
+
};
|
|
184
279
|
const shotFields = shots === undefined ? {} : await shots.finish();
|
|
185
280
|
const deltaFields = replayDeltas === undefined ? {} : { actionDeltas: redactSecretParams(replayDeltaSummary(replayDeltas), journey, params) };
|
|
186
281
|
// #245: the context closed (its video finalized) before the result naming it is returned.
|
|
187
282
|
const videos = await finalizeVideos(videoDir, closeSession);
|
|
188
283
|
if (fx === undefined)
|
|
189
|
-
return { ...result, ...videos, ...shotFields, ...deltaFields };
|
|
284
|
+
return { ...result, ...videos, ...shotFields, ...deltaFields, ...mutationFields };
|
|
190
285
|
await fx.restore();
|
|
191
|
-
|
|
286
|
+
// #399: a fixture output passed in as a secret param (`--param t='${setup.t}'`) is redacted here too.
|
|
287
|
+
return { ...result, ...videos, ...shotFields, ...deltaFields, ...mutationFields, fixtures: redactSecretParams(fx.record(), journey, params) };
|
|
192
288
|
}
|
|
193
289
|
finally {
|
|
194
290
|
await closeSession();
|
|
195
291
|
}
|
|
196
292
|
}
|
|
293
|
+
catch (err) {
|
|
294
|
+
// #399: an error escaping the run (a crash, a closed page) may echo a navigated URL that
|
|
295
|
+
// carried a secret parameter — its message and stack are redacted, its class kept.
|
|
296
|
+
throw redactErrorSecrets(err, secretParamValues(journey, { ...inputParams, ...params }));
|
|
297
|
+
}
|
|
197
298
|
finally {
|
|
198
299
|
await fx?.restore();
|
|
199
300
|
}
|
|
200
301
|
}
|
|
302
|
+
/** How long one blocked step's window stays open for the writes its action triggers (network settle). */
|
|
303
|
+
const BLOCK_WINDOW_SETTLE_MS = 5_000;
|
|
304
|
+
/**
|
|
305
|
+
* #402 block-write: a `ReadOnlyGuard` armed on the page whose action window is ONLY the given step —
|
|
306
|
+
* opened as it begins, closed once the network settled after it. Every held write is aborted (a
|
|
307
|
+
* write navigation too: never answered with a status that could read as a success).
|
|
308
|
+
*/
|
|
309
|
+
async function stepWriteBlocker(page, index, allowedOrigins) {
|
|
310
|
+
const guard = new ReadOnlyGuard(writeClassifier({}), { allowlist: [...allowedOrigins], navigationWrites: "abort" });
|
|
311
|
+
await guard.arm(page);
|
|
312
|
+
const monitor = monitorFor(page);
|
|
313
|
+
await monitor.instrument();
|
|
314
|
+
return {
|
|
315
|
+
guard,
|
|
316
|
+
observer: {
|
|
317
|
+
beforeStep: async ({ index: i }) => {
|
|
318
|
+
if (i === index)
|
|
319
|
+
guard.beginAction();
|
|
320
|
+
},
|
|
321
|
+
afterStep: async ({ index: i }) => {
|
|
322
|
+
if (i !== index)
|
|
323
|
+
return;
|
|
324
|
+
try {
|
|
325
|
+
await monitor.waitSettled({ ceilingMs: BLOCK_WINDOW_SETTLE_MS });
|
|
326
|
+
}
|
|
327
|
+
finally {
|
|
328
|
+
guard.settled();
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
/** `err` with every secret (and its URL-encoded forms) masked in its message and stack — same object, same class. */
|
|
335
|
+
export function redactErrorSecrets(err, secrets) {
|
|
336
|
+
if (!(err instanceof Error) || secrets.length === 0)
|
|
337
|
+
return err;
|
|
338
|
+
err.message = redactText(err.message, secrets);
|
|
339
|
+
if (err.stack !== undefined)
|
|
340
|
+
err.stack = redactText(err.stack, secrets);
|
|
341
|
+
return err;
|
|
342
|
+
}
|
|
201
343
|
//# sourceMappingURL=journey-api.js.map
|