@irtio/cli 0.5.2 → 0.7.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 (39) hide show
  1. package/dist/api-keys-UTLYMZYN.js +222 -0
  2. package/dist/api.d.ts +52 -0
  3. package/dist/api.js +15 -0
  4. package/dist/bundle.js +1 -1
  5. package/dist/{chunk-GBNHBWES.js → chunk-BQBOBFBO.js} +10 -6
  6. package/dist/chunk-IDF46P7R.js +98 -0
  7. package/dist/{chunk-32QTPKVT.js → chunk-JL235KIE.js} +1 -1
  8. package/dist/chunk-OCVALOGK.js +31 -0
  9. package/dist/{chunk-KRQUAEN2.js → chunk-OTSFRVJN.js} +12 -6
  10. package/dist/chunk-UPHQM6NZ.js +72 -0
  11. package/dist/chunk-WFMRNGO5.js +481 -0
  12. package/dist/chunk-ZK5JLUD4.js +94 -0
  13. package/dist/credentials.d.ts +61 -0
  14. package/dist/credentials.js +20 -0
  15. package/dist/delete-project-MXUYNGAO.js +118 -0
  16. package/dist/deploy.d.ts +151 -0
  17. package/dist/{deploy-3SABPL3T.js → deploy.js} +324 -44
  18. package/dist/{dev-QJOGXLKM.js → dev-AUZ4OLA3.js} +3066 -211
  19. package/dist/index.js +121 -25
  20. package/dist/init.d.ts +1 -1
  21. package/dist/init.js +20 -4
  22. package/dist/{keys-XBORZAPI.js → keys-NXRIBJZP.js} +8 -4
  23. package/dist/leaderboard-ZBJBKETM.js +406 -0
  24. package/dist/{login-3EXB4CGX.js → login-3RVN5PPN.js} +12 -5
  25. package/dist/{logs-2EOXLNWF.js → logs-AT7G6YRH.js} +9 -5
  26. package/dist/{migrate-WUO2GBMX.js → migrate-FMXTRVUV.js} +12 -8
  27. package/dist/ratings-XBLX2MUW.js +297 -0
  28. package/dist/{rollback-GA6UY772.js → rollback-LI4TDLQA.js} +9 -5
  29. package/dist/rooms-52Q5KBUS.js +411 -0
  30. package/dist/simulate.d.ts +254 -6
  31. package/dist/simulate.js +925 -64
  32. package/dist/{static-deploy-5TBH4VNA.js → static-deploy-7UCYINJB.js} +6 -4
  33. package/dist/status-JZGKH2P6.js +219 -0
  34. package/dist/usage-7S447INI.js +213 -0
  35. package/dist/{whoami-CI5D5RCC.js → whoami-UFSWPWK6.js} +8 -4
  36. package/package.json +23 -7
  37. package/dist/chunk-BPE452KF.js +0 -180
  38. package/dist/chunk-TV66QHFP.js +0 -167
  39. package/dist/rooms-B66LQIIF.js +0 -226
@@ -1,6 +1,46 @@
1
- import { RunEnd, SimulationReport, TickHealthReading } from '@irtio/bots';
1
+ import { ScenarioDefinition, BotConditions, NetworkConditions, AssertionResult, RunEnd, SimulationReport, HitRow, TruthDiff, TickHealthReading } from '@irtio/bots';
2
2
  import { AnySchema } from '@irtio/schema';
3
3
 
4
+ /**
5
+ * D41: loading a scenario file, and reading the recorded authoritative timeline back off the dev
6
+ * server.
7
+ *
8
+ * Loading follows `loadProjectSchema`'s pattern exactly: esbuild the user's TypeScript to ESM,
9
+ * dynamic-import it with a cache-busting query, and shape-check the export. `bundleRoom` is the
10
+ * wrong tool here for the same reason it is wrong for a schema module, only more so: it enforces
11
+ * the room sandbox's fixed import menu, and a scenario's whole point is that it imports
12
+ * `@irtio/bots`.
13
+ *
14
+ * Reading follows the tick counters' pattern exactly: the dev server's own inspector routes, on
15
+ * the socket's port. A deployed tenant has no authoritative tap, so it has no timeline either,
16
+ * and this module says so rather than inventing one from what a client received.
17
+ */
18
+
19
+ /**
20
+ * A scenario that could not be loaded, or a timeline that could not be recorded. Both are "the
21
+ * run was not performed" rather than "the room failed": nothing was measured either way, and
22
+ * reporting a scenario as passing because its assertions never ran would be the worst outcome
23
+ * this command can produce.
24
+ */
25
+ declare class ScenarioNotRunError extends Error {
26
+ readonly cause?: unknown | undefined;
27
+ readonly name = "ScenarioNotRunError";
28
+ constructor(message: string, cause?: unknown | undefined);
29
+ }
30
+ interface LoadScenarioOptions {
31
+ readonly cwd: string;
32
+ readonly outDir: string;
33
+ /** The scenario file, absolute or relative to `cwd`. */
34
+ readonly file: string;
35
+ /** Redirects `@irtio/*` for the bundle; the monorepo's own `src/` by default. */
36
+ readonly irtioPackages?: Record<string, string> | undefined;
37
+ }
38
+ /** Bundles the scenario module and returns its default export. */
39
+ declare function loadScenario(options: LoadScenarioOptions): Promise<{
40
+ scenario: ScenarioDefinition;
41
+ file: string;
42
+ }>;
43
+
4
44
  /**
5
45
  * `irtio simulate`: point N real clients at a running room, play it randomly for a few
6
46
  * seconds, and print a pass/fail line per built-in invariant.
@@ -19,16 +59,35 @@ interface SimulateArgs {
19
59
  bots: number;
20
60
  seconds: number;
21
61
  cheat: boolean;
62
+ /** D42: bots named by `--cheat-bot`. Empty means "whatever `cheat` says". */
63
+ cheatBots?: number[];
64
+ /** D42: `--conditions`, applied to every bot. */
65
+ conditions?: NetworkConditions;
66
+ /** D42: `--conditions-bot <index>:<json>`, overriding `conditions` for those indices. */
67
+ conditionsPerBot?: Record<number, NetworkConditions>;
68
+ /** D43: `--truth`, take a save at the end of the run and diff it against the clients. */
69
+ truth?: boolean;
22
70
  room?: string;
23
71
  url?: string;
24
72
  key?: string;
25
73
  trace?: string;
74
+ scenario?: string;
26
75
  mispredictionMax?: number;
27
76
  snapsMax?: number;
28
77
  correctionsMax?: number;
29
78
  overrunsMax?: number;
79
+ /** D65: give every bot a bandwidth ledger and print the run's breakdown. */
80
+ profile?: boolean;
81
+ /** D65: rows in that table. Implies `profile`. */
82
+ profileTop?: number;
83
+ /** Queue for rooms instead of building one. Needs `control`. */
84
+ queue?: string;
85
+ /** The control plane the queue lives on. `irtio dev` runs none. */
86
+ control?: string;
87
+ /** Group this many consecutive bots into one party each. */
88
+ party?: number;
30
89
  }
31
- declare const USAGE = "usage: irtio simulate [options]\n\nDrives N real clients at a running room and checks the built-in invariants. Run it from a project\ndirectory so it can load irtio/schema.ts; without one it falls back to a relay run.\n\noptions:\n --bots <n> how many clients to spawn (default 5)\n --seconds <n> how long to play for (default 10)\n --room <code> join this room instead of creating one\n --url <ws://...> where to connect (default ws://localhost:7070)\n --key <projectKey> the project key to present\n --trace <path> write the frame trace here\n --cheat send illegal writes and expect corrections\n --misprediction-max <units> fail if one correction snaps a prediction further than this\n --snaps-max <n> fail above this many cap-exceeded reconciliations\n --corrections-max <perSec> fail above this correction rate per bot\n --overruns-max <n> fail above this many server tick overruns in the run window\n (default 0; the count is read from the server, and the run says\n so when it could not be read)\n -h, --help print this\n\nexit codes: 0 every invariant held, 1 something was measured and failed, 2 the run could not be\nperformed (nobody joined, or it passed its wall-clock ceiling).\n";
90
+ declare const USAGE = "usage: irtio simulate [options]\n\nDrives N real clients at a running room and checks the built-in invariants. Run it from a project\ndirectory so it can load irtio/schema.ts; without one it falls back to a relay run.\n\noptions:\n --bots <n> how many clients to spawn (default 5)\n --seconds <n> how long to play for (default 10)\n --room <code> join this room instead of creating one\n --url <ws://...> where to connect (default ws://localhost:7070)\n --key <projectKey> the project key to present\n --trace <path> write the frame trace here\n --scenario <file> run a scenario module and assert against the recorded timeline\n --cheat send illegal writes and expect corrections, from every bot\n --cheat-bot <index> only this bot cheats (repeatable)\n --conditions <json> inject network conditions into every bot, e.g.\n '{\"rttMs\":200,\"jitterMs\":20,\"loss\":0.02}'. Keys: rttMs, jitterMs,\n loss, duplicate, reorder, reorderMs. Loss, duplicate and reorder\n touch state frames only, so a join always completes\n --conditions-bot <i>:<json> conditions for one bot, overriding --conditions (repeatable)\n --truth save the room at the end and diff it against what each client\n received, within that client's visibility\n --misprediction-max <units> fail if one correction snaps a prediction further than this\n --snaps-max <n> fail above this many cap-exceeded reconciliations\n --corrections-max <perSec> fail above this correction rate per bot\n --overruns-max <n> fail above this many server tick overruns in the run window\n (default 0; the count is read from the server, and the run says\n so when it could not be read)\n --queue <name> queue for rooms through the matchmaker instead of creating one:\n every bot calls the real match API and joins the room its ticket\n names. Needs --control\n --control <https://...> the control plane the queue lives on. `irtio dev` runs none, so a\n queued run points at a real one (staging, or a test plane)\n --party <n> group each n consecutive bots into one party, so they queue\n together and land in one room. Needs --queue\n --profile print where the run's bytes went, by collection and field, as the\n bots saw them\n --profile-top <n> rows in that table (default 12; implies --profile)\n -h, --help print this\n\nexit codes: 0 every invariant held, 1 something was measured and failed, 2 the run could not be\nperformed (nobody joined, or it passed its wall-clock ceiling).\n";
32
91
  /** Hand-rolled, like `parseDevArgs`: the CLI has no argument-parsing dependency. */
33
92
  declare function parseSimulateArgs(args: readonly string[]): SimulateArgs;
34
93
  interface LoadSchemaOptions {
@@ -47,6 +106,7 @@ declare function loadProjectSchema(options: LoadSchemaOptions): Promise<{
47
106
  } | undefined>;
48
107
  /** The shared world-builder's exports, as `joinRoom({ physics })` wants them. */
49
108
  interface LoadedWorld {
109
+ readonly engine: 'rapier3d';
50
110
  readonly gravity: {
51
111
  x: number;
52
112
  y: number;
@@ -58,12 +118,88 @@ interface LoadedWorld {
58
118
  readonly intents?: Record<string, (...args: never[]) => void> | undefined;
59
119
  readonly file: string;
60
120
  }
121
+ /**
122
+ * D73-a: a matter2d world, as `joinRoom({ physics2d })` wants it.
123
+ *
124
+ * `bugs.md` #21 taught this loader to *recognise* a 2D world rather than refuse it, and stopped
125
+ * there, on a comment saying matter2d had no client prediction. That stopped being true when
126
+ * `joinRoom({ physics2d })` shipped (M5 part 4.5), so a 2D world is now loaded to be predicted
127
+ * with, exactly as the Rapier one is.
128
+ *
129
+ * The source is the shared world module, which exports `gravity` (2D) and whichever of `timestep`,
130
+ * `setup`, `bodies`, `intents` and `settle` it has — the same object a browser hands
131
+ * `joinRoom({ physics2d })`. The room's own `physics` declaration is deliberately **not** a
132
+ * source: it is the server's half, and the client half (`settle`, the per-step force over the
133
+ * bodies nobody steers, and the exact timestep) is written on the client. A world built from the
134
+ * room alone would leave every unsteered body hanging in the air locally and be corrected on
135
+ * every tick, which is a worse answer than not predicting.
136
+ */
137
+ interface LoadedWorld2d {
138
+ readonly engine: 'matter2d';
139
+ readonly gravity: {
140
+ x: number;
141
+ y: number;
142
+ };
143
+ readonly timestep?: number | undefined;
144
+ readonly setup?: ((engine: unknown, matter: unknown) => void) | undefined;
145
+ readonly bodies?: Record<string, (...args: never[]) => unknown> | undefined;
146
+ readonly intents?: Record<string, (...args: never[]) => void> | undefined;
147
+ readonly settle?: Record<string, (...args: never[]) => void> | undefined;
148
+ /**
149
+ * The three tuning numbers `ClientPhysics2dOptions` takes, when the world module states them.
150
+ *
151
+ * `epsilon` is the one that matters and it is the one nobody expects: it is a distance in the
152
+ * world's own units, and its default (0.05) is sized for a metre-scale world. A matter.js world
153
+ * in pixels — matter's own convention, and what every matter.js tutorial writes — is two orders
154
+ * of magnitude coarser, so the default calls every ordinary contact resolution a misprediction.
155
+ * A world that states its scale here gets the same suppression a metre-scale world gets for free.
156
+ */
157
+ readonly epsilon?: number | undefined;
158
+ readonly maxPredictedBodies?: number | undefined;
159
+ readonly smoothingHalfLifeMs?: number | undefined;
160
+ readonly file: string;
161
+ }
162
+ /**
163
+ * A matter2d project this loader recognises and cannot predict from: run it, say why in one
164
+ * sentence, and never pretend to a predictor that does not exist.
165
+ *
166
+ * `bugs.md` #21 introduced this for every matter2d project, because there was no client
167
+ * prediction at all. D73-a narrows it to the two cases that are still true: a world module with
168
+ * no `bodies` (there is nothing to build), and an **inert** one — no `intents`, no `settle`, and
169
+ * zero gravity, so nothing in a local world built from it could ever move a body.
170
+ * `examples/matter-chase` is the second case by design: its room applies thrust by hand inside
171
+ * `tick()`, so there is no steering hook to share and its own client does not predict either.
172
+ */
173
+ interface UnpredictedWorld {
174
+ readonly engine: 'matter2d';
175
+ /** One sentence, printed by the run, naming what would have to change. */
176
+ readonly why: string;
177
+ readonly file: string;
178
+ }
179
+ type LoadedAnyWorld = LoadedWorld | LoadedWorld2d | UnpredictedWorld;
180
+ /** Narrows a loaded world to a matter2d one bots can predict with. */
181
+ declare function is2dWorld(world: LoadedAnyWorld | undefined): world is LoadedWorld2d;
182
+ declare function isUnpredictedWorld(world: LoadedAnyWorld | undefined): world is UnpredictedWorld;
183
+ /**
184
+ * D73-a: does this project's room declare matter2d?
185
+ *
186
+ * The one question the room module is asked, and the reason it is asked at all: a matter2d project
187
+ * whose world module is not a client world — `games/dive`, whose engine gravity is zero and
188
+ * applied per body, so there is no `gravity` to export and its `physics2d` block is composed in
189
+ * `main.ts` — used to be refused at the door with "does not export a { x, y, z } gravity", which
190
+ * is why `irtio simulate` had never been run against dive at all. Recognising it here turns that
191
+ * refusal into a run with a sentence.
192
+ *
193
+ * `undefined` for every other answer: no room module, one that will not build or import, or one
194
+ * declaring another engine. None of those is an error here — the world module's own error is the
195
+ * right one to raise.
196
+ */
197
+ declare function detectMatter2dRoom(options: LoadSchemaOptions): Promise<string | undefined>;
61
198
  /**
62
199
  * Bundles and imports the project's shared world-builder module, so the bots predict physics the
63
- * way a browser client would. The engine stays external — it must be the project's own copy,
64
- * resolved at import time, exactly as a room bundle resolves it.
200
+ * way a browser client would.
65
201
  */
66
- declare function loadProjectWorld(options: LoadSchemaOptions): Promise<LoadedWorld | undefined>;
202
+ declare function loadProjectWorld(options: LoadSchemaOptions): Promise<LoadedAnyWorld | undefined>;
67
203
  /**
68
204
  * The build-twice determinism check: builds two worlds from the shared `setup` and compares
69
205
  * Rapier's own snapshots byte for byte. A builder that is not pure over synced inputs
@@ -72,6 +208,23 @@ declare function loadProjectWorld(options: LoadSchemaOptions): Promise<LoadedWor
72
208
  * description, or `undefined` when the worlds agree.
73
209
  */
74
210
  declare function checkWorldDeterminism(world: LoadedWorld): Promise<string | undefined>;
211
+ /**
212
+ * D73-a: the matter2d determinism check.
213
+ *
214
+ * The Rapier check compares two `takeSnapshot()` byte strings, and matter.js has no snapshot at
215
+ * all (`packages/runtime/src/core/matter.ts`'s header says so), so this one is built the only way
216
+ * that is left: build the world twice through the engine the runtime loads, step both the same
217
+ * number of times with the same (empty) intent stream, and compare every body's pose and velocity
218
+ * through a canonical encoding. Two builds that disagree came from a builder that is not pure over
219
+ * synced inputs, which is the same fault the Rapier check catches and the same reason it matters —
220
+ * every "misprediction" it causes is blamed on netcode.
221
+ *
222
+ * Stepping, rather than comparing the two worlds as built, is what makes it catch randomness that
223
+ * is small at t=0: a crate spawned a thousandth of a unit off lands in a different place. The body
224
+ * factories are exercised too, against each collection's zero record, so a `Math.random()` in a
225
+ * factory is caught and not only one in `setup`.
226
+ */
227
+ declare function checkWorld2dDeterminism(world: LoadedWorld2d, schema?: AnySchema): Promise<string | undefined>;
75
228
  /**
76
229
  * The dev server's JSON view, which is where a local run reads the server's own tick counters.
77
230
  * `irtio dev` serves it on the socket's own port; nothing else does, and that is deliberate — a
@@ -82,6 +235,41 @@ interface RoomTickCounters {
82
235
  readonly overruns?: number;
83
236
  readonly maxTickMs?: number;
84
237
  }
238
+ /**
239
+ * `bugs.md` #16: which build of the room a run tested.
240
+ *
241
+ * A `simulate` report used to name the room code, the bot count and the URL, and nothing that
242
+ * identified the code under test. That is a false-green generator: edit `irtio/room.ts`, forget
243
+ * that an older `dev` is still holding the port, and the run measures the bundle you replaced.
244
+ * The information already existed on the inspector; nothing read it.
245
+ *
246
+ * Both fields matter and neither is redundant. The bundle hash catches an edit the server never
247
+ * picked up; the start time catches the case the hash cannot see, which is a *different process*
248
+ * that happens to be serving an identical bundle, and it is the field that tells a reader the
249
+ * server they thought they had restarted did not restart.
250
+ */
251
+ interface BuildIdentity {
252
+ /** The bundle hash the server is serving, as `irtio dev` prints it at startup. */
253
+ readonly bundleHash?: string;
254
+ /** The schema hash behind that bundle, when the server knows one. */
255
+ readonly schemaHash?: string;
256
+ /** Epoch ms when the serving process booted. */
257
+ readonly startedAt?: number;
258
+ /** Where it was read from. */
259
+ readonly source?: string;
260
+ /** Why there is no identity, when there is none. Never both this and the fields above. */
261
+ readonly unavailable?: string;
262
+ }
263
+ /**
264
+ * Reads the serving build's identity off the dev inspector, or says why it could not.
265
+ *
266
+ * Deliberately shaped like `readTickCounters`: a deployed tenant does not serve `state.json`, so
267
+ * against staging this returns an `unavailable` sentence rather than a wrong answer. An unknown
268
+ * build is a fact worth printing, and printing nothing is what caused the bug.
269
+ */
270
+ declare function readBuildIdentity(wsUrl: string): Promise<BuildIdentity>;
271
+ /** The one line `bugs.md` #16 asked for, beside the URL in the report header. */
272
+ declare function formatBuildIdentity(build: BuildIdentity): string;
85
273
  /**
86
274
  * Reads one room's tick counters off the dev server, or explains why it could not. Every failure
87
275
  * path returns a sentence rather than a zero: the `tick-health` invariant has an `unavailable`
@@ -126,6 +314,12 @@ interface RunSimulationOptions extends Partial<SimulateArgs> {
126
314
  * is a fatal run condition, not a clean end.
127
315
  */
128
316
  readonly ceilingMs?: number;
317
+ /**
318
+ * D73-c: how long a queued bot waits for its ticket. The control plane caps this at two minutes
319
+ * and applies its own default (30 s) when it is omitted; a run that wants to see a queue *not*
320
+ * fill needs a bound shorter than its own patience.
321
+ */
322
+ readonly matchTimeoutMs?: number;
129
323
  /** @internal Test seam, same as `startDev`'s. */
130
324
  readonly irtioPackages?: Record<string, string> | undefined;
131
325
  }
@@ -146,13 +340,67 @@ declare class RunNotPerformedError extends Error {
146
340
  * are the two fatal run conditions, and both still print the report and write the trace.
147
341
  */
148
342
  type SimulationEnd = RunEnd | 'ceiling';
343
+ /** D42: the adversarial section. Present whenever anything was injected or anyone cheated. */
344
+ interface AdversarialRun {
345
+ /** Per bot: what was injected and what the wrapper counted. */
346
+ readonly conditions: readonly BotConditions[];
347
+ /** Bot indices that cheated. */
348
+ readonly cheated: readonly number[];
349
+ /** Corrections drawn per cheating bot, in the same order as `cheated`. */
350
+ readonly correctionsPerCheater: readonly number[];
351
+ }
352
+ /** D41: what a scenario run adds to the report. Absent when no `--scenario` ran. */
353
+ interface ScenarioRun {
354
+ readonly file: string;
355
+ /** Every assertion the scenario ran, in order. */
356
+ readonly assertions: readonly AssertionResult[];
357
+ readonly ok: boolean;
358
+ /** Recorded ticks the assertions could read. */
359
+ readonly ticks: number;
360
+ /** Ticks the recorder's caps evicted. Non-zero means the recording is a tail. */
361
+ readonly dropped: number;
362
+ /** Where the recording was written, so a failed run can be re-read without re-running it. */
363
+ readonly timelinePath: string;
364
+ }
365
+ /** D43: the truth-seam section, when a run asked for one. */
366
+ interface TruthRun {
367
+ /** The diff, when a save was taken and decoded. */
368
+ readonly diff?: TruthDiff;
369
+ /** The save generation the diff read. */
370
+ readonly saveId?: string;
371
+ /**
372
+ * The decoded save's state, in `inspectState` shape. Carried because it is the only place a
373
+ * caller can read what the server actually held without starting a room, which is the whole
374
+ * point of the decoder, and because a test asserting "the illegal write was accepted" has to
375
+ * read authoritative state rather than infer it from a correction that did not arrive.
376
+ */
377
+ readonly saveState?: Record<string, unknown>;
378
+ /** Why there is no diff, when there is none. Never both this and `diff`. */
379
+ readonly error?: string;
380
+ }
149
381
  interface SimulationRun {
150
382
  readonly report: SimulationReport;
151
383
  readonly tracePath: string;
384
+ /** `bugs.md` #16: which build of the room this run tested, or why that is not knowable. */
385
+ readonly build: BuildIdentity;
386
+ /** D41: the scenario section, when `--scenario` ran one. */
387
+ readonly scenario?: ScenarioRun;
388
+ /** D42: what was injected per bot, and which bots cheated. */
389
+ readonly adversarial?: AdversarialRun;
390
+ /** D42: one row per `bot.shot(...)`. Absent when nothing fired. */
391
+ readonly hits?: readonly HitRow[];
392
+ /** D43: the truth-seam verdict. Absent unless the run asked for it. */
393
+ readonly truth?: TruthRun;
152
394
  /** `false` when there was no schema module and the run fell back to a relay simulation. */
153
395
  readonly schema: boolean;
154
396
  /** `true` when the bots predicted physics from a shared world-builder module (D22 part 2). */
155
397
  readonly predicted: boolean;
398
+ /** D73-c: bots that never got a ticket. Empty on a run that did not queue. */
399
+ readonly matchFailures: readonly {
400
+ requested: number;
401
+ code: string;
402
+ message: string;
403
+ }[];
156
404
  /** Result of the build-twice world-builder check: the failure text, or `undefined` if it held
157
405
  * (or did not apply). A failure also fails the run. */
158
406
  readonly worldError?: string;
@@ -169,4 +417,4 @@ interface SimulationRun {
169
417
  declare function runSimulation(options?: RunSimulationOptions): Promise<SimulationRun>;
170
418
  declare function simulate(args: readonly string[]): Promise<void>;
171
419
 
172
- export { type LoadSchemaOptions, RunNotPerformedError, type RunSimulationOptions, type SimulateArgs, type SimulationEnd, type SimulationRun, USAGE, ceilingFor, checkWorldDeterminism, loadProjectSchema, loadProjectWorld, parseSimulateArgs, readTickCounters, runSimulation, simulate, stateUrlFor, tickHealthFrom };
420
+ export { type AdversarialRun, type BuildIdentity, type LoadSchemaOptions, type LoadedAnyWorld, type LoadedWorld2d, RunNotPerformedError, type RunSimulationOptions, ScenarioNotRunError, type ScenarioRun, type SimulateArgs, type SimulationEnd, type SimulationRun, type TruthRun, USAGE, type UnpredictedWorld, ceilingFor, checkWorld2dDeterminism, checkWorldDeterminism, detectMatter2dRoom, formatBuildIdentity, is2dWorld, isUnpredictedWorld, loadProjectSchema, loadProjectWorld, loadScenario, parseSimulateArgs, readBuildIdentity, readTickCounters, runSimulation, simulate, stateUrlFor, tickHealthFrom };