@jevitate/cli 0.26.1 → 0.27.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/contracts/jevitate-catalog/v2/manifest.json +4 -4
- package/contracts/jevitate-catalog/v2/schemas/journey.schema.json +528 -0
- package/dist/author-journey-cli.js +54 -3
- package/dist/author-journey-cli.js.map +1 -1
- package/dist/bin.js +950 -161
- package/dist/build-info.generated.js +3 -3
- package/dist/campaign-api.d.ts +5 -0
- package/dist/campaign-api.js +23 -2
- package/dist/campaign-api.js.map +1 -1
- package/dist/campaign-cli.js +8 -3
- package/dist/campaign-cli.js.map +1 -1
- package/dist/campaign-preflight.d.ts +35 -8
- package/dist/campaign-preflight.js +66 -7
- package/dist/campaign-preflight.js.map +1 -1
- package/dist/catalog-contract-identity.d.ts +1 -1
- package/dist/catalog-contract-identity.js +1 -1
- package/dist/check-plan.js +2 -1
- package/dist/check-plan.js.map +1 -1
- package/dist/explore-cli/options.js +1 -0
- package/dist/explore-cli/options.js.map +1 -1
- package/dist/explore-cli/shared.d.ts +1 -0
- package/dist/explore-cli/shared.js.map +1 -1
- package/dist/explore-goal.d.ts +3 -1
- package/dist/explore-goal.js +1 -0
- package/dist/explore-goal.js.map +1 -1
- package/dist/explore-specs.d.ts +2 -2
- package/dist/explore-specs.js +69 -7
- package/dist/explore-specs.js.map +1 -1
- package/dist/fixtures/multi-workspace-app.d.ts +35 -0
- package/dist/fixtures/multi-workspace-app.js +107 -0
- package/dist/fixtures/multi-workspace-app.js.map +1 -0
- package/dist/goal-run-flags.d.ts +2 -0
- package/dist/goal-run-flags.js +2 -0
- package/dist/goal-run-flags.js.map +1 -1
- package/dist/login-cli.d.ts +3 -0
- package/dist/login-cli.js +11 -2
- package/dist/login-cli.js.map +1 -1
- package/dist/mcp-cli-tools.js +4 -0
- package/dist/mcp-cli-tools.js.map +1 -1
- package/dist/multi-run.d.ts +7 -0
- package/dist/multi-run.js +30 -4
- package/dist/multi-run.js.map +1 -1
- package/dist/persona-login.d.ts +37 -0
- package/dist/persona-login.js +167 -4
- package/dist/persona-login.js.map +1 -1
- package/dist/suite-explore-options.d.ts +4 -0
- package/dist/suite-explore-options.js +1 -0
- package/dist/suite-explore-options.js.map +1 -1
- package/dist/target-config.d.ts +2 -0
- package/dist/target-config.js +7 -1
- package/dist/target-config.js.map +1 -1
- package/package.json +10 -10
package/dist/bin.js
CHANGED
|
@@ -26622,6 +26622,33 @@ function goalReasonOf(i) {
|
|
|
26622
26622
|
return "defects";
|
|
26623
26623
|
}
|
|
26624
26624
|
}
|
|
26625
|
+
var STARTUP_ATTEMPT_ERROR_CLASSES = [
|
|
26626
|
+
/** The host name did not resolve (`net::ERR_NAME_NOT_RESOLVED`, `ENOTFOUND`). */
|
|
26627
|
+
"dns",
|
|
26628
|
+
/** The host resolved but nothing accepted the connection (refused, reset, unreachable). */
|
|
26629
|
+
"connectivity",
|
|
26630
|
+
/** The navigation itself did not answer in time (`Timeout …ms exceeded`). */
|
|
26631
|
+
"navigation-timeout",
|
|
26632
|
+
/** The browser or its context could not be launched. */
|
|
26633
|
+
"browser-launch",
|
|
26634
|
+
/** The browser navigation failed although the origin answered the readiness probe. */
|
|
26635
|
+
"origin-readiness",
|
|
26636
|
+
/** No more specific cause could be attributed. */
|
|
26637
|
+
"unclassified"
|
|
26638
|
+
];
|
|
26639
|
+
var StartupAttemptSchema = external_exports.object({
|
|
26640
|
+
/** 0-based position in the attempt sequence. */
|
|
26641
|
+
index: external_exports.number().int().nonnegative(),
|
|
26642
|
+
/** The browser error class, or null when this attempt reached the target. */
|
|
26643
|
+
errorClass: external_exports.enum(STARTUP_ATTEMPT_ERROR_CLASSES).nullable(),
|
|
26644
|
+
/** Plain-words, redacted reason (empty for a successful attempt). */
|
|
26645
|
+
message: external_exports.string(),
|
|
26646
|
+
/** Epoch milliseconds the attempt began (from `clock.now()`). */
|
|
26647
|
+
startedAt: external_exports.number().int().nonnegative(),
|
|
26648
|
+
/** Epoch milliseconds the attempt ended. */
|
|
26649
|
+
endedAt: external_exports.number().int().nonnegative()
|
|
26650
|
+
});
|
|
26651
|
+
var StartupAttemptsSchema = external_exports.array(StartupAttemptSchema);
|
|
26625
26652
|
|
|
26626
26653
|
// ../domain/dist/journey-outcome.js
|
|
26627
26654
|
var JOURNEY_RUN_OUTCOMES = ["ok", "healed-pending-review", "heal-exhausted", "quarantined"];
|
|
@@ -27977,6 +28004,8 @@ function createResourceSignals(platform2 = process.platform, host = defaultHostS
|
|
|
27977
28004
|
// ../playwright/dist/reachability.js
|
|
27978
28005
|
import { connect as netConnect } from "node:net";
|
|
27979
28006
|
import { lookup as dnsLookup } from "node:dns/promises";
|
|
28007
|
+
import { get as httpGet } from "node:http";
|
|
28008
|
+
import { get as httpsGet } from "node:https";
|
|
27980
28009
|
var CHROMIUM_UNSAFE_PORTS = /* @__PURE__ */ new Set([
|
|
27981
28010
|
1,
|
|
27982
28011
|
7,
|
|
@@ -28060,7 +28089,7 @@ var CHROMIUM_UNSAFE_PORTS = /* @__PURE__ */ new Set([
|
|
|
28060
28089
|
10080
|
|
28061
28090
|
]);
|
|
28062
28091
|
var LOOPBACK = /^(localhost|127\.\d+\.\d+\.\d+|\[?::1\]?|0\.0\.0\.0)$/i;
|
|
28063
|
-
|
|
28092
|
+
function probeTarget(url2, deps) {
|
|
28064
28093
|
let u;
|
|
28065
28094
|
try {
|
|
28066
28095
|
u = new URL(url2);
|
|
@@ -28073,22 +28102,24 @@ async function probeReachable(url2, deps = {}) {
|
|
|
28073
28102
|
const port = u.port !== "" ? Number(u.port) : u.protocol === "https:" ? 443 : 80;
|
|
28074
28103
|
if (CHROMIUM_UNSAFE_PORTS.has(port))
|
|
28075
28104
|
return null;
|
|
28076
|
-
const where = `is the app running at ${u.origin}?`;
|
|
28077
28105
|
const loopback = LOOPBACK.test(u.hostname);
|
|
28078
|
-
|
|
28106
|
+
const timeoutMs = loopback ? deps.loopbackTimeoutMs ?? 3e3 : deps.remoteTimeoutMs ?? 1e4;
|
|
28107
|
+
return { host, port, origin: `${u.origin}/`, loopback, timeoutMs, where: `is the app running at ${u.origin}?` };
|
|
28108
|
+
}
|
|
28109
|
+
async function probeTransport(t, deps) {
|
|
28110
|
+
if (!t.loopback) {
|
|
28079
28111
|
try {
|
|
28080
|
-
await (deps.lookup ?? ((h) => dnsLookup(h)))(host);
|
|
28112
|
+
await (deps.lookup ?? ((h) => dnsLookup(h)))(t.host);
|
|
28081
28113
|
} catch (e) {
|
|
28082
28114
|
const code = e.code;
|
|
28083
28115
|
if (code === "ENOTFOUND" || code === "EAI_NONAME" || code === "EAI_FAIL")
|
|
28084
|
-
return `host not found (${host}) \u2014 ${where}
|
|
28085
|
-
return
|
|
28116
|
+
return { kind: "dns", detail: `host not found (${t.host}) \u2014 ${t.where}` };
|
|
28117
|
+
return { kind: "reachable" };
|
|
28086
28118
|
}
|
|
28087
28119
|
}
|
|
28088
|
-
const
|
|
28089
|
-
return new Promise((resolve25) => {
|
|
28120
|
+
const detail = await new Promise((resolve25) => {
|
|
28090
28121
|
let settled = false;
|
|
28091
|
-
const socket = (deps.connect ?? ((p, h) => netConnect(p, h)))(port, host);
|
|
28122
|
+
const socket = (deps.connect ?? ((p, h) => netConnect(p, h)))(t.port, t.host);
|
|
28092
28123
|
const done = (reason) => {
|
|
28093
28124
|
if (settled)
|
|
28094
28125
|
return;
|
|
@@ -28097,18 +28128,62 @@ async function probeReachable(url2, deps = {}) {
|
|
|
28097
28128
|
socket.destroy();
|
|
28098
28129
|
resolve25(reason);
|
|
28099
28130
|
};
|
|
28100
|
-
const timer = clock.setTimeout(() => done(loopback ? `connection refused (nothing answered a connect within ${Math.round(timeoutMs / 1e3)}s) \u2014 ${where}` : `no answer to a connect within ${Math.round(timeoutMs / 1e3)}s \u2014 ${where}`), timeoutMs);
|
|
28131
|
+
const timer = clock.setTimeout(() => done(t.loopback ? `connection refused (nothing answered a connect within ${Math.round(t.timeoutMs / 1e3)}s) \u2014 ${t.where}` : `no answer to a connect within ${Math.round(t.timeoutMs / 1e3)}s \u2014 ${t.where}`), t.timeoutMs);
|
|
28101
28132
|
socket.once("connect", () => done(null));
|
|
28102
28133
|
socket.once("error", (e) => {
|
|
28103
28134
|
if (e.code === "ECONNREFUSED")
|
|
28104
|
-
return done(`connection refused \u2014 ${where}`);
|
|
28135
|
+
return done(`connection refused \u2014 ${t.where}`);
|
|
28105
28136
|
if (e.code === "ENOTFOUND" || e.code === "EAI_NONAME")
|
|
28106
|
-
return done(`host not found (${host}) \u2014 ${where}`);
|
|
28137
|
+
return done(`host not found (${t.host}) \u2014 ${t.where}`);
|
|
28107
28138
|
if (e.code === "EHOSTUNREACH" || e.code === "ENETUNREACH")
|
|
28108
|
-
return done(`host unreachable (${host}) \u2014 ${where}`);
|
|
28139
|
+
return done(`host unreachable (${t.host}) \u2014 ${t.where}`);
|
|
28109
28140
|
done(null);
|
|
28110
28141
|
});
|
|
28111
28142
|
});
|
|
28143
|
+
return detail === null ? { kind: "reachable" } : { kind: "connect", detail };
|
|
28144
|
+
}
|
|
28145
|
+
async function probeReachable(url2, deps = {}) {
|
|
28146
|
+
const t = probeTarget(url2, deps);
|
|
28147
|
+
if (t === null)
|
|
28148
|
+
return null;
|
|
28149
|
+
const transport = await probeTransport(t, deps);
|
|
28150
|
+
return transport.kind === "reachable" ? null : transport.detail;
|
|
28151
|
+
}
|
|
28152
|
+
var READY_STATUS = (status) => status >= 200 && status < 400;
|
|
28153
|
+
function defaultHttpRequest(url2, timeoutMs) {
|
|
28154
|
+
return new Promise((resolve25) => {
|
|
28155
|
+
let settled = false;
|
|
28156
|
+
const get = new URL(url2).protocol === "https:" ? httpsGet : httpGet;
|
|
28157
|
+
let timer;
|
|
28158
|
+
const done = (result) => {
|
|
28159
|
+
if (settled)
|
|
28160
|
+
return;
|
|
28161
|
+
settled = true;
|
|
28162
|
+
clock.clearTimeout(timer);
|
|
28163
|
+
resolve25(result);
|
|
28164
|
+
};
|
|
28165
|
+
const req = get(url2, { method: "GET", headers: { accept: "*/*" } }, (res) => {
|
|
28166
|
+
res.resume();
|
|
28167
|
+
done({ status: res.statusCode ?? 0 });
|
|
28168
|
+
});
|
|
28169
|
+
timer = clock.setTimeout(() => {
|
|
28170
|
+
req.destroy();
|
|
28171
|
+
done({ error: `no response within ${Math.round(timeoutMs / 1e3)}s` });
|
|
28172
|
+
}, timeoutMs);
|
|
28173
|
+
req.once("error", (e) => done({ error: e.message.split("\n")[0] ?? e.message }));
|
|
28174
|
+
});
|
|
28175
|
+
}
|
|
28176
|
+
async function probeOriginReadiness(url2, deps = {}) {
|
|
28177
|
+
const t = probeTarget(url2, deps);
|
|
28178
|
+
if (t === null)
|
|
28179
|
+
return { class: "ready", detail: `${url2} is not an http(s) origin \u2014 not probed` };
|
|
28180
|
+
const transport = await probeTransport(t, deps);
|
|
28181
|
+
if (transport.kind !== "reachable")
|
|
28182
|
+
return { class: transport.kind, detail: transport.detail };
|
|
28183
|
+
const res = await (deps.httpRequest ?? defaultHttpRequest)(t.origin, t.timeoutMs);
|
|
28184
|
+
if ("error" in res)
|
|
28185
|
+
return { class: "http", detail: `no HTTP response (${res.error}) \u2014 ${t.where}` };
|
|
28186
|
+
return READY_STATUS(res.status) ? { class: "ready", status: res.status, detail: `origin answered HTTP ${res.status} \u2014 ${t.where}` } : { class: "http", status: res.status, detail: `origin answered HTTP ${res.status} (not ready) \u2014 ${t.where}` };
|
|
28112
28187
|
}
|
|
28113
28188
|
|
|
28114
28189
|
// ../playwright/dist/extensions.js
|
|
@@ -29372,6 +29447,7 @@ var PlaywrightBrowserPort = class {
|
|
|
29372
29447
|
#liveness;
|
|
29373
29448
|
#openTimeoutMs;
|
|
29374
29449
|
#probe;
|
|
29450
|
+
#originProbe;
|
|
29375
29451
|
#governor;
|
|
29376
29452
|
constructor(deps = {}) {
|
|
29377
29453
|
this.#governor = deps.governor;
|
|
@@ -29382,6 +29458,7 @@ var PlaywrightBrowserPort = class {
|
|
|
29382
29458
|
this.#platform = deps.platform ?? process.platform;
|
|
29383
29459
|
this.#pool = deps.pool;
|
|
29384
29460
|
this.#probe = deps.probe ?? ((url2) => probeReachable(url2));
|
|
29461
|
+
this.#originProbe = deps.originProbe ?? ((url2) => probeOriginReadiness(url2));
|
|
29385
29462
|
}
|
|
29386
29463
|
async open(opts) {
|
|
29387
29464
|
const emulation = resolveEmulation({ viewport: opts.viewport, device: opts.device });
|
|
@@ -29442,7 +29519,7 @@ var PlaywrightBrowserPort = class {
|
|
|
29442
29519
|
await lease.release().catch(() => void 0);
|
|
29443
29520
|
throw err;
|
|
29444
29521
|
}
|
|
29445
|
-
return pooledSession(lease, page, this.#watch(page), this.#probe, await videoPathOf(page), navGuard);
|
|
29522
|
+
return pooledSession(lease, page, this.#watch(page), this.#probe, this.#originProbe, await videoPathOf(page), navGuard);
|
|
29446
29523
|
}
|
|
29447
29524
|
/**
|
|
29448
29525
|
* A persistent-context session: the caller's `persistentProfile`, or (#256) a throwaway profile
|
|
@@ -29504,6 +29581,7 @@ var PlaywrightBrowserPort = class {
|
|
|
29504
29581
|
return JSON.stringify(await context.storageState());
|
|
29505
29582
|
},
|
|
29506
29583
|
probeReachable: this.#probe,
|
|
29584
|
+
probeOriginReadiness: this.#originProbe,
|
|
29507
29585
|
drainBlockedNavigations: () => navGuard?.drain() ?? [],
|
|
29508
29586
|
async close() {
|
|
29509
29587
|
watchdog?.stop();
|
|
@@ -29580,7 +29658,7 @@ function governed(session2, governor, ticket, limits) {
|
|
|
29580
29658
|
}
|
|
29581
29659
|
};
|
|
29582
29660
|
}
|
|
29583
|
-
function pooledSession(lease, page, watchdog, probe, videoPath, navGuard) {
|
|
29661
|
+
function pooledSession(lease, page, watchdog, probe, originProbe, videoPath, navGuard) {
|
|
29584
29662
|
const context = lease.context;
|
|
29585
29663
|
const alive = () => {
|
|
29586
29664
|
if (lease.crash !== void 0)
|
|
@@ -29607,6 +29685,7 @@ function pooledSession(lease, page, watchdog, probe, videoPath, navGuard) {
|
|
|
29607
29685
|
return JSON.stringify(await context.storageState());
|
|
29608
29686
|
},
|
|
29609
29687
|
probeReachable: probe,
|
|
29688
|
+
probeOriginReadiness: originProbe,
|
|
29610
29689
|
drainBlockedNavigations: () => navGuard?.drain() ?? [],
|
|
29611
29690
|
async close() {
|
|
29612
29691
|
watchdog?.stop();
|
|
@@ -32934,6 +33013,86 @@ function selectJsonValues(doc, path2) {
|
|
|
32934
33013
|
return cur.filter((v) => v !== void 0);
|
|
32935
33014
|
}
|
|
32936
33015
|
|
|
33016
|
+
// ../recording/dist/assertion-refs.js
|
|
33017
|
+
var SECRET_PARAM_NAME_RE = /pass(word|code|phrase)?|secret|token|api[_-]?key|otp|pin\b|credential|session|cookie/i;
|
|
33018
|
+
var PLACEHOLDER_RE = /\$\{([^}]*)\}/g;
|
|
33019
|
+
var E_ASSERTION_REF_UNBOUND = "E_ASSERTION_REF_UNBOUND";
|
|
33020
|
+
var E_ASSERTION_REF_SECRET = "E_ASSERTION_REF_SECRET";
|
|
33021
|
+
var E_ASSERTION_REF_TYPE = "E_ASSERTION_REF_TYPE";
|
|
33022
|
+
var AssertionRefError = class extends Error {
|
|
33023
|
+
code;
|
|
33024
|
+
ref;
|
|
33025
|
+
constructor(code, ref, detail) {
|
|
33026
|
+
super(`${code}: \${${ref}} ${detail}`);
|
|
33027
|
+
this.code = code;
|
|
33028
|
+
this.ref = ref;
|
|
33029
|
+
this.name = "AssertionRefError";
|
|
33030
|
+
}
|
|
33031
|
+
};
|
|
33032
|
+
function placeholderNames(text2) {
|
|
33033
|
+
return [...text2.matchAll(PLACEHOLDER_RE)].map((m) => m[1] ?? "");
|
|
33034
|
+
}
|
|
33035
|
+
function expandPlaceholders(text2, resolve25) {
|
|
33036
|
+
return text2.replace(PLACEHOLDER_RE, (_m, name) => resolve25(name));
|
|
33037
|
+
}
|
|
33038
|
+
function operands(a) {
|
|
33039
|
+
switch (a.kind) {
|
|
33040
|
+
case "textIncludes":
|
|
33041
|
+
return [a.text];
|
|
33042
|
+
case "valueEquals":
|
|
33043
|
+
return [a.value];
|
|
33044
|
+
case "attr":
|
|
33045
|
+
return a.value === void 0 ? [] : [a.value];
|
|
33046
|
+
case "urlIncludes":
|
|
33047
|
+
return [a.text];
|
|
33048
|
+
default:
|
|
33049
|
+
return [];
|
|
33050
|
+
}
|
|
33051
|
+
}
|
|
33052
|
+
function operandRefs(op) {
|
|
33053
|
+
return typeof op === "string" ? placeholderNames(op) : [op.var];
|
|
33054
|
+
}
|
|
33055
|
+
function assertionRefs(a) {
|
|
33056
|
+
return [...new Set(operands(a).flatMap(operandRefs))];
|
|
33057
|
+
}
|
|
33058
|
+
function describeOperand(op) {
|
|
33059
|
+
return typeof op === "string" ? op : `\${${op.var}}`;
|
|
33060
|
+
}
|
|
33061
|
+
function literalOperand(op) {
|
|
33062
|
+
if (typeof op === "string")
|
|
33063
|
+
return op;
|
|
33064
|
+
throw new AssertionRefError(E_ASSERTION_REF_UNBOUND, op.var, "was not resolved before the check ran");
|
|
33065
|
+
}
|
|
33066
|
+
function bound(name, lookup) {
|
|
33067
|
+
const b2 = lookup(name);
|
|
33068
|
+
if (b2 === void 0)
|
|
33069
|
+
throw new AssertionRefError(E_ASSERTION_REF_UNBOUND, name, "is not bound in this run (declare it as a parameter, or extract it in an earlier step)");
|
|
33070
|
+
if (b2.secret)
|
|
33071
|
+
throw new AssertionRefError(E_ASSERTION_REF_SECRET, name, "is a secret \u2014 an assertion's expected text is model-visible and persisted, so it may not use one");
|
|
33072
|
+
if (typeof b2.value !== "string")
|
|
33073
|
+
throw new AssertionRefError(E_ASSERTION_REF_TYPE, name, `is bound to a ${b2.value === null ? "null" : typeof b2.value}, not a string`);
|
|
33074
|
+
return b2.value;
|
|
33075
|
+
}
|
|
33076
|
+
function resolveOperand(op, lookup) {
|
|
33077
|
+
return typeof op === "string" ? expandPlaceholders(op, (n3) => bound(n3, lookup)) : bound(op.var, lookup);
|
|
33078
|
+
}
|
|
33079
|
+
function resolveAssertionRefs(a, lookup) {
|
|
33080
|
+
if (assertionRefs(a).length === 0)
|
|
33081
|
+
return a;
|
|
33082
|
+
switch (a.kind) {
|
|
33083
|
+
case "textIncludes":
|
|
33084
|
+
return { ...a, text: resolveOperand(a.text, lookup) };
|
|
33085
|
+
case "valueEquals":
|
|
33086
|
+
return { ...a, value: resolveOperand(a.value, lookup) };
|
|
33087
|
+
case "attr":
|
|
33088
|
+
return a.value === void 0 ? a : { ...a, value: resolveOperand(a.value, lookup) };
|
|
33089
|
+
case "urlIncludes":
|
|
33090
|
+
return { ...a, text: expandPlaceholders(a.text, (n3) => encodeUrlParamValue(bound(n3, lookup))) };
|
|
33091
|
+
default:
|
|
33092
|
+
return a;
|
|
33093
|
+
}
|
|
33094
|
+
}
|
|
33095
|
+
|
|
32937
33096
|
// ../recording/dist/schema.js
|
|
32938
33097
|
function targetDescriptorSelectorProblem(d) {
|
|
32939
33098
|
if (d.testId || d.role && d.name || d.label || d.text || d.css)
|
|
@@ -33261,8 +33420,33 @@ var StatusSpecSchema = external_exports.union([
|
|
|
33261
33420
|
]);
|
|
33262
33421
|
var METHOD = external_exports.string().min(1).max(20);
|
|
33263
33422
|
var PATH_GLOB = external_exports.string().min(1).max(2e3);
|
|
33264
|
-
var
|
|
33265
|
-
var
|
|
33423
|
+
var COUNT_BOUND = external_exports.number().int().min(0).max(1e3);
|
|
33424
|
+
var NetworkCheckCountSchema = external_exports.union([
|
|
33425
|
+
external_exports.object({ exact: COUNT_BOUND }).strict(),
|
|
33426
|
+
external_exports.object({ min: COUNT_BOUND }).strict(),
|
|
33427
|
+
external_exports.object({ max: COUNT_BOUND }).strict()
|
|
33428
|
+
]);
|
|
33429
|
+
var BodyFieldShapeSchema = external_exports.object({ name: external_exports.string().min(1).max(200), predicate: external_exports.enum(["exists", "absent", "string", "number", "boolean", "null", "array", "object"]) }).strict();
|
|
33430
|
+
var RequestBodyShapeSchema = external_exports.object({ fields: external_exports.array(BodyFieldShapeSchema).min(1).max(50) }).strict().superRefine((shape3, ctx) => {
|
|
33431
|
+
shape3.fields.forEach((field3, index) => {
|
|
33432
|
+
if (SECRET_PARAM_NAME_RE.test(field3.name)) {
|
|
33433
|
+
ctx.addIssue({
|
|
33434
|
+
code: "custom",
|
|
33435
|
+
message: `bodyShape field ${JSON.stringify(field3.name)} names a credential \u2014 a body shape declares field names only`,
|
|
33436
|
+
path: ["fields", index, "name"]
|
|
33437
|
+
});
|
|
33438
|
+
}
|
|
33439
|
+
});
|
|
33440
|
+
});
|
|
33441
|
+
var RequestMadeSchema = external_exports.object({ kind: external_exports.literal("requestMade"), method: METHOD, pathGlob: PATH_GLOB, count: NetworkCheckCountSchema.optional(), bodyShape: RequestBodyShapeSchema.optional() }).strict();
|
|
33442
|
+
var ResponseStatusSchema = external_exports.object({
|
|
33443
|
+
kind: external_exports.literal("responseStatus"),
|
|
33444
|
+
method: METHOD,
|
|
33445
|
+
pathGlob: PATH_GLOB,
|
|
33446
|
+
status: StatusSpecSchema,
|
|
33447
|
+
count: NetworkCheckCountSchema.optional(),
|
|
33448
|
+
bodyShape: RequestBodyShapeSchema.optional()
|
|
33449
|
+
}).strict();
|
|
33266
33450
|
var NetworkCheckSchema = external_exports.discriminatedUnion("kind", [RequestMadeSchema, ResponseStatusSchema]);
|
|
33267
33451
|
var HTTP_CHECK_TEXT = external_exports.string().min(1).max(2e3);
|
|
33268
33452
|
var HttpCheckUrlSchema = external_exports.string().min(1).max(4096).refine((url2) => /^https?:\/\//i.test(url2), { message: "http check url must be an absolute http(s):// URL" }).refine((url2) => !/[\t\n\r\s]/.test(url2), { message: "http check url must not contain whitespace" }).superRefine((url2, ctx) => {
|
|
@@ -35178,86 +35362,6 @@ function fixtureEscapesRoot(raw, root2) {
|
|
|
35178
35362
|
return { escapes: !isInsideRoot(real, realRoot), candidate, real };
|
|
35179
35363
|
}
|
|
35180
35364
|
|
|
35181
|
-
// ../recording/dist/assertion-refs.js
|
|
35182
|
-
var SECRET_PARAM_NAME_RE = /pass(word|code|phrase)?|secret|token|api[_-]?key|otp|pin\b|credential|session|cookie/i;
|
|
35183
|
-
var PLACEHOLDER_RE = /\$\{([^}]*)\}/g;
|
|
35184
|
-
var E_ASSERTION_REF_UNBOUND = "E_ASSERTION_REF_UNBOUND";
|
|
35185
|
-
var E_ASSERTION_REF_SECRET = "E_ASSERTION_REF_SECRET";
|
|
35186
|
-
var E_ASSERTION_REF_TYPE = "E_ASSERTION_REF_TYPE";
|
|
35187
|
-
var AssertionRefError = class extends Error {
|
|
35188
|
-
code;
|
|
35189
|
-
ref;
|
|
35190
|
-
constructor(code, ref, detail) {
|
|
35191
|
-
super(`${code}: \${${ref}} ${detail}`);
|
|
35192
|
-
this.code = code;
|
|
35193
|
-
this.ref = ref;
|
|
35194
|
-
this.name = "AssertionRefError";
|
|
35195
|
-
}
|
|
35196
|
-
};
|
|
35197
|
-
function placeholderNames(text2) {
|
|
35198
|
-
return [...text2.matchAll(PLACEHOLDER_RE)].map((m) => m[1] ?? "");
|
|
35199
|
-
}
|
|
35200
|
-
function expandPlaceholders(text2, resolve25) {
|
|
35201
|
-
return text2.replace(PLACEHOLDER_RE, (_m, name) => resolve25(name));
|
|
35202
|
-
}
|
|
35203
|
-
function operands(a) {
|
|
35204
|
-
switch (a.kind) {
|
|
35205
|
-
case "textIncludes":
|
|
35206
|
-
return [a.text];
|
|
35207
|
-
case "valueEquals":
|
|
35208
|
-
return [a.value];
|
|
35209
|
-
case "attr":
|
|
35210
|
-
return a.value === void 0 ? [] : [a.value];
|
|
35211
|
-
case "urlIncludes":
|
|
35212
|
-
return [a.text];
|
|
35213
|
-
default:
|
|
35214
|
-
return [];
|
|
35215
|
-
}
|
|
35216
|
-
}
|
|
35217
|
-
function operandRefs(op) {
|
|
35218
|
-
return typeof op === "string" ? placeholderNames(op) : [op.var];
|
|
35219
|
-
}
|
|
35220
|
-
function assertionRefs(a) {
|
|
35221
|
-
return [...new Set(operands(a).flatMap(operandRefs))];
|
|
35222
|
-
}
|
|
35223
|
-
function describeOperand(op) {
|
|
35224
|
-
return typeof op === "string" ? op : `\${${op.var}}`;
|
|
35225
|
-
}
|
|
35226
|
-
function literalOperand(op) {
|
|
35227
|
-
if (typeof op === "string")
|
|
35228
|
-
return op;
|
|
35229
|
-
throw new AssertionRefError(E_ASSERTION_REF_UNBOUND, op.var, "was not resolved before the check ran");
|
|
35230
|
-
}
|
|
35231
|
-
function bound(name, lookup) {
|
|
35232
|
-
const b2 = lookup(name);
|
|
35233
|
-
if (b2 === void 0)
|
|
35234
|
-
throw new AssertionRefError(E_ASSERTION_REF_UNBOUND, name, "is not bound in this run (declare it as a parameter, or extract it in an earlier step)");
|
|
35235
|
-
if (b2.secret)
|
|
35236
|
-
throw new AssertionRefError(E_ASSERTION_REF_SECRET, name, "is a secret \u2014 an assertion's expected text is model-visible and persisted, so it may not use one");
|
|
35237
|
-
if (typeof b2.value !== "string")
|
|
35238
|
-
throw new AssertionRefError(E_ASSERTION_REF_TYPE, name, `is bound to a ${b2.value === null ? "null" : typeof b2.value}, not a string`);
|
|
35239
|
-
return b2.value;
|
|
35240
|
-
}
|
|
35241
|
-
function resolveOperand(op, lookup) {
|
|
35242
|
-
return typeof op === "string" ? expandPlaceholders(op, (n3) => bound(n3, lookup)) : bound(op.var, lookup);
|
|
35243
|
-
}
|
|
35244
|
-
function resolveAssertionRefs(a, lookup) {
|
|
35245
|
-
if (assertionRefs(a).length === 0)
|
|
35246
|
-
return a;
|
|
35247
|
-
switch (a.kind) {
|
|
35248
|
-
case "textIncludes":
|
|
35249
|
-
return { ...a, text: resolveOperand(a.text, lookup) };
|
|
35250
|
-
case "valueEquals":
|
|
35251
|
-
return { ...a, value: resolveOperand(a.value, lookup) };
|
|
35252
|
-
case "attr":
|
|
35253
|
-
return a.value === void 0 ? a : { ...a, value: resolveOperand(a.value, lookup) };
|
|
35254
|
-
case "urlIncludes":
|
|
35255
|
-
return { ...a, text: expandPlaceholders(a.text, (n3) => encodeUrlParamValue(bound(n3, lookup))) };
|
|
35256
|
-
default:
|
|
35257
|
-
return a;
|
|
35258
|
-
}
|
|
35259
|
-
}
|
|
35260
|
-
|
|
35261
35365
|
// ../interpreter/dist/resolve-target.js
|
|
35262
35366
|
var ReplayTargetError = class extends Error {
|
|
35263
35367
|
kind;
|
|
@@ -39362,6 +39466,12 @@ function classifyRequest(r, apiPrefixes = []) {
|
|
|
39362
39466
|
}
|
|
39363
39467
|
return "other";
|
|
39364
39468
|
}
|
|
39469
|
+
function isDevServerAssetRead(r, apiPrefixes = []) {
|
|
39470
|
+
const method = r.method.toUpperCase();
|
|
39471
|
+
if (method !== "GET" && method !== "HEAD")
|
|
39472
|
+
return false;
|
|
39473
|
+
return classifyRequest({ url: r.url, resourceType: r.resourceType }, apiPrefixes) === "asset";
|
|
39474
|
+
}
|
|
39365
39475
|
var SLOWEST = 3;
|
|
39366
39476
|
function endpointOf(method, url2) {
|
|
39367
39477
|
return `${method.toUpperCase()} ${normalizeRoute(redactUrl(url2))}`;
|
|
@@ -39688,6 +39798,33 @@ function walk(out, key2, node2) {
|
|
|
39688
39798
|
}
|
|
39689
39799
|
}
|
|
39690
39800
|
}
|
|
39801
|
+
function predicateSatisfied(predicate, type) {
|
|
39802
|
+
if (predicate === "exists")
|
|
39803
|
+
return type !== void 0;
|
|
39804
|
+
if (predicate === "absent")
|
|
39805
|
+
return type === void 0;
|
|
39806
|
+
return type === predicate;
|
|
39807
|
+
}
|
|
39808
|
+
function bodyShapeMismatches(shape3, fields) {
|
|
39809
|
+
const types = /* @__PURE__ */ new Map();
|
|
39810
|
+
for (const f2 of fields)
|
|
39811
|
+
types.set(f2.name, f2.type);
|
|
39812
|
+
const out = [];
|
|
39813
|
+
for (const declared of shape3) {
|
|
39814
|
+
if (sensitive(declared.name))
|
|
39815
|
+
continue;
|
|
39816
|
+
const type = types.get(declared.name);
|
|
39817
|
+
if (predicateSatisfied(declared.predicate, type))
|
|
39818
|
+
continue;
|
|
39819
|
+
if (declared.predicate === "absent")
|
|
39820
|
+
out.push(`${declared.name} (expected absent, got ${type})`);
|
|
39821
|
+
else if (type === void 0)
|
|
39822
|
+
out.push(`${declared.name} (missing)`);
|
|
39823
|
+
else
|
|
39824
|
+
out.push(`${declared.name} (expected ${declared.predicate}, got ${type})`);
|
|
39825
|
+
}
|
|
39826
|
+
return out;
|
|
39827
|
+
}
|
|
39691
39828
|
function bodyValues(body, contentType) {
|
|
39692
39829
|
if (body === null || body === "" || body.length > MAX_BODY_CHARS)
|
|
39693
39830
|
return [];
|
|
@@ -39829,6 +39966,69 @@ var INSTRUMENT = `(() => {
|
|
|
39829
39966
|
} catch (e) { /* LCP not exposed by this browser */ }
|
|
39830
39967
|
})();`;
|
|
39831
39968
|
var MAX_CAPTURED = 2e4;
|
|
39969
|
+
var MAX_DECLARED_BODY_CHARS = 64e3;
|
|
39970
|
+
var MAX_DECLARED_FIELDS = 50;
|
|
39971
|
+
function declaredFieldType(value) {
|
|
39972
|
+
if (value === null)
|
|
39973
|
+
return "null";
|
|
39974
|
+
if (Array.isArray(value))
|
|
39975
|
+
return "array";
|
|
39976
|
+
if (typeof value === "string")
|
|
39977
|
+
return "string";
|
|
39978
|
+
if (typeof value === "number")
|
|
39979
|
+
return "number";
|
|
39980
|
+
if (typeof value === "boolean")
|
|
39981
|
+
return "boolean";
|
|
39982
|
+
return "object";
|
|
39983
|
+
}
|
|
39984
|
+
function pushDeclaredField(out, name, value) {
|
|
39985
|
+
if (out.length >= MAX_DECLARED_FIELDS || SECRET_PARAM_NAME_RE.test(name))
|
|
39986
|
+
return;
|
|
39987
|
+
out.push({ name, type: declaredFieldType(value) });
|
|
39988
|
+
}
|
|
39989
|
+
function walkDeclaredFields(out, path2, node2) {
|
|
39990
|
+
if (out.length >= MAX_DECLARED_FIELDS || node2 === null || typeof node2 !== "object")
|
|
39991
|
+
return;
|
|
39992
|
+
if (Array.isArray(node2)) {
|
|
39993
|
+
for (let i = 0; i < node2.length && out.length < MAX_DECLARED_FIELDS; i++) {
|
|
39994
|
+
const child = path2 === "" ? String(i) : `${path2}.${i}`;
|
|
39995
|
+
pushDeclaredField(out, child, node2[i]);
|
|
39996
|
+
walkDeclaredFields(out, child, node2[i]);
|
|
39997
|
+
}
|
|
39998
|
+
return;
|
|
39999
|
+
}
|
|
40000
|
+
for (const [key2, value] of Object.entries(node2)) {
|
|
40001
|
+
if (out.length >= MAX_DECLARED_FIELDS)
|
|
40002
|
+
return;
|
|
40003
|
+
const child = path2 === "" ? key2 : `${path2}.${key2}`;
|
|
40004
|
+
pushDeclaredField(out, child, value);
|
|
40005
|
+
walkDeclaredFields(out, child, value);
|
|
40006
|
+
}
|
|
40007
|
+
}
|
|
40008
|
+
function declaredFields(body, contentType) {
|
|
40009
|
+
if (body === null || body === "" || body.length > MAX_DECLARED_BODY_CHARS)
|
|
40010
|
+
return [];
|
|
40011
|
+
const type = (contentType ?? "").toLowerCase();
|
|
40012
|
+
const out = [];
|
|
40013
|
+
if (type.includes("application/x-www-form-urlencoded")) {
|
|
40014
|
+
for (const [key2] of new URLSearchParams(body)) {
|
|
40015
|
+
if (out.length >= MAX_DECLARED_FIELDS)
|
|
40016
|
+
break;
|
|
40017
|
+
if (out.some((f2) => f2.name === key2) || SECRET_PARAM_NAME_RE.test(key2))
|
|
40018
|
+
continue;
|
|
40019
|
+
out.push({ name: key2, type: "string" });
|
|
40020
|
+
}
|
|
40021
|
+
return out;
|
|
40022
|
+
}
|
|
40023
|
+
if (type === "" || type.includes("json")) {
|
|
40024
|
+
try {
|
|
40025
|
+
walkDeclaredFields(out, "", JSON.parse(body));
|
|
40026
|
+
} catch {
|
|
40027
|
+
return [];
|
|
40028
|
+
}
|
|
40029
|
+
}
|
|
40030
|
+
return out;
|
|
40031
|
+
}
|
|
39832
40032
|
var RequestCapture = class {
|
|
39833
40033
|
/**
|
|
39834
40034
|
* #617: every request that finished during the capture (its whole completion history), each
|
|
@@ -39944,9 +40144,15 @@ var PageMonitor = class {
|
|
|
39944
40144
|
#background = /* @__PURE__ */ new Map();
|
|
39945
40145
|
#ignore = () => false;
|
|
39946
40146
|
#longPollMs = DEFAULT_LONG_POLL_MS;
|
|
40147
|
+
/** #745: the target opted into the dev-server (Vite/HMR) settle policy. */
|
|
40148
|
+
#devServer = false;
|
|
40149
|
+
/** #745: `timing.apiPrefixes` — a configured API path is never read as a dev-server asset. */
|
|
40150
|
+
#apiPrefixes = [];
|
|
39947
40151
|
#documentNavStartedAt = null;
|
|
39948
40152
|
#instrumented;
|
|
39949
40153
|
#captures = /* @__PURE__ */ new Set();
|
|
40154
|
+
/** Consumers observing the declared field names and JSON types of each write request. */
|
|
40155
|
+
#declaredHooks = /* @__PURE__ */ new Set();
|
|
39950
40156
|
constructor(page, now2 = clock.now) {
|
|
39951
40157
|
this.#page = page;
|
|
39952
40158
|
this.#now = now2;
|
|
@@ -39977,6 +40183,19 @@ var PageMonitor = class {
|
|
|
39977
40183
|
if (this.#writeBodies.length > MAX_WRITE_BODIES)
|
|
39978
40184
|
this.#writeBodies.shift();
|
|
39979
40185
|
}
|
|
40186
|
+
if (this.#declaredHooks.size > 0) {
|
|
40187
|
+
let fields = [];
|
|
40188
|
+
try {
|
|
40189
|
+
fields = declaredFields(r.postData(), requestContentType2);
|
|
40190
|
+
} catch {
|
|
40191
|
+
}
|
|
40192
|
+
for (const hook of this.#declaredHooks) {
|
|
40193
|
+
try {
|
|
40194
|
+
hook({ method: r.method().toUpperCase(), url: redactUrl(r.url()), path: pathOf3(r.url()), fields });
|
|
40195
|
+
} catch {
|
|
40196
|
+
}
|
|
40197
|
+
}
|
|
40198
|
+
}
|
|
39980
40199
|
}
|
|
39981
40200
|
for (const c of this.#captures) {
|
|
39982
40201
|
c.began(r, {
|
|
@@ -40116,6 +40335,17 @@ var PageMonitor = class {
|
|
|
40116
40335
|
})();
|
|
40117
40336
|
return this.#instrumented;
|
|
40118
40337
|
}
|
|
40338
|
+
/**
|
|
40339
|
+
* Registers a per-request hook fed the declared field names and JSON types of each write's body
|
|
40340
|
+
* (never values; credential-named fields are dropped by `SECRET_PARAM_NAME_RE`). Returns an
|
|
40341
|
+
* unsubscribe function. Nothing is added to the capture's `CapturedRequest` records.
|
|
40342
|
+
*/
|
|
40343
|
+
onDeclaredFields(hook) {
|
|
40344
|
+
this.#declaredHooks.add(hook);
|
|
40345
|
+
return () => {
|
|
40346
|
+
this.#declaredHooks.delete(hook);
|
|
40347
|
+
};
|
|
40348
|
+
}
|
|
40119
40349
|
/** Starts keeping every request that finishes from now on (until `stopCapture`). */
|
|
40120
40350
|
startCapture() {
|
|
40121
40351
|
const c = new RequestCapture();
|
|
@@ -40126,10 +40356,17 @@ var PageMonitor = class {
|
|
|
40126
40356
|
stopCapture(capture2) {
|
|
40127
40357
|
this.#captures.delete(capture2);
|
|
40128
40358
|
}
|
|
40129
|
-
/**
|
|
40130
|
-
|
|
40359
|
+
/**
|
|
40360
|
+
* Applies the target's settle configuration (idempotent; the latest call wins). `apiPrefixes` is
|
|
40361
|
+
* `timing.apiPrefixes` (#745): perceive re-applies only the settle config before every wait, so
|
|
40362
|
+
* the prefixes a caller with the timing config sets here are kept rather than cleared.
|
|
40363
|
+
*/
|
|
40364
|
+
configure(cfg, apiPrefixes) {
|
|
40131
40365
|
this.#ignore = urlMatcher(cfg?.ignoreRequests);
|
|
40132
40366
|
this.#longPollMs = cfg?.longPollMs ?? DEFAULT_LONG_POLL_MS;
|
|
40367
|
+
this.#devServer = cfg?.devServer === true;
|
|
40368
|
+
if (apiPrefixes !== void 0)
|
|
40369
|
+
this.#apiPrefixes = apiPrefixes;
|
|
40133
40370
|
for (const [r] of this.#inflight)
|
|
40134
40371
|
if (this.#ignore(r.url()))
|
|
40135
40372
|
this.#background.set(r, "ignored");
|
|
@@ -40361,7 +40598,9 @@ var PageMonitor = class {
|
|
|
40361
40598
|
*/
|
|
40362
40599
|
async waitSettled(opts) {
|
|
40363
40600
|
await this.instrument();
|
|
40364
|
-
const
|
|
40601
|
+
const ignoreRequest = opts.ignoreRequest;
|
|
40602
|
+
const discountAssets = opts.discountAssets === true || this.#devServer;
|
|
40603
|
+
const ignore = ignoreRequest === void 0 && !discountAssets ? void 0 : (r) => (ignoreRequest?.(r) ?? false) || discountAssets && isDevServerAssetRead(r, this.#apiPrefixes);
|
|
40365
40604
|
const quietMs = opts.quietMs ?? SETTLE_QUIET_MS;
|
|
40366
40605
|
const start = this.#now();
|
|
40367
40606
|
const remaining = () => opts.ceilingMs - (this.#now() - start);
|
|
@@ -44048,6 +44287,22 @@ function describeUnreachable(message2, netErrorText) {
|
|
|
44048
44287
|
return "timed out before any response";
|
|
44049
44288
|
return messageOf(new Error(message2));
|
|
44050
44289
|
}
|
|
44290
|
+
function startupErrorClass(input2) {
|
|
44291
|
+
const text2 = `${input2.message}
|
|
44292
|
+
${input2.netErrorText ?? ""}`;
|
|
44293
|
+
if (input2.originReady === true && isUnreachableTarget(input2.message))
|
|
44294
|
+
return "origin-readiness";
|
|
44295
|
+
const code = netErrorCode(text2);
|
|
44296
|
+
if (code === "ERR_NAME_NOT_RESOLVED" || /ENOTFOUND|EAI_NONAME|EAI_FAIL/i.test(text2))
|
|
44297
|
+
return "dns";
|
|
44298
|
+
if (code === "ERR_CONNECTION_REFUSED" || code === "ERR_CONNECTION_RESET" || code === "ERR_CONNECTION_CLOSED" || code === "ERR_CONNECTION_TIMED_OUT" || code === "ERR_ADDRESS_UNREACHABLE" || /ECONNREFUSED|ECONNRESET|EHOSTUNREACH|ENETUNREACH/i.test(text2))
|
|
44299
|
+
return "connectivity";
|
|
44300
|
+
if (/Timeout \d+ms exceeded|timed out/i.test(text2))
|
|
44301
|
+
return "navigation-timeout";
|
|
44302
|
+
if (/browserType\.launch|Executable doesn't exist|Failed to launch/i.test(text2))
|
|
44303
|
+
return "browser-launch";
|
|
44304
|
+
return "unclassified";
|
|
44305
|
+
}
|
|
44051
44306
|
async function tryTriage(generation, input2) {
|
|
44052
44307
|
try {
|
|
44053
44308
|
const r = await generation.generate("triage.narrative", input2);
|
|
@@ -44189,6 +44444,7 @@ var MAX_QUIET_WAITS = 3;
|
|
|
44189
44444
|
var MAX_MOVING_SCROLLS = 12;
|
|
44190
44445
|
var LAST_CHANCE_NOTE = "no progress: the last steps left the page unchanged and you have seen the whole page \u2014 act on a visible control, report the answer, or say done/blocked now";
|
|
44191
44446
|
var LAST_CHANCE_NO_WRITE_NOTE = "no progress: the last steps left the page unchanged and you have seen the whole page, but no write of this run has succeeded yet \u2014 the goal asks for a change the app must receive: go back and press the control that submits it (Import, Save, Send\u2026), or say blocked if the app offers none";
|
|
44447
|
+
var NO_PROGRESS_RECOVERY_NOTE = (name, locator) => `no progress: the last steps left the page unchanged \u2014 the unique safe forward control ${JSON.stringify(name)} (${locator}) is the way on: act through it now, or the run ends`;
|
|
44192
44448
|
|
|
44193
44449
|
// ../explore/dist/run-depth.js
|
|
44194
44450
|
var OPEN_ENDED_MIN_ACTIONS = 12;
|
|
@@ -48201,6 +48457,7 @@ var PageDeltas = class {
|
|
|
48201
48457
|
// ../explore/dist/goal-loop/context/observation.js
|
|
48202
48458
|
function initObservation(ctx, cfg) {
|
|
48203
48459
|
ctx.timings = [];
|
|
48460
|
+
monitorFor(ctx.page).configure(cfg.settle, cfg.timingConfig?.apiPrefixes);
|
|
48204
48461
|
ctx.isWrite = writeClassifier(cfg.safety?.readRequests === void 0 ? {} : { readRequests: cfg.safety.readRequests });
|
|
48205
48462
|
ctx.safety = new SafetyPolicy(cfg.safety, { goal: cfg.goal });
|
|
48206
48463
|
ctx.firstParty = new FirstPartyOrigins(cfg.allowlist);
|
|
@@ -48700,6 +48957,13 @@ function takeRecoveryTurn(ctx) {
|
|
|
48700
48957
|
turn.pending = false;
|
|
48701
48958
|
return turn.safe;
|
|
48702
48959
|
}
|
|
48960
|
+
function armRecoveryTurn(ctx, safe) {
|
|
48961
|
+
const turn = RECOVERY_TURNS.get(ctx);
|
|
48962
|
+
if (turn !== void 0 && turn.given)
|
|
48963
|
+
return false;
|
|
48964
|
+
RECOVERY_TURNS.set(ctx, { safe, given: true, pending: true });
|
|
48965
|
+
return true;
|
|
48966
|
+
}
|
|
48703
48967
|
function resumeIdentity(e) {
|
|
48704
48968
|
return JSON.stringify(e.resume);
|
|
48705
48969
|
}
|
|
@@ -48725,14 +48989,15 @@ function instructionText(controls, visibleText) {
|
|
|
48725
48989
|
}
|
|
48726
48990
|
return text2;
|
|
48727
48991
|
}
|
|
48728
|
-
function
|
|
48992
|
+
function forwardControls(ctx, step) {
|
|
48729
48993
|
const goalTerms = terms(ctx.goalText);
|
|
48730
48994
|
if (goalTerms.size === 0)
|
|
48731
|
-
return
|
|
48995
|
+
return [];
|
|
48732
48996
|
const instruction = instructionText(step.modelControls, step.visibleText);
|
|
48733
48997
|
const instructionTerms = terms(instruction);
|
|
48734
48998
|
if (instructionTerms.size === 0 || !DIRECTIVE.test(instruction))
|
|
48735
|
-
return
|
|
48999
|
+
return [];
|
|
49000
|
+
const out = [];
|
|
48736
49001
|
for (const control of step.modelControls) {
|
|
48737
49002
|
if (!control.enabled)
|
|
48738
49003
|
continue;
|
|
@@ -48745,9 +49010,12 @@ function primaryForwardControl(ctx, step) {
|
|
|
48745
49010
|
if (controlTerms.size === 0)
|
|
48746
49011
|
continue;
|
|
48747
49012
|
if ([...controlTerms].some((t) => goalTerms.has(t)) && [...controlTerms].every((t) => instructionTerms.has(t)))
|
|
48748
|
-
|
|
49013
|
+
out.push(control);
|
|
48749
49014
|
}
|
|
48750
|
-
return
|
|
49015
|
+
return out;
|
|
49016
|
+
}
|
|
49017
|
+
function primaryForwardControl(ctx, step) {
|
|
49018
|
+
return forwardControls(ctx, step)[0] ?? null;
|
|
48751
49019
|
}
|
|
48752
49020
|
async function handleBlocked(ctx, step) {
|
|
48753
49021
|
const { modelControls, record: record3 } = step;
|
|
@@ -48822,15 +49090,14 @@ async function handleBlocked(ctx, step) {
|
|
|
48822
49090
|
}
|
|
48823
49091
|
const evidence = await collectActionableControlEvidence(ctx.page, { screenshots: false }).catch(() => []);
|
|
48824
49092
|
const safe = evidence.filter((e) => e.kind === "safe" && e.name !== "");
|
|
48825
|
-
|
|
48826
|
-
if ((turn === void 0 || !turn.given) && safe.length > 0) {
|
|
48827
|
-
RECOVERY_TURNS.set(ctx, { safe, given: true, pending: true });
|
|
49093
|
+
if (safe.length > 0 && armRecoveryTurn(ctx, safe)) {
|
|
48828
49094
|
const note = `blocked: one recovery turn is available through the safe control(s) ${safe.map((e) => `${quote(e.name, 40)} (${e.locator})`).join(", ")} \u2014 act through one of them or the run ends blocked`;
|
|
48829
49095
|
ctx.history.push(note);
|
|
48830
49096
|
record3(false, note, { origin: "engine" });
|
|
48831
49097
|
return "continue";
|
|
48832
49098
|
}
|
|
48833
49099
|
record3(true, "model blocked");
|
|
49100
|
+
const turn = RECOVERY_TURNS.get(ctx);
|
|
48834
49101
|
const turnSafe = turn !== void 0 && turn.given && turn.safe.length > 0 ? turn.safe : null;
|
|
48835
49102
|
const excluded = evidence.filter((e) => e.kind !== "safe");
|
|
48836
49103
|
const classes = [...new Set(excluded.map((e) => e.kind))].join(", ");
|
|
@@ -50378,7 +50645,8 @@ async function checkProgress(ctx, step) {
|
|
|
50378
50645
|
if (!ctx.lastChanceGiven) {
|
|
50379
50646
|
ctx.lastChanceGiven = true;
|
|
50380
50647
|
ctx.lastChanceTurn = true;
|
|
50381
|
-
|
|
50648
|
+
const targeted = await targetedRecoveryNote(ctx, snap, text2);
|
|
50649
|
+
ctx.history.push(targeted ?? (ctx.writeGoal && !ctx.wroteOk ? LAST_CHANCE_NO_WRITE_NOTE : LAST_CHANCE_NOTE));
|
|
50382
50650
|
} else {
|
|
50383
50651
|
ctx.stop = "no-progress";
|
|
50384
50652
|
return "stop";
|
|
@@ -50396,6 +50664,19 @@ async function checkProgress(ctx, step) {
|
|
|
50396
50664
|
ctx.seen.add(snap.signature);
|
|
50397
50665
|
return "next";
|
|
50398
50666
|
}
|
|
50667
|
+
async function targetedRecoveryNote(ctx, snap, text2) {
|
|
50668
|
+
const evidence = await collectActionableControlEvidence(ctx.page, { screenshots: false }).catch(() => []);
|
|
50669
|
+
const safeNames = new Set(evidence.filter((e) => e.kind === "safe" && e.name !== "").map((e) => e.name));
|
|
50670
|
+
if (safeNames.size === 0)
|
|
50671
|
+
return null;
|
|
50672
|
+
const forward = forwardControls(ctx, { snap, modelControls: snap.controls, visibleText: text2 }).filter((c) => safeNames.has(c.name.trim()));
|
|
50673
|
+
if (forward.length !== 1)
|
|
50674
|
+
return null;
|
|
50675
|
+
const target = evidence.find((e) => e.kind === "safe" && e.name === forward[0].name.trim());
|
|
50676
|
+
if (target === void 0 || !armRecoveryTurn(ctx, [target]))
|
|
50677
|
+
return null;
|
|
50678
|
+
return NO_PROGRESS_RECOVERY_NOTE(target.name, target.locator);
|
|
50679
|
+
}
|
|
50399
50680
|
async function noteCycle(ctx, snap, text2) {
|
|
50400
50681
|
const step = ctx.cycleAction;
|
|
50401
50682
|
ctx.cycleAction = null;
|
|
@@ -50844,6 +51125,99 @@ async function finishRun(ctx) {
|
|
|
50844
51125
|
};
|
|
50845
51126
|
}
|
|
50846
51127
|
|
|
51128
|
+
// ../explore/dist/startup-retry.js
|
|
51129
|
+
var DEFAULT_STARTUP_RETRY = Object.freeze({
|
|
51130
|
+
maxAttempts: 3,
|
|
51131
|
+
perAttemptTimeoutMs: 15e3,
|
|
51132
|
+
backoffMs: 500
|
|
51133
|
+
});
|
|
51134
|
+
var STARTUP_RETRY_BUDGET_MS = 6e4;
|
|
51135
|
+
function startupRetryBudgetMs(policy) {
|
|
51136
|
+
return policy.maxAttempts * policy.perAttemptTimeoutMs + (policy.maxAttempts - 1) * policy.backoffMs;
|
|
51137
|
+
}
|
|
51138
|
+
var StartupRetryExhaustedError = class extends Error {
|
|
51139
|
+
name = "StartupRetryExhaustedError";
|
|
51140
|
+
attempts;
|
|
51141
|
+
cause;
|
|
51142
|
+
constructor(cause, attempts) {
|
|
51143
|
+
super(cause instanceof Error ? cause.message : String(cause));
|
|
51144
|
+
this.attempts = attempts;
|
|
51145
|
+
this.cause = cause;
|
|
51146
|
+
}
|
|
51147
|
+
};
|
|
51148
|
+
function messageOf2(e) {
|
|
51149
|
+
return e instanceof Error ? e.message.split("\n")[0] ?? e.message : String(e);
|
|
51150
|
+
}
|
|
51151
|
+
function defaultNavigate(actor, url2, timeoutMs) {
|
|
51152
|
+
return async () => {
|
|
51153
|
+
await actor.ability(BrowseTheWebToken).session.page.goto(url2, { timeout: timeoutMs });
|
|
51154
|
+
};
|
|
51155
|
+
}
|
|
51156
|
+
var attemptsByActor = /* @__PURE__ */ new WeakMap();
|
|
51157
|
+
function startupAttemptsFor(actor) {
|
|
51158
|
+
return attemptsByActor.get(actor) ?? [];
|
|
51159
|
+
}
|
|
51160
|
+
async function navigateWithStartupRetry(actor, url2, options = {}) {
|
|
51161
|
+
const policy = { ...DEFAULT_STARTUP_RETRY, ...options.policy };
|
|
51162
|
+
if (!Number.isInteger(policy.maxAttempts) || policy.maxAttempts < 1) {
|
|
51163
|
+
throw new RangeError(`startup retry maxAttempts must be a positive integer, got ${JSON.stringify(policy.maxAttempts)}`);
|
|
51164
|
+
}
|
|
51165
|
+
const budget = startupRetryBudgetMs(policy);
|
|
51166
|
+
if (budget > STARTUP_RETRY_BUDGET_MS) {
|
|
51167
|
+
throw new RangeError(`startup retry worst case ${budget}ms exceeds the ${STARTUP_RETRY_BUDGET_MS}ms bound`);
|
|
51168
|
+
}
|
|
51169
|
+
const session2 = tryAbility(actor, BrowseTheWebToken)?.session;
|
|
51170
|
+
const page = session2?.page;
|
|
51171
|
+
const probe = options.probeOriginReadiness ?? session2?.probeOriginReadiness?.bind(session2);
|
|
51172
|
+
const navigate = options.navigate ?? defaultNavigate(actor, url2, policy.perAttemptTimeoutMs);
|
|
51173
|
+
let netErrorText = null;
|
|
51174
|
+
const onRequestFailed = (req) => {
|
|
51175
|
+
const text2 = req.failure()?.errorText;
|
|
51176
|
+
if (text2 !== void 0)
|
|
51177
|
+
netErrorText = text2;
|
|
51178
|
+
};
|
|
51179
|
+
page?.on("requestfailed", onRequestFailed);
|
|
51180
|
+
const attempts = [];
|
|
51181
|
+
attemptsByActor.set(actor, attempts);
|
|
51182
|
+
const record3 = (attempt2) => {
|
|
51183
|
+
attempts.push(attempt2);
|
|
51184
|
+
options.onAttempt?.(attempt2);
|
|
51185
|
+
};
|
|
51186
|
+
try {
|
|
51187
|
+
for (let index = 0; index < policy.maxAttempts; index += 1) {
|
|
51188
|
+
netErrorText = null;
|
|
51189
|
+
const startedAt = clock.now();
|
|
51190
|
+
try {
|
|
51191
|
+
await navigate();
|
|
51192
|
+
record3({ index, errorClass: null, message: "", startedAt, endedAt: clock.now() });
|
|
51193
|
+
return attempts;
|
|
51194
|
+
} catch (e) {
|
|
51195
|
+
const message2 = messageOf2(e);
|
|
51196
|
+
if (peekBlockedDeparture(actor) !== void 0) {
|
|
51197
|
+
record3({ index, errorClass: "unclassified", message: describeUnreachable(message2, netErrorText), startedAt, endedAt: clock.now() });
|
|
51198
|
+
throw e;
|
|
51199
|
+
}
|
|
51200
|
+
let originReady = false;
|
|
51201
|
+
if (probe !== void 0) {
|
|
51202
|
+
try {
|
|
51203
|
+
originReady = (await probe(url2)).class === "ready";
|
|
51204
|
+
} catch {
|
|
51205
|
+
originReady = false;
|
|
51206
|
+
}
|
|
51207
|
+
}
|
|
51208
|
+
const errorClass = startupErrorClass({ message: message2, netErrorText, originReady });
|
|
51209
|
+
record3({ index, errorClass, message: describeUnreachable(message2, netErrorText), startedAt, endedAt: clock.now() });
|
|
51210
|
+
if (index === policy.maxAttempts - 1)
|
|
51211
|
+
throw new StartupRetryExhaustedError(e, attempts);
|
|
51212
|
+
await clock.sleep(policy.backoffMs);
|
|
51213
|
+
}
|
|
51214
|
+
}
|
|
51215
|
+
return attempts;
|
|
51216
|
+
} finally {
|
|
51217
|
+
page?.off("requestfailed", onRequestFailed);
|
|
51218
|
+
}
|
|
51219
|
+
}
|
|
51220
|
+
|
|
50847
51221
|
// ../explore/dist/goal-loop/start.js
|
|
50848
51222
|
async function openRun(ctx) {
|
|
50849
51223
|
const { cfg } = ctx;
|
|
@@ -50855,7 +51229,7 @@ async function openRun(ctx) {
|
|
|
50855
51229
|
try {
|
|
50856
51230
|
if (cfg.startInPlace !== true) {
|
|
50857
51231
|
await assertSeedReachable(cfg.actor, cfg.startUrl);
|
|
50858
|
-
await
|
|
51232
|
+
await navigateWithStartupRetry(cfg.actor, cfg.startUrl, cfg.startupRetry === void 0 ? {} : { policy: cfg.startupRetry });
|
|
50859
51233
|
}
|
|
50860
51234
|
} catch (e) {
|
|
50861
51235
|
const message2 = firstLine(e);
|
|
@@ -51195,13 +51569,31 @@ function describeCheck(c) {
|
|
|
51195
51569
|
case "reloadThen":
|
|
51196
51570
|
return `reloadThen:${describeAssertionSpec(c.assertion)}`;
|
|
51197
51571
|
case "requestMade":
|
|
51198
|
-
return `requestMade:${c.method} ${c.pathGlob}`;
|
|
51199
51572
|
case "responseStatus":
|
|
51200
|
-
return
|
|
51573
|
+
return describeNetworkCheck(c);
|
|
51201
51574
|
case "http":
|
|
51202
51575
|
return describeHttpCheck(c);
|
|
51203
51576
|
}
|
|
51204
51577
|
}
|
|
51578
|
+
function describeCount(count) {
|
|
51579
|
+
if ("exact" in count)
|
|
51580
|
+
return `exact:${count.exact}`;
|
|
51581
|
+
if ("min" in count)
|
|
51582
|
+
return `min:${count.min}`;
|
|
51583
|
+
return `max:${count.max}`;
|
|
51584
|
+
}
|
|
51585
|
+
function describeBodyShape(shape3) {
|
|
51586
|
+
return shape3.fields.map((f2) => `${f2.name}:${f2.predicate}`).join(",");
|
|
51587
|
+
}
|
|
51588
|
+
function describeNetworkCheck(c) {
|
|
51589
|
+
const base = c.kind === "requestMade" ? `requestMade:${c.method} ${c.pathGlob}` : `responseStatus:${c.method} ${c.pathGlob}=${describeStatus2(c.status)}`;
|
|
51590
|
+
const extras = [];
|
|
51591
|
+
if (c.count !== void 0)
|
|
51592
|
+
extras.push(`count=${describeCount(c.count)}`);
|
|
51593
|
+
if (c.bodyShape !== void 0)
|
|
51594
|
+
extras.push(`body=${describeBodyShape(c.bodyShape)}`);
|
|
51595
|
+
return [base, ...extras].join("|");
|
|
51596
|
+
}
|
|
51205
51597
|
function escapeHttpValue(v) {
|
|
51206
51598
|
return v.replace(/\\/g, "\\\\").replace(/\|/g, "\\|");
|
|
51207
51599
|
}
|
|
@@ -51222,6 +51614,13 @@ function describeHttpCheck(c) {
|
|
|
51222
51614
|
function statusMatches(status, spec) {
|
|
51223
51615
|
return "class" in spec ? Math.floor(status / 100) === spec.class : status === spec.code;
|
|
51224
51616
|
}
|
|
51617
|
+
function countHolds(count, n3) {
|
|
51618
|
+
if ("exact" in count)
|
|
51619
|
+
return n3 === count.exact;
|
|
51620
|
+
if ("min" in count)
|
|
51621
|
+
return n3 >= count.min;
|
|
51622
|
+
return n3 <= count.max;
|
|
51623
|
+
}
|
|
51225
51624
|
function matching(requests, method, pathGlob2) {
|
|
51226
51625
|
const m = method.toUpperCase();
|
|
51227
51626
|
return requests.filter((r) => (m === "*" || r.method === m) && matchGlob(pathGlob2, r.path));
|
|
@@ -51272,7 +51671,24 @@ function nearMissHint(requests, method, pathGlob2) {
|
|
|
51272
51671
|
return null;
|
|
51273
51672
|
return `saw ${top.method} ${top.path} \u2192 ${top.status} (${top.count}\xD7)`;
|
|
51274
51673
|
}
|
|
51275
|
-
function
|
|
51674
|
+
function declaredMatching(declared, method, pathGlob2) {
|
|
51675
|
+
const m = method.toUpperCase();
|
|
51676
|
+
return declared.filter((d) => (m === "*" || d.method === m) && matchGlob(pathGlob2, d.path));
|
|
51677
|
+
}
|
|
51678
|
+
function describeCountExpectation(count) {
|
|
51679
|
+
if ("exact" in count)
|
|
51680
|
+
return `exactly ${count.exact}`;
|
|
51681
|
+
if ("min" in count)
|
|
51682
|
+
return `at least ${count.min}`;
|
|
51683
|
+
return `at most ${count.max}`;
|
|
51684
|
+
}
|
|
51685
|
+
function missingBodyFields(shape3, candidates) {
|
|
51686
|
+
if (candidates.length === 0)
|
|
51687
|
+
return bodyShapeMismatches(shape3.fields, []);
|
|
51688
|
+
const lists = candidates.map((d) => bodyShapeMismatches(shape3.fields, d.fields));
|
|
51689
|
+
return lists.reduce((best, cur) => cur.length < best.length ? cur : best);
|
|
51690
|
+
}
|
|
51691
|
+
function evaluateNetworkCheck(check3, requests, truncated = false, declared = []) {
|
|
51276
51692
|
const spec = describeCheck(check3);
|
|
51277
51693
|
const hits = matching(requests, check3.method, check3.pathGlob);
|
|
51278
51694
|
const note = truncated ? " (the capture dropped its oldest requests)" : "";
|
|
@@ -51285,6 +51701,19 @@ function evaluateNetworkCheck(check3, requests, truncated = false) {
|
|
|
51285
51701
|
detail: `no ${check3.method.toUpperCase()} request matched ${check3.pathGlob} (${captured} requests captured)${note}${hint2 === null ? "" : `; ${hint2}`}`
|
|
51286
51702
|
};
|
|
51287
51703
|
}
|
|
51704
|
+
if (check3.count !== void 0 && !countHolds(check3.count, hits.length)) {
|
|
51705
|
+
return { check: spec, passed: false, detail: `expected ${describeCountExpectation(check3.count)} matching request(s), saw ${hits.length}${note}` };
|
|
51706
|
+
}
|
|
51707
|
+
if (check3.bodyShape !== void 0) {
|
|
51708
|
+
const candidates = declaredMatching(declared, check3.method, check3.pathGlob);
|
|
51709
|
+
if (!candidates.some((d) => bodyShapeMismatches(check3.bodyShape.fields, d.fields).length === 0)) {
|
|
51710
|
+
return {
|
|
51711
|
+
check: spec,
|
|
51712
|
+
passed: false,
|
|
51713
|
+
detail: `none of the ${hits.length} matching request(s) declared the required body shape: ${missingBodyFields(check3.bodyShape, candidates).join("; ")}${note}`
|
|
51714
|
+
};
|
|
51715
|
+
}
|
|
51716
|
+
}
|
|
51288
51717
|
const inFlight = hits.filter((r) => r.pending === true).length;
|
|
51289
51718
|
if (check3.kind === "requestMade") {
|
|
51290
51719
|
return {
|
|
@@ -52855,9 +53284,29 @@ function whyNot(run, results) {
|
|
|
52855
53284
|
function sentSinceFirstAction(requests, firstActionAt) {
|
|
52856
53285
|
return firstActionAt === null ? [] : requests.filter((r) => r.startedAt !== void 0 && r.startedAt >= firstActionAt);
|
|
52857
53286
|
}
|
|
52858
|
-
|
|
52859
|
-
|
|
52860
|
-
|
|
53287
|
+
var MAX_DECLARED_FIELDS2 = 500;
|
|
53288
|
+
var declaredFieldsByPage = /* @__PURE__ */ new WeakMap();
|
|
53289
|
+
function captureDeclaredBodyFields(page) {
|
|
53290
|
+
if (declaredFieldsByPage.has(page))
|
|
53291
|
+
return;
|
|
53292
|
+
const fields = [];
|
|
53293
|
+
declaredFieldsByPage.set(page, fields);
|
|
53294
|
+
monitorFor(page).onDeclaredFields((declared) => {
|
|
53295
|
+
fields.push(declared);
|
|
53296
|
+
if (fields.length > MAX_DECLARED_FIELDS2)
|
|
53297
|
+
fields.shift();
|
|
53298
|
+
});
|
|
53299
|
+
}
|
|
53300
|
+
function declaredFieldsFor(page) {
|
|
53301
|
+
return declaredFieldsByPage.get(page) ?? [];
|
|
53302
|
+
}
|
|
53303
|
+
function declaredWithin(declared, requests) {
|
|
53304
|
+
return declared.filter((d) => requests.some((r) => r.method === d.method && r.path === d.path));
|
|
53305
|
+
}
|
|
53306
|
+
function judgeNetworkCheck(c, requests, truncated, scope2, onVacuous, declared = []) {
|
|
53307
|
+
const scoped = sentSinceFirstAction(requests, scope2.firstActionAt);
|
|
53308
|
+
const after = evaluateNetworkCheck(c, scoped, truncated, declaredWithin(declared, scoped));
|
|
53309
|
+
const all = evaluateNetworkCheck(c, requests, truncated, declaredWithin(declared, requests));
|
|
52861
53310
|
const vacuous = !after.passed && all.passed;
|
|
52862
53311
|
if (vacuous)
|
|
52863
53312
|
onVacuous?.(after.check);
|
|
@@ -52869,6 +53318,8 @@ function judgeNetworkCheck(c, requests, truncated, scope2, onVacuous) {
|
|
|
52869
53318
|
} : after;
|
|
52870
53319
|
}
|
|
52871
53320
|
async function evaluateChecks(cfg, checks, page, capture2, scope2, onVacuous) {
|
|
53321
|
+
captureDeclaredBodyFields(page);
|
|
53322
|
+
const declared = declaredFieldsFor(page);
|
|
52872
53323
|
return evaluateOutcomeChecks(checks, {
|
|
52873
53324
|
actor: cfg.actor,
|
|
52874
53325
|
page,
|
|
@@ -52876,7 +53327,7 @@ async function evaluateChecks(cfg, checks, page, capture2, scope2, onVacuous) {
|
|
|
52876
53327
|
...cfg.secrets === void 0 ? {} : { secrets: cfg.secrets },
|
|
52877
53328
|
timeoutMs: cfg.oracleTimeoutMs ?? 3e3,
|
|
52878
53329
|
settleMs: cfg.oracleSettleMs ?? DEFAULT_ORACLE_SETTLE_MS,
|
|
52879
|
-
judgeNetwork: (c, requests, truncated) => judgeNetworkCheck(c, requests, truncated, scope2, onVacuous),
|
|
53330
|
+
judgeNetwork: (c, requests, truncated) => judgeNetworkCheck(c, requests, truncated, scope2, onVacuous, declared),
|
|
52880
53331
|
...cfg.httpProbe === void 0 ? {} : { httpProbe: cfg.httpProbe },
|
|
52881
53332
|
...cfg.httpRefs === void 0 ? {} : { resolveRef: cfg.httpRefs }
|
|
52882
53333
|
});
|
|
@@ -53286,6 +53737,8 @@ function setupAdjudication(cfg, checks, page, capture2, declared) {
|
|
|
53286
53737
|
const httpChecks = [...checks.entries()].filter((e) => e[1].kind === "http");
|
|
53287
53738
|
const holdsNow = (a) => checkAssertion(cfg.actor, a, { timeoutMs: HELD_CHECK_TIMEOUT_MS }).catch(() => false);
|
|
53288
53739
|
const networkChecks = checks.filter((c) => c.kind === "requestMade" || c.kind === "responseStatus");
|
|
53740
|
+
if (networkChecks.some((c) => c.bodyShape !== void 0))
|
|
53741
|
+
captureDeclaredBodyFields(page);
|
|
53289
53742
|
const answerRequired = checks.length > 0 && goalAsksForReport(cfg.goal);
|
|
53290
53743
|
const stopWhenHeld = (cfg.successWhen === "held" || cfg.successWhen === "each") && checks.length > 0 && !checks.some(isDeferredCheck) && !answerRequired;
|
|
53291
53744
|
const hasChecks = checks.length > 0;
|
|
@@ -53463,6 +53916,11 @@ function declaredInvariants(cfg, page) {
|
|
|
53463
53916
|
};
|
|
53464
53917
|
}
|
|
53465
53918
|
async function adjudicatedRun(cfg, checks, page, capture2, declared) {
|
|
53919
|
+
const result = await assembleGoalResult(cfg, checks, page, capture2, declared);
|
|
53920
|
+
const attempts = startupAttemptsFor(cfg.actor);
|
|
53921
|
+
return attempts.length === 0 ? result : { ...result, attempts: [...attempts] };
|
|
53922
|
+
}
|
|
53923
|
+
async function assembleGoalResult(cfg, checks, page, capture2, declared) {
|
|
53466
53924
|
const ctx = setupAdjudication(cfg, checks, page, capture2, declared);
|
|
53467
53925
|
const acted = await act2(ctx);
|
|
53468
53926
|
if (acted.kind === "ended")
|
|
@@ -58986,7 +59444,7 @@ async function loadSeed(ctx) {
|
|
|
58986
59444
|
try {
|
|
58987
59445
|
if (params.startInPlace !== true) {
|
|
58988
59446
|
await ctx.guard(assertSeedReachable(ctx.sessions.actor, params.seedUrl));
|
|
58989
|
-
await ctx.guard(ctx.sessions.actor
|
|
59447
|
+
await ctx.guard(navigateWithStartupRetry(ctx.sessions.actor, params.seedUrl));
|
|
58990
59448
|
}
|
|
58991
59449
|
} catch (e) {
|
|
58992
59450
|
const message2 = e instanceof Error ? e.message.split("\n")[0] ?? e.message : String(e);
|
|
@@ -61935,7 +62393,7 @@ async function loadSeed2(ctx, params) {
|
|
|
61935
62393
|
try {
|
|
61936
62394
|
if (params.startInPlace !== true) {
|
|
61937
62395
|
await assertSeedReachable(ctx.sessions.actor, params.seedUrl);
|
|
61938
|
-
await
|
|
62396
|
+
await navigateWithStartupRetry(ctx.sessions.actor, params.seedUrl);
|
|
61939
62397
|
}
|
|
61940
62398
|
} catch (e) {
|
|
61941
62399
|
const message2 = e instanceof Error ? e.message.split("\n")[0] ?? e.message : String(e);
|
|
@@ -62761,7 +63219,7 @@ async function runFeatureFrontier(params, declared, safety, budget, overlay = nu
|
|
|
62761
63219
|
sessions.page.on("requestfailed", onFirstNavRequestFailed);
|
|
62762
63220
|
try {
|
|
62763
63221
|
await guard(assertSeedReachable(sessions.actor, params.seedUrl));
|
|
62764
|
-
await guard(sessions.actor
|
|
63222
|
+
await guard(navigateWithStartupRetry(sessions.actor, params.seedUrl));
|
|
62765
63223
|
} catch (e) {
|
|
62766
63224
|
const message2 = e instanceof Error ? e.message.split("\n")[0] ?? e.message : String(e);
|
|
62767
63225
|
if (peekBlockedDeparture(sessions.actor) === void 0) {
|
|
@@ -63608,9 +64066,9 @@ function hostStarvedFailure(failure3, health) {
|
|
|
63608
64066
|
}
|
|
63609
64067
|
|
|
63610
64068
|
// src/build-info.generated.ts
|
|
63611
|
-
var GENERATED_COMMIT = "
|
|
63612
|
-
var GENERATED_TREE = "
|
|
63613
|
-
var GENERATED_BUILT_AT = "2026-10-
|
|
64069
|
+
var GENERATED_COMMIT = "b4bfd12";
|
|
64070
|
+
var GENERATED_TREE = "f8e0769";
|
|
64071
|
+
var GENERATED_BUILT_AT = "2026-10-30T20:01:06.112Z";
|
|
63614
64072
|
|
|
63615
64073
|
// src/version.ts
|
|
63616
64074
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
@@ -63632,13 +64090,13 @@ function readCliVersion() {
|
|
|
63632
64090
|
|
|
63633
64091
|
// src/engine.ts
|
|
63634
64092
|
function bundledCommit() {
|
|
63635
|
-
return true ? "
|
|
64093
|
+
return true ? "b4bfd12" : void 0;
|
|
63636
64094
|
}
|
|
63637
64095
|
function bundledBuiltAt() {
|
|
63638
|
-
return true ? "2026-10-
|
|
64096
|
+
return true ? "2026-10-30T20:01:06.112Z" : void 0;
|
|
63639
64097
|
}
|
|
63640
64098
|
function bundledTree() {
|
|
63641
|
-
return true ? "
|
|
64099
|
+
return true ? "f8e0769" : void 0;
|
|
63642
64100
|
}
|
|
63643
64101
|
function currentBuildTree() {
|
|
63644
64102
|
return bundledTree() ?? GENERATED_TREE;
|
|
@@ -64816,6 +65274,51 @@ function parseStatusSpec(text2) {
|
|
|
64816
65274
|
if (/^[1-5]\d\d$/.test(text2)) return { code: Number(text2) };
|
|
64817
65275
|
throw new Error(`responseStatus expects 2xx, 4xx \u2026 or a status code, got ${JSON.stringify(text2)}`);
|
|
64818
65276
|
}
|
|
65277
|
+
var BODY_PREDICATES = /* @__PURE__ */ new Set(["exists", "absent", "string", "number", "boolean", "null", "array", "object"]);
|
|
65278
|
+
function parseCountSpec(kind, value) {
|
|
65279
|
+
const m = /^(exact|min|max):(\d+)$/.exec(value);
|
|
65280
|
+
if (m === null) throw new Error(`${kind}: count expects exact:<n>, min:<n> or max:<n>, got ${JSON.stringify(value)}`);
|
|
65281
|
+
const n3 = Number(m[2]);
|
|
65282
|
+
if (n3 > 1e3) throw new Error(`${kind}: count bound must be 0..1000, got ${n3}`);
|
|
65283
|
+
const bound2 = m[1];
|
|
65284
|
+
return bound2 === "exact" ? { exact: n3 } : bound2 === "min" ? { min: n3 } : { max: n3 };
|
|
65285
|
+
}
|
|
65286
|
+
function parseBodyShapeSpec(kind, value) {
|
|
65287
|
+
const fields = value.split(",").map((part) => {
|
|
65288
|
+
const idx = part.lastIndexOf(":");
|
|
65289
|
+
if (idx <= 0) throw new Error(`${kind}: body expects name:predicate, got ${JSON.stringify(part)}`);
|
|
65290
|
+
const name = part.slice(0, idx);
|
|
65291
|
+
const predicate = part.slice(idx + 1);
|
|
65292
|
+
if (!BODY_PREDICATES.has(predicate)) throw new Error(`${kind}: body predicate must be one of ${[...BODY_PREDICATES].join(", ")}, got ${JSON.stringify(predicate)}`);
|
|
65293
|
+
if (SECRET_PARAM_NAME_RE.test(name)) {
|
|
65294
|
+
throw new Error(`${kind}: body field ${JSON.stringify(name)} names a credential \u2014 a body shape declares field names and predicates only`);
|
|
65295
|
+
}
|
|
65296
|
+
return { name, predicate };
|
|
65297
|
+
});
|
|
65298
|
+
return { fields };
|
|
65299
|
+
}
|
|
65300
|
+
function splitNetworkSuffixes(kind, text2) {
|
|
65301
|
+
const parts2 = text2.split("|");
|
|
65302
|
+
const core = parts2[0] ?? "";
|
|
65303
|
+
const out = { core };
|
|
65304
|
+
for (const part of parts2.slice(1)) {
|
|
65305
|
+
const eq = part.indexOf("=");
|
|
65306
|
+
const key2 = eq === -1 ? part : part.slice(0, eq);
|
|
65307
|
+
const value = eq === -1 ? void 0 : part.slice(eq + 1);
|
|
65308
|
+
if (key2 === "count") {
|
|
65309
|
+
if (out.count !== void 0) throw new Error(`${kind}: count declared more than once`);
|
|
65310
|
+
if (value === void 0) throw new Error(`${kind}: count expects exact:<n>, min:<n> or max:<n>`);
|
|
65311
|
+
out.count = parseCountSpec(kind, value);
|
|
65312
|
+
} else if (key2 === "body") {
|
|
65313
|
+
if (out.bodyShape !== void 0) throw new Error(`${kind}: body declared more than once`);
|
|
65314
|
+
if (value === void 0) throw new Error(`${kind}: body expects name:predicate,\u2026`);
|
|
65315
|
+
out.bodyShape = parseBodyShapeSpec(kind, value);
|
|
65316
|
+
} else {
|
|
65317
|
+
throw new Error(`${kind}: unknown option ${JSON.stringify(part)} (expected count=\u2026 or body=\u2026)`);
|
|
65318
|
+
}
|
|
65319
|
+
}
|
|
65320
|
+
return out;
|
|
65321
|
+
}
|
|
64819
65322
|
function parseSuccessSpec(spec) {
|
|
64820
65323
|
const ci = spec.indexOf(":");
|
|
64821
65324
|
const kind = ci === -1 ? spec : spec.slice(0, ci);
|
|
@@ -64824,14 +65327,17 @@ function parseSuccessSpec(spec) {
|
|
|
64824
65327
|
case "reloadThen":
|
|
64825
65328
|
if (rest.startsWith("reloadThen:")) throw new Error("reloadThen cannot be nested");
|
|
64826
65329
|
return { kind: "reloadThen", assertion: parseAssertionSpec(rest) };
|
|
64827
|
-
case "requestMade":
|
|
64828
|
-
|
|
65330
|
+
case "requestMade": {
|
|
65331
|
+
const { core, ...extras } = splitNetworkSuffixes(kind, rest);
|
|
65332
|
+
return { kind: "requestMade", ...parseRequestSpec(kind, core), ...extras };
|
|
65333
|
+
}
|
|
64829
65334
|
case "http":
|
|
64830
65335
|
return parseHttpCheckSpec(rest);
|
|
64831
65336
|
case "responseStatus": {
|
|
64832
|
-
const
|
|
65337
|
+
const { core, ...extras } = splitNetworkSuffixes(kind, rest);
|
|
65338
|
+
const eq = core.lastIndexOf("=");
|
|
64833
65339
|
if (eq === -1) throw new Error('responseStatus requires "<METHOD> <path-glob>=<2xx|4xx|code>"');
|
|
64834
|
-
return { kind: "responseStatus", ...parseRequestSpec(kind,
|
|
65340
|
+
return { kind: "responseStatus", ...parseRequestSpec(kind, core.slice(0, eq)), status: parseStatusSpec(core.slice(eq + 1)), ...extras };
|
|
64835
65341
|
}
|
|
64836
65342
|
default:
|
|
64837
65343
|
return { kind: "page", assertion: parseAssertionSpec(spec) };
|
|
@@ -84848,7 +85354,7 @@ async function readChangeScope(o) {
|
|
|
84848
85354
|
try {
|
|
84849
85355
|
({ stdout } = await exec(diffArgs, { cwd: o.cwd, env: process.env }));
|
|
84850
85356
|
} catch (error62) {
|
|
84851
|
-
throw new ChangesInputError(`git diff failed: ${
|
|
85357
|
+
throw new ChangesInputError(`git diff failed: ${messageOf3(error62)}`);
|
|
84852
85358
|
}
|
|
84853
85359
|
if (Buffer.byteLength(stdout, "utf8") > maxBytes) {
|
|
84854
85360
|
throw new ChangesInputError(`change diff exceeds ${maxBytes} bytes`);
|
|
@@ -84871,7 +85377,7 @@ async function resolveCommit(exec, cwd, rev) {
|
|
|
84871
85377
|
try {
|
|
84872
85378
|
({ stdout } = await exec(args, { cwd, env: process.env }));
|
|
84873
85379
|
} catch (error62) {
|
|
84874
|
-
throw new ChangesInputError(`cannot resolve change revision ${JSON.stringify(rev)}: ${
|
|
85380
|
+
throw new ChangesInputError(`cannot resolve change revision ${JSON.stringify(rev)}: ${messageOf3(error62)}`);
|
|
84875
85381
|
}
|
|
84876
85382
|
const sha = stdout.trim();
|
|
84877
85383
|
if (!SHA_RE2.test(sha)) throw new ChangesInputError(`cannot resolve change revision ${JSON.stringify(rev)}`);
|
|
@@ -84882,7 +85388,7 @@ async function mergeBase(exec, cwd, shaA, shaB) {
|
|
|
84882
85388
|
try {
|
|
84883
85389
|
({ stdout } = await exec(["merge-base", shaA, shaB], { cwd, env: process.env }));
|
|
84884
85390
|
} catch (error62) {
|
|
84885
|
-
throw new ChangesInputError(`cannot find merge base: ${
|
|
85391
|
+
throw new ChangesInputError(`cannot find merge base: ${messageOf3(error62)}`);
|
|
84886
85392
|
}
|
|
84887
85393
|
const sha = stdout.trim();
|
|
84888
85394
|
if (!SHA_RE2.test(sha)) throw new ChangesInputError(`cannot find merge base for ${shaA}...${shaB}`);
|
|
@@ -84916,7 +85422,7 @@ function unquotePath(value) {
|
|
|
84916
85422
|
if (trimmed.length >= 2 && trimmed.startsWith('"') && trimmed.endsWith('"')) return trimmed.slice(1, -1);
|
|
84917
85423
|
return trimmed;
|
|
84918
85424
|
}
|
|
84919
|
-
function
|
|
85425
|
+
function messageOf3(error62) {
|
|
84920
85426
|
return error62 instanceof Error ? error62.message : String(error62);
|
|
84921
85427
|
}
|
|
84922
85428
|
|
|
@@ -88753,6 +89259,13 @@ var LoginFailedError = class extends Error {
|
|
|
88753
89259
|
this.name = "LoginFailedError";
|
|
88754
89260
|
}
|
|
88755
89261
|
};
|
|
89262
|
+
var LoginContextError = class extends Error {
|
|
89263
|
+
code = "E_LOGIN_CONTEXT";
|
|
89264
|
+
constructor(message2) {
|
|
89265
|
+
super(message2);
|
|
89266
|
+
this.name = "LoginContextError";
|
|
89267
|
+
}
|
|
89268
|
+
};
|
|
88756
89269
|
var VAR_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
88757
89270
|
var RESERVED_VARS = /* @__PURE__ */ new Set(["OPENROUTER_API_KEY", "TYPESAFE_API_KEY", "GITHUB_TOKEN"]);
|
|
88758
89271
|
function assertEnvVarName(name, what) {
|
|
@@ -88774,8 +89287,8 @@ function httpUrl2(v, what) {
|
|
|
88774
89287
|
if (u.username !== "" || u.password !== "") throw new LoginArgsError(`${what} must not carry credentials in the URL`);
|
|
88775
89288
|
return u.href;
|
|
88776
89289
|
}
|
|
88777
|
-
var LOGIN_KEYS = ["url", "api", "userEnv", "passwordEnv", "userField", "passwordField", "submit", "success"];
|
|
88778
|
-
var FORM_ONLY_KEYS = ["url", "userField", "passwordField", "submit", "success"];
|
|
89290
|
+
var LOGIN_KEYS = ["url", "api", "userEnv", "passwordEnv", "userField", "passwordField", "submit", "success", "organizationField", "roleField"];
|
|
89291
|
+
var FORM_ONLY_KEYS = ["url", "userField", "passwordField", "submit", "success", "organizationField", "roleField"];
|
|
88779
89292
|
var API_KEYS = ["url", "userKey", "passwordKey", "tokenPath", "storageKey", "storage", "verifyUrl"];
|
|
88780
89293
|
var BODY_KEY = /^[A-Za-z_][A-Za-z0-9_-]{0,63}$/;
|
|
88781
89294
|
var PATH_SEGMENT = /^(?:[A-Za-z_][A-Za-z0-9_-]{0,63}|0|[1-9][0-9]{0,5})$/;
|
|
@@ -88882,7 +89395,9 @@ function parsePersonaLogin(v, where) {
|
|
|
88882
89395
|
...optional2("userField", str7(o, "userField", `${where}.userField`, false)),
|
|
88883
89396
|
...optional2("passwordField", str7(o, "passwordField", `${where}.passwordField`, false)),
|
|
88884
89397
|
...optional2("submit", str7(o, "submit", `${where}.submit`, false)),
|
|
88885
|
-
...optional2("success", str7(o, "success", `${where}.success`, false))
|
|
89398
|
+
...optional2("success", str7(o, "success", `${where}.success`, false)),
|
|
89399
|
+
...optional2("organizationField", str7(o, "organizationField", `${where}.organizationField`, false)),
|
|
89400
|
+
...optional2("roleField", str7(o, "roleField", `${where}.roleField`, false))
|
|
88886
89401
|
};
|
|
88887
89402
|
}
|
|
88888
89403
|
assertEnvVarName(login.userEnv, `${where}.userEnv`);
|
|
@@ -88900,7 +89415,9 @@ function loginFromFlags(f2) {
|
|
|
88900
89415
|
["--user-field", f2.userField],
|
|
88901
89416
|
["--password-field", f2.passwordField],
|
|
88902
89417
|
["--submit", f2.submit],
|
|
88903
|
-
["--success", f2.success]
|
|
89418
|
+
["--success", f2.success],
|
|
89419
|
+
["--organization-field", f2.organizationField],
|
|
89420
|
+
["--role-field", f2.roleField]
|
|
88904
89421
|
].filter(([, v]) => v !== void 0).map(([k]) => k);
|
|
88905
89422
|
if (formFlags.length > 0) throw new LoginArgsError(`--api signs in through an HTTP endpoint, not a form: ${formFlags.join(", ")} ${formFlags.length === 1 ? "does" : "do"} not apply`);
|
|
88906
89423
|
const login2 = { api: parseApiLogin(f2.api), userEnv: f2.userEnv, passwordEnv: f2.passwordEnv };
|
|
@@ -88916,7 +89433,9 @@ function loginFromFlags(f2) {
|
|
|
88916
89433
|
...optional2("userField", f2.userField),
|
|
88917
89434
|
...optional2("passwordField", f2.passwordField),
|
|
88918
89435
|
...optional2("submit", f2.submit),
|
|
88919
|
-
...optional2("success", f2.success)
|
|
89436
|
+
...optional2("success", f2.success),
|
|
89437
|
+
...optional2("organizationField", f2.organizationField),
|
|
89438
|
+
...optional2("roleField", f2.roleField)
|
|
88920
89439
|
};
|
|
88921
89440
|
assertEnvVarName(login.userEnv, "--user-env");
|
|
88922
89441
|
assertEnvVarName(login.passwordEnv, "--password-env");
|
|
@@ -89132,6 +89651,103 @@ async function performLogin(page, login, secrets, opts) {
|
|
|
89132
89651
|
throw new LoginFailedError(scrub2(`the sign-in failed: ${message2}`, secrets));
|
|
89133
89652
|
}
|
|
89134
89653
|
}
|
|
89654
|
+
var CONTEXT_CONTROL_LABEL = {
|
|
89655
|
+
organization: /organization|organisation|\borg\b|workspace|tenant|account/i,
|
|
89656
|
+
role: /\brole\b/i
|
|
89657
|
+
};
|
|
89658
|
+
var CHOOSER_ROLES = ["button", "link", "option", "radio"];
|
|
89659
|
+
var CHOOSER_SETTLE_MS = 3e3;
|
|
89660
|
+
function escapeRegExp(text2) {
|
|
89661
|
+
return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
89662
|
+
}
|
|
89663
|
+
async function chooserMatches(page, name, exact = false) {
|
|
89664
|
+
const out = [];
|
|
89665
|
+
const query = exact && typeof name === "string" ? { name, exact: true } : { name };
|
|
89666
|
+
for (const role of CHOOSER_ROLES) {
|
|
89667
|
+
const found = page.getByRole(role, query).filter({ visible: true });
|
|
89668
|
+
const count = await found.count().catch(() => 0);
|
|
89669
|
+
for (let i = 0; i < count; i++) {
|
|
89670
|
+
const item = found.nth(i);
|
|
89671
|
+
if (await item.isEnabled().catch(() => true)) out.push(item);
|
|
89672
|
+
}
|
|
89673
|
+
}
|
|
89674
|
+
return out;
|
|
89675
|
+
}
|
|
89676
|
+
async function chooserOffered(page) {
|
|
89677
|
+
const names = [];
|
|
89678
|
+
for (const role of CHOOSER_ROLES) {
|
|
89679
|
+
const found = page.getByRole(role).filter({ visible: true });
|
|
89680
|
+
const count = await found.count().catch(() => 0);
|
|
89681
|
+
for (let i = 0; i < count; i++) {
|
|
89682
|
+
const item = found.nth(i);
|
|
89683
|
+
if (!await item.isEnabled().catch(() => true)) continue;
|
|
89684
|
+
const name = await item.evaluate((el) => (el.getAttribute("aria-label") ?? el.textContent ?? "").replace(/\s+/g, " ").trim()).catch(() => "") || "";
|
|
89685
|
+
names.push(name);
|
|
89686
|
+
}
|
|
89687
|
+
}
|
|
89688
|
+
return names;
|
|
89689
|
+
}
|
|
89690
|
+
async function chooserCandidates(page, value) {
|
|
89691
|
+
const exact = await chooserMatches(page, value, true);
|
|
89692
|
+
if (exact.length > 0) return exact;
|
|
89693
|
+
const loose = await chooserMatches(page, new RegExp(`\\b${escapeRegExp(value)}\\b`, "i"));
|
|
89694
|
+
const safe = [];
|
|
89695
|
+
for (const card of loose) {
|
|
89696
|
+
const name = await card.evaluate((el) => (el.getAttribute("aria-label") ?? el.textContent ?? "").replace(/\s+/g, " ").trim()).catch(() => "") || "";
|
|
89697
|
+
if (!SESSION_END.test(name) && !DESTRUCTIVE.test(name)) safe.push(card);
|
|
89698
|
+
}
|
|
89699
|
+
return safe;
|
|
89700
|
+
}
|
|
89701
|
+
async function selectContextCard(page, what, value, timeoutMs) {
|
|
89702
|
+
const before = page.url();
|
|
89703
|
+
const matches2 = await chooserCandidates(page, value);
|
|
89704
|
+
if (matches2.length === 0) {
|
|
89705
|
+
const offered = await chooserOffered(page);
|
|
89706
|
+
throw new LoginContextError(`the declared ${what} ${JSON.stringify(value)} is unavailable on ${pathOf12(before)}: the chooser offers ${offered.length === 0 ? "nothing" : offered.map((n3) => JSON.stringify(n3)).join(", ")}`);
|
|
89707
|
+
}
|
|
89708
|
+
if (matches2.length > 1) {
|
|
89709
|
+
const offered = await Promise.all(matches2.map(async (m) => await m.evaluate((el) => (el.getAttribute("aria-label") ?? el.textContent ?? "").replace(/\s+/g, " ").trim()).catch(() => "") || ""));
|
|
89710
|
+
throw new LoginContextError(`the declared ${what} ${JSON.stringify(value)} is ambiguous on ${pathOf12(before)}: ${matches2.length} chooser cards match (${offered.map((n3) => JSON.stringify(n3)).join(", ")})`);
|
|
89711
|
+
}
|
|
89712
|
+
await matches2[0].click({ timeout: timeoutMs });
|
|
89713
|
+
await page.waitForLoadState("load", { timeout: timeoutMs }).catch(() => void 0);
|
|
89714
|
+
await page.waitForLoadState("networkidle", { timeout: Math.min(timeoutMs, CHOOSER_SETTLE_MS) }).catch(() => void 0);
|
|
89715
|
+
if (page.url() !== before) return;
|
|
89716
|
+
if ((await chooserCandidates(page, value)).length > 0) {
|
|
89717
|
+
throw new LoginContextError(`the declared ${what} ${JSON.stringify(value)} was clicked on ${pathOf12(page.url())}, but the chooser still offers it \u2014 the selection did not take`);
|
|
89718
|
+
}
|
|
89719
|
+
}
|
|
89720
|
+
async function selectLoginContext(page, context, opts) {
|
|
89721
|
+
await selectContextValue(page, "organization", context.organization, opts.login.organizationField, opts.timeoutMs);
|
|
89722
|
+
await selectContextValue(page, "role", context.role, opts.login.roleField, opts.timeoutMs);
|
|
89723
|
+
}
|
|
89724
|
+
async function selectContextValue(page, what, value, field3, timeoutMs) {
|
|
89725
|
+
if (value === void 0 || value === "") return;
|
|
89726
|
+
const candidates = field3 === void 0 ? [page.getByLabel(CONTEXT_CONTROL_LABEL[what]), page.getByRole("combobox", { name: CONTEXT_CONTROL_LABEL[what] })] : fieldBySpec(page, field3);
|
|
89727
|
+
const control = await firstVisible(candidates);
|
|
89728
|
+
if (control === void 0) {
|
|
89729
|
+
if (field3 !== void 0) throw new LoginContextError(`the declared ${what} ${JSON.stringify(value)} is unavailable: no ${what} control on ${pathOf12(page.url())} matching ${JSON.stringify(field3)}`);
|
|
89730
|
+
await selectContextCard(page, what, value, timeoutMs);
|
|
89731
|
+
return;
|
|
89732
|
+
}
|
|
89733
|
+
const labels = await control.evaluate((el) => el.tagName === "SELECT" ? Array.from(el.options).map((o) => (o.textContent ?? "").trim()) : null).catch(() => void 0);
|
|
89734
|
+
if (labels == null) {
|
|
89735
|
+
await control.click({ timeout: timeoutMs });
|
|
89736
|
+
const option = page.getByRole("option", { name: value, exact: true }).first();
|
|
89737
|
+
const visible3 = await option.waitFor({ state: "visible", timeout: timeoutMs }).then(() => true).catch(() => false);
|
|
89738
|
+
if (!visible3) throw new LoginContextError(`the declared ${what} ${JSON.stringify(value)} is unavailable on ${pathOf12(page.url())}: the ${what} control does not offer it`);
|
|
89739
|
+
await option.click({ timeout: timeoutMs });
|
|
89740
|
+
return;
|
|
89741
|
+
}
|
|
89742
|
+
if (!labels.includes(value)) {
|
|
89743
|
+
throw new LoginContextError(`the declared ${what} ${JSON.stringify(value)} is unavailable on ${pathOf12(page.url())}: the ${what} control offers ${labels.length === 0 ? "nothing" : labels.map((l2) => JSON.stringify(l2)).join(", ")}`);
|
|
89744
|
+
}
|
|
89745
|
+
await control.selectOption({ label: value }, { timeout: timeoutMs });
|
|
89746
|
+
const selected = await control.evaluate((el) => el.selectedOptions[0]?.textContent?.trim() ?? null);
|
|
89747
|
+
if (selected !== value) {
|
|
89748
|
+
throw new LoginContextError(`the declared ${what} ${JSON.stringify(value)} was not selected: the ${what} control shows ${JSON.stringify(selected)}`);
|
|
89749
|
+
}
|
|
89750
|
+
}
|
|
89135
89751
|
function checkSaveTarget(file2, what) {
|
|
89136
89752
|
const path2 = resolve10(file2);
|
|
89137
89753
|
const refusal = sessionFileInProjectRefusal(path2, what);
|
|
@@ -89191,11 +89807,13 @@ async function mintStorageState(o) {
|
|
|
89191
89807
|
try {
|
|
89192
89808
|
session2 = await o.port.open({ ...sessionLaunchOptions(launch), allowedOrigins: [...o.allowlist], baseUrl: o.login.url });
|
|
89193
89809
|
const landed = await performLogin(session2.page, o.login, secrets, { allowlist: o.allowlist, timeoutMs: o.timeoutMs });
|
|
89810
|
+
if (o.context !== void 0) await selectLoginContext(session2.page, o.context, { login: o.login, timeoutMs: o.timeoutMs });
|
|
89194
89811
|
const state = session2.captureStorageState !== void 0 ? await session2.captureStorageState() : JSON.stringify(await session2.page.context().storageState());
|
|
89195
89812
|
writeStorageStateFile(save, state);
|
|
89196
89813
|
return { saved: save, landedUrl: scrub2(redactUrl(landed), secrets) };
|
|
89197
89814
|
} catch (err) {
|
|
89198
89815
|
if (err instanceof LoginFailedError || err instanceof LoginArgsError) throw err;
|
|
89816
|
+
if (err instanceof LoginContextError) throw new LoginContextError(scrub2(err.message, secrets));
|
|
89199
89817
|
throw new LoginFailedError(scrub2(`the sign-in failed: ${err instanceof Error ? err.message.split("\n")[0] : String(err)}`, secrets));
|
|
89200
89818
|
} finally {
|
|
89201
89819
|
await session2?.close().catch(() => void 0);
|
|
@@ -89260,6 +89878,15 @@ async function mintApiStorageState(o, api) {
|
|
|
89260
89878
|
else if (typeof v === "string" && v !== "") token = v;
|
|
89261
89879
|
else throw new LoginFailedError(`the sign-in endpoint's JSON response has no ${v === void 0 || v === null ? "value" : "string value"} at --token-path ${api.tokenPath}`);
|
|
89262
89880
|
}
|
|
89881
|
+
if (o.context !== void 0) {
|
|
89882
|
+
await session2.page.goto(appUrl, { waitUntil: "load", timeout: o.timeoutMs });
|
|
89883
|
+
if (token !== void 0 && api.storageKey !== void 0) {
|
|
89884
|
+
await session2.page.evaluate((args) => window.localStorage.setItem(args[0], args[1]), [api.storageKey, token]);
|
|
89885
|
+
await session2.page.reload({ waitUntil: "load", timeout: o.timeoutMs });
|
|
89886
|
+
}
|
|
89887
|
+
assertPageAuthorized(session2.page, o.allowlist);
|
|
89888
|
+
await selectLoginContext(session2.page, o.context, { login: o.login, timeoutMs: o.timeoutMs });
|
|
89889
|
+
}
|
|
89263
89890
|
const raw = session2.captureStorageState !== void 0 ? await session2.captureStorageState() : JSON.stringify(await context.storageState());
|
|
89264
89891
|
const state = JSON.parse(raw);
|
|
89265
89892
|
if (token === void 0 && (state.cookies ?? []).length === 0) {
|
|
@@ -89291,6 +89918,7 @@ async function mintApiStorageState(o, api) {
|
|
|
89291
89918
|
} catch (err) {
|
|
89292
89919
|
if (err instanceof LoginFailedError) throw new LoginFailedError(scrub2(err.message, secrets, token));
|
|
89293
89920
|
if (err instanceof LoginArgsError) throw new LoginArgsError(scrub2(err.message, secrets, token));
|
|
89921
|
+
if (err instanceof LoginContextError) throw new LoginContextError(scrub2(err.message, secrets, token));
|
|
89294
89922
|
throw new LoginFailedError(scrub2(`the sign-in failed: ${err instanceof Error ? err.message.split("\n")[0] : String(err)}`, secrets, token));
|
|
89295
89923
|
} finally {
|
|
89296
89924
|
if (tmp !== void 0) rmSync(tmp, { force: true });
|
|
@@ -89390,7 +90018,7 @@ async function ensurePersonaSession(o) {
|
|
|
89390
90018
|
const landedUrl = first !== void 0 && !first.ok ? first.landedUrl : void 0;
|
|
89391
90019
|
if (o.login === void 0) return { ok: false, reason: expired, ...landedUrl === void 0 ? {} : { landedUrl } };
|
|
89392
90020
|
try {
|
|
89393
|
-
await mintStorageState({ login: o.login, save: o.storageState, allowlist: o.allowlist, env: o.env, port: o.port, ...o.browser === void 0 ? {} : { browser: o.browser }, timeoutMs: o.timeoutMs });
|
|
90021
|
+
await mintStorageState({ login: o.login, save: o.storageState, allowlist: o.allowlist, env: o.env, port: o.port, ...o.browser === void 0 ? {} : { browser: o.browser }, ...o.context === void 0 ? {} : { context: o.context }, timeoutMs: o.timeoutMs });
|
|
89394
90022
|
} catch (err) {
|
|
89395
90023
|
if (!(err instanceof LoginFailedError) && !(err instanceof LoginArgsError)) throw err;
|
|
89396
90024
|
return { ok: false, reason: `${expired}; re-signing in ${who} failed: ${err.message}`, ...landedUrl === void 0 ? {} : { landedUrl } };
|
|
@@ -89429,14 +90057,35 @@ var MultiRunArgsError = class extends Error {
|
|
|
89429
90057
|
};
|
|
89430
90058
|
var MAX_REPEAT = 20;
|
|
89431
90059
|
var PERSONA_NAME = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/;
|
|
89432
|
-
function persona(name, storageState, base, noun = "persona", login) {
|
|
90060
|
+
function persona(name, storageState, base, noun = "persona", login, context) {
|
|
89433
90061
|
if (!PERSONA_NAME.test(name)) {
|
|
89434
90062
|
throw new MultiRunArgsError(`${noun} name ${JSON.stringify(name)} must be 1-64 of [A-Za-z0-9_.-], starting alphanumeric`);
|
|
89435
90063
|
}
|
|
89436
90064
|
if (storageState.trim() === "") throw new MultiRunArgsError(`${noun} ${name}: storage state path is empty`);
|
|
89437
90065
|
const path2 = isAbsolute9(storageState) ? storageState : resolve11(base, storageState);
|
|
89438
90066
|
if (login === void 0 && !existsSync11(path2)) throw new MultiRunArgsError(`${noun} ${name}: storage state not found: ${path2}`);
|
|
89439
|
-
return {
|
|
90067
|
+
return {
|
|
90068
|
+
name,
|
|
90069
|
+
storageState: path2,
|
|
90070
|
+
...login === void 0 ? {} : { login },
|
|
90071
|
+
...context === void 0 ? {} : { context }
|
|
90072
|
+
};
|
|
90073
|
+
}
|
|
90074
|
+
function entryContext(v, where) {
|
|
90075
|
+
if (v === void 0) return void 0;
|
|
90076
|
+
if (!isRecord(v)) throw new MultiRunArgsError(`${where} must be an object {"organization"?: string, "role"?: string}`);
|
|
90077
|
+
for (const key2 of Object.keys(v)) {
|
|
90078
|
+
if (key2 !== "organization" && key2 !== "role") {
|
|
90079
|
+
throw new MultiRunArgsError(`${where} has unknown key ${JSON.stringify(key2)} \u2014 allowed keys are "organization" and "role"`);
|
|
90080
|
+
}
|
|
90081
|
+
}
|
|
90082
|
+
const { organization, role } = v;
|
|
90083
|
+
if (organization !== void 0 && typeof organization !== "string") throw new MultiRunArgsError(`${where}: organization must be text`);
|
|
90084
|
+
if (role !== void 0 && typeof role !== "string") throw new MultiRunArgsError(`${where}: role must be text`);
|
|
90085
|
+
return {
|
|
90086
|
+
...organization === void 0 ? {} : { organization },
|
|
90087
|
+
...role === void 0 ? {} : { role }
|
|
90088
|
+
};
|
|
89440
90089
|
}
|
|
89441
90090
|
function entryLogin(v, where) {
|
|
89442
90091
|
if (v === void 0) return void 0;
|
|
@@ -89514,7 +90163,16 @@ function loadPersonasFile(path2, opts = {}) {
|
|
|
89514
90163
|
continue;
|
|
89515
90164
|
}
|
|
89516
90165
|
if (typeof value.storageState === "string") {
|
|
89517
|
-
out.push(
|
|
90166
|
+
out.push(
|
|
90167
|
+
persona(
|
|
90168
|
+
name,
|
|
90169
|
+
value.storageState,
|
|
90170
|
+
base,
|
|
90171
|
+
"persona",
|
|
90172
|
+
entryLogin(value.login, `personas file ${path2}: ${name}.login`),
|
|
90173
|
+
entryContext(value.context, `personas file ${path2}: ${name}.context`)
|
|
90174
|
+
)
|
|
90175
|
+
);
|
|
89518
90176
|
continue;
|
|
89519
90177
|
}
|
|
89520
90178
|
if (value.storageState === void 0 && value.login === void 0 && CATALOG_ONLY_KEYS.some((k) => value[k] !== void 0)) continue;
|
|
@@ -89527,7 +90185,7 @@ function loadPersonasFile(path2, opts = {}) {
|
|
|
89527
90185
|
}
|
|
89528
90186
|
return out;
|
|
89529
90187
|
}
|
|
89530
|
-
var CATALOG_ONLY_KEYS = ["description", "role", "approval"];
|
|
90188
|
+
var CATALOG_ONLY_KEYS = ["description", "role", "approval", "context"];
|
|
89531
90189
|
function wantsMultiRun(f2) {
|
|
89532
90190
|
return f2.repeat !== void 0 || f2.minAgreement !== void 0 || f2.persona.length > 0 || f2.personas !== void 0;
|
|
89533
90191
|
}
|
|
@@ -90048,6 +90706,7 @@ var SUITE_EXPLORE_OPTIONS = {
|
|
|
90048
90706
|
hangReplayWrites: { shape: "boolean", appliesTo: ALL_KINDS },
|
|
90049
90707
|
settleIgnore: { shape: "strings", appliesTo: ALL_KINDS },
|
|
90050
90708
|
longPollMs: { shape: "integer", appliesTo: ALL_KINDS },
|
|
90709
|
+
devServer: { shape: "boolean", appliesTo: ALL_KINDS },
|
|
90051
90710
|
apiPrefix: { shape: "strings", appliesTo: ALL_KINDS },
|
|
90052
90711
|
ignoreNoProgress: { shape: "strings", appliesTo: ALL_KINDS },
|
|
90053
90712
|
hangReplays: { shape: "count", appliesTo: ["goal", "adversarial"] },
|
|
@@ -92280,6 +92939,10 @@ function parseTarget(v, where, baseDir) {
|
|
|
92280
92939
|
}
|
|
92281
92940
|
settle2.longPollMs = s3.longPollMs;
|
|
92282
92941
|
}
|
|
92942
|
+
if (s3.devServer !== void 0) {
|
|
92943
|
+
if (typeof s3.devServer !== "boolean") throw new TargetConfigError(`${where}.settle.devServer must be a boolean`);
|
|
92944
|
+
settle2.devServer = s3.devServer;
|
|
92945
|
+
}
|
|
92283
92946
|
out.settle = settle2;
|
|
92284
92947
|
}
|
|
92285
92948
|
if (o.hangs !== void 0) {
|
|
@@ -92355,6 +93018,7 @@ function resolveTargetConfig(file2, origin, flags = {}) {
|
|
|
92355
93018
|
const base = file2[origin] ?? {};
|
|
92356
93019
|
const ignoreRequests = [...base.settle?.ignoreRequests ?? [], ...flags.settleIgnore ?? []];
|
|
92357
93020
|
const longPollMs = flags.longPollMs ?? base.settle?.longPollMs;
|
|
93021
|
+
const devServer = flags.devServer === true || base.settle?.devServer === true;
|
|
92358
93022
|
const ignoreNoProgress = [...base.hangs?.ignoreNoProgress ?? [], ...flags.ignoreNoProgress ?? []];
|
|
92359
93023
|
const apiPrefixes = [...base.timing?.apiPrefixes ?? [], ...flags.apiPrefixes ?? []];
|
|
92360
93024
|
const deny = [...base.safety?.deny ?? [], ...flags.deny ?? []];
|
|
@@ -92380,7 +93044,7 @@ function resolveTargetConfig(file2, origin, flags = {}) {
|
|
|
92380
93044
|
return {
|
|
92381
93045
|
...Object.keys(safety).length === 0 ? {} : { safety },
|
|
92382
93046
|
timing: apiPrefixes.length === 0 ? {} : { apiPrefixes },
|
|
92383
|
-
settle: { ...ignoreRequests.length === 0 ? {} : { ignoreRequests }, ...longPollMs === void 0 ? {} : { longPollMs } },
|
|
93047
|
+
settle: { ...ignoreRequests.length === 0 ? {} : { ignoreRequests }, ...longPollMs === void 0 ? {} : { longPollMs }, ...devServer ? { devServer } : {} },
|
|
92384
93048
|
hangs: ignoreNoProgress.length === 0 ? {} : { ignoreNoProgress },
|
|
92385
93049
|
...base.fixtures === void 0 ? {} : { fixtures: base.fixtures },
|
|
92386
93050
|
// #142 follow-up: pass-through only — no CLI flag merges into these (a `--log-source` on the
|
|
@@ -93280,6 +93944,7 @@ async function runExploration(opts) {
|
|
|
93280
93944
|
assertionPassed: mission.assertionPassed,
|
|
93281
93945
|
checks: mission.checks,
|
|
93282
93946
|
...mission.warnings === void 0 ? {} : { checkWarnings: mission.warnings },
|
|
93947
|
+
...mission.attempts === void 0 ? {} : { attempts: mission.attempts },
|
|
93283
93948
|
...(() => {
|
|
93284
93949
|
const lost = sessionLostReason({
|
|
93285
93950
|
target: primaryState === void 0 ? {} : { storageStatePath: resolvePath7(primaryState) },
|
|
@@ -95008,6 +95673,7 @@ var TARGET_FLAG_KEYS = [
|
|
|
95008
95673
|
"hangReplayWrites",
|
|
95009
95674
|
"settleIgnore",
|
|
95010
95675
|
"longPollMs",
|
|
95676
|
+
"devServer",
|
|
95011
95677
|
"apiPrefix",
|
|
95012
95678
|
"ignoreNoProgress"
|
|
95013
95679
|
];
|
|
@@ -95048,7 +95714,8 @@ function itemSetup(p, item, kind, opts, refs, prefix) {
|
|
|
95048
95714
|
...x.allowWrites === true ? { allowWrites: true } : {},
|
|
95049
95715
|
...x.allowWrite === void 0 ? {} : { allowWrite: x.allowWrite },
|
|
95050
95716
|
...x.hangReplayWrites === true ? { hangReplayWrites: true } : {},
|
|
95051
|
-
...x.longPollMs === void 0 ? {} : { longPollMs: x.longPollMs }
|
|
95717
|
+
...x.longPollMs === void 0 ? {} : { longPollMs: x.longPollMs },
|
|
95718
|
+
...x.devServer === true ? { devServer: true } : {}
|
|
95052
95719
|
});
|
|
95053
95720
|
}
|
|
95054
95721
|
let serverLog = p.serverLog;
|
|
@@ -105137,7 +105804,7 @@ import { basename as basename16, dirname as dirname24, join as join56, resolve a
|
|
|
105137
105804
|
var CATALOG_CONTRACT_IDENTITY = {
|
|
105138
105805
|
"kind": "jevitate.catalog-contract",
|
|
105139
105806
|
"version": "2.0.0",
|
|
105140
|
-
"manifestSha256": "
|
|
105807
|
+
"manifestSha256": "c0fa630296bfb654da15d3949b77e7ce19a984846baa147ea9d4a0ce3b2d438a"
|
|
105141
105808
|
};
|
|
105142
105809
|
|
|
105143
105810
|
// src/catalog-bundle.ts
|
|
@@ -114498,13 +115165,39 @@ import { dirname as dirname31, isAbsolute as isAbsolute13, join as join71, resol
|
|
|
114498
115165
|
|
|
114499
115166
|
// src/campaign-preflight.ts
|
|
114500
115167
|
import { basename as basename18 } from "node:path";
|
|
115168
|
+
var CampaignHostStarvedError = class extends Error {
|
|
115169
|
+
code = "E_HOST_STARVED";
|
|
115170
|
+
constructor(message2) {
|
|
115171
|
+
super(message2);
|
|
115172
|
+
this.name = "CampaignHostStarvedError";
|
|
115173
|
+
}
|
|
115174
|
+
};
|
|
115175
|
+
function readinessReason(r) {
|
|
115176
|
+
if (r.class === "ready") return null;
|
|
115177
|
+
if (r.class === "http") return r.status !== void 0 && r.status < 500 ? null : r.detail;
|
|
115178
|
+
return r.detail;
|
|
115179
|
+
}
|
|
115180
|
+
function contextMismatch(declared, session2) {
|
|
115181
|
+
for (const field3 of ["organization", "role"]) {
|
|
115182
|
+
const d = declared[field3];
|
|
115183
|
+
const s3 = session2[field3];
|
|
115184
|
+
if (d !== void 0 && s3 !== void 0 && d !== s3) return `its ${field3} ${JSON.stringify(s3)} does not match the Journey's ${field3} ${JSON.stringify(d)}`;
|
|
115185
|
+
}
|
|
115186
|
+
return null;
|
|
115187
|
+
}
|
|
114501
115188
|
var SESSION_CHECK_TIMEOUT_MS = 3e4;
|
|
114502
|
-
function defaultPreflightDeps(portFactory = () => new PlaywrightBrowserPort()) {
|
|
115189
|
+
function defaultPreflightDeps(portFactory = () => new PlaywrightBrowserPort(), opts = {}) {
|
|
114503
115190
|
return {
|
|
114504
|
-
|
|
115191
|
+
// #746: an HTTP origin-readiness probe (dns/connect/http/ready); 4xx on the origin root still means up.
|
|
115192
|
+
readiness: (url2) => probeOriginReadiness(url2),
|
|
114505
115193
|
checkSession: async (o) => {
|
|
114506
115194
|
const r = await ensurePersonaSession({ ...o, check: { mode: "auto" }, port: portFactory(), env: {}, timeoutMs: SESSION_CHECK_TIMEOUT_MS });
|
|
114507
115195
|
return r.ok ? { ok: true } : { ok: false, reason: r.reason, landedUrl: r.landedUrl ?? "" };
|
|
115196
|
+
},
|
|
115197
|
+
personaContext: (storageState) => projectPersonaFor(storageState)?.context,
|
|
115198
|
+
hostReadiness: async () => {
|
|
115199
|
+
const refusal = await resourcePreflight({ ...opts.ignoreHostLoad === true ? { ignoreHostLoad: true } : {} });
|
|
115200
|
+
return refusal === null ? null : refusal.error?.message ?? "the host is starved";
|
|
114508
115201
|
}
|
|
114509
115202
|
};
|
|
114510
115203
|
}
|
|
@@ -114517,7 +115210,7 @@ function checkableUrl(url2) {
|
|
|
114517
115210
|
}
|
|
114518
115211
|
}
|
|
114519
115212
|
async function checkCampaignTargets(jobs, deps) {
|
|
114520
|
-
const probe = deps.probe ?? ((url2) =>
|
|
115213
|
+
const probe = deps.probe ?? (async (url2) => readinessReason(await (deps.readiness ?? probeOriginReadiness)(url2)));
|
|
114521
115214
|
const problems = [];
|
|
114522
115215
|
const probed = /* @__PURE__ */ new Map();
|
|
114523
115216
|
const sessions = /* @__PURE__ */ new Map();
|
|
@@ -114564,6 +115257,19 @@ ${url2}`;
|
|
|
114564
115257
|
});
|
|
114565
115258
|
}
|
|
114566
115259
|
}
|
|
115260
|
+
for (const job of jobs) {
|
|
115261
|
+
if (job.storageState === void 0 || job.declaredContext === void 0) continue;
|
|
115262
|
+
const session2 = deps.personaContext?.(job.storageState);
|
|
115263
|
+
if (session2 === void 0) continue;
|
|
115264
|
+
const mismatch = contextMismatch(job.declaredContext, session2);
|
|
115265
|
+
if (mismatch !== null) {
|
|
115266
|
+
problems.push({
|
|
115267
|
+
check: "context",
|
|
115268
|
+
job: job.id,
|
|
115269
|
+
message: `job ${job.id}: the persona session in ${basename18(job.storageState)} declares ${mismatch} \u2014 every mission would run as the wrong persona; re-save it with \`jevitate login\` for the Journey's persona (or point the job's storageState at that session)`
|
|
115270
|
+
});
|
|
115271
|
+
}
|
|
115272
|
+
}
|
|
114567
115273
|
return { problems };
|
|
114568
115274
|
}
|
|
114569
115275
|
function allowOriginProblems(job, writeOrigins) {
|
|
@@ -114732,6 +115438,7 @@ async function validateCampaignSpec(raw, specPath, opts) {
|
|
|
114732
115438
|
}
|
|
114733
115439
|
}
|
|
114734
115440
|
totalRuns += anchors.length * job.strategies.length;
|
|
115441
|
+
const declaredContext = declaredContextOf(full.metadata);
|
|
114735
115442
|
jobs.push({
|
|
114736
115443
|
id: job.id,
|
|
114737
115444
|
journey: job.journey,
|
|
@@ -114739,6 +115446,7 @@ async function validateCampaignSpec(raw, specPath, opts) {
|
|
|
114739
115446
|
...session2 === void 0 ? {} : { storageState: session2 },
|
|
114740
115447
|
startUrl: journeyStartUrl(full.recording),
|
|
114741
115448
|
allowlist: environment === void 0 ? [new URL(full.recording.site).origin] : [...environment.allowedOrigins],
|
|
115449
|
+
...declaredContext === void 0 ? {} : { declaredContext },
|
|
114742
115450
|
anchors,
|
|
114743
115451
|
strategies: job.strategies,
|
|
114744
115452
|
...job.goal === void 0 ? {} : { goal: job.goal },
|
|
@@ -114770,6 +115478,13 @@ function journeyStartUrl(recording) {
|
|
|
114770
115478
|
return recording.site;
|
|
114771
115479
|
}
|
|
114772
115480
|
}
|
|
115481
|
+
function declaredContextOf(metadata) {
|
|
115482
|
+
if (metadata.persona !== void 0) {
|
|
115483
|
+
const context = projectPersonas().find((p) => p.name === metadata.persona)?.context;
|
|
115484
|
+
if (context !== void 0) return context;
|
|
115485
|
+
}
|
|
115486
|
+
return metadata.role === void 0 ? void 0 : { role: metadata.role };
|
|
115487
|
+
}
|
|
114773
115488
|
async function runChild(newProgram, argv) {
|
|
114774
115489
|
const lines = [];
|
|
114775
115490
|
const child = newProgram();
|
|
@@ -114834,9 +115549,12 @@ async function runCampaign(plan2, opts) {
|
|
|
114834
115549
|
journey: job.journey,
|
|
114835
115550
|
startUrl: job.startUrl ?? "",
|
|
114836
115551
|
allowlist: job.allowlist ?? [],
|
|
114837
|
-
...job.storageState === void 0 ? {} : { storageState: job.storageState }
|
|
115552
|
+
...job.storageState === void 0 ? {} : { storageState: job.storageState },
|
|
115553
|
+
...job.declaredContext === void 0 ? {} : { declaredContext: job.declaredContext }
|
|
114838
115554
|
}));
|
|
114839
115555
|
if (opts.preflight !== void 0 && opts.preflight !== "skip") {
|
|
115556
|
+
const starved = opts.ignoreHostLoad === true || opts.preflight.hostReadiness === void 0 ? null : await opts.preflight.hostReadiness();
|
|
115557
|
+
if (starved !== null) throw new CampaignHostStarvedError(`the host is starved, so no mission would give a trustworthy result: ${starved}`);
|
|
114840
115558
|
const targets = await checkCampaignTargets(preflightJobs.filter((j) => j.startUrl !== ""), opts.preflight);
|
|
114841
115559
|
preflightProblems.push(...targets.problems);
|
|
114842
115560
|
blocked = new Set(targets.problems.map((p) => p.job));
|
|
@@ -115002,7 +115720,7 @@ function campaignMarkdown(r, reportMarkdown) {
|
|
|
115002
115720
|
"",
|
|
115003
115721
|
`${r.missions.length} anchored mission(s) \xB7 outcome ${r.missionOutcome} (exit ${r.exitCode}) \xB7 ${r.report.summary.defects} deduped defect(s), ${r.report.summary.advisory} advisory.`,
|
|
115004
115722
|
"",
|
|
115005
|
-
...r.preflight === void 0 ? [] : ["## Preflight", "", r.preflight.skipped === true ? "Skipped (--skip-preflight)." : "Passed: every start URL
|
|
115723
|
+
...r.preflight === void 0 ? [] : ["## Preflight", "", r.preflight.skipped === true ? "Skipped (--skip-preflight)." : "Passed: every start URL ready, every session signed in and from the declared persona context, no allow-list mismatch.", ""],
|
|
115006
115724
|
"## Discovery",
|
|
115007
115725
|
"",
|
|
115008
115726
|
...r.discovery.length === 0 ? ["Skipped (discovery: false)."] : ["| job | journey | outcome | reason |", "| --- | --- | --- | --- |", ...r.discovery.map((d) => `| ${d.job} | ${d.journeyId} | ${d.outcome} | ${(d.reason ?? "").replace(/\|/g, "\\|")} |`)],
|
|
@@ -115070,6 +115788,9 @@ function registerCampaignCommands(program2, deps, buildProgram2) {
|
|
|
115070
115788
|
"--skip-preflight",
|
|
115071
115789
|
"do not check the campaign's preconditions first (start URLs reachable, sessions signed in, no allow-list origin mismatch in discovery) \u2014 by default a failed check refuses the campaign (exit 64) before any mission",
|
|
115072
115790
|
false
|
|
115791
|
+
).option(
|
|
115792
|
+
"--ignore-host-load",
|
|
115793
|
+
"start even when the host is starved (the campaign-start check and every mission) instead of refusing with E_HOST_STARVED; the runs are throttled and their results record it"
|
|
115073
115794
|
).option("--real", "use live Jev + OpenRouter gateways for the missions (requires keys)", false).option("--fake-ai", "use deterministic fake gateways (pipeline smoke only)", false).option("--jev-provider <provider>", JEV_PROVIDER_FLAG_HELP, jevProviderArg).option("--json", "emit the JSON envelope (default: a human summary)").option(TAG_FLAG, TAG_HELP, collectTag, []).action(taggedAction(program2, "campaign run", async function(spec) {
|
|
115074
115795
|
const o = this.opts();
|
|
115075
115796
|
const emit5 = (envelope, exitCode) => emitCommandResult(program2, envelope, { json: o.json === true, command: "campaign run", human: formatCampaignHuman, ...exitCode === void 0 ? {} : { exitCode } });
|
|
@@ -115093,12 +115814,14 @@ function registerCampaignCommands(program2, deps, buildProgram2) {
|
|
|
115093
115814
|
...o.real === true ? { real: true } : {},
|
|
115094
115815
|
...o.fakeAi === true ? { fakeAi: true } : {},
|
|
115095
115816
|
missionArgs: campaignMissionArgv(this),
|
|
115817
|
+
// #746: --ignore-host-load starts the campaign on a starved host (and is forwarded to every mission).
|
|
115818
|
+
...o.ignoreHostLoad === true ? { ignoreHostLoad: true } : {},
|
|
115096
115819
|
// #485: the preflight's checks open the same browser port the missions use.
|
|
115097
|
-
preflight: o.skipPreflight === true ? "skip" : defaultPreflightDeps(deps.explore?.browserPortFactory)
|
|
115820
|
+
preflight: o.skipPreflight === true ? "skip" : defaultPreflightDeps(deps.explore?.browserPortFactory, { ...o.ignoreHostLoad === true ? { ignoreHostLoad: true } : {} })
|
|
115098
115821
|
});
|
|
115099
115822
|
emit5(ok(withEngine(result)), result.exitCode);
|
|
115100
115823
|
} catch (err) {
|
|
115101
|
-
if (err instanceof CampaignSpecError || err instanceof CampaignPreflightError) emit5(fail(err.code, err.message));
|
|
115824
|
+
if (err instanceof CampaignSpecError || err instanceof CampaignPreflightError || err instanceof CampaignHostStarvedError) emit5(fail(err.code, err.message));
|
|
115102
115825
|
else emit5(fail("E_CAMPAIGN_RUN", String(err instanceof Error ? err.message : err)));
|
|
115103
115826
|
}
|
|
115104
115827
|
}));
|
|
@@ -115197,6 +115920,10 @@ var GOAL_RUN_OPTIONS = {
|
|
|
115197
115920
|
),
|
|
115198
115921
|
settleIgnore: () => new Option("--settle-ignore <pattern>", "a request URL pattern the target marks as background (never pending work; repeatable, * wildcard)").default([]).argParser(collect7),
|
|
115199
115922
|
longPollMs: () => new Option("--long-poll-ms <n>", "a request pending this long on an interactive page is a long-poll (default 5000)").argParser(nonNegativeIntArg),
|
|
115923
|
+
devServer: () => new Option(
|
|
115924
|
+
"--dev-server",
|
|
115925
|
+
"opt-in (#745): a Vite/HMR dev target discounts asset-classified reads (its own modules, scripts, styles, images) from settling. A non-asset write still keeps settle waiting and stays tracked, and a configured --api-prefix still wins over a Vite-shaped path"
|
|
115926
|
+
).default(false),
|
|
115200
115927
|
apiPrefix: () => new Option("--api-prefix <path>", "a path prefix whose requests are the app's API in the timing summary (repeatable), e.g. /api/").default([]).argParser(collect7),
|
|
115201
115928
|
ignoreNoProgress: () => new Option("--ignore-no-progress <pattern>", "a route / action label / busy indicator (its description or visible words) where ui-no-progress is expected (repeatable, * wildcard)").default([]).argParser(collect7),
|
|
115202
115929
|
allowSecretCmd: () => new Option(
|
|
@@ -115274,7 +116001,8 @@ function targetFlagsFromFlags(o, dialogs) {
|
|
|
115274
116001
|
...o.allowWrites === true ? { allowWrites: true } : {},
|
|
115275
116002
|
allowWrite: o.allowWrite ?? [],
|
|
115276
116003
|
...o.hangReplayWrites === true ? { hangReplayWrites: true } : {},
|
|
115277
|
-
...o.longPollMs === void 0 ? {} : { longPollMs: Number(o.longPollMs) }
|
|
116004
|
+
...o.longPollMs === void 0 ? {} : { longPollMs: Number(o.longPollMs) },
|
|
116005
|
+
...o.devServer === true ? { devServer: true } : {}
|
|
115278
116006
|
};
|
|
115279
116007
|
}
|
|
115280
116008
|
function resolveGoalRunFlags(o, url2, deps = {}) {
|
|
@@ -115489,7 +116217,7 @@ function declareExploreCommand(program2) {
|
|
|
115489
116217
|
).option("--issue-repo <owner/name>", "the system-under-test repo findings for THIS target are filed to").addOption(GOAL_RUN_OPTIONS.hangReplays()).option(
|
|
115490
116218
|
"--hang-replay-writes",
|
|
115491
116219
|
"let hang replays re-send a paid/destructive write the run sent (default: such a hang is reported inconclusive, never replayed)"
|
|
115492
|
-
).addOption(GOAL_RUN_OPTIONS.settleIgnore()).addOption(GOAL_RUN_OPTIONS.longPollMs()).addOption(GOAL_RUN_OPTIONS.apiPrefix()).addOption(GOAL_RUN_OPTIONS.ignoreNoProgress()).option("--jevitate-repo <owner/name>", "where jevitate engine findings are filed (default matt-cochran/jevitate)").option(
|
|
116220
|
+
).addOption(GOAL_RUN_OPTIONS.settleIgnore()).addOption(GOAL_RUN_OPTIONS.longPollMs()).addOption(GOAL_RUN_OPTIONS.devServer()).addOption(GOAL_RUN_OPTIONS.apiPrefix()).addOption(GOAL_RUN_OPTIONS.ignoreNoProgress()).option("--jevitate-repo <owner/name>", "where jevitate engine findings are filed (default matt-cochran/jevitate)").option(
|
|
115493
116221
|
"--min-control-coverage <ratio>",
|
|
115494
116222
|
"adversarial: share of the target's controls (0..1) a run must exercise before 'found nothing' is clean (default 0.25); below it the run is inconclusive",
|
|
115495
116223
|
ratioArg
|
|
@@ -116631,18 +117359,19 @@ function declaredPersona(name, personasFile) {
|
|
|
116631
117359
|
function registerLoginCommand(program2, deps) {
|
|
116632
117360
|
program2.command("login").description(
|
|
116633
117361
|
"#427: sign in as a persona with credentials from environment variables and save its Playwright storage state (mode 0600) \u2014 the session `explore --storage-state/--persona` starts from. Credentials are never accepted as values, never printed or recorded"
|
|
116634
|
-
).option("--persona <name>", "the persona being signed in (names it in the result); with a declared persona (--personas or .jevitate/personas.json) its login parameters and storage state path are the defaults").option("--personas <file>", "personas JSON to read --persona's login parameters from (default: the project's .jevitate/personas.json)").option("--url <loginUrl>", "the sign-in page (must be an authorized origin: its own, or --allow)").option("--api <url>", "#449: sign in through this HTTP endpoint instead of a form: the credentials are POSTed as JSON (must be an authorized origin; redirects are never followed)").option("--api-user-key <key>", "with --api: the JSON body key the username is sent under (default username)").option("--api-password-key <key>", "with --api: the JSON body key the password is sent under (default password)").option("--token-path <path>", "with --api: a dotted path into the JSON response whose value is the session token (e.g. token, data.accessToken); without it the response's cookies are the session").option("--storage-key <key>", "with --token-path: the localStorage key the token is written under (default: the path's last segment)").option("--storage <where>", "with --token-path: local (the only choice \u2014 a Playwright storage state has no sessionStorage)").option("--verify-url <url>", "with --api: the app page the new session is proven on, and whose origin receives the token (default: the endpoint's origin root)").option("--auth-check <check>", "with --api: how --verify-url proves the session: off | auto | urlExcludes:<text> | selector:<css> (default auto)").option("--user-env <VAR>", "environment variable holding the username (its NAME \u2014 the value is read from the environment)").option("--password-env <VAR>", "environment variable holding the password (its NAME \u2014 the value is read from the environment)").option("--user-field <field>", "the username field: its label, else a CSS selector (default: found by autocomplete/type/name)").option("--password-field <field>", "the password field: its label, else a CSS selector (default: the visible password input)").option("--submit <name>", "the submit button's accessible name (default: the form's submit button, else Enter)").option("--success <check>", "how a successful sign-in is recognised: urlIncludes:<text> | selector:<css> | text:<text> (default: the page leaves the sign-in form \u2014 no login-like URL, no password field)").option("--save <file>", "where to write the storage state (parent directory created; mode 0600; never inside a repo's .jevitate/)").option("--allow <origin>", "an origin credentials may be typed into (repeatable; default: the sign-in page's own) \u2014 e.g. an SSO provider", (v, prev) => [...prev, v], []).option("--timeout <seconds>", `how long each step of the sign-in may take (default ${DEFAULT_TIMEOUT_S})`, positiveIntArg).option("--json", "emit a JSON envelope").action(async function() {
|
|
117362
|
+
).option("--persona <name>", "the persona being signed in (names it in the result); with a declared persona (--personas or .jevitate/personas.json) its login parameters and storage state path are the defaults").option("--personas <file>", "personas JSON to read --persona's login parameters from (default: the project's .jevitate/personas.json)").option("--url <loginUrl>", "the sign-in page (must be an authorized origin: its own, or --allow)").option("--api <url>", "#449: sign in through this HTTP endpoint instead of a form: the credentials are POSTed as JSON (must be an authorized origin; redirects are never followed)").option("--api-user-key <key>", "with --api: the JSON body key the username is sent under (default username)").option("--api-password-key <key>", "with --api: the JSON body key the password is sent under (default password)").option("--token-path <path>", "with --api: a dotted path into the JSON response whose value is the session token (e.g. token, data.accessToken); without it the response's cookies are the session").option("--storage-key <key>", "with --token-path: the localStorage key the token is written under (default: the path's last segment)").option("--storage <where>", "with --token-path: local (the only choice \u2014 a Playwright storage state has no sessionStorage)").option("--verify-url <url>", "with --api: the app page the new session is proven on, and whose origin receives the token (default: the endpoint's origin root)").option("--auth-check <check>", "with --api: how --verify-url proves the session: off | auto | urlExcludes:<text> | selector:<css> (default auto)").option("--user-env <VAR>", "environment variable holding the username (its NAME \u2014 the value is read from the environment)").option("--password-env <VAR>", "environment variable holding the password (its NAME \u2014 the value is read from the environment)").option("--user-field <field>", "the username field: its label, else a CSS selector (default: found by autocomplete/type/name)").option("--password-field <field>", "the password field: its label, else a CSS selector (default: the visible password input)").option("--organization-field <field>", "#741: the organization selection control: its label, else a CSS selector (default: a control labelled organization/workspace/tenant/account, else a chooser card)").option("--role-field <field>", "#741: the role selection control: its label, else a CSS selector (default: a control labelled role, else a chooser card)").option("--submit <name>", "the submit button's accessible name (default: the form's submit button, else Enter)").option("--success <check>", "how a successful sign-in is recognised: urlIncludes:<text> | selector:<css> | text:<text> (default: the page leaves the sign-in form \u2014 no login-like URL, no password field)").option("--save <file>", "where to write the storage state (parent directory created; mode 0600; never inside a repo's .jevitate/)").option("--allow <origin>", "an origin credentials may be typed into (repeatable; default: the sign-in page's own) \u2014 e.g. an SSO provider", (v, prev) => [...prev, v], []).option("--timeout <seconds>", `how long each step of the sign-in may take (default ${DEFAULT_TIMEOUT_S})`, positiveIntArg).option("--json", "emit a JSON envelope").action(async function() {
|
|
116635
117363
|
const o = this.opts();
|
|
116636
117364
|
const emit5 = (envelope) => emitCommandResult(program2, envelope, { json: o.json === true, command: "login", human: formatLoginHuman });
|
|
116637
117365
|
let login;
|
|
116638
117366
|
let save;
|
|
116639
117367
|
let warnings;
|
|
116640
117368
|
let authCheck;
|
|
117369
|
+
let declared;
|
|
116641
117370
|
try {
|
|
116642
117371
|
if (o.persona !== void 0 && !PERSONA_NAME2.test(o.persona)) {
|
|
116643
117372
|
throw new LoginArgsError(`--persona ${JSON.stringify(o.persona)} must be 1-64 of [A-Za-z0-9_.-], starting alphanumeric`);
|
|
116644
117373
|
}
|
|
116645
|
-
|
|
117374
|
+
declared = o.persona === void 0 ? void 0 : declaredPersona(o.persona, o.personas);
|
|
116646
117375
|
if (o.personas !== void 0 && o.persona === void 0) throw new LoginArgsError("--personas reads a declared persona's login parameters: pass --persona <name> too");
|
|
116647
117376
|
if (o.personas !== void 0 && declared === void 0) throw new LoginArgsError(`persona ${o.persona} is not declared in ${o.personas}`);
|
|
116648
117377
|
const base = declared?.login;
|
|
@@ -116690,7 +117419,9 @@ function registerLoginCommand(program2, deps) {
|
|
|
116690
117419
|
...pick3("userField", o.userField ?? (viaApi ? void 0 : base?.userField)),
|
|
116691
117420
|
...pick3("passwordField", o.passwordField ?? (viaApi ? void 0 : base?.passwordField)),
|
|
116692
117421
|
...pick3("submit", o.submit ?? (viaApi ? void 0 : base?.submit)),
|
|
116693
|
-
...pick3("success", o.success ?? (viaApi ? void 0 : base?.success))
|
|
117422
|
+
...pick3("success", o.success ?? (viaApi ? void 0 : base?.success)),
|
|
117423
|
+
...pick3("organizationField", viaApi ? void 0 : base?.organizationField),
|
|
117424
|
+
...pick3("roleField", viaApi ? void 0 : base?.roleField)
|
|
116694
117425
|
});
|
|
116695
117426
|
({ path: save, warnings } = checkSaveTarget(target, "--save"));
|
|
116696
117427
|
} catch (err) {
|
|
@@ -116712,6 +117443,7 @@ function registerLoginCommand(program2, deps) {
|
|
|
116712
117443
|
env: deps.explore?.env ?? process.env,
|
|
116713
117444
|
port: (deps.explore?.browserPortFactory ?? (() => new PlaywrightBrowserPort()))(),
|
|
116714
117445
|
timeoutMs: (o.timeout ?? DEFAULT_TIMEOUT_S) * 1e3,
|
|
117446
|
+
...declared?.context === void 0 ? {} : { context: declared.context },
|
|
116715
117447
|
...login.api === void 0 ? {} : { verify: { url: apiVerifyUrl(login.api), check: authCheck } }
|
|
116716
117448
|
});
|
|
116717
117449
|
const result = {
|
|
@@ -116722,11 +117454,13 @@ function registerLoginCommand(program2, deps) {
|
|
|
116722
117454
|
landedUrl: minted.landedUrl,
|
|
116723
117455
|
userEnv: login.userEnv,
|
|
116724
117456
|
passwordEnv: login.passwordEnv,
|
|
117457
|
+
...declared?.context === void 0 ? {} : { context: declared.context },
|
|
116725
117458
|
warnings
|
|
116726
117459
|
};
|
|
116727
117460
|
emit5(ok(result));
|
|
116728
117461
|
} catch (err) {
|
|
116729
117462
|
if (err instanceof LoginArgsError) emit5(fail("E_LOGIN_ARGS", err.message));
|
|
117463
|
+
else if (err instanceof LoginContextError) emit5(fail("E_LOGIN_CONTEXT", err.message));
|
|
116730
117464
|
else if (err instanceof LoginFailedError) emit5(fail("E_LOGIN_FAILED", err.message));
|
|
116731
117465
|
else throw err;
|
|
116732
117466
|
}
|
|
@@ -117329,6 +118063,7 @@ var CLI_TOOL_SPECS = [
|
|
|
117329
118063
|
checkOverflow: b("--check-overflow"),
|
|
117330
118064
|
apiPrefix: many("--api-prefix"),
|
|
117331
118065
|
longPollMs: n2("--long-poll-ms"),
|
|
118066
|
+
devServer: b("--dev-server"),
|
|
117332
118067
|
jobWaitMs: n2("--job-wait-ms"),
|
|
117333
118068
|
replyWaitMs: n2("--reply-wait-ms"),
|
|
117334
118069
|
replyQuietMs: n2("--reply-quiet-ms"),
|
|
@@ -117394,6 +118129,8 @@ var CLI_TOOL_SPECS = [
|
|
|
117394
118129
|
evidenceVideo: b("--evidence-video"),
|
|
117395
118130
|
// #485: like run_exploration authCheck "off" — an explicit opt-out, recorded on the report (preflight.skipped).
|
|
117396
118131
|
skipPreflight: b("--skip-preflight"),
|
|
118132
|
+
// #205/#746: start on a starved host (the campaign-start check and every mission) instead of E_HOST_STARVED.
|
|
118133
|
+
ignoreHostLoad: b("--ignore-host-load"),
|
|
117397
118134
|
...JEV_AI
|
|
117398
118135
|
},
|
|
117399
118136
|
omitted: {
|
|
@@ -117504,6 +118241,7 @@ var CLI_TOOL_SPECS = [
|
|
|
117504
118241
|
hangReplays: n2("--hang-replays"),
|
|
117505
118242
|
settleIgnore: many("--settle-ignore"),
|
|
117506
118243
|
longPollMs: n2("--long-poll-ms"),
|
|
118244
|
+
devServer: b("--dev-server"),
|
|
117507
118245
|
apiPrefix: many("--api-prefix"),
|
|
117508
118246
|
ignoreNoProgress: many("--ignore-no-progress"),
|
|
117509
118247
|
// #544: mission fixtures set up before / restored after every take; the Journey records the file.
|
|
@@ -118954,6 +119692,7 @@ var AUTHOR_GOAL_RUN_FLAGS = [
|
|
|
118954
119692
|
"hangReplays",
|
|
118955
119693
|
"settleIgnore",
|
|
118956
119694
|
"longPollMs",
|
|
119695
|
+
"devServer",
|
|
118957
119696
|
"apiPrefix",
|
|
118958
119697
|
"ignoreNoProgress",
|
|
118959
119698
|
"allowSecretCmd",
|
|
@@ -118991,6 +119730,42 @@ function formatAuthorJourneyHuman(data) {
|
|
|
118991
119730
|
return `${lines.join("\n")}
|
|
118992
119731
|
`;
|
|
118993
119732
|
}
|
|
119733
|
+
var REQUIRED_AUTHOR_ARGS = [
|
|
119734
|
+
{ flag: "--url", attr: "url", placeholder: "<url>" },
|
|
119735
|
+
{ flag: "--goal", attr: "goal", placeholder: "<text>" },
|
|
119736
|
+
{ flag: "--success", attr: "success", placeholder: "<spec>" },
|
|
119737
|
+
{ flag: "--id", attr: "id", placeholder: "<id>" },
|
|
119738
|
+
{ flag: "--name", attr: "name", placeholder: "<name>" }
|
|
119739
|
+
];
|
|
119740
|
+
var SECRET_ARG_FLAGS = /* @__PURE__ */ new Set(["--secret", "--secret-field", "--totp"]);
|
|
119741
|
+
function quoteArg(v) {
|
|
119742
|
+
return /[\s"'`$\\|&;<>(){}*?!#]/.test(v) ? JSON.stringify(v) : v;
|
|
119743
|
+
}
|
|
119744
|
+
function missingAuthorArgs(opts) {
|
|
119745
|
+
return REQUIRED_AUTHOR_ARGS.filter((a) => {
|
|
119746
|
+
const v = opts[a.attr];
|
|
119747
|
+
return v === void 0 || v === "" || Array.isArray(v) && v.length === 0;
|
|
119748
|
+
});
|
|
119749
|
+
}
|
|
119750
|
+
function authorCorrectedInvocation(cmd, opts, missing) {
|
|
119751
|
+
const parts2 = ["jevitate", "explore-author-journey"];
|
|
119752
|
+
for (const opt of cmd.options) {
|
|
119753
|
+
if (opt.long === void 0 || opt.long === "--json") continue;
|
|
119754
|
+
const value = opts[opt.attributeName()];
|
|
119755
|
+
if (value === void 0 || value === false) continue;
|
|
119756
|
+
if (Array.isArray(value)) {
|
|
119757
|
+
for (const item of value) parts2.push(opt.long, SECRET_ARG_FLAGS.has(opt.long) ? "<redacted>" : quoteArg(String(item)));
|
|
119758
|
+
continue;
|
|
119759
|
+
}
|
|
119760
|
+
if (value === true) {
|
|
119761
|
+
parts2.push(opt.long);
|
|
119762
|
+
continue;
|
|
119763
|
+
}
|
|
119764
|
+
parts2.push(opt.long, SECRET_ARG_FLAGS.has(opt.long) ? "<redacted>" : quoteArg(String(value)));
|
|
119765
|
+
}
|
|
119766
|
+
for (const m of missing) parts2.push(m.flag, m.placeholder);
|
|
119767
|
+
return parts2.join(" ");
|
|
119768
|
+
}
|
|
118994
119769
|
function registerAuthorJourneyCommands(program2, deps) {
|
|
118995
119770
|
const cmd = withServerLogFlags(
|
|
118996
119771
|
withBrowserLaunchFlags(
|
|
@@ -119026,12 +119801,26 @@ function registerAuthorJourneyCommands(program2, deps) {
|
|
|
119026
119801
|
].join("\n")
|
|
119027
119802
|
).action(async function() {
|
|
119028
119803
|
const o = this.opts();
|
|
119804
|
+
const optsRecord = o;
|
|
119029
119805
|
if (o.storageState !== void 0 && !existsSync41(o.storageState)) {
|
|
119030
|
-
emitJson(
|
|
119806
|
+
emitJson(
|
|
119807
|
+
program2,
|
|
119808
|
+
fail(
|
|
119809
|
+
"E_EXPLORE_AUTHOR_JOURNEY_ARGS",
|
|
119810
|
+
`--storage-state <file> not found: ${o.storageState} \u2014 corrected invocation: ${authorCorrectedInvocation(this, optsRecord, missingAuthorArgs(optsRecord))}`
|
|
119811
|
+
)
|
|
119812
|
+
);
|
|
119031
119813
|
return;
|
|
119032
119814
|
}
|
|
119033
119815
|
if (!o.url || !o.goal || o.success.length === 0 || !o.id || !o.name) {
|
|
119034
|
-
|
|
119816
|
+
const missingArgs = missingAuthorArgs(optsRecord);
|
|
119817
|
+
emitJson(
|
|
119818
|
+
program2,
|
|
119819
|
+
fail(
|
|
119820
|
+
"E_EXPLORE_AUTHOR_JOURNEY_ARGS",
|
|
119821
|
+
`${missingArgs.map((m) => m.flag).join(", ")} ${missingArgs.length === 1 ? "is" : "are"} required \u2014 corrected invocation: ${authorCorrectedInvocation(this, optsRecord, missingArgs)}`
|
|
119822
|
+
)
|
|
119823
|
+
);
|
|
119035
119824
|
return;
|
|
119036
119825
|
}
|
|
119037
119826
|
const successChecks = [];
|