@jevitate/cli 0.4.0 → 0.6.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.
Files changed (72) hide show
  1. package/dist/author-journey-cli.d.ts +2 -0
  2. package/dist/author-journey-cli.js +106 -11
  3. package/dist/author-journey-cli.js.map +1 -1
  4. package/dist/bin.js +2988 -776
  5. package/dist/build-info.generated.js +2 -2
  6. package/dist/check-execute.js +1 -0
  7. package/dist/check-execute.js.map +1 -1
  8. package/dist/check-plan.js +2 -1
  9. package/dist/check-plan.js.map +1 -1
  10. package/dist/check-suite.d.ts +6 -6
  11. package/dist/check-suite.js +18 -12
  12. package/dist/check-suite.js.map +1 -1
  13. package/dist/cli-shared.d.ts +5 -2
  14. package/dist/cli-shared.js +10 -3
  15. package/dist/cli-shared.js.map +1 -1
  16. package/dist/conversation-options.d.ts +3 -0
  17. package/dist/conversation-options.js +1 -0
  18. package/dist/conversation-options.js.map +1 -1
  19. package/dist/demo-aspect-api.js +2 -0
  20. package/dist/demo-aspect-api.js.map +1 -1
  21. package/dist/demo-capture.d.ts +16 -4
  22. package/dist/demo-capture.js +82 -15
  23. package/dist/demo-capture.js.map +1 -1
  24. package/dist/exit-codes.d.ts +1 -1
  25. package/dist/exit-codes.js +1 -1
  26. package/dist/explore-author.d.ts +28 -6
  27. package/dist/explore-author.js +66 -32
  28. package/dist/explore-author.js.map +1 -1
  29. package/dist/explore-cli.js +68 -79
  30. package/dist/explore-cli.js.map +1 -1
  31. package/dist/explore-goal.d.ts +5 -1
  32. package/dist/explore-goal.js +19 -1
  33. package/dist/explore-goal.js.map +1 -1
  34. package/dist/explore-specs.js +12 -2
  35. package/dist/explore-specs.js.map +1 -1
  36. package/dist/goal-run-flags.d.ts +120 -0
  37. package/dist/goal-run-flags.js +220 -0
  38. package/dist/goal-run-flags.js.map +1 -0
  39. package/dist/journey-api.js +4 -1
  40. package/dist/journey-api.js.map +1 -1
  41. package/dist/journey-network-checks.d.ts +15 -0
  42. package/dist/journey-network-checks.js +36 -0
  43. package/dist/journey-network-checks.js.map +1 -0
  44. package/dist/kill-signal.d.ts +30 -3
  45. package/dist/kill-signal.js +51 -6
  46. package/dist/kill-signal.js.map +1 -1
  47. package/dist/mcp-api.js +8 -4
  48. package/dist/mcp-api.js.map +1 -1
  49. package/dist/mcp-cli-tools.js +43 -5
  50. package/dist/mcp-cli-tools.js.map +1 -1
  51. package/dist/mission-queue-cli.js +3 -1
  52. package/dist/mission-queue-cli.js.map +1 -1
  53. package/dist/secret-command.d.ts +7 -0
  54. package/dist/secret-command.js +52 -0
  55. package/dist/secret-command.js.map +1 -0
  56. package/dist/signal-triage.js +5 -1
  57. package/dist/signal-triage.js.map +1 -1
  58. package/dist/source-run-api.js +3 -1
  59. package/dist/source-run-api.js.map +1 -1
  60. package/dist/suite-explore-options.d.ts +9 -0
  61. package/dist/suite-explore-options.js +5 -0
  62. package/dist/suite-explore-options.js.map +1 -1
  63. package/dist/target-config.d.ts +3 -1
  64. package/dist/target-config.js +6 -0
  65. package/dist/target-config.js.map +1 -1
  66. package/dist/usability-capture.js +21 -3
  67. package/dist/usability-capture.js.map +1 -1
  68. package/dist/ux-usability.d.ts +5 -1
  69. package/dist/ux-usability.js +19 -2
  70. package/dist/ux-usability.js.map +1 -1
  71. package/package.json +7 -7
  72. package/skills/jevitate-explore/SKILL.md +7 -0
@@ -1,8 +1,7 @@
1
- import { PlaywrightBrowserPort } from "@jevitate/playwright";
2
- import { sessionLaunchOptions } from "./browser-run-options.js";
3
- import { CastActor, BrowseTheWeb } from "@jevitate/screenplay";
4
1
  import { authorJourney, assertAuthorizedExploreTarget, } from "@jevitate/explore";
5
2
  import { FsJourneyStore } from "@jevitate/journey";
3
+ import { runExploration } from "./explore-goal.js";
4
+ import { screenshotsDirFor } from "./run-screenshots.js";
6
5
  /**
7
6
  * The programmatic surface behind `jevitate explore author-journey` — drives
8
7
  * the goal-based exploration mission and feeds its take(s) through RxD's
@@ -23,7 +22,8 @@ export async function runAuthorJourney(opts) {
23
22
  url: opts.url,
24
23
  origin,
25
24
  goal: opts.goal,
26
- successAssertion: opts.successAssertion,
25
+ ...(opts.successAssertion === undefined ? {} : { successAssertion: opts.successAssertion }),
26
+ ...(opts.successChecks === undefined ? {} : { successChecks: opts.successChecks }),
27
27
  allowlist: opts.allowlist,
28
28
  judge: opts.judge,
29
29
  gen: opts.gen,
@@ -35,6 +35,9 @@ export async function runAuthorJourney(opts) {
35
35
  browser: opts.browser,
36
36
  ...(opts.storageState !== undefined ? { storageState: opts.storageState } : {}),
37
37
  ...(opts.safety === undefined ? {} : { safety: opts.safety }),
38
+ ...(opts.goalRun === undefined ? {} : { goalRun: opts.goalRun }),
39
+ ...(opts.outDir === undefined ? {} : { outDir: opts.outDir }),
40
+ ...(opts.usage === undefined ? {} : { usage: opts.usage }),
38
41
  });
39
42
  if (result.outcome === "authored") {
40
43
  // #170: a Journey authored behind a login (--storage-state) declares it, so a run without a
@@ -47,40 +50,71 @@ export async function runAuthorJourney(opts) {
47
50
  }
48
51
  return result;
49
52
  }
50
- /** Default authoring step: opens a real browser, builds an actor, authors. */
53
+ /**
54
+ * Default authoring step (#369): every take — the discovery and each corroborating one — is a full
55
+ * goal run (`runExploration`, exactly what `jevitate explore --strategy goal` runs, with the same
56
+ * run-shaping options), in its own browser, writing its own result, transcript and Recording; its
57
+ * verdict (`goalOutcome`) decides whether the take reached the goal.
58
+ */
51
59
  async function authorViaBrowser(args) {
52
60
  if (!args.judge || !args.gen) {
53
61
  throw new Error("runAuthorJourney: judge and gen gateways are required to drive the authoring mission");
54
62
  }
55
63
  const judge = args.judge;
56
64
  const gen = args.gen;
57
- const portFactory = args.browserPortFactory ?? (() => new PlaywrightBrowserPort());
58
- const port = portFactory();
59
- const session = await port.open({
60
- ...sessionLaunchOptions(args.browser),
61
- allowedOrigins: [...args.allowlist],
62
- baseUrl: args.origin,
63
- ...(args.storageState !== undefined ? { storageState: args.storageState } : {}),
65
+ const g = args.goalRun ?? {};
66
+ // targets.json's safety (`goalRun.target`) wins; a caller's `safety` (#249) applies without one.
67
+ const target = g.target ?? (args.safety === undefined ? undefined : { safety: args.safety });
68
+ return authorJourney({
69
+ goal: args.goal,
70
+ ...(args.successAssertion === undefined ? {} : { successAssertion: args.successAssertion }),
71
+ ...(args.successChecks === undefined ? {} : { successChecks: args.successChecks }),
72
+ allowlist: args.allowlist,
73
+ startUrl: args.url,
74
+ bounds: args.bounds,
75
+ takes: args.takes,
76
+ journeyId: args.journeyId,
77
+ journeyName: args.journeyName,
78
+ runTake: async ({ goal, successChecks }) => {
79
+ const r = await runExploration({
80
+ ...g,
81
+ ...(target === undefined ? {} : { target }),
82
+ url: args.url,
83
+ goal,
84
+ successChecks,
85
+ allowlist: args.allowlist,
86
+ judge,
87
+ gen,
88
+ ...(args.usage === undefined ? {} : { usage: args.usage }),
89
+ bounds: args.bounds,
90
+ ...(args.outDir === undefined ? {} : { outDir: args.outDir }),
91
+ ...(args.browserPortFactory === undefined ? {} : { browserPortFactory: args.browserPortFactory }),
92
+ ...(args.browser === undefined ? {} : { browser: args.browser }),
93
+ ...(args.storageState === undefined ? {} : { storageState: args.storageState }),
94
+ });
95
+ return goalRunTake(r, g);
96
+ },
64
97
  });
65
- try {
66
- const actor = CastActor.named("author").whoCan(new BrowseTheWeb(session, [...args.allowlist]));
67
- return await authorJourney({
68
- goal: args.goal,
69
- successAssertion: args.successAssertion,
70
- allowlist: args.allowlist,
71
- startUrl: args.url,
72
- bounds: args.bounds,
73
- actor,
74
- judgment: judge,
75
- generation: gen,
76
- takes: args.takes,
77
- journeyId: args.journeyId,
78
- journeyName: args.journeyName,
79
- ...(args.safety === undefined ? {} : { safety: args.safety }),
80
- });
81
- }
82
- finally {
83
- await session.close();
84
- }
98
+ }
99
+ /** #369: one goal run as an authoring take — its Recording and where everything it wrote is. */
100
+ export function goalRunTake(r, g = {}) {
101
+ const reason = r.reason ?? r.failure?.message;
102
+ const recordingPath = r.recordingPaths[0];
103
+ const diagnostics = {
104
+ outcome: r.goalOutcome,
105
+ ...(reason === undefined ? {} : { reason }),
106
+ stop: r.stop,
107
+ runOutcome: r.runOutcome,
108
+ ...(r.failure === undefined ? {} : { failure: { kind: r.failure.kind, message: r.failure.message } }),
109
+ checks: r.checks,
110
+ decisions: r.decisions,
111
+ actions: r.actions,
112
+ finalUrl: r.finalUrl,
113
+ resultPath: r.resultPath,
114
+ transcriptPath: r.transcriptPath,
115
+ recordingPaths: r.recordingPaths,
116
+ ...(g.screenshots === undefined || recordingPath === undefined ? {} : { screenshotsDir: screenshotsDirFor(g.screenshots, recordingPath) }),
117
+ };
118
+ return { outcome: r.goalOutcome, recording: r.recording, diagnostics };
85
119
  }
86
120
  //# sourceMappingURL=explore-author.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"explore-author.js","sourceRoot":"","sources":["../src/explore-author.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAA+C,MAAM,sBAAsB,CAAC;AAC1G,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAG/D,OAAO,EACL,aAAa,EACb,6BAA6B,GAG9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAgEnD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAA6B;IAClE,wEAAwE;IACxE,MAAM,MAAM,GAAG,6BAA6B,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAEvE,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,IAAI,gBAAgB,CAAC;IACjD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC;QACxB,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,MAAM;QACN,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC;QACtB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;QAC3C,OAAO,EAAE,IAAI,CAAC,OAAO;QACnB,GAAG,CAAC,IAAI,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjF,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;KAC9D,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;QAClC,4FAA4F;QAC5F,0FAA0F;QAC1F,MAAM,OAAO,GACX,IAAI,CAAC,YAAY,KAAK,SAAS,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,KAAK,IAAI;YAC9E,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;YACrF,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;QACrB,MAAM,IAAI,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACxD,OAAO,OAAO,KAAK,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,CAAC;IACtE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,8EAA8E;AAC9E,KAAK,UAAU,gBAAgB,CAAC,IAA0B;IACxD,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,sFAAsF,CAAC,CAAC;IAC1G,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACzB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;IAErB,MAAM,WAAW,GAAG,IAAI,CAAC,kBAAkB,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,qBAAqB,EAAE,CAAC,CAAC;IACnF,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;QAC9B,GAAG,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC;QACrC,cAAc,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;QACnC,OAAO,EAAE,IAAI,CAAC,MAAM;QACpB,GAAG,CAAC,IAAI,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChF,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC/F,OAAO,MAAM,aAAa,CAAC;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,QAAQ,EAAE,IAAI,CAAC,GAAG;YAClB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK;YACL,QAAQ,EAAE,KAAK;YACf,UAAU,EAAE,GAAG;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;SAC9D,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"explore-author.js","sourceRoot":"","sources":["../src/explore-author.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { envCredentialStore, MissingCredentialError } from "@jevitate/ai-core";
3
3
  import { loadLocalCredentials } from "./credentials-file.js";
4
- import { FixtureNotFoundError, ScopeUnderivableError, UnauthorizedExploreTargetError, resolveCoverageThresholds, parseSecretField, SecretFieldSpecError, TypeFixtureSpecError, validateDenyPatterns, } from "@jevitate/explore";
4
+ import { FixtureNotFoundError, ScopeUnderivableError, UnauthorizedExploreTargetError, resolveCoverageThresholds, TypeFixtureSpecError, validateDenyPatterns, } from "@jevitate/explore";
5
5
  import { loadTypeFixtures } from "./type-fixture-file.js";
6
+ import { secretCommandRunner } from "./secret-command.js";
6
7
  import { ok, fail } from "./envelope.js";
7
8
  import { SessionFileInProjectError, assertSessionFileOutsideProject } from "./project-dir.js";
8
9
  import { InvariantsFileError, loadInvariantFiles, resolveInvariantAuthTokens } from "./invariants-file.js";
@@ -17,7 +18,7 @@ import { LITERAL_SECRET_WARNING, SecretArgError, resolveSecretArgs } from "./sec
17
18
  import { withEngine } from "./engine.js";
18
19
  import { setKillSwitchOutput } from "./kill-signal.js";
19
20
  import { EXIT_CODES } from "./exit-codes.js";
20
- import { intArg, positiveNumberArg, nonNegativeIntArg, positiveIntArg, ratioArg } from "./cli-args.js";
21
+ import { positiveNumberArg, nonNegativeIntArg, positiveIntArg, ratioArg } from "./cli-args.js";
21
22
  import { formatMissionHuman, formatMultiRunHuman } from "./cli-output.js";
22
23
  import { runExploration, runCoverageMission, runAdversarialCliMission, CLI_ADVERSARIAL_STRATEGIES, runFeatureCliMission, parseSuccessSpec, resolveExploreAllowlist, } from "./explore-api.js";
23
24
  import { parseLogSourceSpecs, LogSourceSpecError } from "./log-sources.js";
@@ -44,6 +45,7 @@ import { formatCampaignHuman } from "./campaign-cli.js";
44
45
  import { forwardedArgv } from "./multi-run-cli.js";
45
46
  import { ANCHORED_STRATEGIES, JourneyPrefixArgsError, JourneyPrefixStaleError, journeyStaleResult, resolveJourneyPrefix, } from "./journey-prefix.js";
46
47
  import { clock } from "@jevitate/domain";
48
+ import { GOAL_RUN_OPTIONS, GoalRunFlagError, conversationFromFlags, dialogsFromFlags, secretFieldsFromFlags, successWhenFromFlags, targetFlagsFromFlags, } from "./goal-run-flags.js";
47
49
  /**
48
50
  * #293: the flags a sweep sets on each of its missions itself (the rest of the command line is
49
51
  * forwarded to every mission as given).
@@ -94,71 +96,59 @@ export function registerExploreCommands(program, deps, buildProgram) {
94
96
  "Omit it for a find-out goal (e.g. \"find out how many contacts... report the answer\"): the run must then end",
95
97
  "with the model's own `report` op, and the grounded answer (#101) is the verdict — no page/network check needed.",
96
98
  ].join(" "), (v, prev) => [...prev, v], [])
97
- .option("--success-when <when>", "when the --success page checks must hold: final (default; on the final page) | held (on the final page, or all together at any settled step — a one-time secret, a toast). reloadThen is always final")
98
- .option("--allow-vacuous-checks", "downgrade a vacuous --success check to a warning. By default a check satisfied before the run's first action — a page check that held on the seed page and never changed " +
99
- "(an empty result container), a requestMade/responseStatus matched only by a page-load or polling request — FAILS: it cannot verify the goal")
100
- .option("--action-deltas", "opt-in (#303; every --strategy, not --feature): record what each action changed on the page — an accessibility snapshot before and after, announcements, " +
101
- "the action's requests — redacted, with a code verdict per step (no-change | relevant-change | inconclusive) used by the goal loop's no-progress check and a persistence re-check after writes (goal), and as defect evidence (adversarial, coverage); " +
102
- "adds `delta` to every transcript step (and Recording step, goal) and `actionDeltas` to the result. Costs about 50-100 ms per action on a small page, 0.3-0.5 s on a large one")
99
+ .addOption(GOAL_RUN_OPTIONS.successWhen())
100
+ .addOption(GOAL_RUN_OPTIONS.allowVacuousChecks())
101
+ .addOption(GOAL_RUN_OPTIONS.actionDeltas())
103
102
  .option("--feature <name>", "run the capability-scoped feature-testing mission (instead of --goal/--success)")
104
103
  .option("--route <glob>", "in-scope route glob (repeatable), e.g. /thread/** — for --feature it replaces the default scope (the start URL's route and everything under it); it widens --strategy adversarial/coverage/exploratory beyond the start URL's route", (v, prev) => [...prev, v], [])
105
104
  .option("--scope <mode>", "--strategy coverage/exploratory: 'app' widens containment to the whole app (same as --route '/**'); default: the start URL's route plus --route globs")
106
105
  .option("--allow <origin>", "authorized origin (repeatable); REPLACES the default allowlist when given (the URL's own origin is used only when --allow is omitted entirely) -- include the URL's own origin explicitly if you still need it", (v, prev) => [...prev, v], [])
107
- .option("--secret <value|env:VAR>", "REDACTION ONLY: a secret/PII value kept out of every model call and artifact (repeatable); env:VAR reads it from the environment (preferred: a literal is visible in the process list and shell history). It is never typed into a field — to log in, bind it with --secret-field (or start from --storage-state)", (v, prev) => [...prev, v], [])
108
- .option("--secret-field <binding>", "goal/usability strategy: '<label|testId|type|id|name>=<value>=env:<VAR>' (repeatable), e.g. 'label=Password=env:APP_PASSWORD'. When the run types into a matching field, code types $VAR itself; the model sees only «secret:VAR» and the Recording {redacted:true}", (v, prev) => [...prev, v], [])
109
- .option("--totp <binding>", "goal/usability strategy: '<descriptor>=env:<VAR>' with $VAR a base32 TOTP seed (repeatable), e.g. 'label=Authentication code=env:APP_TOTP_SEED'. The 6-digit code is computed locally (RFC 6238) when the field is typed; the seed never reaches a model or disk", (v, prev) => [...prev, v], [])
110
- .option("--type-fixture <binding>", "goal strategy: '<label|testId|type|id|name>=<value>=<file>' (repeatable), e.g. 'label=Paste your text=./fixtures/import.txt'. When the run types into a matching field, code types the file's exact text verbatim (line breaks kept, never paraphrased or capped); the model sees only «fixture:<file name>». Recorded as typed unless it holds a --secret", (v, prev) => [...prev, v], [])
111
- .option("--fixture <path>", "local file the upload op attaches to a file input (goal and usability strategies); must exist")
106
+ .addOption(GOAL_RUN_OPTIONS.secret())
107
+ .addOption(GOAL_RUN_OPTIONS.secretField())
108
+ .addOption(GOAL_RUN_OPTIONS.totp())
109
+ .addOption(GOAL_RUN_OPTIONS.typeFixture())
110
+ .addOption(GOAL_RUN_OPTIONS.fixture())
112
111
  .option("--storage-state <file>", "Playwright storageState JSON to start the session authenticated (deterministic login pre-step); must exist")
113
112
  .option("--actor <name=storageState>", "multi-actor mission (#147, goal only; repeatable): the FIRST actor is the primary (the only one the model drives, " +
114
113
  "from its own storageState); every other actor is an observer in its OWN fresh context that only runs the " +
115
114
  "--invariants' cross-actor checks (capture + probe as:/deniedAs) — never clicks or types. Replaces --storage-state", (v, prev) => [...prev, v], [])
116
- .option("--save-storage-state <file>", "write the context's storageState (cookies + origin storage) here when the run ends; mode 0600, contents never logged. " +
117
- "Useful with a rotating refresh token: --storage-state's file goes stale after one authenticated run refreshes it, " +
118
- "so point --save-storage-state at the SAME file (or a new one) to keep it usable for the next run. " +
119
- "Written on every exit path -- a crash or a SIGTERM/SIGINT kill included (#159), not only a clean end -- but " +
120
- "never over a good file with a session that already looks lost/logged-out; the last known-good state is used " +
121
- "instead, or nothing is written if none was ever captured.")
115
+ .addOption(GOAL_RUN_OPTIONS.saveStorageState())
122
116
  .option("--max-actions <n>", "hard cap on executed actions", positiveIntArg)
123
117
  .option("--max-decisions <n>", "hard cap on model decisions", positiveIntArg)
124
118
  .option("--stall-timeout <seconds>", "--strategy coverage/exploratory and --feature: end the run inconclusive (stalled) when no step completes within this many seconds (default 120)", positiveNumberArg)
125
- .option("--reply-wait-ms <ms>", "conversational pages: how long to keep waiting for a reply while the page shows no sign of working on one " +
126
- "(goal and usability; default 60000). While a request the message started is in flight, a busy indicator shows, " +
127
- "or the reply is still growing, the wait continues up to --reply-ceiling-ms", positiveIntArg)
128
- .option("--reply-ceiling-ms <ms>", "conversational pages: hard ceiling on one reply wait, however busy the page stays (default 180000; never below --reply-wait-ms)", positiveIntArg)
129
- .option("--reply-max-chars <n>", "conversational pages: cap on each generated chat message (goal and usability; default 300)", intArg({ min: 20, max: 2000 }))
130
- .option("--job-wait-ms <ms>", "goal and usability: while the page shows an in-progress status (\"Simulating…\", aria-busy, a job \"is running\"), " +
131
- "waits keep waiting with backoff — and a model 'blocked' is deferred — up to this budget (default: --reply-ceiling-ms, 180000); " +
132
- "it also bounds a busy indicator the app visibly keeps working behind (live progress, a job poll) before it is a hang, " +
133
- "and a wait the page documents (\"usually takes a minute\") can raise it", positiveIntArg)
134
- .option("--deny <pattern>", "a control no mission may click (repeatable): an accessible-name regex (/Archive/i or Archive) or a descriptor role=button;name=Archive. " +
135
- "Session-ending (Sign out), destructive (Delete, Revoke, Rotate) and paid (Buy, Run simulation, Generate, Send invite) controls are refused by default", (v, prev) => [...prev, v], [])
136
- .option("--paid <pattern>", "an app control that costs money or credits (repeatable; same syntax as --deny), e.g. /^(Analyze|Draft|Improve)\\b/i: treated like the built-in paid " +
137
- "vocabulary — the budget guard sees it, hang replays never repeat it, and a goal that asks for it may still click it", (v, prev) => [...prev, v], [])
138
- .option("--allow-destructive", "let missions click session-ending, destructive and paid controls (a --deny pattern still holds). A goal run already may click one its goal asks for")
119
+ .addOption(GOAL_RUN_OPTIONS.replyWaitMs())
120
+ .addOption(GOAL_RUN_OPTIONS.replyQuietMs())
121
+ .addOption(GOAL_RUN_OPTIONS.replyCeilingMs())
122
+ .addOption(GOAL_RUN_OPTIONS.replyMaxChars())
123
+ .addOption(GOAL_RUN_OPTIONS.jobWaitMs())
124
+ .addOption(GOAL_RUN_OPTIONS.deny())
125
+ .addOption(GOAL_RUN_OPTIONS.paid())
126
+ .addOption(GOAL_RUN_OPTIONS.allowDestructive())
127
+ .addOption(GOAL_RUN_OPTIONS.dialogs())
139
128
  .option("--allow-writes", "let a find-out goal (no --success check, ended by report) change the app. By default it is read-only: controls that start a write flow " +
140
129
  "(checkout, upgrade, create, save, submit…) are refused and the write requests an action fires are blocked, unless the goal itself asks for a change")
141
130
  .option("--allow-write <glob>", "a write-request path a read-only find-out goal never blocks (repeatable; ** spans segments; a glob starting with https:// matches " +
142
131
  "origin + path, e.g. https://abc.supabase.co/rest/v1/**), beyond the built-in auth-refresh ones " +
143
132
  "(**/refresh*, **/token*, **/oauth/**, **/auth/**/refresh*). The app's background writes outside an action always pass", (v, prev) => [...prev, v], [])
144
- .option("--read-rpc <glob>", "a POST request that only READS (repeatable): an RPC-method glob (Estimate*, pkg.Service/Preview*) or a path glob (/api/search*). " +
145
- "gRPC-web/Connect Get*/List*/Search*/Find*/Watch*/Stream*/Count*/Describe*/Read* methods are reads already. Reads are never guarded or reported as duplicate writes", (v, prev) => [...prev, v], [])
133
+ .addOption(GOAL_RUN_OPTIONS.readRpc())
146
134
  .option("--real", "use live Jev + OpenRouter gateways (requires keys)", false)
147
135
  .option("--fake-ai", "use deterministic fake gateways (pipeline smoke only)", false)
148
136
  .option("--out <dir>", "directory to write the emitted Recording")
149
137
  .option("--file-issues", "file findings as issues (needs a repo: --issue-repo or ~/.jevitate/filing.json); default: drafts only")
150
138
  .option("--issue-repo <owner/name>", "the system-under-test repo findings for THIS target are filed to")
151
- .option("--hang-replays <n>", "fresh-context replays that confirm a hang (default 2; 0 = don't replay, the hang is reported unconfirmed)", nonNegativeIntArg)
139
+ .addOption(GOAL_RUN_OPTIONS.hangReplays())
152
140
  .option("--hang-replay-writes", "let hang replays re-send a paid/destructive write the run sent (default: such a hang is reported inconclusive, never replayed)")
153
- .option("--settle-ignore <pattern>", "a request URL pattern the target marks as background (never pending work; repeatable, * wildcard)", (v, prev) => [...prev, v], [])
154
- .option("--long-poll-ms <n>", "a request pending this long on an interactive page is a long-poll (default 5000)", nonNegativeIntArg)
155
- .option("--api-prefix <path>", "a path prefix whose requests are the app's API in the timing summary (repeatable), e.g. /api/", (v, prev) => [...prev, v], [])
156
- .option("--ignore-no-progress <pattern>", "a route / action label / busy indicator where ui-no-progress is expected (repeatable, * wildcard)", (v, prev) => [...prev, v], [])
141
+ .addOption(GOAL_RUN_OPTIONS.settleIgnore())
142
+ .addOption(GOAL_RUN_OPTIONS.longPollMs())
143
+ .addOption(GOAL_RUN_OPTIONS.apiPrefix())
144
+ .addOption(GOAL_RUN_OPTIONS.ignoreNoProgress())
157
145
  .option("--jevitate-repo <owner/name>", "where jevitate engine findings are filed (default matt-cochran/jevitate)")
158
146
  .option("--min-control-coverage <ratio>", "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", ratioArg)
159
147
  .option("--no-require-form-submit", "adversarial: do not require a submitted form for a clean result (default: required when the target has a form)")
160
148
  .option("--invariants <file>", "app-declared invariants JSON (repeatable; goal, coverage, exploratory, adversarial, --feature): checked around every action, a violation is a defect (exit 1). Validated before any browser opens; probes are GET/HEAD on an --allow origin only", (v, prev) => [...prev, v], [])
161
149
  .option("--log-source <spec>", "backend log source (repeatable; every strategy, incl. usability): file:<path> (tailed from its current end) | docker:<container> (docker logs -f --since 0s) | cmd:<command> (needs --allow-log-cmd). Read-only, operator-declared, never the model's choice. Error/warning lines are correlated to the step they landed during and attached to its transcript evidence, redacted", (v, prev) => [...prev, v], [])
150
+ .addOption(GOAL_RUN_OPTIONS.allowSecretCmd())
151
+ .addOption(GOAL_RUN_OPTIONS.secretCmdAttempts())
162
152
  .option("--allow-log-cmd", "opt-in: a --log-source cmd:<command> may run as a subprocess (operator-declared only; refused otherwise)", false)
163
153
  .option("--log-defect <level|/regex/>", "backend log lines matching this (repeatable) become a server-log defect: a level (error|warn|info|debug, matched as level>=this) or a /regex/flags/ over the raw line. Its fingerprint is the normalized message (ids/numbers/uuids/timestamps stripped) plus the correlated route; verify-fix re-checks it by re-tailing the same --log-source(s)", (v, prev) => [...prev, v], [])
164
154
  .option("--log-quiet-ok <spec>", "declares a --log-source spec (exact match, repeatable) as legitimately quiet: zero lines from it does not make the --log-defect oracle unhealthy (#169). Without it, a declared source that opened but delivered not one line makes an otherwise-clean run inconclusive, same as one that failed to open", (v, prev) => [...prev, v], [])
@@ -210,6 +200,17 @@ export function registerExploreCommands(program, deps, buildProgram) {
210
200
  emitExplore(fail("E_EXPLORE_ARGS", `unknown strategy ${JSON.stringify(o.strategy)} (one of ${EXPLORE_STRATEGIES.join(", ")})`));
211
201
  return;
212
202
  }
203
+ // #334: a mistyped --dialogs never silently means "dismiss".
204
+ let dialogs;
205
+ try {
206
+ dialogs = dialogsFromFlags(o);
207
+ }
208
+ catch (err) {
209
+ if (!(err instanceof GoalRunFlagError))
210
+ throw err;
211
+ emitExplore(fail(err.code, err.message));
212
+ return;
213
+ }
213
214
  // #195: a session file never lands in the repo's .jevitate/ (refused before any run, multi-runs included).
214
215
  if (o.saveStorageState !== undefined) {
215
216
  try {
@@ -432,22 +433,14 @@ export function registerExploreCommands(program, deps, buildProgram) {
432
433
  // #120: a killed run prints what this command would have printed — the envelope with --json,
433
434
  // else the human summary (#210) — before it exits.
434
435
  setKillSwitchOutput(o.json === true ? "envelope" : "human");
435
- const conversation = {
436
- ...(o.replyWaitMs === undefined ? {} : { replyWaitMs: Number(o.replyWaitMs) }),
437
- ...(o.replyCeilingMs === undefined ? {} : { replyCeilingMs: Number(o.replyCeilingMs) }),
438
- ...(o.replyMaxChars === undefined ? {} : { replyMaxChars: Number(o.replyMaxChars) }),
439
- ...(o.jobWaitMs === undefined ? {} : { jobWaitMs: Number(o.jobWaitMs) }),
440
- };
441
- if ((conversation.replyWaitMs !== undefined && !(Number.isInteger(conversation.replyWaitMs) && conversation.replyWaitMs > 0)) ||
442
- (conversation.replyCeilingMs !== undefined &&
443
- !(Number.isInteger(conversation.replyCeilingMs) && conversation.replyCeilingMs > 0)) ||
444
- (conversation.replyMaxChars !== undefined &&
445
- !(Number.isInteger(conversation.replyMaxChars) && conversation.replyMaxChars >= 20 && conversation.replyMaxChars <= 2000))) {
446
- emitExplore(fail("E_EXPLORE_ARGS", "--reply-wait-ms and --reply-ceiling-ms must be positive integers; --reply-max-chars an integer in 20..2000"));
447
- return;
436
+ let conversation;
437
+ try {
438
+ conversation = conversationFromFlags(o);
448
439
  }
449
- if (conversation.jobWaitMs !== undefined && !(Number.isInteger(conversation.jobWaitMs) && conversation.jobWaitMs > 0)) {
450
- emitExplore(fail("E_EXPLORE_ARGS", "--job-wait-ms must be a positive integer"));
440
+ catch (err) {
441
+ if (!(err instanceof GoalRunFlagError))
442
+ throw err;
443
+ emitExplore(fail(err.code, err.message));
451
444
  return;
452
445
  }
453
446
  // #154: refused BEFORE any browser opens — `nonNegativeIntArg` already rejects a bad value at
@@ -495,19 +488,7 @@ export function registerExploreCommands(program, deps, buildProgram) {
495
488
  let target;
496
489
  if (o.url !== undefined) {
497
490
  try {
498
- target = resolveTargetConfig(loadTargetsFile(deps.explore?.targetsConfigPath), new URL(setupRefFreeUrl(o.url)).origin, {
499
- settleIgnore: o.settleIgnore,
500
- ignoreNoProgress: o.ignoreNoProgress,
501
- apiPrefixes: o.apiPrefix,
502
- deny: o.deny,
503
- paid: o.paid,
504
- readRpc: o.readRpc,
505
- ...(o.allowDestructive === true ? { allowDestructive: true } : {}),
506
- ...(o.allowWrites === true ? { allowWrites: true } : {}),
507
- allowWrite: o.allowWrite,
508
- ...(o.hangReplayWrites === true ? { hangReplayWrites: true } : {}),
509
- ...(o.longPollMs === undefined ? {} : { longPollMs: Number(o.longPollMs) }),
510
- });
491
+ target = resolveTargetConfig(loadTargetsFile(deps.explore?.targetsConfigPath), new URL(setupRefFreeUrl(o.url)).origin, targetFlagsFromFlags(o, dialogs));
511
492
  }
512
493
  catch (err) {
513
494
  if (err instanceof TargetConfigError) {
@@ -672,18 +653,17 @@ export function registerExploreCommands(program, deps, buildProgram) {
672
653
  return;
673
654
  }
674
655
  try {
675
- secretFields = [
676
- ...o.secretField.map((s) => parseSecretField(s, "value", process.env)),
677
- ...o.totp.map((s) => parseSecretField(s, "totp", process.env)),
678
- ];
656
+ secretFields = secretFieldsFromFlags(o);
679
657
  }
680
658
  catch (err) {
681
- if (!(err instanceof SecretFieldSpecError))
659
+ if (!(err instanceof GoalRunFlagError))
682
660
  throw err;
683
661
  emitExplore(fail(err.code, err.message));
684
662
  return;
685
663
  }
686
664
  }
665
+ // #324: a cmd: binding's command runs at type time; its output becomes a run secret at once.
666
+ const secretCommand = secretFields.some((f) => f.kind === "cmd") ? secretCommandRunner() : undefined;
687
667
  // #281: fields typed with a file's exact text — read (and checked) before any browser opens.
688
668
  let typeFixtures = [];
689
669
  if (o.typeFixture.length > 0) {
@@ -910,8 +890,8 @@ export function registerExploreCommands(program, deps, buildProgram) {
910
890
  emitExplore(fail("E_EXPLORE_ASSERTION", String(err instanceof Error ? err.message : err)));
911
891
  return;
912
892
  }
913
- if (o.successWhen !== undefined && o.successWhen !== "held" && o.successWhen !== "final") {
914
- emitExplore(fail("E_EXPLORE_ARGS", `--success-when must be "held" or "final", got ${JSON.stringify(o.successWhen)}`));
893
+ if (o.successWhen !== undefined && o.successWhen !== "held" && o.successWhen !== "final" && o.successWhen !== "each") {
894
+ emitExplore(fail("E_EXPLORE_ARGS", `--success-when must be "final", "held" or "each", got ${JSON.stringify(o.successWhen)}`));
915
895
  return;
916
896
  }
917
897
  if (uxSuccessChecks.length === 0 && (o.successWhen !== undefined || o.allowVacuousChecks === true)) {
@@ -962,6 +942,8 @@ export function registerExploreCommands(program, deps, buildProgram) {
962
942
  conversation,
963
943
  secrets: o.secret.length > 0 ? o.secret : undefined,
964
944
  ...(secretFields.length > 0 ? { secretFields } : {}),
945
+ ...(secretCommand === undefined ? {} : { secretCommand }),
946
+ ...(o.secretCmdAttempts === undefined ? {} : { secretCommandAttempts: o.secretCmdAttempts }),
965
947
  fixture: o.fixture,
966
948
  outDir: o.out,
967
949
  browserPortFactory: deps.explore?.browserPortFactory,
@@ -974,7 +956,7 @@ export function registerExploreCommands(program, deps, buildProgram) {
974
956
  ...triagedServerLog(serverLog, uxJudge, o.real === true),
975
957
  ...withInvariants,
976
958
  ...(uxSuccessChecks.length === 0 ? {} : { successChecks: uxSuccessChecks }),
977
- ...(o.successWhen === "held" || o.successWhen === "final" ? { successWhen: o.successWhen } : {}),
959
+ ...(o.successWhen === "held" || o.successWhen === "final" || o.successWhen === "each" ? { successWhen: o.successWhen } : {}),
978
960
  ...(o.allowVacuousChecks === true ? { allowVacuousChecks: true } : {}),
979
961
  ...withPrefix,
980
962
  ...(o.actionDeltas === true ? { actionDeltas: true } : {}),
@@ -1080,11 +1062,16 @@ export function registerExploreCommands(program, deps, buildProgram) {
1080
1062
  emitExplore(fail("E_EXPLORE_ASSERTION", String(err instanceof Error ? err.message : err)));
1081
1063
  return;
1082
1064
  }
1083
- if (o.successWhen !== undefined && o.successWhen !== "held" && o.successWhen !== "final") {
1084
- emitExplore(fail("E_EXPLORE_ARGS", `--success-when must be "held" or "final", got ${JSON.stringify(o.successWhen)}`));
1065
+ let successWhen;
1066
+ try {
1067
+ successWhen = successWhenFromFlags(o);
1068
+ }
1069
+ catch (err) {
1070
+ if (!(err instanceof GoalRunFlagError))
1071
+ throw err;
1072
+ emitExplore(fail(err.code, err.message));
1085
1073
  return;
1086
1074
  }
1087
- const successWhen = o.successWhen === "held" || o.successWhen === "final" ? o.successWhen : undefined;
1088
1075
  const allowlist = resolveExploreAllowlist(setupRefFreeUrl(o.url), o.allow);
1089
1076
  // Fixtures (#140/#144): the spec and every ${setup.x} reference are validated here, before any
1090
1077
  // browser or request; the setup itself runs just before the mission (below).
@@ -1170,6 +1157,8 @@ export function registerExploreCommands(program, deps, buildProgram) {
1170
1157
  bounds: Object.keys(bounds).length > 0 ? bounds : undefined,
1171
1158
  secrets: o.secret.length > 0 ? o.secret : undefined,
1172
1159
  ...(secretFields.length > 0 ? { secretFields } : {}),
1160
+ ...(secretCommand === undefined ? {} : { secretCommand }),
1161
+ ...(o.secretCmdAttempts === undefined ? {} : { secretCommandAttempts: o.secretCmdAttempts }),
1173
1162
  ...(typeFixtures.length > 0 ? { typeFixtures } : {}),
1174
1163
  fixture: o.fixture,
1175
1164
  outDir: o.out,