kaloko 0.3.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 (68) hide show
  1. package/README.md +119 -0
  2. package/bin/kaloko.mjs +4 -0
  3. package/bin/qawalk.mjs +4 -0
  4. package/package.json +42 -0
  5. package/schemas/bundle.v1.json +19 -0
  6. package/schemas/config.v1.json +268 -0
  7. package/schemas/revision.v1.json +36 -0
  8. package/schemas/run.v1.json +549 -0
  9. package/schemas/scenario.v1.json +668 -0
  10. package/schemas/tokens.v1.json +102 -0
  11. package/skills/kaloko/SKILL.md +94 -0
  12. package/skills/kaloko/reference/acceptance.md +208 -0
  13. package/skills/kaloko/reference/apps.md +161 -0
  14. package/skills/kaloko/reference/collaboration.md +218 -0
  15. package/skills/kaloko/reference/design.md +143 -0
  16. package/skills/kaloko/reference/documentation.md +72 -0
  17. package/skills/kaloko/reference/environments.md +140 -0
  18. package/skills/kaloko/reference/evaluation.md +62 -0
  19. package/skills/kaloko/reference/execution.md +75 -0
  20. package/skills/kaloko/reference/mcp.md +69 -0
  21. package/skills/kaloko/reference/stacks.md +122 -0
  22. package/src/cli.mjs +1344 -0
  23. package/src/lib/access.mjs +169 -0
  24. package/src/lib/apps/capture.mjs +59 -0
  25. package/src/lib/apps/drivers.mjs +284 -0
  26. package/src/lib/apps/helper.swift +102 -0
  27. package/src/lib/apps/tree.mjs +222 -0
  28. package/src/lib/browser-daemon.mjs +41 -0
  29. package/src/lib/browser.mjs +161 -0
  30. package/src/lib/build.mjs +149 -0
  31. package/src/lib/calibrate.mjs +97 -0
  32. package/src/lib/capture.mjs +233 -0
  33. package/src/lib/compare.mjs +88 -0
  34. package/src/lib/config.mjs +117 -0
  35. package/src/lib/console.mjs +13 -0
  36. package/src/lib/coverage.mjs +75 -0
  37. package/src/lib/design-cli.mjs +440 -0
  38. package/src/lib/design-core.mjs +163 -0
  39. package/src/lib/design.mjs +164 -0
  40. package/src/lib/doctor.mjs +149 -0
  41. package/src/lib/draft.mjs +76 -0
  42. package/src/lib/drift.mjs +145 -0
  43. package/src/lib/evaluate.mjs +222 -0
  44. package/src/lib/git.mjs +70 -0
  45. package/src/lib/htmlview.mjs +130 -0
  46. package/src/lib/jev.mjs +95 -0
  47. package/src/lib/mail.mjs +182 -0
  48. package/src/lib/manifest-core.mjs +127 -0
  49. package/src/lib/pack-help.mjs +55 -0
  50. package/src/lib/packs.mjs +121 -0
  51. package/src/lib/pr.mjs +78 -0
  52. package/src/lib/probe.mjs +379 -0
  53. package/src/lib/recheck.mjs +70 -0
  54. package/src/lib/run.mjs +85 -0
  55. package/src/lib/scenario.mjs +98 -0
  56. package/src/lib/schema.mjs +25 -0
  57. package/src/lib/secrets.mjs +59 -0
  58. package/src/lib/service.mjs +52 -0
  59. package/src/lib/stack.mjs +130 -0
  60. package/src/lib/tokens.mjs +352 -0
  61. package/src/lib/util.mjs +97 -0
  62. package/templates/ACCEPTANCE.md +55 -0
  63. package/templates/cs/ACCEPTANCE.md +55 -0
  64. package/templates/kaloko.config.yml +76 -0
  65. package/templates/mail/letter-opener.mjs +47 -0
  66. package/templates/scenario.yml +119 -0
  67. package/templates/steps/consent.mjs +33 -0
  68. package/viewer/index.html +2772 -0
package/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # Kaloko
2
+
3
+ Acceptance QA walkthroughs rendered as a shareable flow canvas.
4
+
5
+ Your coding agent (Claude Code, Codex or any LLM with a shell) runs QA from
6
+ your acceptance plan with the `kaloko` skill. Kaloko captures every screen and
7
+ e-mail of the flow, checks the criteria and builds a canvas that shows the
8
+ whole flow (screens, branches, locales, evidence) for review, sign-off and
9
+ living documentation.
10
+
11
+ - Product and guides: <https://kaloko.app>
12
+ - Service (organizations, shared canvases, reviews): <https://kaloko.app>
13
+
14
+ Kaloko also walks **native apps**: Android over adb, the iOS Simulator,
15
+ Electron and macOS apps. Screens taken by any other tool (Maestro, Appium,
16
+ XCUITest) are imported with their UI tree. See the `apps` reference of the
17
+ skill.
18
+
19
+ ## Install
20
+
21
+ Requires Node 20 or newer.
22
+
23
+ ```bash
24
+ npm install --save-dev kaloko
25
+ npx kaloko init --agent claude --org <your-org> # or --agent codex | all
26
+ npx kaloko doctor
27
+ ```
28
+
29
+ `init` creates `kaloko.config.yml` and copies the `kaloko` skill to
30
+ `.claude/skills/kaloko` (Claude Code) or `skills/kaloko` (Codex). Run it again
31
+ after `npm update kaloko` to refresh the skill.
32
+
33
+ ## Then ask your agent
34
+
35
+ With the skill installed you do not type the commands below; the agent does.
36
+ It writes the acceptance plan and the scenario from the task, walks the
37
+ screens, evaluates, shares the canvas and later processes reviewers' feedback.
38
+ For example:
39
+
40
+ > Read the task in docs/tasks/TASK-123, write its acceptance plan and a Kaloko
41
+ > scenario, walk it on local and share the result on the pull request.
42
+
43
+ > Go through the open Kaloko feedback on TASK-123: fix what reviewers
44
+ > rejected, run the affected steps again, share, and answer each comment with
45
+ > what changed.
46
+
47
+ > Draft a Kaloko scenario for https://example.com (home, pricing, contact)
48
+ > with the seo, a11y and perf packs, run it on production read-only and
49
+ > summarise what fails.
50
+
51
+ ## Run a scenario by hand (or in CI)
52
+
53
+ ```bash
54
+ npx kaloko validate qa/flows/checkout.yml
55
+ npx kaloko start --scenario qa/flows/checkout.yml --env local
56
+ npx kaloko browser start
57
+ npx kaloko walk # playwright steps
58
+ npx kaloko capture --step <id> # agent or manual steps
59
+ npx kaloko evaluate # deterministic checks + semantic evaluator
60
+ npx kaloko preview # local canvas
61
+ npx kaloko share --pr # upload to kaloko.app, comment on the PR
62
+ ```
63
+
64
+ `npx kaloko help` lists every command. When something does not work (Chrome,
65
+ token, environment URL, evaluator key), `npx kaloko doctor` says what to fix.
66
+
67
+ ## Design (Kaloko Design)
68
+
69
+ Design the screens of a flow as live HTML with your own agent and skills, in the same scenario development and
70
+ QA use. A design environment (`kind: design`, a folder of steps) is walked and synced into a shared draft the
71
+ team sees live; `kaloko share` there publishes a version of the flow (unchanged steps are stored once). An
72
+ accepted version becomes the design reference, and `packs: [design]` checks every implementation run against
73
+ it (pixels, structure, tokens, contrast). See `skills/kaloko/reference/design.md`, `docs/GLOSSARY.md` and
74
+ `docs/SURFACES.md`.
75
+
76
+ ```bash
77
+ npx kaloko start --scenario qa/flows/checkout.yml --env design
78
+ npx kaloko walk && npx kaloko sync # only changed steps render and travel
79
+ npx kaloko share --name "Checkout v2" # a version of the whole flow
80
+ npx kaloko reference --design --version 2 # after the team accepted it
81
+ ```
82
+
83
+ ## Configuration and secrets
84
+
85
+ Project configuration is `kaloko.config.yml` in the repository root:
86
+ environments, readonly rules for production, browser, mailbox and evaluator.
87
+ `npx kaloko init` writes a commented template.
88
+
89
+ Secrets are never written in the config. Wherever a secret is needed, the
90
+ config names an environment variable of your choice (`password_env:
91
+ STAGING_PASSWORD`, `{ env: CF_ACCESS_CLIENT_SECRET }`). Values are read from the
92
+ environment, then the project `.env` next to `kaloko.config.yml`, then
93
+ `~/.config/kaloko/.env` (existing variables win), and are redacted from
94
+ captured HTML, page text and console output.
95
+
96
+ - **Protected environments** (staging, review apps): `access` sends HTTP Basic
97
+ credentials, service-token headers (Cloudflare Access, Vercel protection
98
+ bypass, your own) or a client certificate, to the environment's own origins
99
+ only. `base_url` may use `${VAR}` for review-app URLs from CI.
100
+ - **Signed-in users**: `accounts` maps the scenario sessions' `account` keys to
101
+ credentials per environment (user, password, TOTP). For SSO or MFA a person
102
+ signs in once with `npx kaloko auth save --env <env> --account <key>`.
103
+ - **E-mails**: built-in Mailpit client; any other mailbox through a small
104
+ adapter module (`provider: script`, example in `templates/mail/`), or hand a
105
+ message over with `npx kaloko mail --step <id> --file message.eml`.
106
+ - **Service and evaluator**: `KALOKO_TOKEN` for `share`, `feedback`, `inbox` and clean-up (`runs`, `trash`, `prune`, `keep`, `archive`)
107
+ (create it in your organization's settings); `TYPESAFE_API_KEY` for the
108
+ semantic evaluator (optional). Both names can be changed in the config.
109
+
110
+ `npx kaloko doctor` checks that every variable the config names is set and that
111
+ each environment lets you in.
112
+
113
+ Set `browser.channel: chrome` to use an installed Chrome, or run
114
+ `npx playwright install chromium`.
115
+
116
+ ## License
117
+
118
+ Proprietary. Use of Kaloko is subject to the terms at
119
+ <https://kaloko.app/terms>.
package/bin/kaloko.mjs ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { main } from '../src/cli.mjs'
3
+
4
+ await main(process.argv.slice(2))
package/bin/qawalk.mjs ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { main } from '../src/cli.mjs'
3
+
4
+ await main(process.argv.slice(2))
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "kaloko",
3
+ "version": "0.3.0",
4
+ "description": "See what your AI agents built: every screen, language and device walked, assessed and approved on one canvas.",
5
+ "keywords": [
6
+ "qa",
7
+ "acceptance-testing",
8
+ "playwright",
9
+ "claude-code",
10
+ "codex",
11
+ "agent-skills"
12
+ ],
13
+ "homepage": "https://kaloko.app",
14
+ "type": "module",
15
+ "license": "UNLICENSED",
16
+ "bin": {
17
+ "kaloko": "bin/kaloko.mjs",
18
+ "qawalk": "bin/qawalk.mjs"
19
+ },
20
+ "files": [
21
+ "bin",
22
+ "src",
23
+ "schemas",
24
+ "templates",
25
+ "viewer",
26
+ "skills/kaloko"
27
+ ],
28
+ "engines": {
29
+ "node": ">=20"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "scripts": {
35
+ "test": "node --test --test-timeout=300000 test/*.test.mjs worker/test/*.test.mjs"
36
+ },
37
+ "dependencies": {
38
+ "ajv": "^8.20.0",
39
+ "js-yaml": "^4.1.0",
40
+ "playwright": "^1.55.0"
41
+ }
42
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://qawalk.com/schemas/bundle.v1.json",
4
+ "title": "qawalk.bundle.v1",
5
+ "description": "The live files of one design step as the CLI and the service exchange them (#42). Paths are relative to the design folder: <step>/index.html (or <step>/<locale>/index.html), shared files in _shared/, the token set in tokens.json. Enforced by src/lib/design-core.mjs on both sides: at most 400 files, 10 MB per file, 40 MB per revision, 8 folders deep, 240 characters per path.",
6
+ "type": "object",
7
+ "required": ["entry", "files"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "entry": { "type": "string", "pattern": "\\.html$", "description": "The page of the step." },
11
+ "files": {
12
+ "type": "object",
13
+ "description": "Path → sha256 of the file content.",
14
+ "propertyNames": { "pattern": "^(?!/)(?!.*(?:^|/)\\.)[A-Za-z0-9_][A-Za-z0-9._-]*(?:/[A-Za-z0-9_][A-Za-z0-9._-]*)*\\.(html|css|js|mjs|json|svg|woff2|woff|png|jpg|jpeg|webp|avif|gif|mp4|webm|txt)$" },
15
+ "additionalProperties": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
16
+ "maxProperties": 400
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,268 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://qawalk.com/schemas/config.v1.json",
4
+ "title": "Kaloko project configuration (kaloko.config.yml)",
5
+ "type": "object",
6
+ "required": ["schema", "org", "environments"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "schema": { "const": "qawalk.config.v1" },
10
+ "org": { "type": "string", "description": "Organization slug on kaloko.app." },
11
+ "project": { "type": "string" },
12
+ "scenarios": { "type": "array", "items": { "type": "string" }, "description": "Glob patterns of scenario files." },
13
+ "output": { "type": "string", "description": "Run folder; default tmp/kaloko (tmp/qawalk when only that one exists)" },
14
+ "browser": {
15
+ "type": "object",
16
+ "additionalProperties": false,
17
+ "properties": {
18
+ "channel": { "type": "string", "description": "Playwright channel, e.g. chrome. Empty = bundled Chromium." },
19
+ "port": { "type": "integer", "default": 9333 }
20
+ }
21
+ },
22
+ "environments": {
23
+ "type": "object",
24
+ "minProperties": 1,
25
+ "additionalProperties": { "$ref": "#/$defs/environment" }
26
+ },
27
+ "evaluators": {
28
+ "type": "object",
29
+ "additionalProperties": false,
30
+ "properties": {
31
+ "jev": {
32
+ "type": "object",
33
+ "additionalProperties": false,
34
+ "properties": {
35
+ "model": { "type": "string", "default": "jev-1.13.0" },
36
+ "api_key_env": { "type": "string", "default": "TYPESAFE_API_KEY" },
37
+ "base_url": { "type": "string", "default": "https://api.typesafe.ai" },
38
+ "thresholds": {
39
+ "type": "object",
40
+ "additionalProperties": false,
41
+ "properties": { "pass": { "type": "number" }, "fail": { "type": "number" } }
42
+ },
43
+ "max_state_chars": { "type": "integer", "default": 90000 },
44
+ "hosted": { "type": "boolean", "default": true, "description": "Without a local evaluator key, evaluate through the organization's service (plan allowance per month). false: only your own key." },
45
+ "signals": { "type": "boolean", "default": true, "description": "Quick signals on every screen (purpose met, expected language, looks broken); never change verdicts." },
46
+ "concurrency": { "type": "integer", "minimum": 1, "maximum": 16, "default": 6, "description": "Evaluator requests in flight at once." }
47
+ }
48
+ }
49
+ }
50
+ },
51
+ "share": {
52
+ "type": "object",
53
+ "additionalProperties": false,
54
+ "properties": {
55
+ "url": { "type": "string", "default": "https://kaloko.app" },
56
+ "token_env": { "type": "string", "default": "KALOKO_TOKEN" },
57
+ "expires_days": { "type": "integer", "default": 30 }
58
+ }
59
+ }
60
+ },
61
+ "$defs": {
62
+ "environment": {
63
+ "type": "object",
64
+ "additionalProperties": false,
65
+ "if": { "properties": { "kind": { "const": "design" } }, "required": ["kind"] },
66
+ "then": { "required": ["serve"] },
67
+ "else": { "anyOf": [{ "required": ["base_url"] }, { "required": ["app"] }] },
68
+ "properties": {
69
+ "kind": { "enum": ["app", "design"], "default": "app", "description": "app: the application under test (a web app at base_url, or a native app in the app block). design: a folder of live HTML steps served locally by the CLI (serve); its runs are published as design versions (#42)." },
70
+ "serve": { "type": "string", "description": "kind: design. Folder with one sub-folder per step (<step>/index.html, optional <step>/<locale>/index.html) and shared files in _shared/; relative to the config." },
71
+ "fixtures": { "type": "string", "description": "kind: design. Folder of JSON fixtures live steps may fetch instead of real APIs; relative to the config." },
72
+ "tokens_mode": { "type": "string", "description": "kind: design. Token mode (e.g. light, dark) steps are rendered in; default: the token set's first mode." },
73
+ "html_view": { "type": "boolean", "default": false, "description": "Experimental (#42): keep a static HTML view of every capture (assets rehosted, scripts removed, secrets scrubbed). Refused on production unless the environment is readonly." },
74
+ "base_url": {
75
+ "oneOf": [
76
+ { "type": "string" },
77
+ { "type": "object", "additionalProperties": { "type": "string" }, "description": "Per-locale base URL; key 'default' is the fallback." }
78
+ ],
79
+ "description": "Base URL, or per-locale base URLs (key \"default\" is the fallback). ${VAR} is replaced from the environment, e.g. https://${REVIEW_HOST} for review apps."
80
+ },
81
+ "readonly": { "type": "boolean", "default": false },
82
+ "app": {
83
+ "type": "object",
84
+ "additionalProperties": false,
85
+ "description": "The app under test in this environment, per platform (scenario platform: android, ios, electron, desktop). Paths of app scenarios are deep links.",
86
+ "properties": {
87
+ "android": {
88
+ "type": "object", "additionalProperties": false,
89
+ "properties": {
90
+ "package": { "type": "string", "description": "Application id, e.g. com.acme.shop.staging." },
91
+ "activity": { "type": "string", "description": "Launch activity (default: the launcher activity)." },
92
+ "apk": { "type": "string", "description": "APK to install before the walk (path relative to the repository)." },
93
+ "install": { "type": "boolean" },
94
+ "serial": { "type": "string", "description": "adb device serial; default ANDROID_SERIAL or the only device." },
95
+ "device": { "type": "string", "description": "Label for the canvas; default the device model." },
96
+ "reset": { "type": "boolean", "description": "Clear app data at the start of every locale (a new user)." },
97
+ "adb": { "type": "string" }, "launch_wait": { "type": "integer" }, "settle": { "type": "integer" }
98
+ }
99
+ },
100
+ "ios": {
101
+ "type": "object", "additionalProperties": false,
102
+ "properties": {
103
+ "bundle": { "type": "string", "description": "Bundle id, e.g. com.acme.shop." },
104
+ "app": { "type": "string", "description": ".app build for the simulator to install before the walk." },
105
+ "install": { "type": "boolean" },
106
+ "device": { "type": "string", "description": "Simulator name, e.g. iPhone 17; booted when needed." },
107
+ "udid": { "type": "string" },
108
+ "reset": { "type": "boolean" }, "args": { "type": "array", "items": { "type": "string" } },
109
+ "idb": { "type": "boolean", "description": "Use idb for the UI tree and taps when installed (default true)." },
110
+ "ocr": { "type": "boolean", "description": "Without idb, read the screen text by OCR (macOS Vision; default true)." },
111
+ "status_bar": { "type": "boolean", "description": "Clean status bar (9:41, full battery) for captures; default true." },
112
+ "scale": { "type": "integer" }, "launch_wait": { "type": "integer" }, "settle": { "type": "integer" }
113
+ }
114
+ },
115
+ "electron": {
116
+ "type": "object", "additionalProperties": false,
117
+ "properties": {
118
+ "main": { "type": "string", "description": "Main script of the app (e.g. dist/main.js), started with the project's electron." },
119
+ "executable": { "type": "string", "description": "Electron binary or the packaged app's executable." },
120
+ "args": { "type": "array", "items": { "type": "string" } },
121
+ "env": { "type": "object", "additionalProperties": { "type": "string" } },
122
+ "size": { "type": "array", "items": { "type": "integer" }, "minItems": 2, "maxItems": 2 }
123
+ }
124
+ },
125
+ "desktop": {
126
+ "type": "object", "additionalProperties": false,
127
+ "properties": {
128
+ "name": { "type": "string", "description": "The app as macOS shows it (window owner), e.g. Notes." },
129
+ "bundle": { "type": "string" }, "path": { "type": "string" },
130
+ "args": { "type": "array", "items": { "type": "string" } },
131
+ "launch": { "type": "boolean", "description": "Open the app at the start (default true)." },
132
+ "reset": { "type": "boolean" }, "ocr": { "type": "boolean" }, "launch_wait": { "type": "integer" }, "settle": { "type": "integer" }
133
+ }
134
+ }
135
+ }
136
+ },
137
+ "may_create_data": { "type": "boolean", "default": true },
138
+ "blocked_paths": { "type": "array", "items": { "type": "string" }, "description": "Path globs a readonly run must never open." },
139
+ "allowed_post_paths": { "type": "array", "items": { "type": "string" }, "description": "Path globs a readonly run may POST to (e.g. cart)." },
140
+ "http_auth": { "$ref": "#/$defs/basicAuth", "description": "Deprecated: use access.basic. Still read as access.basic." },
141
+ "mail": {
142
+ "type": "object",
143
+ "additionalProperties": false,
144
+ "properties": {
145
+ "provider": {
146
+ "enum": ["mailpit", "script", "none"],
147
+ "description": "mailpit: built-in REST client; script: a project adapter module (any other mailbox); none: capture e-mails with kaloko mail --file."
148
+ },
149
+ "url": { "type": "string" },
150
+ "user_env": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", "description": "Deprecated: use auth.basic." },
151
+ "password_env": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", "description": "Deprecated: use auth.basic." },
152
+ "address": { "type": "string", "description": "Recipient template, {run} is replaced by a short run id, e.g. qa+{run}@example.com." },
153
+ "script": {
154
+ "type": "string",
155
+ "description": "provider: script — module path relative to the config; default export async ({ to, subject, since, options, root, env }) => message | null."
156
+ },
157
+ "options": { "type": "object", "description": "Free-form options handed to the script adapter." },
158
+ "auth": {
159
+ "type": "object",
160
+ "additionalProperties": false,
161
+ "properties": { "basic": { "$ref": "#/$defs/basicAuth" }, "headers": { "$ref": "#/$defs/headers" } }
162
+ }
163
+ }
164
+ },
165
+ "access": {
166
+ "type": "object",
167
+ "additionalProperties": false,
168
+ "description": "Protection in front of the whole environment (staging, review apps). The CLI applies it to every browser context it creates, for the listed origins only.",
169
+ "properties": {
170
+ "origins": {
171
+ "type": "array",
172
+ "items": { "type": "string" },
173
+ "description": "Origins that receive the credentials; default: the base URL origins. https://*.example.com covers subdomains."
174
+ },
175
+ "basic": { "$ref": "#/$defs/basicAuth" },
176
+ "headers": {
177
+ "$ref": "#/$defs/headers",
178
+ "description": "Service-token headers, e.g. CF-Access-Client-Id / CF-Access-Client-Secret, x-vercel-protection-bypass."
179
+ },
180
+ "client_certificate": {
181
+ "type": "object",
182
+ "additionalProperties": false,
183
+ "description": "Mutual TLS. *_file is a path relative to the config; *_env holds a PEM or a path.",
184
+ "properties": {
185
+ "cert_file": { "type": "string" },
186
+ "key_file": { "type": "string" },
187
+ "cert_env": {
188
+ "type": "string",
189
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
190
+ "description": "Name of an environment variable (the value is never written in the config)."
191
+ },
192
+ "key_env": {
193
+ "type": "string",
194
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
195
+ "description": "Name of an environment variable (the value is never written in the config)."
196
+ },
197
+ "passphrase_env": {
198
+ "type": "string",
199
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
200
+ "description": "Name of an environment variable (the value is never written in the config)."
201
+ }
202
+ }
203
+ }
204
+ }
205
+ },
206
+ "accounts": {
207
+ "type": "object",
208
+ "description": "People who sign in to the application in this environment, keyed like the scenario sessions' `account`. Step scripts get them as session.credentials.",
209
+ "additionalProperties": {
210
+ "type": "object",
211
+ "additionalProperties": false,
212
+ "properties": {
213
+ "user": { "type": "string", "description": "Literal user name or e-mail (not secret)." },
214
+ "user_env": {
215
+ "type": "string",
216
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
217
+ "description": "Name of an environment variable (the value is never written in the config)."
218
+ },
219
+ "password_env": {
220
+ "type": "string",
221
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
222
+ "description": "Name of an environment variable (the value is never written in the config)."
223
+ },
224
+ "totp_env": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", "description": "Base32 TOTP secret for MFA; scripts call session.credentials.totp()." },
225
+ "storage_state": { "type": "string", "description": "Saved sign-in (kaloko auth save); default <output>/.auth/<env>/<account>.json. Never commit it." }
226
+ }
227
+ }
228
+ }
229
+ }
230
+ },
231
+ "secretRef": {
232
+ "type": "object",
233
+ "additionalProperties": false,
234
+ "required": ["env"],
235
+ "properties": {
236
+ "env": {
237
+ "type": "string",
238
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
239
+ "description": "Name of an environment variable (the value is never written in the config)."
240
+ }
241
+ },
242
+ "description": "A value read from the environment variable `env`."
243
+ },
244
+ "basicAuth": {
245
+ "type": "object",
246
+ "additionalProperties": false,
247
+ "required": ["user_env", "password_env"],
248
+ "properties": {
249
+ "user_env": {
250
+ "type": "string",
251
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
252
+ "description": "Name of an environment variable (the value is never written in the config)."
253
+ },
254
+ "password_env": {
255
+ "type": "string",
256
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
257
+ "description": "Name of an environment variable (the value is never written in the config)."
258
+ }
259
+ },
260
+ "description": "HTTP Basic credentials, both read from environment variables."
261
+ },
262
+ "headers": {
263
+ "type": "object",
264
+ "description": "Request headers; a string is a literal (non-secret) value, { env: NAME } reads a secret.",
265
+ "additionalProperties": { "oneOf": [{ "type": "string" }, { "$ref": "#/$defs/secretRef" }] }
266
+ }
267
+ }
268
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://qawalk.com/schemas/revision.v1.json",
4
+ "title": "qawalk.revision.v1",
5
+ "description": "A saved state of one step in one locale (and state) of a design draft (#42). The id is derived from the content (src/lib/design-core.mjs revisionId), so the same files make the same revision on every machine. Captures are renderings of a revision per viewport.",
6
+ "type": "object",
7
+ "required": ["id", "node", "locale", "entry", "files"],
8
+ "properties": {
9
+ "id": { "type": "string", "pattern": "^[0-9a-f]{32}$" },
10
+ "node": { "type": "string" },
11
+ "locale": { "type": "string" },
12
+ "state": { "type": "string", "default": "default", "description": "default, a design state (empty, error, …) or view:<viewport> for an HTML view." },
13
+ "kind": { "enum": ["design", "html_view"], "default": "design" },
14
+ "entry": { "type": "string" },
15
+ "files": { "$ref": "bundle.v1.json#/properties/files" },
16
+ "tokens": { "type": ["object", "null"], "properties": { "project": { "type": ["string", "null"] }, "version": { "type": ["integer", "null"] }, "hash": { "type": ["string", "null"] } } },
17
+ "based_on": { "type": ["string", "null"], "description": "The revision this one changed (a canvas edit, a sync on top of the draft)." },
18
+ "captures": {
19
+ "type": "object",
20
+ "description": "Viewport → rendered capture: blobs of the screenshot, thumbnail and DOM snapshot, the probe record and the evaluation.",
21
+ "additionalProperties": {
22
+ "type": "object",
23
+ "required": ["image", "html", "record"],
24
+ "properties": {
25
+ "image": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
26
+ "thumb": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
27
+ "html": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
28
+ "record": { "type": "object" },
29
+ "evaluation": { "type": ["object", "null"] },
30
+ "renderer": { "type": "string" },
31
+ "mode": { "type": "string" }
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }