agent-scenario-loop 0.1.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/LICENSE +21 -0
- package/README.md +119 -0
- package/app/profile-session.ts +812 -0
- package/core/config-template.json +41 -0
- package/dist/core/agent-summary.d.ts +15 -0
- package/dist/core/agent-summary.js +177 -0
- package/dist/core/artifact-contract.d.ts +151 -0
- package/dist/core/artifact-contract.js +897 -0
- package/dist/core/artifact-layout.d.ts +56 -0
- package/dist/core/artifact-layout.js +61 -0
- package/dist/core/artifact-writer.d.ts +44 -0
- package/dist/core/artifact-writer.js +55 -0
- package/dist/core/comparison.d.ts +133 -0
- package/dist/core/comparison.js +294 -0
- package/dist/core/evidence-interpreter.d.ts +28 -0
- package/dist/core/evidence-interpreter.js +69 -0
- package/dist/core/execution-plan.d.ts +44 -0
- package/dist/core/execution-plan.js +95 -0
- package/dist/core/planner.d.ts +132 -0
- package/dist/core/planner.js +812 -0
- package/dist/core/ports.d.ts +198 -0
- package/dist/core/ports.js +146 -0
- package/dist/core/run-index.d.ts +62 -0
- package/dist/core/run-index.js +143 -0
- package/dist/core/schema-validator.d.ts +86 -0
- package/dist/core/schema-validator.js +407 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +27 -0
- package/dist/runner/agent-device-driver.d.ts +126 -0
- package/dist/runner/agent-device-driver.js +168 -0
- package/dist/runner/agent-device.d.ts +295 -0
- package/dist/runner/agent-device.js +1271 -0
- package/dist/runner/android-adb-driver.d.ts +175 -0
- package/dist/runner/android-adb-driver.js +399 -0
- package/dist/runner/android-adb.d.ts +254 -0
- package/dist/runner/android-adb.js +1618 -0
- package/dist/runner/argent-driver.d.ts +183 -0
- package/dist/runner/argent-driver.js +297 -0
- package/dist/runner/argent.d.ts +349 -0
- package/dist/runner/argent.js +1211 -0
- package/dist/runner/check-plan.d.ts +45 -0
- package/dist/runner/check-plan.js +210 -0
- package/dist/runner/cli.d.ts +20 -0
- package/dist/runner/cli.js +23 -0
- package/dist/runner/compare-latest.d.ts +99 -0
- package/dist/runner/compare-latest.js +233 -0
- package/dist/runner/compare.d.ts +58 -0
- package/dist/runner/compare.js +157 -0
- package/dist/runner/demo-loop.d.ts +45 -0
- package/dist/runner/demo-loop.js +170 -0
- package/dist/runner/example-android-live.d.ts +137 -0
- package/dist/runner/example-android-live.js +454 -0
- package/dist/runner/example-ios-live.d.ts +137 -0
- package/dist/runner/example-ios-live.js +471 -0
- package/dist/runner/host-doctor.d.ts +131 -0
- package/dist/runner/host-doctor.js +628 -0
- package/dist/runner/init-project.d.ts +88 -0
- package/dist/runner/init-project.js +263 -0
- package/dist/runner/ios-simctl-driver.d.ts +69 -0
- package/dist/runner/ios-simctl-driver.js +97 -0
- package/dist/runner/ios-simctl.d.ts +254 -0
- package/dist/runner/ios-simctl.js +1415 -0
- package/dist/runner/live-android.d.ts +137 -0
- package/dist/runner/live-android.js +539 -0
- package/dist/runner/live-comparison.d.ts +67 -0
- package/dist/runner/live-comparison.js +147 -0
- package/dist/runner/live-ios.d.ts +137 -0
- package/dist/runner/live-ios.js +460 -0
- package/dist/runner/live-proof-summary.d.ts +263 -0
- package/dist/runner/live-proof-summary.js +465 -0
- package/dist/runner/live-proof.d.ts +467 -0
- package/dist/runner/live-proof.js +920 -0
- package/dist/runner/local-env.d.ts +64 -0
- package/dist/runner/local-env.js +155 -0
- package/dist/runner/profile-android.d.ts +82 -0
- package/dist/runner/profile-android.js +671 -0
- package/dist/runner/profile-ios.d.ts +108 -0
- package/dist/runner/profile-ios.js +532 -0
- package/dist/runner/profile-mobile.d.ts +254 -0
- package/dist/runner/profile-mobile.js +1307 -0
- package/dist/runner/validate-project.d.ts +273 -0
- package/dist/runner/validate-project.js +1501 -0
- package/docs/adapters.md +145 -0
- package/docs/api.md +94 -0
- package/docs/authoring.md +196 -0
- package/docs/concepts.md +136 -0
- package/docs/consumer-rehearsal.md +115 -0
- package/docs/contracts.md +267 -0
- package/docs/live-proofs.md +270 -0
- package/docs/principles.md +46 -0
- package/examples/event-logs/app-startup-baseline.log +4 -0
- package/examples/event-logs/app-startup-current.log +4 -0
- package/examples/minimal-app/README.md +70 -0
- package/examples/mobile-app/README.md +302 -0
- package/examples/mobile-app/app.json +22 -0
- package/examples/mobile-app/asl/package-scripts.json +32 -0
- package/examples/mobile-app/asl.config.json +37 -0
- package/examples/mobile-app/event-logs/android-app-startup.log +4 -0
- package/examples/mobile-app/event-logs/android-open-close-cycle.log +12 -0
- package/examples/mobile-app/event-logs/android-scroll-settle.log +12 -0
- package/examples/mobile-app/event-logs/app-startup.log +4 -0
- package/examples/mobile-app/event-logs/open-close-cycle.log +12 -0
- package/examples/mobile-app/event-logs/scroll-settle.log +12 -0
- package/examples/mobile-app/index.ts +20 -0
- package/examples/mobile-app/metro.config.js +20 -0
- package/examples/mobile-app/package.json +62 -0
- package/examples/mobile-app/patches/expo-modules-jsi@56.0.10.patch +19 -0
- package/examples/mobile-app/plugins/with-ios-build-compat.js +271 -0
- package/examples/mobile-app/pnpm-lock.yaml +4440 -0
- package/examples/mobile-app/runner-manifests/evidence-provider.json +79 -0
- package/examples/mobile-app/runner-manifests/primary-runner.json +19 -0
- package/examples/mobile-app/scenarios/android/app-startup-video.json +73 -0
- package/examples/mobile-app/scenarios/android/app-startup.json +44 -0
- package/examples/mobile-app/scenarios/android/open-close-cycle.json +54 -0
- package/examples/mobile-app/scenarios/android/scroll-settle.json +49 -0
- package/examples/mobile-app/scenarios/ios/app-startup.json +44 -0
- package/examples/mobile-app/scenarios/ios/open-close-cycle.json +54 -0
- package/examples/mobile-app/scenarios/ios/scroll-settle.json +49 -0
- package/examples/mobile-app/scenarios/mobile/app-startup.json +91 -0
- package/examples/mobile-app/scenarios/mobile/open-close-cycle.json +160 -0
- package/examples/mobile-app/scenarios/mobile/scroll-settle.json +148 -0
- package/examples/mobile-app/scripts/asl-capture-accessibility-provider.mjs +112 -0
- package/examples/mobile-app/scripts/asl-capture-profiler-provider.mjs +127 -0
- package/examples/mobile-app/src/devtools/profile-session.ts +7 -0
- package/examples/mobile-app/src/example-screen.tsx +322 -0
- package/examples/mobile-app/tsconfig.json +16 -0
- package/examples/mobile-app/tsconfig.typecheck.json +13 -0
- package/examples/runners/README.md +44 -0
- package/examples/runners/adb-android.json +25 -0
- package/examples/runners/agent-device-android.json +27 -0
- package/examples/runners/agent-device-ios.json +27 -0
- package/examples/runners/argent-android.json +32 -0
- package/examples/runners/argent-ios.json +32 -0
- package/examples/runners/argent-react-profiler-provider.json +15 -0
- package/examples/runners/axe-accessibility-provider.json +24 -0
- package/examples/runners/manual-log-ingest.json +9 -0
- package/examples/runners/rozenite-profiler-provider.json +9 -0
- package/examples/runners/script-accessibility-provider.json +24 -0
- package/examples/runners/script-memory-provider.json +24 -0
- package/examples/runners/script-network-provider.json +24 -0
- package/examples/runners/script-profiler-provider.json +30 -0
- package/examples/runners/xcodebuildmcp-ios.json +29 -0
- package/examples/scenarios/ios/app-startup.json +28 -0
- package/examples/scenarios/ios/open-close-cycle.json +35 -0
- package/examples/scenarios/mobile/app-startup.json +72 -0
- package/examples/scenarios/mobile/media-open-close.json +141 -0
- package/examples/scenarios/mobile/open-close-cycle.json +135 -0
- package/examples/scenarios/mobile/scroll-settle.json +106 -0
- package/package.json +240 -0
- package/schemas/budget-verdict.schema.json +115 -0
- package/schemas/causal-run.schema.json +279 -0
- package/schemas/comparison.schema.json +196 -0
- package/schemas/health.schema.json +108 -0
- package/schemas/live-proof-set.schema.json +195 -0
- package/schemas/live-proof.schema.json +413 -0
- package/schemas/manifest.schema.json +204 -0
- package/schemas/metrics.schema.json +137 -0
- package/schemas/project-validation.schema.json +343 -0
- package/schemas/runner-capabilities.schema.json +217 -0
- package/schemas/scenario.schema.json +400 -0
- package/schemas/verdict.schema.json +88 -0
- package/templates/evidence-provider.json +83 -0
- package/templates/gitignore-snippet +9 -0
- package/templates/integration-readme.md +125 -0
- package/templates/mobile-scenario.json +133 -0
- package/templates/package-scripts.json +32 -0
- package/templates/primary-runner.json +19 -0
- package/templates/project.config.json +37 -0
- package/templates/scripts/asl-capture-accessibility-provider.mjs +112 -0
- package/templates/scripts/asl-capture-profiler-provider.mjs +127 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Agent Scenario Loop Integration
|
|
2
|
+
|
|
3
|
+
This folder was generated by `asl-init` for the `{{SCENARIO_ID}}` scenario.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
- `asl.config.json`: app identifiers, artifact roots, and runner defaults
|
|
8
|
+
- `scenarios/mobile/{{SCENARIO_ID}}.json`: first durable mobile scenario
|
|
9
|
+
- `runner-manifests/primary-runner.json`: primary runner capability manifest
|
|
10
|
+
- `runner-manifests/evidence-provider.json`: optional evidence-provider manifest
|
|
11
|
+
- `scripts/asl-capture-accessibility-provider.mjs`: runnable starter accessibility provider command
|
|
12
|
+
- `scripts/asl-capture-profiler-provider.mjs`: runnable starter profiler, memory, and network provider command
|
|
13
|
+
- `src/devtools/profile-session.ts`: React Native app-side truth-event helper
|
|
14
|
+
- `asl/package-scripts.json`: package-script snippets to merge into your app `package.json`
|
|
15
|
+
- `asl/gitignore-snippet`: artifact ignore rules to merge into your app `.gitignore`
|
|
16
|
+
|
|
17
|
+
`asl.config.json` declares the supported fixture, adb, simctl, agent-device, and Argent lanes that the generated scripts expose. Keep that list aligned with the scripts your project intends agents to run; `asl-validate-project` reports invalid required config as errors and omitted optional package drivers as warnings.
|
|
18
|
+
|
|
19
|
+
For iOS apps with multiple installed variants on the same simulator, add sibling bundle ids to `app.iosConflictingBundleIds`. ASL treats those as a preflight guard: if a declared sibling variant is installed beside the target bundle, iOS live proof fails before launch so deep-link and profile-session evidence cannot come from the wrong app.
|
|
20
|
+
|
|
21
|
+
## App Wiring
|
|
22
|
+
|
|
23
|
+
1. Install the peer app dependencies used by `src/devtools/profile-session.ts` if your app does not already have them:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add @react-native-async-storage/async-storage expo-linking
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
2. Mount `useProfileSessionBootstrap()` once near the app root.
|
|
30
|
+
3. Emit app-owned truth events with `emitProfileEvent()` at scenario milestones.
|
|
31
|
+
4. Register command targets with `registerProfileCommandTargetHandler()` when a runner should activate app behavior directly.
|
|
32
|
+
5. Append `asl/gitignore-snippet` to your app `.gitignore`; commit scenarios, runner manifests, and app truth-event wiring. Keep `paths.artifactRoot`, `paths.iosArtifactsRoot`, and `paths.androidArtifactsRoot` under ignored project-local artifact paths so agent sandboxes and git state agree on where runtime proof evidence belongs.
|
|
33
|
+
|
|
34
|
+
## First Proof Loop
|
|
35
|
+
|
|
36
|
+
Start with planner validation before live device proof:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
asl-validate-project --root . --platform all --out artifacts/asl/project-validation
|
|
40
|
+
asl-check-plan --scenario scenarios/mobile/{{SCENARIO_ID}}.json --runner runner-manifests/primary-runner.json --provider runner-manifests/evidence-provider.json --platform ios --out artifacts/asl/plan/{{SCENARIO_ID}}-ios
|
|
41
|
+
asl-check-plan --scenario scenarios/mobile/{{SCENARIO_ID}}.json --runner runner-manifests/primary-runner.json --provider runner-manifests/evidence-provider.json --platform android --out artifacts/asl/plan/{{SCENARIO_ID}}-android
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Keep deterministic validation and live device proof as separate lanes. Planner checks and fixture profile runs should work in ordinary build or agent sandboxes. Live runs that touch adb, simctl, agent-device, Argent, emulators, simulators, physical devices, Metro, or native build tools need host/device access from the first attempt. If a live command cannot reach those host services, classify it as runner environment health before treating it as a scenario regression.
|
|
45
|
+
|
|
46
|
+
Use sandboxed execution for `asl-validate-project`, `asl-check-plan`, fixture profile runs that read `--events`, and `asl-live-proof` inspection. Use host/device execution for `asl-profile-android --adb-capture`, `asl-profile-ios --simctl-capture`, `asl-live-android`, `asl-live-ios`, `asl-agent-device`, `asl-argent`, and any aggregate live script with `--agent-device-proof` or `--argent-proof`. Preserve the failed health artifact and rerun the same command with the right device access instead of changing the scenario to work around environment health.
|
|
47
|
+
|
|
48
|
+
When the app emits profile events, run platform profile commands against captured evidence or a live runner:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
ASL_PROFILE_IOS_EVENTS=event-logs/{{SCENARIO_ID}}-ios.log pnpm asl:profile:ios
|
|
52
|
+
ASL_PROFILE_ANDROID_EVENTS=event-logs/{{SCENARIO_ID}}-android.log pnpm asl:profile:android
|
|
53
|
+
ASL_PROFILE_IOS_EVENTS=event-logs/{{SCENARIO_ID}}-ios.log pnpm asl:profile:ios:provider
|
|
54
|
+
ASL_PROFILE_ANDROID_EVENTS=event-logs/{{SCENARIO_ID}}-android.log pnpm asl:profile:android:provider
|
|
55
|
+
asl-profile-ios --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json --simctl-capture --profile-session --profile-session-storage --launch --comparison-lane {{SCENARIO_ID}}-ios-live --out artifacts/asl/ios --run-id {{SCENARIO_ID}}-ios-live
|
|
56
|
+
asl-profile-android --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json --adb-capture --profile-session --clear-logcat --launch --comparison-lane {{SCENARIO_ID}}-android-live --out artifacts/asl/android --run-id {{SCENARIO_ID}}-android-live
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Those commands write `health.json`, `verdict.json`, `agent-summary.md`, `metrics.json`, `causal-run.json`, and raw evidence under the printed run directory. The `*:provider` scripts also execute the starter provider commands through `runner-manifests/evidence-provider.json`, then inventory the generated accessibility, profiler, memory, and network evidence in `manifest.artifacts.evidenceAttachments`. Compare a trusted current run with an explicit artifact path:
|
|
60
|
+
|
|
61
|
+
When `--wait-ms` is omitted from profile-session live capture, ASL derives the final adb or simctl evidence window from the scenario execution steps and cycle count. Pass `--wait-ms` only when a target app needs an explicit override.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
ASL_COMPARE_IOS_CURRENT=artifacts/asl/ios/{{SCENARIO_ID}}/{{SCENARIO_ID}}-ios-live pnpm asl:compare:ios
|
|
65
|
+
ASL_COMPARE_ANDROID_CURRENT=artifacts/asl/android/{{SCENARIO_ID}}/{{SCENARIO_ID}}-android-live pnpm asl:compare:android
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
For aggregate live proof, use the generated live scripts:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
ASL_IOS_UDID=<simulator-udid> ASL_IOS_APP_ID=<bundle-id> pnpm asl:ios:live
|
|
72
|
+
ASL_ANDROID_SERIAL=<emulator-or-device-serial> ASL_ANDROID_APP_ID=<package-name> pnpm asl:android:live
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Those scripts pass `--compare-latest --fail-on-regression`. A missing trusted baseline is recorded as skipped comparison evidence; a real regression exits nonzero after artifacts are written.
|
|
76
|
+
|
|
77
|
+
For repeated local runs, put machine-specific runner values in an ignored `.asl.local.env` file at your app root instead of repeating inline environment variables. ASL CLIs load the nearest `.asl.local.env` without overriding already-exported values:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
ASL_HOST_DOCTOR_REQUIRE=android,ios,agent-device,argent
|
|
81
|
+
ASL_AGENT_DEVICE_REQUIRED_PLATFORMS=ios,android
|
|
82
|
+
ASL_ANDROID_SERIAL=<emulator-or-device-serial>
|
|
83
|
+
ASL_IOS_UDID=<simulator-udid>
|
|
84
|
+
ASL_ANDROID_APP_ID=<package-name>
|
|
85
|
+
ASL_IOS_APP_ID=<bundle-id>
|
|
86
|
+
ASL_IOS_DEV_CLIENT_URL=<dev-client-url>
|
|
87
|
+
ASL_ARGENT_BIN=pnpm
|
|
88
|
+
ASL_ARGENT_BASE_ARGS="dlx @swmansion/argent run"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Set `ASL_IOS_DEV_CLIENT_URL` only when an iOS dev-client shell must open a specific Metro URL before profile-session evidence is collected. Keep the actual URL in ignored local env state because it depends on the local app id, host, and port.
|
|
92
|
+
|
|
93
|
+
## Portable Interaction Proof
|
|
94
|
+
|
|
95
|
+
Use the generated interaction scripts when an external driver should prove launch, visible UI, gestures, screenshots, or another scenario-declared driver action without changing the scenario file:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
ASL_IOS_UDID=<simulator-udid> ASL_IOS_APP_ID=<bundle-id> pnpm asl:agent-device:ios
|
|
99
|
+
ASL_ANDROID_SERIAL=<emulator-or-device-serial> ASL_ANDROID_APP_ID=<package-name> pnpm asl:agent-device:android
|
|
100
|
+
ASL_ARGENT_BIN=/path/to/argent pnpm asl:argent:check
|
|
101
|
+
ASL_ARGENT_BIN=/path/to/argent ASL_IOS_APP_ID=<bundle-id> pnpm asl:argent:ios
|
|
102
|
+
ASL_ARGENT_BIN=/path/to/argent ASL_ANDROID_SERIAL=<emulator-or-device-serial> ASL_ANDROID_APP_ID=<package-name> pnpm asl:argent:android
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
If agent-device already owns the device through a named session, set `ASL_IOS_AGENT_DEVICE_SESSION` or `ASL_ANDROID_AGENT_DEVICE_SESSION` instead of relying only on the direct UDID or serial; the generated direct agent-device scripts and aggregate live sidecar scripts both honor those variables. The default session mode is `reuse`, which lets that existing session own target selection. Set `ASL_IOS_AGENT_DEVICE_SESSION_MODE=bind` or `ASL_ANDROID_AGENT_DEVICE_SESSION_MODE=bind` when ASL should name the sidecar session while still forwarding the configured UDID or serial. Run `pnpm asl:agent-device:check` and `pnpm asl:argent:check` before device-bound sidecars when you need to verify installed tool surfaces; those checks write availability artifacts under `artifacts/asl/agent-device-check` and `artifacts/asl/argent-check`. The agent-device check also records active sessions in `raw/agent-device-availability.json` and summarizes them in `agent-summary.md`, so agents can choose a compatible session without rerunning discovery manually. Prefer a real `argent` executable on PATH, or set `ASL_ARGENT_BIN=/path/to/argent` when the package manager installed it somewhere else. `ASL_ARGENT_BIN=npx` with `ASL_ARGENT_BASE_ARGS="--yes @swmansion/argent run"` is supported as a wrapper shape, but verify it with `pnpm asl:argent:check` before relying on it. Argent uses `--udid` for both iOS simulators and Android emulators; ASL resolves the iOS `booted` shorthand to the concrete simulator UDID before invoking Argent. Set `ASL_ARGENT_COMMAND_TIMEOUT_MS` when package-manager or simulator startup is slower than the default command window. Set `ASL_ARGENT_IOS_SIMCTL_SCREENSHOT_FALLBACK=1` when iOS Argent launch and accessibility proof should keep running even if Argent's screenshot backend is unhealthy and simctl can still capture the screen.
|
|
106
|
+
|
|
107
|
+
If your project adds an aggregate batch runner that writes `live-proof.json`, inspect it with:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
ASL_LIVE_PROOF=artifacts/asl/<platform>/_live-proof/<run-id>/live-proof.json pnpm asl:live-proof
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
After Android and iOS live proofs have both written aggregate artifacts, run the generated platform-set gate:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pnpm asl:live-proof:both
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
That script requires both platform proofs, writes `artifacts/asl/live-proof-set/live-proof-set.json` plus `agent-summary.md`, and exits nonzero when either platform is missing, failed, or regressed.
|
|
120
|
+
|
|
121
|
+
Set `ASL_REQUIRE_LIVE_PROOF_ARTIFACTS=1` before this gate when the local artifact tree itself is part of the proof, not just the aggregate `live-proof.json` files.
|
|
122
|
+
|
|
123
|
+
Run `pnpm asl:host:doctor` before host/device live proof when adb, CoreSimulator, agent-device, or Argent availability is uncertain. Set `ASL_HOST_DOCTOR_REQUIRE=android,ios,agent-device,argent` in `.asl.local.env` or the shell when sidecar proof availability is part of the gate.
|
|
124
|
+
|
|
125
|
+
The package-script snippets in `asl/package-scripts.json` include fixture, host doctor, portable agent-device and Argent interaction proof, live profile, compare, and proof-inspection commands. Merge the snippets you use into your app `package.json` so future agents can run the loop without rediscovering command arguments.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0.0",
|
|
3
|
+
"id": "first-journey",
|
|
4
|
+
"flowId": "first-journey",
|
|
5
|
+
"description": "Prove one durable mobile app journey with app-owned truth events.",
|
|
6
|
+
"journey": {
|
|
7
|
+
"name": "First journey",
|
|
8
|
+
"intent": "Run one important user journey and prove the app reached the expected state.",
|
|
9
|
+
"actor": "returning user",
|
|
10
|
+
"startState": "app ready for the journey",
|
|
11
|
+
"endState": "journey completed"
|
|
12
|
+
},
|
|
13
|
+
"platforms": ["ios", "android"],
|
|
14
|
+
"requiredCapabilities": ["launch", "sessionControl", "command", "logCapture", "artifactWrite"],
|
|
15
|
+
"optionalCapabilities": ["screenshot", "uiTree", "profiler"],
|
|
16
|
+
"truthEvents": {
|
|
17
|
+
"journeyStarted": {
|
|
18
|
+
"event": "first_journey_started",
|
|
19
|
+
"required": true,
|
|
20
|
+
"timeoutMs": 3000,
|
|
21
|
+
"phase": "intent"
|
|
22
|
+
},
|
|
23
|
+
"journeyCompleted": {
|
|
24
|
+
"event": "first_journey_completed",
|
|
25
|
+
"required": true,
|
|
26
|
+
"timeoutMs": 8000,
|
|
27
|
+
"phase": "completion"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"milestones": [
|
|
31
|
+
{
|
|
32
|
+
"id": "journeyStarted",
|
|
33
|
+
"event": "first_journey_started",
|
|
34
|
+
"description": "The app accepted the journey intent.",
|
|
35
|
+
"required": true,
|
|
36
|
+
"timeoutMs": 3000,
|
|
37
|
+
"phase": "intent"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "journeyCompleted",
|
|
41
|
+
"event": "first_journey_completed",
|
|
42
|
+
"description": "The app emitted the completion truth event.",
|
|
43
|
+
"required": true,
|
|
44
|
+
"timeoutMs": 8000,
|
|
45
|
+
"phase": "completion"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"expectedEvents": ["first_journey_started", "first_journey_completed"],
|
|
49
|
+
"cycles": {
|
|
50
|
+
"iterations": 3,
|
|
51
|
+
"warmupIterations": 0,
|
|
52
|
+
"stopOnFailure": true
|
|
53
|
+
},
|
|
54
|
+
"budgets": [
|
|
55
|
+
{
|
|
56
|
+
"name": "journey p95",
|
|
57
|
+
"source": "milestone",
|
|
58
|
+
"metric": "p95",
|
|
59
|
+
"unit": "ms",
|
|
60
|
+
"limit": 8000,
|
|
61
|
+
"fromMilestone": "journeyStarted",
|
|
62
|
+
"toMilestone": "journeyCompleted"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "failures",
|
|
66
|
+
"source": "milestone",
|
|
67
|
+
"metric": "failures",
|
|
68
|
+
"unit": "count",
|
|
69
|
+
"limit": 0
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
"steps": [
|
|
73
|
+
{
|
|
74
|
+
"id": "launch",
|
|
75
|
+
"kind": "launch"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"id": "start-journey",
|
|
79
|
+
"kind": "command",
|
|
80
|
+
"command": "activate-target:first-journey",
|
|
81
|
+
"driverAction": "tap",
|
|
82
|
+
"selector": {
|
|
83
|
+
"kind": "testId",
|
|
84
|
+
"value": "first-journey-start"
|
|
85
|
+
},
|
|
86
|
+
"adapterOptions": {
|
|
87
|
+
"argent": {
|
|
88
|
+
"x": 0.5,
|
|
89
|
+
"y": 0.6
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"timeoutMs": 3000
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"id": "wait-for-completion",
|
|
96
|
+
"kind": "waitForMilestone",
|
|
97
|
+
"milestone": "journeyCompleted",
|
|
98
|
+
"timeoutMs": 8000
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"id": "capture-final-screen",
|
|
102
|
+
"kind": "captureEvidence",
|
|
103
|
+
"artifact": "screenshot",
|
|
104
|
+
"driverAction": "screenshot",
|
|
105
|
+
"required": false
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
"artifacts": {
|
|
109
|
+
"required": ["logs", "signals"],
|
|
110
|
+
"optional": ["screenshot", "uiTree", "profiler"]
|
|
111
|
+
},
|
|
112
|
+
"adapterOptions": {
|
|
113
|
+
"androidAdb": {
|
|
114
|
+
"commands": [
|
|
115
|
+
{
|
|
116
|
+
"command": "activate-target:first-journey",
|
|
117
|
+
"label": "start first journey",
|
|
118
|
+
"waitMs": 300
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
"iosSimctl": {
|
|
123
|
+
"commands": [
|
|
124
|
+
{
|
|
125
|
+
"command": "activate-target:first-journey",
|
|
126
|
+
"label": "start first journey",
|
|
127
|
+
"waitMs": 300
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
"repeat": 3
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"asl:check:ios": "asl-check-plan --scenario scenarios/mobile/{{SCENARIO_ID}}.json --runner runner-manifests/primary-runner.json --provider runner-manifests/evidence-provider.json --platform ios --out artifacts/asl/plan/{{SCENARIO_ID}}-ios",
|
|
3
|
+
"asl:check:android": "asl-check-plan --scenario scenarios/mobile/{{SCENARIO_ID}}.json --runner runner-manifests/primary-runner.json --provider runner-manifests/evidence-provider.json --platform android --out artifacts/asl/plan/{{SCENARIO_ID}}-android",
|
|
4
|
+
"asl:validate": "asl-validate-project --root . --platform all --out artifacts/asl/project-validation",
|
|
5
|
+
"asl:host:doctor": "asl-host-doctor --out artifacts/asl/host-doctor",
|
|
6
|
+
"asl:profile:ios": "asl-profile-ios --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json ${ASL_PROFILE_IOS_EVENTS:+--events $ASL_PROFILE_IOS_EVENTS} --comparison-lane {{SCENARIO_ID}}-ios-fixture --out artifacts/asl/ios --run-id {{SCENARIO_ID}}-ios",
|
|
7
|
+
"asl:profile:android": "asl-profile-android --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json ${ASL_PROFILE_ANDROID_EVENTS:+--events $ASL_PROFILE_ANDROID_EVENTS} --comparison-lane {{SCENARIO_ID}}-android-fixture --out artifacts/asl/android --run-id {{SCENARIO_ID}}-android",
|
|
8
|
+
"asl:profile:ios:provider": "asl-profile-ios --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json ${ASL_PROFILE_IOS_EVENTS:+--events $ASL_PROFILE_IOS_EVENTS} --provider runner-manifests/evidence-provider.json --comparison-lane {{SCENARIO_ID}}-ios-provider --out artifacts/asl/ios --run-id {{SCENARIO_ID}}-ios-provider",
|
|
9
|
+
"asl:profile:android:provider": "asl-profile-android --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json ${ASL_PROFILE_ANDROID_EVENTS:+--events $ASL_PROFILE_ANDROID_EVENTS} --provider runner-manifests/evidence-provider.json --comparison-lane {{SCENARIO_ID}}-android-provider --out artifacts/asl/android --run-id {{SCENARIO_ID}}-android-provider",
|
|
10
|
+
"asl:agent-device:check": "asl-agent-device --check --out artifacts/asl/agent-device-check",
|
|
11
|
+
"asl:agent-device:ios": "asl-agent-device --platform ios ${ASL_IOS_AGENT_DEVICE_SESSION:+--session $ASL_IOS_AGENT_DEVICE_SESSION} ${ASL_IOS_AGENT_DEVICE_SESSION_MODE:+--session-mode $ASL_IOS_AGENT_DEVICE_SESSION_MODE} ${ASL_IOS_UDID:+--udid $ASL_IOS_UDID} ${ASL_AGENT_DEVICE_COMMAND_TIMEOUT_MS:+--command-timeout-ms $ASL_AGENT_DEVICE_COMMAND_TIMEOUT_MS} --scenario scenarios/mobile/{{SCENARIO_ID}}.json --app ${ASL_IOS_APP_ID:-com.example.app} --open --wait-ms 1000 --out artifacts/asl/agent-device-ios --run-id {{SCENARIO_ID}}-ios-agent-device",
|
|
12
|
+
"asl:agent-device:android": "asl-agent-device --platform android ${ASL_ANDROID_AGENT_DEVICE_SESSION:+--session $ASL_ANDROID_AGENT_DEVICE_SESSION} ${ASL_ANDROID_AGENT_DEVICE_SESSION_MODE:+--session-mode $ASL_ANDROID_AGENT_DEVICE_SESSION_MODE} ${ASL_ANDROID_SERIAL:+--serial $ASL_ANDROID_SERIAL} ${ASL_AGENT_DEVICE_COMMAND_TIMEOUT_MS:+--command-timeout-ms $ASL_AGENT_DEVICE_COMMAND_TIMEOUT_MS} --scenario scenarios/mobile/{{SCENARIO_ID}}.json --app ${ASL_ANDROID_APP_ID:-com.example.app} --open --wait-ms 1000 --out artifacts/asl/agent-device-android --run-id {{SCENARIO_ID}}-android-agent-device",
|
|
13
|
+
"asl:argent:check": "asl-argent --check --out artifacts/asl/argent-check",
|
|
14
|
+
"asl:argent:ios": "asl-argent --platform ios ${ASL_ARGENT_BIN:+--argent $ASL_ARGENT_BIN} ${ASL_ARGENT_BASE_ARGS:+--base-args \"$ASL_ARGENT_BASE_ARGS\"} ${ASL_ARGENT_DEVICE_FLAG:+--device-flag $ASL_ARGENT_DEVICE_FLAG} ${ASL_ARGENT_APP_FLAG:+--app-flag $ASL_ARGENT_APP_FLAG} ${ASL_ARGENT_COMMAND_TIMEOUT_MS:+--command-timeout-ms $ASL_ARGENT_COMMAND_TIMEOUT_MS} ${ASL_ARGENT_IOS_SIMCTL_SCREENSHOT_FALLBACK:+--ios-simctl-screenshot-fallback} ${ASL_XCRUN_PATH:+--xcrun $ASL_XCRUN_PATH} --scenario scenarios/mobile/{{SCENARIO_ID}}.json --app ${ASL_IOS_APP_ID:-com.example.app} --device ${ASL_IOS_UDID:-booted} --out artifacts/asl/argent-ios --run-id {{SCENARIO_ID}}-ios-argent",
|
|
15
|
+
"asl:argent:android": "asl-argent --platform android ${ASL_ARGENT_BIN:+--argent $ASL_ARGENT_BIN} ${ASL_ARGENT_BASE_ARGS:+--base-args \"$ASL_ARGENT_BASE_ARGS\"} ${ASL_ARGENT_DEVICE_FLAG:+--device-flag $ASL_ARGENT_DEVICE_FLAG} ${ASL_ARGENT_APP_FLAG:+--app-flag $ASL_ARGENT_APP_FLAG} ${ASL_ARGENT_COMMAND_TIMEOUT_MS:+--command-timeout-ms $ASL_ARGENT_COMMAND_TIMEOUT_MS} --scenario scenarios/mobile/{{SCENARIO_ID}}.json --app ${ASL_ANDROID_APP_ID:-com.example.app} --device ${ASL_ANDROID_SERIAL:-emulator-5554} --out artifacts/asl/argent-android --run-id {{SCENARIO_ID}}-android-argent",
|
|
16
|
+
"asl:android:live": "asl-live-android --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json ${ASL_ANDROID_SERIAL:+--serial $ASL_ANDROID_SERIAL} ${ASL_ANDROID_APP_ID:+--package $ASL_ANDROID_APP_ID} ${ASL_ANDROID_REACT_NATIVE_DEBUG_HOST:+--react-native-debug-host $ASL_ANDROID_REACT_NATIVE_DEBUG_HOST} --out artifacts/asl/android-live --compare-latest --fail-on-regression",
|
|
17
|
+
"asl:android:live:agent-device": "asl-live-android --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json ${ASL_ANDROID_SERIAL:+--serial $ASL_ANDROID_SERIAL} ${ASL_ANDROID_APP_ID:+--package $ASL_ANDROID_APP_ID} ${ASL_ANDROID_REACT_NATIVE_DEBUG_HOST:+--react-native-debug-host $ASL_ANDROID_REACT_NATIVE_DEBUG_HOST} ${ASL_ANDROID_AGENT_DEVICE_SESSION:+--agent-device-session $ASL_ANDROID_AGENT_DEVICE_SESSION} ${ASL_ANDROID_AGENT_DEVICE_SESSION_MODE:+--agent-device-session-mode $ASL_ANDROID_AGENT_DEVICE_SESSION_MODE} --out artifacts/asl/android-live --run-suffix agent-device --agent-device-proof --compare-latest --fail-on-regression",
|
|
18
|
+
"asl:android:live:argent": "asl-live-android --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json ${ASL_ANDROID_SERIAL:+--serial $ASL_ANDROID_SERIAL} ${ASL_ANDROID_APP_ID:+--package $ASL_ANDROID_APP_ID} ${ASL_ANDROID_REACT_NATIVE_DEBUG_HOST:+--react-native-debug-host $ASL_ANDROID_REACT_NATIVE_DEBUG_HOST} --out artifacts/asl/android-live --run-suffix argent --argent-proof --compare-latest --fail-on-regression",
|
|
19
|
+
"asl:android:live:runners": "asl-live-android --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json ${ASL_ANDROID_SERIAL:+--serial $ASL_ANDROID_SERIAL} ${ASL_ANDROID_APP_ID:+--package $ASL_ANDROID_APP_ID} ${ASL_ANDROID_REACT_NATIVE_DEBUG_HOST:+--react-native-debug-host $ASL_ANDROID_REACT_NATIVE_DEBUG_HOST} ${ASL_ANDROID_AGENT_DEVICE_SESSION:+--agent-device-session $ASL_ANDROID_AGENT_DEVICE_SESSION} ${ASL_ANDROID_AGENT_DEVICE_SESSION_MODE:+--agent-device-session-mode $ASL_ANDROID_AGENT_DEVICE_SESSION_MODE} --out artifacts/asl/android-live --run-suffix runners --agent-device-proof --argent-proof --compare-latest --fail-on-regression",
|
|
20
|
+
"asl:ios:live": "asl-live-ios --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json ${ASL_IOS_UDID:+--device $ASL_IOS_UDID} ${ASL_IOS_APP_ID:+--bundle $ASL_IOS_APP_ID} --out artifacts/asl/ios-live --compare-latest --fail-on-regression",
|
|
21
|
+
"asl:ios:live:agent-device": "asl-live-ios --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json ${ASL_IOS_UDID:+--device $ASL_IOS_UDID} ${ASL_IOS_APP_ID:+--bundle $ASL_IOS_APP_ID} ${ASL_IOS_AGENT_DEVICE_SESSION:+--agent-device-session $ASL_IOS_AGENT_DEVICE_SESSION} ${ASL_IOS_AGENT_DEVICE_SESSION_MODE:+--agent-device-session-mode $ASL_IOS_AGENT_DEVICE_SESSION_MODE} --out artifacts/asl/ios-live --run-suffix agent-device --agent-device-proof --compare-latest --fail-on-regression",
|
|
22
|
+
"asl:ios:live:argent": "asl-live-ios --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json ${ASL_IOS_UDID:+--device $ASL_IOS_UDID} ${ASL_IOS_APP_ID:+--bundle $ASL_IOS_APP_ID} --out artifacts/asl/ios-live --run-suffix argent --argent-proof --compare-latest --fail-on-regression",
|
|
23
|
+
"asl:ios:live:runners": "asl-live-ios --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json ${ASL_IOS_UDID:+--device $ASL_IOS_UDID} ${ASL_IOS_APP_ID:+--bundle $ASL_IOS_APP_ID} ${ASL_IOS_AGENT_DEVICE_SESSION:+--agent-device-session $ASL_IOS_AGENT_DEVICE_SESSION} ${ASL_IOS_AGENT_DEVICE_SESSION_MODE:+--agent-device-session-mode $ASL_IOS_AGENT_DEVICE_SESSION_MODE} --out artifacts/asl/ios-live --run-suffix runners --agent-device-proof --argent-proof --compare-latest --fail-on-regression",
|
|
24
|
+
"asl:profile:ios:live": "asl-profile-ios --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json --simctl-capture --profile-session --profile-session-storage --launch --comparison-lane {{SCENARIO_ID}}-ios-live --out artifacts/asl/ios --run-id {{SCENARIO_ID}}-ios-live",
|
|
25
|
+
"asl:profile:android:live": "asl-profile-android --config asl.config.json --scenario scenarios/mobile/{{SCENARIO_ID}}.json --adb-capture --profile-session --clear-logcat --launch --comparison-lane {{SCENARIO_ID}}-android-live --out artifacts/asl/android --run-id {{SCENARIO_ID}}-android-live",
|
|
26
|
+
"asl:compare:ios": "asl-compare-latest --root artifacts/asl/ios --scenario {{SCENARIO_ID}} --current ${ASL_COMPARE_IOS_CURRENT:?set_ASL_COMPARE_IOS_CURRENT} --out artifacts/asl/ios/comparisons/{{SCENARIO_ID}} --fail-on-regression",
|
|
27
|
+
"asl:compare:android": "asl-compare-latest --root artifacts/asl/android --scenario {{SCENARIO_ID}} --current ${ASL_COMPARE_ANDROID_CURRENT:?set_ASL_COMPARE_ANDROID_CURRENT} --out artifacts/asl/android/comparisons/{{SCENARIO_ID}} --fail-on-regression",
|
|
28
|
+
"asl:live-proof:android": "asl-live-proof --file ${ASL_ANDROID_LIVE_PROOF:-artifacts/asl/android-live/_live-proof/android-live-proof/live-proof.json} --fail-on-regression",
|
|
29
|
+
"asl:live-proof:ios": "asl-live-proof --file ${ASL_IOS_LIVE_PROOF:-artifacts/asl/ios-live/_live-proof/ios-live-proof/live-proof.json} --fail-on-regression",
|
|
30
|
+
"asl:live-proof:both": "asl-live-proof --file ${ASL_ANDROID_LIVE_PROOF:-artifacts/asl/android-live/_live-proof/android-live-proof/live-proof.json} --file ${ASL_IOS_LIVE_PROOF:-artifacts/asl/ios-live/_live-proof/ios-live-proof/live-proof.json} --require-platforms android,ios --out artifacts/asl/live-proof-set --fail-on-regression ${ASL_REQUIRE_LIVE_PROOF_ARTIFACTS:+--require-artifacts}",
|
|
31
|
+
"asl:live-proof": "asl-live-proof --file ${ASL_LIVE_PROOF:?set_ASL_LIVE_PROOF} --fail-on-regression"
|
|
32
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0.0",
|
|
3
|
+
"runnerId": "example-mobile-runner",
|
|
4
|
+
"kind": "primary",
|
|
5
|
+
"platforms": ["ios", "android"],
|
|
6
|
+
"capabilities": ["launch", "sessionControl", "command", "logCapture", "artifactWrite"],
|
|
7
|
+
"driverActions": ["tap", "readLogs"],
|
|
8
|
+
"artifactOutputs": ["logs", "signals"],
|
|
9
|
+
"lifecycle": [
|
|
10
|
+
"prepare",
|
|
11
|
+
"launch",
|
|
12
|
+
"startSession",
|
|
13
|
+
"executeStep",
|
|
14
|
+
"waitForTruthEvent",
|
|
15
|
+
"captureEvidence",
|
|
16
|
+
"stopSession",
|
|
17
|
+
"finalize"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"projectName": "replace-me",
|
|
3
|
+
"app": {
|
|
4
|
+
"displayName": "Example App",
|
|
5
|
+
"scheme": "example-app",
|
|
6
|
+
"profileSessionScheme": "example-app",
|
|
7
|
+
"iosBundleId": "com.example.app",
|
|
8
|
+
"iosConflictingBundleIds": [],
|
|
9
|
+
"androidPackage": "com.example.app",
|
|
10
|
+
"ios": {
|
|
11
|
+
"xcodeScheme": "Example App",
|
|
12
|
+
"launchConfiguration": "Debug",
|
|
13
|
+
"profileConfiguration": "Release"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"paths": {
|
|
17
|
+
"artifactRoot": "artifacts/asl",
|
|
18
|
+
"iosArtifactsRoot": "artifacts/asl/ios",
|
|
19
|
+
"androidArtifactsRoot": "artifacts/asl/android",
|
|
20
|
+
"scenarioRoot": "scenarios/mobile"
|
|
21
|
+
},
|
|
22
|
+
"drivers": {
|
|
23
|
+
"default": "fixture-log-ingest",
|
|
24
|
+
"supported": ["fixture-log-ingest", "adb", "ios-simctl", "agent-device", "argent", "xcodebuildmcp"]
|
|
25
|
+
},
|
|
26
|
+
"versionControl": {
|
|
27
|
+
"commit": [
|
|
28
|
+
"scenarios/**",
|
|
29
|
+
"runner-manifests/**",
|
|
30
|
+
"docs/**"
|
|
31
|
+
],
|
|
32
|
+
"ignore": [
|
|
33
|
+
"artifacts/asl/**",
|
|
34
|
+
"raw simulator recordings, screenshots, and ephemeral profiler exports"
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parses simple `--key value` CLI arguments.
|
|
8
|
+
*
|
|
9
|
+
* @param {string[]} argv
|
|
10
|
+
* @returns {Record<string, string | boolean>}
|
|
11
|
+
*/
|
|
12
|
+
function parseArgs(argv) {
|
|
13
|
+
const args = {};
|
|
14
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
15
|
+
const token = argv[index];
|
|
16
|
+
if (!token?.startsWith('--')) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const key = token.slice(2);
|
|
21
|
+
const value = argv[index + 1];
|
|
22
|
+
if (value && !value.startsWith('--')) {
|
|
23
|
+
args[key] = value;
|
|
24
|
+
index += 1;
|
|
25
|
+
} else {
|
|
26
|
+
args[key] = true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return args;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Reads a required string CLI argument.
|
|
34
|
+
*
|
|
35
|
+
* @param {Record<string, string | boolean>} args
|
|
36
|
+
* @param {string} key
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
function requireStringArg(args, key) {
|
|
40
|
+
const value = args[key];
|
|
41
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
42
|
+
throw new Error(`Missing required --${key} value.`);
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Writes a formatted JSON artifact.
|
|
49
|
+
*
|
|
50
|
+
* @param {string} outPath
|
|
51
|
+
* @param {Record<string, unknown>} payload
|
|
52
|
+
* @returns {void}
|
|
53
|
+
*/
|
|
54
|
+
function writeJsonArtifact(outPath, payload) {
|
|
55
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
56
|
+
fs.writeFileSync(outPath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Writes deterministic accessibility evidence for a scaffolded provider command.
|
|
61
|
+
*
|
|
62
|
+
* @param {{outPath: string, platform: string, runId: string, scenarioId: string}} options
|
|
63
|
+
* @returns {void}
|
|
64
|
+
*/
|
|
65
|
+
function writeAccessibilityEvidence({
|
|
66
|
+
outPath,
|
|
67
|
+
platform,
|
|
68
|
+
runId,
|
|
69
|
+
scenarioId,
|
|
70
|
+
}) {
|
|
71
|
+
writeJsonArtifact(outPath, {
|
|
72
|
+
checks: [
|
|
73
|
+
{
|
|
74
|
+
id: 'scenario-entrypoint-visible',
|
|
75
|
+
selector: {
|
|
76
|
+
kind: 'testId',
|
|
77
|
+
value: 'asl-example-title',
|
|
78
|
+
},
|
|
79
|
+
status: 'passed',
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
platform,
|
|
83
|
+
providerId: 'example-evidence-provider',
|
|
84
|
+
runId,
|
|
85
|
+
scenarioId,
|
|
86
|
+
schemaVersion: '1.0.0',
|
|
87
|
+
summary: 'Replace this scaffold accessibility evidence with project-local axe, inspector, or driver output.',
|
|
88
|
+
violations: [],
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Runs the provider script.
|
|
94
|
+
*
|
|
95
|
+
* @returns {void}
|
|
96
|
+
*/
|
|
97
|
+
function main() {
|
|
98
|
+
const args = parseArgs(process.argv.slice(2));
|
|
99
|
+
writeAccessibilityEvidence({
|
|
100
|
+
outPath: requireStringArg(args, 'out'),
|
|
101
|
+
platform: requireStringArg(args, 'platform'),
|
|
102
|
+
runId: requireStringArg(args, 'run-id'),
|
|
103
|
+
scenarioId: requireStringArg(args, 'scenario'),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
main();
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
111
|
+
process.exitCode = 1;
|
|
112
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parses simple `--key value` CLI arguments.
|
|
8
|
+
*
|
|
9
|
+
* @param {string[]} argv
|
|
10
|
+
* @returns {Record<string, string | boolean>}
|
|
11
|
+
*/
|
|
12
|
+
function parseArgs(argv) {
|
|
13
|
+
const args = {};
|
|
14
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
15
|
+
const token = argv[index];
|
|
16
|
+
if (!token?.startsWith('--')) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const key = token.slice(2);
|
|
21
|
+
const value = argv[index + 1];
|
|
22
|
+
if (value && !value.startsWith('--')) {
|
|
23
|
+
args[key] = value;
|
|
24
|
+
index += 1;
|
|
25
|
+
} else {
|
|
26
|
+
args[key] = true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return args;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Reads a required string CLI argument.
|
|
34
|
+
*
|
|
35
|
+
* @param {Record<string, string | boolean>} args
|
|
36
|
+
* @param {string} key
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
function requireStringArg(args, key) {
|
|
40
|
+
const value = args[key];
|
|
41
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
42
|
+
throw new Error(`Missing required --${key} value.`);
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Writes a formatted JSON artifact.
|
|
49
|
+
*
|
|
50
|
+
* @param {string} outPath
|
|
51
|
+
* @param {Record<string, unknown>} payload
|
|
52
|
+
* @returns {void}
|
|
53
|
+
*/
|
|
54
|
+
function writeJsonArtifact(outPath, payload) {
|
|
55
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
56
|
+
fs.writeFileSync(outPath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Writes deterministic evidence for a scaffolded provider command.
|
|
61
|
+
*
|
|
62
|
+
* @param {{memoryOutPath: string, networkOutPath: string, outPath: string, platform: string, runId: string, scenarioId: string}} options
|
|
63
|
+
* @returns {void}
|
|
64
|
+
*/
|
|
65
|
+
function writeProviderEvidence({
|
|
66
|
+
memoryOutPath,
|
|
67
|
+
networkOutPath,
|
|
68
|
+
outPath,
|
|
69
|
+
platform,
|
|
70
|
+
runId,
|
|
71
|
+
scenarioId,
|
|
72
|
+
}) {
|
|
73
|
+
const shared = {
|
|
74
|
+
platform,
|
|
75
|
+
providerId: 'example-evidence-provider',
|
|
76
|
+
runId,
|
|
77
|
+
scenarioId,
|
|
78
|
+
schemaVersion: '1.0.0',
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
writeJsonArtifact(outPath, {
|
|
82
|
+
...shared,
|
|
83
|
+
samples: [],
|
|
84
|
+
summary: 'Replace this scaffold provider with a project-local profiler command when real profiler data is available.',
|
|
85
|
+
});
|
|
86
|
+
writeJsonArtifact(memoryOutPath, {
|
|
87
|
+
...shared,
|
|
88
|
+
jsHeapBytes: null,
|
|
89
|
+
nativeHeapBytes: null,
|
|
90
|
+
summary: 'Replace this scaffold memory signal with device or runtime memory evidence.',
|
|
91
|
+
});
|
|
92
|
+
writeJsonArtifact(networkOutPath, {
|
|
93
|
+
log: {
|
|
94
|
+
creator: {
|
|
95
|
+
name: 'agent-scenario-loop scaffold provider',
|
|
96
|
+
version: '1.0.0',
|
|
97
|
+
},
|
|
98
|
+
entries: [],
|
|
99
|
+
version: '1.2',
|
|
100
|
+
},
|
|
101
|
+
...shared,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Runs the provider script.
|
|
107
|
+
*
|
|
108
|
+
* @returns {void}
|
|
109
|
+
*/
|
|
110
|
+
function main() {
|
|
111
|
+
const args = parseArgs(process.argv.slice(2));
|
|
112
|
+
writeProviderEvidence({
|
|
113
|
+
memoryOutPath: requireStringArg(args, 'memory-out'),
|
|
114
|
+
networkOutPath: requireStringArg(args, 'network-out'),
|
|
115
|
+
outPath: requireStringArg(args, 'out'),
|
|
116
|
+
platform: requireStringArg(args, 'platform'),
|
|
117
|
+
runId: requireStringArg(args, 'run-id'),
|
|
118
|
+
scenarioId: requireStringArg(args, 'scenario'),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
main();
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
126
|
+
process.exitCode = 1;
|
|
127
|
+
}
|