ravensight-playtest 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.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +380 -0
  3. package/addons/ravensight_driver/driver.gd +836 -0
  4. package/addons/ravensight_driver/export_plugin.gd +51 -0
  5. package/addons/ravensight_driver/plugin.cfg +7 -0
  6. package/addons/ravensight_driver/plugin.gd +36 -0
  7. package/bin/ravensight-playtest.js +31 -0
  8. package/package.json +45 -0
  9. package/src/api/README.md +500 -0
  10. package/src/api/client.js +340 -0
  11. package/src/api/errors.js +115 -0
  12. package/src/api/http.js +194 -0
  13. package/src/api/index.js +107 -0
  14. package/src/auth/deviceCode.js +79 -0
  15. package/src/auth/keychain.js +159 -0
  16. package/src/auth/session.js +128 -0
  17. package/src/cli.js +335 -0
  18. package/src/commands/brief.js +303 -0
  19. package/src/commands/check.js +318 -0
  20. package/src/commands/fakeCore.js +379 -0
  21. package/src/commands/init.js +120 -0
  22. package/src/commands/login.js +90 -0
  23. package/src/commands/logout.js +70 -0
  24. package/src/commands/open.js +125 -0
  25. package/src/commands/profile.js +262 -0
  26. package/src/commands/resume.js +156 -0
  27. package/src/commands/run.js +1015 -0
  28. package/src/commands/upload.js +137 -0
  29. package/src/config.js +100 -0
  30. package/src/dashboard.js +97 -0
  31. package/src/detect.js +77 -0
  32. package/src/errors.js +44 -0
  33. package/src/fsutil.js +77 -0
  34. package/src/godot.js +85 -0
  35. package/src/packs/index.js +191 -0
  36. package/src/paths.js +129 -0
  37. package/src/run/aggregate.js +658 -0
  38. package/src/run/args.js +111 -0
  39. package/src/run/context.js +181 -0
  40. package/src/run/deps.js +184 -0
  41. package/src/run/drivers/driver.js +183 -0
  42. package/src/run/drivers/godot-observation.js +138 -0
  43. package/src/run/drivers/godot-project.js +475 -0
  44. package/src/run/drivers/godot-rpc.js +225 -0
  45. package/src/run/drivers/godot.js +587 -0
  46. package/src/run/drivers/index.js +52 -0
  47. package/src/run/drivers/web.js +385 -0
  48. package/src/run/exit.js +21 -0
  49. package/src/run/heartbeat.js +131 -0
  50. package/src/run/index.js +31 -0
  51. package/src/run/json.js +56 -0
  52. package/src/run/model.js +384 -0
  53. package/src/run/paths.js +88 -0
  54. package/src/run/personaLoop.js +871 -0
  55. package/src/run/profile.js +214 -0
  56. package/src/run/regenerate.js +149 -0
  57. package/src/run/repoTools.js +286 -0
  58. package/src/run/report.js +222 -0
  59. package/src/run/resume.js +272 -0
  60. package/src/run/secretScan.js +171 -0
  61. package/src/run/state.js +198 -0
  62. package/src/run/synthetic.js +206 -0
  63. package/src/run/tools.js +344 -0
  64. package/src/run/transcript.js +93 -0
  65. package/src/run/usage.js +115 -0
  66. package/src/state/index.js +105 -0
  67. package/src/states.js +104 -0
  68. package/src/ui/index.js +195 -0
  69. package/src/upload/allowlist.js +116 -0
  70. package/src/upload/index.js +467 -0
  71. package/src/upload/queue.js +114 -0
  72. package/src/version.js +63 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Reality Software Entertainment
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,380 @@
1
+ # ravensight-playtest
2
+
3
+ AI personas play your game, then file a report you can read, mark up and argue
4
+ with. The personas run on your machine, against your build; Ravensight stores
5
+ the reports and renders them.
6
+
7
+ You need a Ravensight account and Node 22 or newer. You do not need an
8
+ Anthropic API key: model calls go through Ravensight under the same token you
9
+ log in with, so there is one credential on your machine and you can revoke it
10
+ from the dashboard.
11
+
12
+ ## Install
13
+
14
+ Run it without installing anything:
15
+
16
+ ```bash
17
+ npx ravensight-playtest login
18
+ ```
19
+
20
+ Or install it globally:
21
+
22
+ ```bash
23
+ npm install -g ravensight-playtest
24
+ ravensight-playtest login
25
+ ```
26
+
27
+ Nothing is downloaded until you need it. Playwright is an optional dependency,
28
+ so `login`, `check`, `init` and `brief` work without a browser installed;
29
+ `run --driver playwright_web` is what asks for one. `keytar` is optional too,
30
+ which is why a machine with no keychain still installs cleanly.
31
+
32
+ ## First run
33
+
34
+ ```bash
35
+ ravensight-playtest login # device code, approved in the browser
36
+ cd path/to/your/game
37
+ ravensight-playtest init --game <gameId> # links this repo
38
+ ravensight-playtest check # confirms the machine and the balance
39
+ ravensight-playtest brief # four questions, two minutes
40
+ ravensight-playtest run # prints the price, asks, then plays
41
+ ```
42
+
43
+ `login` stores the token in your OS keychain (macOS Keychain, Windows
44
+ Credential Manager, libsecret on Linux). With no keychain available it falls
45
+ back to a file with 0600 permissions under your home directory, and says so.
46
+ In CI, set `RAVENSIGHT_TOKEN` instead and skip `login` entirely.
47
+
48
+ ## Commands
49
+
50
+ Eleven commands. Flags below are the ones the code accepts, not the ones the
51
+ prose elsewhere describes; `--help` on any command prints the same list.
52
+
53
+ Every command takes these four:
54
+
55
+ | Flag | Effect |
56
+ |---|---|
57
+ | `--api-url <url>` | Point at another Ravensight deployment. Defaults to `RAVENSIGHT_API_URL`. |
58
+ | `--repo <dir>` | Work in a different directory. |
59
+ | `--json` | Machine readable output, and nothing else on stdout. Stable for CI. |
60
+ | `-y, --yes` | Answer every confirmation with yes. Required for CI, since a prompt with no terminal is treated as no. |
61
+
62
+ ### login
63
+
64
+ Device code flow. Prints a code, waits for you to approve it in the dashboard,
65
+ stores the token.
66
+
67
+ | Flag | Effect |
68
+ |---|---|
69
+ | `--no-browser` | Do not try to open a browser. `RAVENSIGHT_NO_BROWSER=1` does the same thing. |
70
+
71
+ The token goes in your OS keychain (macOS Keychain, Windows Credential Manager,
72
+ libsecret on Linux). With no keychain available it falls back to a file with 0600
73
+ permissions under `RAVENSIGHT_HOME`, and says which store it used. In CI, set
74
+ `RAVENSIGHT_TOKEN` and skip `login` entirely.
75
+
76
+ ### logout
77
+
78
+ Revokes the token on the server, then forgets it locally. No flags of its own. A
79
+ token from `RAVENSIGHT_TOKEN` is left alone: it is probably a CI secret shared
80
+ with other jobs.
81
+
82
+ ### check
83
+
84
+ The doctor. Node version, server reachable, CLI new enough, credential valid and
85
+ which store it came from, game visible, balance against the next job's price,
86
+ content pack verified by sha256, disk, recommended concurrency, and the two
87
+ optional tools. Exit code 3 when any row fails; a missing optional tool is a
88
+ warning and never a failure, so CI does not break on it.
89
+
90
+ Past `--personas` in `POST /jobs`, requesting more than 100 personas or the same
91
+ slug twice is refused with 400 `too_many_personas`, and a slug this game does
92
+ not have is 400 `unknown_persona`; `run` checks the count against the same
93
+ ceiling before it asks the server anything. Past 8 personas the job is still
94
+ allowed, but a warning row explains that the later runs may not get to play
95
+ inside the job's wall clock; any run that does not play is refunded
96
+ automatically. `check` shows that same warning as a row when it applies.
97
+
98
+ | Flag | Effect |
99
+ |---|---|
100
+ | `--game <gameId>` | Check against this game instead of the linked one. |
101
+ | `--modules <list>` | Price this module list, comma separated. |
102
+ | `--personas <list>` | Price this persona list. Without it the balance row prices no personas, so the quote is lower than a real job's. |
103
+ | `--godot-path <path>` | Where Godot is, if it is not on the PATH. |
104
+
105
+ ### init
106
+
107
+ Links this repo to a game and writes `.ravensight/config.json`. Refuses a game
108
+ your token cannot see, so the first job does not fail with a 404. Adds
109
+ `.ravensight/jobs/` to an existing `.gitignore`.
110
+
111
+ | Flag | Effect |
112
+ |---|---|
113
+ | `--game <gameId>` | The game to link. With one visible game it is optional. |
114
+ | `--force` | Relink a repo that is already linked. |
115
+
116
+ ### brief
117
+
118
+ Writes the expectations brief: your goals, what success looks like, how the game
119
+ is controlled, where a persona starts. Then prints what a job would cost,
120
+ whether your balance covers it, and, past 8 personas, the same wall-clock
121
+ warning `run` shows. A brief is required before `run`; the server answers 409
122
+ `brief_required` without one.
123
+
124
+ | Flag | Effect |
125
+ |---|---|
126
+ | `--show` | Print the current brief and stop. |
127
+ | `--open` | Open the brief form in the dashboard instead of asking here. |
128
+ | `--draft` | Save as a draft even when it looks complete. |
129
+ | `--from <file>` | Read the fields from a JSON file instead of asking. |
130
+ | `--game <gameId>` | The game, when this repo is not linked. |
131
+ | `--modules <list>`, `--personas <list>` | Price this module and persona list in the estimate it prints. |
132
+
133
+ ### profile
134
+
135
+ Builds the game profile and the capability report: what your game is, what can
136
+ drive it, what a persona will be able to do. Takes `--game <gameId>` and all the
137
+ run options below.
138
+
139
+ ### run
140
+
141
+ Registers a job, plays it, writes the reports, uploads them, finishes the job.
142
+ Prints the price and asks before spending.
143
+
144
+ There is no product limit on how many personas one job can name; an unplayed
145
+ run is refunded automatically when the job settles, so a large job never costs
146
+ more than what actually got played. `--personas` past 100, or the same slug
147
+ twice, is refused before anything is charged, checked locally against the
148
+ content pack so the round trip to the server is never spent finding that out.
149
+ Past 8 personas the job is still allowed; `run` prints a warning above the
150
+ price prompt saying the later runs may not fit inside the job's wall clock, and
151
+ splitting the job in two is what gets everything played.
152
+
153
+ | Flag | Effect |
154
+ |---|---|
155
+ | `--game <gameId>` | The game, when this repo is not linked. |
156
+ | `--personas <list>` | Comma separated persona slugs. Defaults to the pack's set. |
157
+ | `--driver <name>` | `playwright_web` or `godot_driver`. |
158
+ | `--build-url <url>` | The web build to play. |
159
+ | `--godot-project <dir>` | The Godot project directory. |
160
+ | `--cli <command>` | The command that starts a text build. |
161
+ | `--max-actions <n>` | Cap the actions one persona may take. |
162
+ | `--concurrency <n>` | Personas at once. Default 2; `check` recommends a number for this machine. |
163
+ | `--upload-video` | Upload `session.webm` as well as the reports. |
164
+ | `--upload-transcript` | Upload `transcript.jsonl` as well as the reports. |
165
+ | `--dry-upload` | List every file and byte that would be sent, and send nothing. |
166
+ | `--headed`, `--headless` | Show or hide the browser. |
167
+ | `--verbose` | Print each turn. |
168
+
169
+ Four flags are accepted and reported as not applied by this release, rather than
170
+ silently ignored: `--budget-usd` and `--quality` (the budget and the model tier
171
+ are the server's, enforced at the proxy), `--offline-upload` (uploads already
172
+ queue) and `--build-dir` (serve the build and pass `--build-url`).
173
+
174
+ ### resume
175
+
176
+ With no job id, lists the jobs this repo has a journal for, newest first. With
177
+ one, keeps finished runs, restarts or replays the rest, and never charges twice:
178
+ the register is replayed under the journal's own idempotency key, which answers
179
+ the stored job without pricing anything.
180
+
181
+ | Flag | Effect |
182
+ |---|---|
183
+ | `--game <gameId>` | The game, when the job journal is missing. |
184
+ | `--status` | Report and stop, without restarting anything. |
185
+ | `--restart-interrupted` | Replay an interrupted run from the start. By default a run that got far enough is finalised from the transcript it already has, which spends no model turns; this forces the replay, which spends the action budget again. |
186
+
187
+ ### upload
188
+
189
+ Finishes the uploads a crashed or offline run still owes. Safe to run
190
+ repeatedly: files already sent are skipped by checksum.
191
+
192
+ | Flag | Effect |
193
+ |---|---|
194
+ | `--game <gameId>` | The game, when the job journal is missing. |
195
+ | `--dry-upload` | List what would be sent and send nothing. |
196
+ | `--video` | Include the session video. |
197
+ | `--transcript` | Include the transcript. |
198
+
199
+ ### review
200
+
201
+ Opens the post job review form. Your marks on the findings are what tunes the
202
+ personas, so this is the useful half of the loop. Takes `--game <gameId>` and an
203
+ optional job id.
204
+
205
+ ### open
206
+
207
+ Opens the dashboard page for a job, or for the game. Takes `--game <gameId>` and
208
+ an optional job id.
209
+
210
+ ## Playing a Godot game
211
+
212
+ `--driver godot_driver` launches your own Godot binary against your own project,
213
+ with a small driver addon injected so the persona can read the scene tree and
214
+ send input.
215
+
216
+ **Your project directory is never written to.** The project is copied to a temp
217
+ directory and the addon is injected into the copy, through an `override.cfg` and
218
+ the copy's own `project.godot`. Nothing about the copy is written back. The copy
219
+ excludes `.git` and `node_modules` and is refused above 2 GB, and its temp root
220
+ is `0700`, because a project directory can contain a `.env`.
221
+
222
+ The addon only ever activates deliberately: it needs `--ravensight-driver` on the
223
+ command line or `RAVENSIGHT_DRIVER=1` in the environment, it binds `127.0.0.1`
224
+ and nothing else, it requires a per run token in a `hello` call before it will
225
+ answer any other method, it accepts one client at a time, and it frees itself the
226
+ moment any of that fails. On a release export it is stripped from the build
227
+ unless the preset carries the `ravensight_driver` feature tag.
228
+
229
+ **Headless Godot routes no synthesized mouse or GUI input.** A click on a button
230
+ does nothing under `--headless` and works windowed, and nothing takes keyboard
231
+ focus without a click first. So `--headless` is a smoke test mode, not a playtest
232
+ mode, for anything with a user interface: on a machine with no display, use Xvfb
233
+ and a windowed Godot rather than `--headless`. The driver prints this in the
234
+ run's console log when it launches headless.
235
+
236
+ Video from the Godot driver is a `frames/` sequence at 4 fps rather than a
237
+ container, so stitching needs ffmpeg and ffmpeg stays optional.
238
+
239
+ `docs/godot-driver.md` has the protocol, the method set and the error codes.
240
+
241
+ ## What a report has to contain
242
+
243
+ `report.json` is validated against the pack schema before it is sent, and
244
+ `report.md` is linted against the four sections the server expects:
245
+
246
+ - **What worked**
247
+ - **Top issues**
248
+ - **What this run didn't cover**
249
+ - **Suggested next step**
250
+
251
+ A missing section is a lint finding rather than a rejection, so a report is never
252
+ lost to it. The CLI lints locally first and feeds any miss back into one repair
253
+ turn, which costs a model turn; a report written with the four headings from the
254
+ start avoids that.
255
+
256
+ ## Exit codes
257
+
258
+ | Code | Meaning |
259
+ |---|---|
260
+ | 0 | Success |
261
+ | 1 | Failure |
262
+ | 2 | Budget exceeded. Partial results were written and uploaded. |
263
+ | 3 | Environment check failed |
264
+ | 4 | Authentication failed |
265
+ | 5 | Model API error after retries |
266
+ | 10 | Canceled |
267
+
268
+ CI can treat 2 as a success with a warning.
269
+
270
+ ## Environment variables
271
+
272
+ | Variable | Effect |
273
+ |---|---|
274
+ | `RAVENSIGHT_TOKEN` | Use this token instead of the keychain. The CI path. |
275
+ | `RAVENSIGHT_API_URL` | The API base URL. Defaults to `https://api.ravensight.io`. |
276
+ | `RAVENSIGHT_DASHBOARD_URL` | Where the dashboard is, if it is not derivable from the API URL. |
277
+ | `RAVENSIGHT_GAME_ID` | The game, when there is no linked repo. |
278
+ | `RAVENSIGHT_INGEST_KEY` | The game's ingest key, so a web run can emit its synthetic session into your event stream. `ingest_key` in `.ravensight/config.json` does the same. Without either the run still reports; it just sends no telemetry. |
279
+ | `RAVENSIGHT_GODOT` | Path to the Godot binary. Tried first, then `$GODOT`, then `$GODOT_PATH`, then the PATH, then the platform's usual install locations. `--godot-path` beats all of them. |
280
+ | `RAVENSIGHT_FFMPEG` | Path to ffmpeg. |
281
+ | `RAVENSIGHT_HOME` | Where per user state lives. Defaults to `~/.ravensight-playtest`. |
282
+ | `RAVENSIGHT_CACHE_DIR` | Where the content pack cache lives. Defaults to the OS cache directory. |
283
+ | `RAVENSIGHT_NO_BROWSER` | Set to 1 to never try to open a browser. |
284
+
285
+ ## What ends up on disk
286
+
287
+ In your repo:
288
+
289
+ ```
290
+ .ravensight/
291
+ config.json the game link and your defaults
292
+ jobs/<jobId>/
293
+ state.json the resumable journal
294
+ upload-queue.jsonl what was uploaded, and what is still owed
295
+ runs/<runId>/
296
+ report.md report.json usage.json
297
+ screenshots/NN-slug.png
298
+ transcript.jsonl only when you opt in
299
+ session.webm only when you opt in
300
+ ```
301
+
302
+ Add `.ravensight/jobs/` to your `.gitignore`; `init` does it for you when you
303
+ already have one.
304
+
305
+ ## What leaves your machine
306
+
307
+ The dashboard has the full answer, artifact by artifact, at
308
+
309
+ ```
310
+ https://app.ravensight.io/games/<gameId>/playtest/what-leaves-your-machine
311
+ ```
312
+
313
+ which is the page to trust: it is rendered from the server's own allowlist, so
314
+ it cannot drift from what is actually accepted. `ravensight-playtest open` links
315
+ to it. The short version:
316
+
317
+ Uploaded by default: `report.md`, `report.json`, `usage.json`, the aggregate and
318
+ capability reports, and `screenshots/`. Uploaded only when you ask:
319
+ `transcript.jsonl` (`--upload-transcript`) and `session.webm`
320
+ (`--upload-video`). **Source code never, at any path.** The upload allowlist has
321
+ no entry for it, the CLI refuses it locally, and the server refuses anything not
322
+ on its own copy of the list. `src/upload/allowlist.js` is that copy, and a test
323
+ pins it equal to the server's file when a checkout is on the same machine.
324
+
325
+ Findings cite a file and a line range. The source text behind that line is not
326
+ uploaded.
327
+
328
+ Run `upload --dry-upload` or `run --dry-upload` to see the exact file list and
329
+ byte count before anything leaves the machine.
330
+
331
+ Model calls go to Ravensight's proxy rather than to Anthropic, under the token
332
+ you logged in with. No Anthropic key exists on your machine, and the CLI builds
333
+ its client with `apiKey` explicitly null, so an `ANTHROPIC_API_KEY` already in
334
+ your environment is never picked up and never sent anywhere.
335
+
336
+ Before anything is written to a run directory, the transcript and `usage.json`
337
+ are scanned for secrets. A hit is replaced by a line naming the shape that
338
+ matched, never the value.
339
+
340
+ ### The 90 day artifacts
341
+
342
+ The session video and the transcript expire after 90 days; everything else lives
343
+ for the life of the game. That is enforced as an S3 object tag rather than as a
344
+ filename rule, because a lifecycle rule can filter on a tag or a key prefix and
345
+ the filename is at the end of the key.
346
+
347
+ The tag is a contract between three parties, and the CLI is one of them: the
348
+ presign response hands back the exact `x-amz-tagging` value that was signed, and
349
+ the CLI sends it **verbatim** on the PUT. Changing it, or dropping it, is a
350
+ `SignatureDoesNotMatch` rather than an untagged object, which is the failure you
351
+ want: an untagged 200 MB video would sit in the bucket forever and nothing would
352
+ say so. The CLI never computes the value itself.
353
+
354
+ ## Offline
355
+
356
+ After one online fetch the content pack is cached and verified by sha256, so a
357
+ run works with the network down except for the model calls themselves. Uploads
358
+ and progress reports queue up and flush when the server is reachable again;
359
+ `upload <jobId>` drains them by hand.
360
+
361
+ ## Embedding
362
+
363
+ `src/api/README.md` documents the one module other code may import, which is
364
+ also this package's entry point:
365
+
366
+ ```js
367
+ import { createClient, packs, uploadRunDir } from 'ravensight-playtest';
368
+ ```
369
+
370
+ ## Reference
371
+
372
+ | File | What is in it |
373
+ |---|---|
374
+ | `docs/api-contract.md` | Every server route this CLI calls, with the headers it sends and the status codes it handles. The list to check a server change against. |
375
+ | `docs/godot-driver.md` | The Godot driver protocol, its method set, its error codes and its safety rules. |
376
+ | `src/api/README.md` | The module other code may import, and what the runner half expects from it. |
377
+
378
+ ## License
379
+
380
+ MIT. Copyright Reality Software Entertainment.