@bitmagic/cli 0.1.13 → 0.1.14
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/README.md +14 -1
- package/dist/cli.d.ts +14 -1
- package/dist/cli.js +8 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/build.d.ts +6 -1
- package/dist/commands/build.js +14 -4
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/publish.d.ts +10 -1
- package/dist/commands/publish.js +23 -8
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/verify.d.ts +4 -0
- package/dist/commands/verify.js +28 -6
- package/dist/commands/verify.js.map +1 -1
- package/dist/output.d.ts +36 -0
- package/dist/output.js +25 -0
- package/dist/output.js.map +1 -0
- package/dist/verify/browser.d.ts +22 -0
- package/dist/verify/browser.js +85 -1
- package/dist/verify/browser.js.map +1 -1
- package/dist/verify/classify.d.ts +12 -0
- package/dist/verify/classify.js +22 -0
- package/dist/verify/classify.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ bitmagic publish # verify-gate, build, and ship — private by defa
|
|
|
34
34
|
| `bitmagic init [dir] [--template <id>] [--name <name>] [--env <env>] [--force]` | Scaffold a new project: mints a game, downloads the vendored engine, writes `AGENTS.md` and templates. |
|
|
35
35
|
| `bitmagic check` | Typecheck (`tsc --noEmit`) against the vendored engine. Fast; does not prove the game runs. |
|
|
36
36
|
| `bitmagic dev [--port <port>]` | Build, then serve with `tsc --watch` + `vite`, default port 3010. |
|
|
37
|
-
| `bitmagic verify [--timeout <ms>]` | Build, boot the game in a headless browser, and report what broke. Writes the artifacts the publish gate reads (see below). |
|
|
37
|
+
| `bitmagic verify [--timeout <ms>]` | Build, boot the game in a headless browser, click the engine's Play button like a player would, and report what broke. A game without the button passes as long as gameplay starts by itself (auto-starting genres, custom start UIs); a run where gameplay never starts fails. `--timeout` is settle time in ms measured after gameplay starts, so the screenshot captures gameplay. Writes the artifacts the publish gate reads (see below). |
|
|
38
38
|
| `bitmagic build` | Bundle the game into one self-contained `.bitmagic/build/index.html`. Rarely needs to be run by hand — `publish` builds automatically when the project changed. |
|
|
39
39
|
| `bitmagic publish [--visibility public\|private] [--name <name>] [--description <desc>] [--force]` | Verify-gate, build if needed, and upload straight to GCS. **Private unless `--visibility public` is passed.** |
|
|
40
40
|
| `bitmagic upgrade [--engine <version>] [--force]` | Refresh vendored platform files to the currently published engine. Refuses a dirty git tree unless `--force`. |
|
|
@@ -132,6 +132,19 @@ build` stops with a message naming `bitmagic upgrade`, which vendors the file wi
|
|
|
132
132
|
your game code. Run the printed install command afterwards if `upgrade` reports missing
|
|
133
133
|
dependencies too.
|
|
134
134
|
|
|
135
|
+
## Machine-readable output
|
|
136
|
+
|
|
137
|
+
`build`, `verify` and `publish` accept `--json`:
|
|
138
|
+
|
|
139
|
+
- **stdout** carries exactly one JSON document — the result, or on failure
|
|
140
|
+
`{ "ok": false, "exitCode": N, "error": "..." }`
|
|
141
|
+
- **stderr** carries everything else: progress, warnings, and both upgrade nudges
|
|
142
|
+
|
|
143
|
+
So `bitmagic publish --json > result.json` works while you still watch progress, and an agent
|
|
144
|
+
parses one stream whether the command succeeded or not. The exit code remains authoritative.
|
|
145
|
+
|
|
146
|
+
Without `--json` nothing changes: prose goes to stdout as before and no JSON is emitted.
|
|
147
|
+
|
|
135
148
|
## Staying current
|
|
136
149
|
|
|
137
150
|
Two nudges, both advisory — nothing is ever blocked because something is out of date.
|
package/dist/cli.d.ts
CHANGED
|
@@ -71,8 +71,17 @@ declare const rawCommands: {
|
|
|
71
71
|
readonly type: "string";
|
|
72
72
|
readonly description: "Settle time in ms (default 15000).";
|
|
73
73
|
};
|
|
74
|
+
readonly json: {
|
|
75
|
+
readonly type: "boolean";
|
|
76
|
+
readonly description: "Print the result as JSON on stdout; all progress goes to stderr.";
|
|
77
|
+
};
|
|
78
|
+
}>;
|
|
79
|
+
build: CommandDef<{
|
|
80
|
+
readonly json: {
|
|
81
|
+
readonly type: "boolean";
|
|
82
|
+
readonly description: "Print the result as JSON on stdout; all progress goes to stderr.";
|
|
83
|
+
};
|
|
74
84
|
}>;
|
|
75
|
-
build: CommandDef<ArgsDef>;
|
|
76
85
|
generate: CommandDef<ArgsDef>;
|
|
77
86
|
forge: CommandDef<{
|
|
78
87
|
readonly prompt: {
|
|
@@ -125,6 +134,10 @@ declare const rawCommands: {
|
|
|
125
134
|
readonly type: "boolean";
|
|
126
135
|
readonly description: "Skip the post-publish render check. The game still publishes; it is simply not confirmed to render.";
|
|
127
136
|
};
|
|
137
|
+
readonly json: {
|
|
138
|
+
readonly type: "boolean";
|
|
139
|
+
readonly description: "Print the result as JSON on stdout; all progress goes to stderr.";
|
|
140
|
+
};
|
|
128
141
|
}>;
|
|
129
142
|
};
|
|
130
143
|
type CommandName = keyof typeof rawCommands;
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineCommand } from 'citty';
|
|
2
2
|
import { CliError } from './errors.js';
|
|
3
|
+
import { jsonErrorResult } from './output.js';
|
|
3
4
|
import { loginCommand } from './commands/login.js';
|
|
4
5
|
import { logoutCommand } from './commands/logout.js';
|
|
5
6
|
import { whoamiCommand } from './commands/whoami.js';
|
|
@@ -44,7 +45,14 @@ export function withCleanErrors(command) {
|
|
|
44
45
|
}
|
|
45
46
|
catch (error) {
|
|
46
47
|
if (error instanceof CliError) {
|
|
48
|
+
// The message always goes to stderr — that is where it already went, and where it
|
|
49
|
+
// belongs under `--json` too. Under `--json` stdout additionally gets the failure as a
|
|
50
|
+
// document, so an agent parses one stream for the outcome instead of branching between
|
|
51
|
+
// "parse stdout" on success and "read prose" on failure.
|
|
47
52
|
console.error(error.message);
|
|
53
|
+
if (context.args?.json === true) {
|
|
54
|
+
console.log(JSON.stringify(jsonErrorResult(error.message, error.exitCode)));
|
|
55
|
+
}
|
|
48
56
|
process.exit(error.exitCode);
|
|
49
57
|
}
|
|
50
58
|
// Not one of ours: let citty's default handling show it, stack trace included — that's
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/build.d.ts
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
export declare const buildCommand: import("citty").CommandDef<
|
|
1
|
+
export declare const buildCommand: import("citty").CommandDef<{
|
|
2
|
+
readonly json: {
|
|
3
|
+
readonly type: "boolean";
|
|
4
|
+
readonly description: "Print the result as JSON on stdout; all progress goes to stderr.";
|
|
5
|
+
};
|
|
6
|
+
}>;
|
package/dist/commands/build.js
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
import { defineCommand } from 'citty';
|
|
2
2
|
import { loadProjectContext } from '../project/context.js';
|
|
3
|
-
import { runBuild } from '../publish/bundle.js';
|
|
3
|
+
import { buildBundlePaths, runBuild } from '../publish/bundle.js';
|
|
4
|
+
import { createOutput } from '../output.js';
|
|
4
5
|
export const buildCommand = defineCommand({
|
|
5
6
|
meta: {
|
|
6
7
|
name: 'build',
|
|
7
8
|
description: 'Bundle the game for publishing into .bitmagic/build/index.html.',
|
|
8
9
|
},
|
|
9
|
-
|
|
10
|
+
args: {
|
|
11
|
+
json: {
|
|
12
|
+
type: 'boolean',
|
|
13
|
+
description: 'Print the result as JSON on stdout; all progress goes to stderr.',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
async run({ args }) {
|
|
17
|
+
const output = createOutput(args.json === true);
|
|
10
18
|
const context = loadProjectContext();
|
|
11
|
-
|
|
19
|
+
output.info('Building…');
|
|
12
20
|
const manifest = runBuild(context.root, context.metadata.engineVersion);
|
|
13
|
-
|
|
21
|
+
const { htmlPath, manifestPath } = buildBundlePaths(context.root);
|
|
22
|
+
output.info(`Bundle ready: .bitmagic/build/index.html (${manifest.bytes} bytes)`);
|
|
23
|
+
output.result({ ok: true, bundlePath: htmlPath, manifestPath, ...manifest });
|
|
14
24
|
},
|
|
15
25
|
});
|
|
16
26
|
//# sourceMappingURL=build.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC;IACxC,IAAI,EAAE;QACJ,IAAI,EAAE,OAAO;QACb,WAAW,EAAE,iEAAiE;KAC/E;IACD,IAAI,EAAE;QACJ,IAAI,EAAE;YACJ,IAAI,EAAE,SAAS;YACf,WAAW,EAAE,kEAAkE;SAChF;KACF;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;QAChB,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QAChD,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QACxE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAClE,MAAM,CAAC,IAAI,CAAC,6CAA6C,QAAQ,CAAC,KAAK,SAAS,CAAC,CAAC;QAClF,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;IAC/E,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -10,7 +10,11 @@ export interface PublishRunDeps {
|
|
|
10
10
|
startDir?: string;
|
|
11
11
|
/** Test-only override; production omits it and the credential helpers read ~/.bitmagic. */
|
|
12
12
|
baseDir?: string;
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Test-only override. Production omits it: prose goes through the Output seam, which routes to
|
|
15
|
+
* stderr under `--json` so it cannot land beside the result document on stdout.
|
|
16
|
+
*/
|
|
17
|
+
log?: (message: string) => void;
|
|
14
18
|
/** Injectable so a test can assert reuse-vs-rebuild without a real tsc/vite toolchain. */
|
|
15
19
|
runBuild: (root: string, engineVersion: string) => BuildManifest;
|
|
16
20
|
readBuildManifest: (root: string) => BuildManifest | null;
|
|
@@ -27,6 +31,7 @@ export interface PublishArgs {
|
|
|
27
31
|
force?: boolean;
|
|
28
32
|
/** Hyphenated to match the flag citty parses; there is no camelCase alias. */
|
|
29
33
|
'skip-smoke'?: boolean;
|
|
34
|
+
json?: boolean;
|
|
30
35
|
}
|
|
31
36
|
/**
|
|
32
37
|
* The whole command. See the file header for why the order below cannot be reshuffled.
|
|
@@ -53,4 +58,8 @@ export declare const publishCommand: import("citty").CommandDef<{
|
|
|
53
58
|
readonly type: "boolean";
|
|
54
59
|
readonly description: "Skip the post-publish render check. The game still publishes; it is simply not confirmed to render.";
|
|
55
60
|
};
|
|
61
|
+
readonly json: {
|
|
62
|
+
readonly type: "boolean";
|
|
63
|
+
readonly description: "Print the result as JSON on stdout; all progress goes to stderr.";
|
|
64
|
+
};
|
|
56
65
|
}>;
|
package/dist/commands/publish.js
CHANGED
|
@@ -32,6 +32,7 @@ import * as fs from 'fs';
|
|
|
32
32
|
import * as path from 'path';
|
|
33
33
|
import { CliError } from '../errors.js';
|
|
34
34
|
import { engineUpdateNotice } from '../project/engine-update.js';
|
|
35
|
+
import { createOutput } from '../output.js';
|
|
35
36
|
import { loadProjectContext } from '../project/context.js';
|
|
36
37
|
import { resolveEnvironment, resolveAuth0ClientId } from '../config/environments.js';
|
|
37
38
|
import { readDefaultEnvironment } from '../config/credentials.js';
|
|
@@ -58,7 +59,6 @@ function defaultDeps() {
|
|
|
58
59
|
return {
|
|
59
60
|
fetch: globalThis.fetch,
|
|
60
61
|
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
61
|
-
log: (message) => console.log(message),
|
|
62
62
|
runBuild: runBuildImpl,
|
|
63
63
|
readBuildManifest: readBuildManifestImpl,
|
|
64
64
|
// Imported lazily: ../smoke/run.js reaches playwright-core, which drags a browser driver into
|
|
@@ -97,6 +97,10 @@ async function uploadThumbnail(context, environment, token, apiDeps) {
|
|
|
97
97
|
* The whole command. See the file header for why the order below cannot be reshuffled.
|
|
98
98
|
*/
|
|
99
99
|
export async function runPublish(args, deps = defaultDeps()) {
|
|
100
|
+
const output = createOutput(args.json === true);
|
|
101
|
+
// `deps.log` stays the seam tests capture; production falls through to the Output, so a
|
|
102
|
+
// `--json` run keeps stdout clean for the single result document.
|
|
103
|
+
const log = deps.log ?? output.info;
|
|
100
104
|
const visibility = resolveVisibility(args.visibility);
|
|
101
105
|
const force = args.force === true;
|
|
102
106
|
const context = loadProjectContext(deps.startDir);
|
|
@@ -179,25 +183,32 @@ export async function runPublish(args, deps = defaultDeps()) {
|
|
|
179
183
|
// failure of the publish that just succeeded.
|
|
180
184
|
const upgrade = engineUpdateNotice(context.metadata.engineVersion, begun.currentEngineVersion);
|
|
181
185
|
if (upgrade !== null)
|
|
182
|
-
|
|
183
|
-
|
|
186
|
+
log(upgrade);
|
|
187
|
+
log(`Published ${done.visibility}: ${done.url}`);
|
|
184
188
|
if (done.visibility === 'private') {
|
|
185
|
-
|
|
189
|
+
log('Not listed. Publish with --visibility public when you are ready.');
|
|
186
190
|
}
|
|
191
|
+
output.result({
|
|
192
|
+
ok: true,
|
|
193
|
+
url: done.url,
|
|
194
|
+
visibility: done.visibility,
|
|
195
|
+
publishVersion: done.publishVersion,
|
|
196
|
+
gameId: context.metadata.gameId,
|
|
197
|
+
});
|
|
187
198
|
// Everything below runs AFTER the game is live. `complete` has written the bytes and the row,
|
|
188
199
|
// so nothing here can un-publish anything — this stage only decides what we tell the creator.
|
|
189
200
|
if (args['skip-smoke'] === true) {
|
|
190
201
|
// Said plainly, because "Published" on its own now carries a claim that the game renders.
|
|
191
202
|
// A skip has to visibly retract that claim, or it becomes the same silent green signal that
|
|
192
203
|
// let four defects reach published games.
|
|
193
|
-
|
|
204
|
+
log('Smoke check skipped — this game has NOT been confirmed to render.');
|
|
194
205
|
return;
|
|
195
206
|
}
|
|
196
207
|
const smokeDir = path.join(context.root, '.bitmagic', 'smoke');
|
|
197
|
-
|
|
208
|
+
log('Checking the published game renders…');
|
|
198
209
|
const smoke = await deps.runSmokeCheck({ publishedUrl: done.url, artifactDir: smokeDir });
|
|
199
210
|
for (const warning of smoke.warnings)
|
|
200
|
-
|
|
211
|
+
log(` warning: ${warning}`);
|
|
201
212
|
if (!smoke.ok) {
|
|
202
213
|
// Exit 7, distinct from 1-6: every one of those means the publish did NOT happen. This means
|
|
203
214
|
// the opposite, and an agent that conflates them would retry a publish that already succeeded.
|
|
@@ -206,7 +217,7 @@ export async function runPublish(args, deps = defaultDeps()) {
|
|
|
206
217
|
`Artifacts: ${smokeDir}\n` +
|
|
207
218
|
'Fix the game and publish again, or re-check without republishing once it builds.', 7);
|
|
208
219
|
}
|
|
209
|
-
|
|
220
|
+
log('Smoke check passed — the published game renders.');
|
|
210
221
|
}
|
|
211
222
|
export const publishCommand = defineCommand({
|
|
212
223
|
meta: {
|
|
@@ -234,6 +245,10 @@ export const publishCommand = defineCommand({
|
|
|
234
245
|
type: 'boolean',
|
|
235
246
|
description: 'Skip the post-publish render check. The game still publishes; it is simply not confirmed to render.',
|
|
236
247
|
},
|
|
248
|
+
json: {
|
|
249
|
+
type: 'boolean',
|
|
250
|
+
description: 'Print the result as JSON on stdout; all progress goes to stderr.',
|
|
251
|
+
},
|
|
237
252
|
},
|
|
238
253
|
async run({ args }) {
|
|
239
254
|
await runPublish(args);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAuB,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAoB,MAAM,2BAA2B,CAAC;AACvG,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EACL,QAAQ,IAAI,YAAY,EACxB,iBAAiB,IAAI,qBAAqB,EAC1C,gBAAgB,GAEjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,YAAY,EACZ,eAAe,EACf,SAAS,EACT,yBAAyB,EACzB,YAAY,GAEb,MAAM,sBAAsB,CAAC;AAE9B,2FAA2F;AAC3F,SAAS,iBAAiB,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;AAChD,CAAC;AAED;mFACmF;AACnF,MAAM,UAAU,iBAAiB,CAAC,GAAuB;IACvD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IACtD,MAAM,IAAI,QAAQ,CAAC,mDAAmD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAChG,CAAC;
|
|
1
|
+
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,4 +3,8 @@ export declare const verifyCommand: import("citty").CommandDef<{
|
|
|
3
3
|
readonly type: "string";
|
|
4
4
|
readonly description: "Settle time in ms (default 15000).";
|
|
5
5
|
};
|
|
6
|
+
readonly json: {
|
|
7
|
+
readonly type: "boolean";
|
|
8
|
+
readonly description: "Print the result as JSON on stdout; all progress goes to stderr.";
|
|
9
|
+
};
|
|
6
10
|
}>;
|
package/dist/commands/verify.js
CHANGED
|
@@ -5,11 +5,12 @@ import { spawn, spawnSync } from 'child_process';
|
|
|
5
5
|
import { CliError } from '../errors.js';
|
|
6
6
|
import { loadProjectContext, projectBin } from '../project/context.js';
|
|
7
7
|
import { reserveCorsAllowedPort, CORS_ALLOWED_PORT_MIN, CORS_ALLOWED_PORT_MAX } from '../local-port.js';
|
|
8
|
-
import { runHeadlessCheck } from '../verify/browser.js';
|
|
8
|
+
import { runHeadlessCheck, DEFAULT_START_INTERACTION } from '../verify/browser.js';
|
|
9
9
|
import { classifyVerifyRun } from '../verify/classify.js';
|
|
10
10
|
import { waitForServer } from '../verify/wait-for-server.js';
|
|
11
11
|
import { computeFingerprint } from '../publish/fingerprint.js';
|
|
12
12
|
import { writeVerifyRecord, decideVerifyOutcome, recordAndGateVerify } from '../verify/result.js';
|
|
13
|
+
import { createOutput } from '../output.js';
|
|
13
14
|
const DEFAULT_SETTLE_MS = 15_000;
|
|
14
15
|
/**
|
|
15
16
|
* A free port INSIDE the CORS-allowed window, never an OS-assigned ephemeral one.
|
|
@@ -65,13 +66,18 @@ export const verifyCommand = defineCommand({
|
|
|
65
66
|
meta: { name: 'verify', description: 'Boot the game in a real browser and report what broke.' },
|
|
66
67
|
args: {
|
|
67
68
|
timeout: { type: 'string', description: `Settle time in ms (default ${DEFAULT_SETTLE_MS}).` },
|
|
69
|
+
json: {
|
|
70
|
+
type: 'boolean',
|
|
71
|
+
description: 'Print the result as JSON on stdout; all progress goes to stderr.',
|
|
72
|
+
},
|
|
68
73
|
},
|
|
69
74
|
async run({ args }) {
|
|
70
75
|
const settleMs = parseTimeoutArg(args.timeout);
|
|
71
76
|
const context = loadProjectContext();
|
|
72
77
|
const tsc = projectBin(context.root, 'tsc');
|
|
73
78
|
const vite = projectBin(context.root, 'vite');
|
|
74
|
-
|
|
79
|
+
const buildOutput = createOutput(args.json === true);
|
|
80
|
+
buildOutput.info('Building…');
|
|
75
81
|
const build = spawnSync(tsc, [], { cwd: context.root, stdio: 'inherit' });
|
|
76
82
|
if (build.error) {
|
|
77
83
|
throw new CliError(`Could not run the TypeScript compiler: ${build.error.message}`);
|
|
@@ -99,21 +105,37 @@ export const verifyCommand = defineCommand({
|
|
|
99
105
|
waitForServer(port, 30_000),
|
|
100
106
|
watchForEarlyExit(server, () => viteStderr),
|
|
101
107
|
]);
|
|
102
|
-
// ?autostart=1
|
|
103
|
-
//
|
|
108
|
+
// No ?autostart=1: verify clicks the engine Play button like a real player would
|
|
109
|
+
// (startInteraction), which both proves the menu came up AND that gameplay starts from it.
|
|
110
|
+
// Autostart bypassed the menu — a game whose Play flow was broken could still pass.
|
|
104
111
|
const observations = await runHeadlessCheck({
|
|
105
|
-
url: `http://localhost:${port}
|
|
112
|
+
url: `http://localhost:${port}/`,
|
|
106
113
|
screenshotPath,
|
|
107
114
|
consolePath,
|
|
108
115
|
settleMs,
|
|
116
|
+
startInteraction: DEFAULT_START_INTERACTION,
|
|
109
117
|
});
|
|
110
118
|
const result = classifyVerifyRun(observations);
|
|
111
119
|
const outcome = decideVerifyOutcome(result, computeFingerprint(context.root), new Date().toISOString());
|
|
120
|
+
const output = createOutput(args.json === true);
|
|
112
121
|
recordAndGateVerify(artifactDir, outcome, { consolePath, screenshotPath }, {
|
|
113
122
|
write: writeVerifyRecord,
|
|
114
|
-
|
|
123
|
+
// Under --json both streams are prose and belong on stderr; the result document below
|
|
124
|
+
// is the only thing stdout carries.
|
|
125
|
+
info: (line) => output.info(line),
|
|
115
126
|
error: (line) => console.error(line),
|
|
116
127
|
});
|
|
128
|
+
// Only reached when the run PASSED — recordAndGateVerify throws on failure, and the
|
|
129
|
+
// wrapper then emits the error document. Both paths leave stdout parseable.
|
|
130
|
+
output.result({
|
|
131
|
+
ok: true,
|
|
132
|
+
failures: outcome.record.failures,
|
|
133
|
+
warnings: outcome.record.warnings,
|
|
134
|
+
fingerprint: outcome.record.fingerprint,
|
|
135
|
+
artifactDir,
|
|
136
|
+
consolePath,
|
|
137
|
+
screenshotPath,
|
|
138
|
+
});
|
|
117
139
|
}
|
|
118
140
|
finally {
|
|
119
141
|
server?.kill('SIGTERM');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,SAAS,EAAqB,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACxG,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"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"}
|
package/dist/output.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a command's words go, and where its result goes.
|
|
3
|
+
*
|
|
4
|
+
* The lane's stated convention (pro-lane design, "Conventions that make this agent-drivable") is
|
|
5
|
+
* **progress to stderr, result JSON to stdout**, so `bitmagic forge --json > result.json` works
|
|
6
|
+
* while the agent still watches progress on a minutes-long pipeline.
|
|
7
|
+
*
|
|
8
|
+
* That only holds if EVERY human-facing line goes through `info`. A stray `console.log` in
|
|
9
|
+
* `--json` mode lands on stdout beside the JSON document and corrupts it — which is not a
|
|
10
|
+
* hypothetical: the engine-upgrade nudge did exactly that before this existed, printing through
|
|
11
|
+
* the same `log` seam as the result line.
|
|
12
|
+
*/
|
|
13
|
+
export interface Output {
|
|
14
|
+
/** Progress, warnings, prose. stdout normally; stderr under `--json`. */
|
|
15
|
+
info(message: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* The machine-readable result. Prints only under `--json`, as a single line on stdout.
|
|
18
|
+
*
|
|
19
|
+
* In human mode this is a no-op: the command has already said the same thing through `info`,
|
|
20
|
+
* in a form a person reads.
|
|
21
|
+
*/
|
|
22
|
+
result(value: unknown): void;
|
|
23
|
+
}
|
|
24
|
+
export declare function createOutput(json: boolean): Output;
|
|
25
|
+
/**
|
|
26
|
+
* The failure shape, so an agent can branch on one parse rather than on prose.
|
|
27
|
+
*
|
|
28
|
+
* Emitted on stdout like any other result: under `--json` the contract is "stdout is exactly one
|
|
29
|
+
* JSON document describing what happened", and a failure is as much an outcome as a success. The
|
|
30
|
+
* exit code is still authoritative and unchanged — this only makes the reason machine-readable.
|
|
31
|
+
*/
|
|
32
|
+
export declare function jsonErrorResult(message: string, exitCode: number): {
|
|
33
|
+
ok: false;
|
|
34
|
+
exitCode: number;
|
|
35
|
+
error: string;
|
|
36
|
+
};
|
package/dist/output.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function createOutput(json) {
|
|
2
|
+
return {
|
|
3
|
+
info: (message) => {
|
|
4
|
+
if (json)
|
|
5
|
+
console.error(message);
|
|
6
|
+
else
|
|
7
|
+
console.log(message);
|
|
8
|
+
},
|
|
9
|
+
result: (value) => {
|
|
10
|
+
if (json)
|
|
11
|
+
console.log(JSON.stringify(value));
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The failure shape, so an agent can branch on one parse rather than on prose.
|
|
17
|
+
*
|
|
18
|
+
* Emitted on stdout like any other result: under `--json` the contract is "stdout is exactly one
|
|
19
|
+
* JSON document describing what happened", and a failure is as much an outcome as a success. The
|
|
20
|
+
* exit code is still authoritative and unchanged — this only makes the reason machine-readable.
|
|
21
|
+
*/
|
|
22
|
+
export function jsonErrorResult(message, exitCode) {
|
|
23
|
+
return { ok: false, exitCode, error: message };
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAwBA,MAAM,UAAU,YAAY,CAAC,IAAa;IACxC,OAAO;QACL,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE;YAChB,IAAI,IAAI;gBAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;;gBAC5B,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;QACD,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAChB,IAAI,IAAI;gBAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/C,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,QAAgB;IAC/D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AACjD,CAAC"}
|
package/dist/verify/browser.d.ts
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import type { VerifyObservations } from './classify.js';
|
|
2
|
+
export interface StartInteractionOptions {
|
|
3
|
+
/** Selector for the engine Play button. Its label is localizable and per-game overridable, so
|
|
4
|
+
* never match by text. */
|
|
5
|
+
playButtonSelector: string;
|
|
6
|
+
/** Console line the engine logs when `handleGameStart()` runs — every start path (button
|
|
7
|
+
* click, auto-starting genres, custom start UIs calling `engine.startGame()`) produces it. */
|
|
8
|
+
startedConsoleMarker: string;
|
|
9
|
+
/** How long the Play button (or an auto-start) gets to show up after page load. */
|
|
10
|
+
buttonTimeoutMs: number;
|
|
11
|
+
/** How long after clicking the button the start marker gets to appear. */
|
|
12
|
+
postClickStartTimeoutMs: number;
|
|
13
|
+
}
|
|
14
|
+
export declare const DEFAULT_START_INTERACTION: StartInteractionOptions;
|
|
2
15
|
export interface HeadlessCheckOptions {
|
|
3
16
|
url: string;
|
|
4
17
|
screenshotPath: string;
|
|
@@ -15,6 +28,15 @@ export interface HeadlessCheckOptions {
|
|
|
15
28
|
* classifies as "the game did not start" even when it rendered perfectly.
|
|
16
29
|
*/
|
|
17
30
|
probeFrameUrlIncludes?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Start gameplay the way a player does — wait for the engine Play button and click it —
|
|
33
|
+
* before settling, and report what happened in `VerifyObservations.start`.
|
|
34
|
+
*
|
|
35
|
+
* Only `bitmagic verify` sets this. `publish`'s smoke check loads the game in an iframe where
|
|
36
|
+
* these top-level selectors would not reach, and its job is "does the published bundle come
|
|
37
|
+
* up", not "does gameplay start".
|
|
38
|
+
*/
|
|
39
|
+
startInteraction?: StartInteractionOptions;
|
|
18
40
|
}
|
|
19
41
|
/**
|
|
20
42
|
* Drives the creator's installed Chrome. `channel: 'chrome'` deliberately uses the system
|
package/dist/verify/browser.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import { chromium } from 'playwright-core';
|
|
3
3
|
import { CliError } from '../errors.js';
|
|
4
|
+
export const DEFAULT_START_INTERACTION = {
|
|
5
|
+
playButtonSelector: '#play-button, .hud-play-button',
|
|
6
|
+
startedConsoleMarker: '🎮 Starting game...',
|
|
7
|
+
buttonTimeoutMs: 30_000,
|
|
8
|
+
postClickStartTimeoutMs: 10_000,
|
|
9
|
+
};
|
|
4
10
|
/**
|
|
5
11
|
* Drives the creator's installed Chrome. `channel: 'chrome'` deliberately uses the system
|
|
6
12
|
* browser rather than a downloaded one, so installing the CLI stays a small, fast npm install.
|
|
@@ -41,6 +47,9 @@ export async function runHeadlessCheck(options) {
|
|
|
41
47
|
const detail = error instanceof Error ? error.message : String(error);
|
|
42
48
|
throw new CliError(`The page never finished loading at ${options.url}: ${detail}`);
|
|
43
49
|
}
|
|
50
|
+
const interaction = options.startInteraction === undefined
|
|
51
|
+
? null
|
|
52
|
+
: await performStartInteraction(page, consoleLines, options.startInteraction);
|
|
44
53
|
await page.waitForTimeout(options.settleMs);
|
|
45
54
|
// Same probe either way; only the frame it runs in differs. A missing child frame leaves
|
|
46
55
|
// `target` null, which classifies as "no canvas" — correct, because a harness whose iframe
|
|
@@ -68,7 +77,17 @@ export async function runHeadlessCheck(options) {
|
|
|
68
77
|
});
|
|
69
78
|
const screenshotBuffer = await page.screenshot();
|
|
70
79
|
fs.writeFileSync(options.screenshotPath, screenshotBuffer);
|
|
71
|
-
|
|
80
|
+
let start;
|
|
81
|
+
if (options.startInteraction !== undefined && interaction !== null) {
|
|
82
|
+
start = {
|
|
83
|
+
...interaction,
|
|
84
|
+
// Recomputed from the full console capture rather than taken from the interaction step,
|
|
85
|
+
// so a start that only lands during the settle (slow world load) still counts.
|
|
86
|
+
gameStartDetected: hasStartMarker(consoleLines, options.startInteraction.startedConsoleMarker),
|
|
87
|
+
menuVisibleAfterSettle: await isMenuVisible(page),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return { consoleLines, pageErrors, canvas, screenshotBytes: screenshotBuffer.length, start };
|
|
72
91
|
}
|
|
73
92
|
finally {
|
|
74
93
|
// Written on success and failure alike, including when the try block above threw before
|
|
@@ -78,4 +97,69 @@ export async function runHeadlessCheck(options) {
|
|
|
78
97
|
await browser.close();
|
|
79
98
|
}
|
|
80
99
|
}
|
|
100
|
+
const START_POLL_MS = 250;
|
|
101
|
+
function hasStartMarker(consoleLines, marker) {
|
|
102
|
+
return consoleLines.some((line) => line.includes(marker));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Start gameplay the way a player does: wait for the engine Play button and click it. The button
|
|
106
|
+
* only becomes visible once the world has finished loading, so "button appeared" doubles as a
|
|
107
|
+
* load-complete signal. Games without the button (auto-starting genres, custom start UIs) are
|
|
108
|
+
* accepted through the start marker instead.
|
|
109
|
+
*
|
|
110
|
+
* Never throws for a game that misbehaves — the run continues to settle/probe/screenshot so a
|
|
111
|
+
* failing game still leaves its menu screenshot and console log as artifacts, and classification
|
|
112
|
+
* decides pass/fail from what is recorded here.
|
|
113
|
+
*/
|
|
114
|
+
async function performStartInteraction(page, consoleLines, options) {
|
|
115
|
+
const button = page.locator(options.playButtonSelector).first();
|
|
116
|
+
let playButtonSeen = false;
|
|
117
|
+
let playButtonClicked = false;
|
|
118
|
+
// One poll loop instead of racing a locator.waitFor() against a console watcher: the marker
|
|
119
|
+
// lands in `consoleLines` (already captured by the page handlers), and isVisible() never
|
|
120
|
+
// throws on a missing element, so there are no promises left dangling when either side wins.
|
|
121
|
+
const buttonDeadline = Date.now() + options.buttonTimeoutMs;
|
|
122
|
+
while (Date.now() < buttonDeadline) {
|
|
123
|
+
if (hasStartMarker(consoleLines, options.startedConsoleMarker)) {
|
|
124
|
+
return { playButtonSeen, playButtonClicked };
|
|
125
|
+
}
|
|
126
|
+
if (await button.isVisible()) {
|
|
127
|
+
playButtonSeen = true;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
await page.waitForTimeout(START_POLL_MS);
|
|
131
|
+
}
|
|
132
|
+
if (playButtonSeen) {
|
|
133
|
+
try {
|
|
134
|
+
await button.click({ timeout: 5_000 });
|
|
135
|
+
playButtonClicked = true;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Visible but unclickable (detached mid-fade, covered by another element) — recorded as
|
|
139
|
+
// seen-but-not-clicked; the game can still pass if it started some other way.
|
|
140
|
+
}
|
|
141
|
+
const startDeadline = Date.now() + options.postClickStartTimeoutMs;
|
|
142
|
+
while (Date.now() < startDeadline && !hasStartMarker(consoleLines, options.startedConsoleMarker)) {
|
|
143
|
+
await page.waitForTimeout(START_POLL_MS);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { playButtonSeen, playButtonClicked };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Whether the engine's start menu overlay is still displayed. After a successful start the
|
|
150
|
+
* StartScreen fades out and sets `display: none`; a menu still up at the end of the run means
|
|
151
|
+
* the game bounced back to (or never left) the menu, whatever the console claimed.
|
|
152
|
+
*/
|
|
153
|
+
async function isMenuVisible(page) {
|
|
154
|
+
return page.evaluate(() => {
|
|
155
|
+
// Runs inside the browser, where `document`/`window` are real globals — see the canvas probe
|
|
156
|
+
// above for why eslint needs telling.
|
|
157
|
+
// eslint-disable-next-line no-undef
|
|
158
|
+
const menu = document.querySelector('#game-menu');
|
|
159
|
+
if (!(menu instanceof HTMLElement))
|
|
160
|
+
return false;
|
|
161
|
+
// eslint-disable-next-line no-undef
|
|
162
|
+
return window.getComputedStyle(menu).display !== 'none';
|
|
163
|
+
});
|
|
164
|
+
}
|
|
81
165
|
//# sourceMappingURL=browser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/verify/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/verify/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAa,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAgBxC,MAAM,CAAC,MAAM,yBAAyB,GAA4B;IAChE,kBAAkB,EAAE,gCAAgC;IACpD,oBAAoB,EAAE,qBAAqB;IAC3C,eAAe,EAAE,MAAM;IACvB,uBAAuB,EAAE,MAAM;CAChC,CAAC;AA6BF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAA6B;IAE7B,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,2FAA2F;QAC3F,oDAAoD;QACpD,MAAM,IAAI,QAAQ,CAChB,wFAAwF;YACtF,gFAAgF,CACnF,CAAC;IACJ,CAAC;IAED,8FAA8F;IAC9F,+FAA+F;IAC/F,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QACrF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QAErC,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;QAC3F,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAChE,IAAI,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,OAAO,EAAE,EAAE,CACnC,YAAY,CAAC,IAAI,CAAC,kBAAkB,OAAO,CAAC,GAAG,EAAE,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,SAAS,IAAI,EAAE,EAAE,CAAC,CAC3F,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QACvE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,uFAAuF;YACvF,0FAA0F;YAC1F,IAAI,CAAC;gBACH,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,cAAc,EAAE,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;YACpE,CAAC;YAAC,MAAM,CAAC;gBACP,qFAAqF;YACvF,CAAC;YACD,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACtE,MAAM,IAAI,QAAQ,CAAC,sCAAsC,OAAO,CAAC,GAAG,KAAK,MAAM,EAAE,CAAC,CAAC;QACrF,CAAC;QAED,MAAM,WAAW,GAAG,OAAO,CAAC,gBAAgB,KAAK,SAAS;YACxD,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,MAAM,uBAAuB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAEhF,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAE5C,yFAAyF;QACzF,2FAA2F;QAC3F,qFAAqF;QACrF,MAAM,MAAM,GAAG,OAAO,CAAC,qBAAqB,KAAK,SAAS;YACxD,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,qBAA+B,CAAC,CAAC,IAAI,IAAI,CAAC;QAEzG,MAAM,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE;YACjE,0FAA0F;YAC1F,uFAAuF;YACvF,iDAAiD;YACjD,oCAAoC;YACpC,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,wBAAwB,CAAC,CAAC;YACjE,IAAI,CAAC,CAAC,OAAO,YAAY,iBAAiB,CAAC;gBAAE,OAAO,IAAI,CAAC;YACzD,MAAM,IAAI,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;YAC7C,OAAO;gBACL,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;gBAC7B,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;gBAC/B,yFAAyF;gBACzF,0CAA0C;gBAC1C,WAAW,EAAE,OAAO,CAAC,KAAK;gBAC1B,YAAY,EAAE,OAAO,CAAC,MAAM;aAC7B,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACjD,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;QAE3D,IAAI,KAAoC,CAAC;QACzC,IAAI,OAAO,CAAC,gBAAgB,KAAK,SAAS,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACnE,KAAK,GAAG;gBACN,GAAG,WAAW;gBACd,wFAAwF;gBACxF,+EAA+E;gBAC/E,iBAAiB,EAAE,cAAc,CAAC,YAAY,EAAE,OAAO,CAAC,gBAAgB,CAAC,oBAAoB,CAAC;gBAC9F,sBAAsB,EAAE,MAAM,aAAa,CAAC,IAAI,CAAC;aAClD,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;IAC/F,CAAC;YAAS,CAAC;QACT,wFAAwF;QACxF,4FAA4F;QAC5F,wDAAwD;QACxD,EAAE,CAAC,aAAa,CACd,OAAO,CAAC,WAAW,EACnB,CAAC,GAAG,YAAY,EAAE,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAC1E,CAAC;QACF,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC;AAED,MAAM,aAAa,GAAG,GAAG,CAAC;AAE1B,SAAS,cAAc,CAAC,YAAsB,EAAE,MAAc;IAC5D,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;;;;;GASG;AACH,KAAK,UAAU,uBAAuB,CACpC,IAAU,EACV,YAAsB,EACtB,OAAgC;IAEhC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,KAAK,EAAE,CAAC;IAChE,IAAI,cAAc,GAAG,KAAK,CAAC;IAC3B,IAAI,iBAAiB,GAAG,KAAK,CAAC;IAE9B,4FAA4F;IAC5F,yFAAyF;IACzF,6FAA6F;IAC7F,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC;IAC5D,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc,EAAE,CAAC;QACnC,IAAI,cAAc,CAAC,YAAY,EAAE,OAAO,CAAC,oBAAoB,CAAC,EAAE,CAAC;YAC/D,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,CAAC;QAC/C,CAAC;QACD,IAAI,MAAM,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC;YAC7B,cAAc,GAAG,IAAI,CAAC;YACtB,MAAM;QACR,CAAC;QACD,MAAM,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,cAAc,EAAE,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YACvC,iBAAiB,GAAG,IAAI,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,wFAAwF;YACxF,8EAA8E;QAChF,CAAC;QACD,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,uBAAuB,CAAC;QACnE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,aAAa,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,OAAO,CAAC,oBAAoB,CAAC,EAAE,CAAC;YACjG,MAAM,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,aAAa,CAAC,IAAU;IACrC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;QACxB,6FAA6F;QAC7F,sCAAsC;QACtC,oCAAoC;QACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;QAClD,IAAI,CAAC,CAAC,IAAI,YAAY,WAAW,CAAC;YAAE,OAAO,KAAK,CAAC;QACjD,oCAAoC;QACpC,OAAO,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -13,6 +13,15 @@ export interface CanvasObservation {
|
|
|
13
13
|
bufferWidth: number;
|
|
14
14
|
bufferHeight: number;
|
|
15
15
|
}
|
|
16
|
+
/** What the start interaction (waiting for and clicking the engine Play button) observed. */
|
|
17
|
+
export interface StartObservations {
|
|
18
|
+
playButtonSeen: boolean;
|
|
19
|
+
playButtonClicked: boolean;
|
|
20
|
+
/** The engine's game-start console line appeared at any point during the run. */
|
|
21
|
+
gameStartDetected: boolean;
|
|
22
|
+
/** The start menu overlay was still displayed after settling. */
|
|
23
|
+
menuVisibleAfterSettle: boolean;
|
|
24
|
+
}
|
|
16
25
|
export interface VerifyObservations {
|
|
17
26
|
/** Console lines, each prefixed with its type, e.g. `error: …`. */
|
|
18
27
|
consoleLines: string[];
|
|
@@ -20,6 +29,9 @@ export interface VerifyObservations {
|
|
|
20
29
|
canvas: CanvasObservation | null;
|
|
21
30
|
/** Byte length of the PNG screenshot taken after settling. */
|
|
22
31
|
screenshotBytes: number;
|
|
32
|
+
/** Only present when the run was asked to perform the start interaction (`bitmagic verify`);
|
|
33
|
+
* `publish`'s smoke check never sets it and classifies exactly as before. */
|
|
34
|
+
start?: StartObservations;
|
|
23
35
|
}
|
|
24
36
|
export interface VerifyResult {
|
|
25
37
|
ok: boolean;
|
package/dist/verify/classify.js
CHANGED
|
@@ -53,6 +53,28 @@ export function classifyVerifyRun(observations) {
|
|
|
53
53
|
for (const error of observations.pageErrors) {
|
|
54
54
|
failures.push(`Uncaught error on the page: ${error}`);
|
|
55
55
|
}
|
|
56
|
+
const { start } = observations;
|
|
57
|
+
if (start) {
|
|
58
|
+
if (!start.gameStartDetected) {
|
|
59
|
+
// Auto-starting genres and custom start UIs never show the button, so its absence alone is
|
|
60
|
+
// fine — what is never fine is a run that ends without gameplay having started at all.
|
|
61
|
+
if (start.playButtonClicked) {
|
|
62
|
+
failures.push('The Play button was clicked but the game never started — the start handler most '
|
|
63
|
+
+ 'likely threw.');
|
|
64
|
+
}
|
|
65
|
+
else if (start.playButtonSeen) {
|
|
66
|
+
failures.push('The Play button appeared but could not be clicked, and the game never started.');
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
failures.push('The game never started — no Play button appeared and no game start was detected.');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if (start.menuVisibleAfterSettle) {
|
|
73
|
+
// Started-then-back-to-menu is a mobile-fullscreen bounce the engine allows; it should not
|
|
74
|
+
// happen in a headless desktop run, but the start DID happen, so surface rather than fail.
|
|
75
|
+
warnings.push('The game started but the start menu was still visible when the run ended.');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
56
78
|
const { canvas } = observations;
|
|
57
79
|
if (!canvas) {
|
|
58
80
|
failures.push('No <canvas> was rendered inside #game-container — the game did not start.');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/verify/classify.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/verify/classify.ts"],"names":[],"mappings":"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"}
|