@bitmagic/cli 0.1.36 → 0.1.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +16 -3
  2. package/dist/browser-gl.d.ts +23 -0
  3. package/dist/browser-gl.js +50 -0
  4. package/dist/browser-gl.js.map +1 -1
  5. package/dist/cli.d.ts +27 -1
  6. package/dist/cli.js +2 -0
  7. package/dist/cli.js.map +1 -1
  8. package/dist/commands/forge.js +39 -26
  9. package/dist/commands/forge.js.map +1 -1
  10. package/dist/commands/login.js +10 -2
  11. package/dist/commands/login.js.map +1 -1
  12. package/dist/commands/usage.d.ts +76 -0
  13. package/dist/commands/usage.js +117 -0
  14. package/dist/commands/usage.js.map +1 -0
  15. package/dist/commands/verify.d.ts +13 -1
  16. package/dist/commands/verify.js +139 -15
  17. package/dist/commands/verify.js.map +1 -1
  18. package/dist/commands/whoami.d.ts +10 -0
  19. package/dist/commands/whoami.js +11 -1
  20. package/dist/commands/whoami.js.map +1 -1
  21. package/dist/editor/shell-page.js +123 -7
  22. package/dist/editor/shell-page.js.map +1 -1
  23. package/dist/forge/browser-host.d.ts +0 -10
  24. package/dist/forge/browser-host.js +0 -12
  25. package/dist/forge/browser-host.js.map +1 -1
  26. package/dist/forge/progress.d.ts +80 -0
  27. package/dist/forge/progress.js +158 -0
  28. package/dist/forge/progress.js.map +1 -0
  29. package/dist/forge/run-pipeline.d.ts +11 -2
  30. package/dist/forge/run-pipeline.js +9 -7
  31. package/dist/forge/run-pipeline.js.map +1 -1
  32. package/dist/forge/stream.d.ts +14 -3
  33. package/dist/forge/stream.js +62 -9
  34. package/dist/forge/stream.js.map +1 -1
  35. package/dist/forge/transport.js +14 -3
  36. package/dist/forge/transport.js.map +1 -1
  37. package/dist/forge/upload-proxy.js +1 -1
  38. package/dist/http/client.js +3 -2
  39. package/dist/http/client.js.map +1 -1
  40. package/dist/project/jobs.d.ts +22 -1
  41. package/dist/project/jobs.js +60 -3
  42. package/dist/project/jobs.js.map +1 -1
  43. package/dist/scaffold/claude-settings.js +4 -0
  44. package/dist/scaffold/claude-settings.js.map +1 -1
  45. package/dist/scaffold/project-files.d.ts +9 -0
  46. package/dist/scaffold/project-files.js +67 -8
  47. package/dist/scaffold/project-files.js.map +1 -1
  48. package/dist/scaffold/project.js +2 -1
  49. package/dist/scaffold/project.js.map +1 -1
  50. package/dist/verify/browser.d.ts +37 -1
  51. package/dist/verify/browser.js +389 -97
  52. package/dist/verify/browser.js.map +1 -1
  53. package/dist/verify/classify.d.ts +75 -2
  54. package/dist/verify/classify.js +48 -2
  55. package/dist/verify/classify.js.map +1 -1
  56. package/dist/verify/iterate.d.ts +47 -0
  57. package/dist/verify/iterate.js +162 -0
  58. package/dist/verify/iterate.js.map +1 -0
  59. package/dist/verify/result.d.ts +16 -3
  60. package/dist/verify/result.js +13 -3
  61. package/dist/verify/result.js.map +1 -1
  62. package/dist/verify/settle.d.ts +60 -0
  63. package/dist/verify/settle.js +90 -0
  64. package/dist/verify/settle.js.map +1 -0
  65. package/package.json +3 -3
  66. package/dist/forge/bake-progress.d.ts +0 -44
  67. package/dist/forge/bake-progress.js +0 -77
  68. package/dist/forge/bake-progress.js.map +0 -1
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The decision "has the game settled enough to observe", extracted pure so it is testable
3
+ * without a browser.
4
+ *
5
+ * Verify used to wait a fixed 15 seconds regardless of what the game was doing. The samples the
6
+ * browser loop feeds this function come from the live engine (`window.gameTemplate`), so a
7
+ * healthy game that reaches gameplay and holds steady is observed after ~4-5 seconds, while the
8
+ * fixed `--timeout` stays as the hard cap for games that never stabilise. An engine too old to
9
+ * answer the probes never settles early — it gets exactly the old fixed-wait behaviour.
10
+ */
11
+ export const DEFAULT_SETTLE_CONFIG = {
12
+ minSettleMs: 4_000,
13
+ minFrames: 90,
14
+ quietMs: 1_500,
15
+ };
16
+ export const SETTLE_POLL_MS = 500;
17
+ /**
18
+ * Per-sample drop (world units per poll) below which the player counts as falling. A walking
19
+ * player descends a slope far slower; a player falling out of the world drops tens of units
20
+ * between 500ms polls.
21
+ */
22
+ const FREEFALL_DROP_PER_SAMPLE = 2;
23
+ /** How many consecutive falling samples make a freefall. One can be a jump; three in a row
24
+ * (a full second and a half of accelerating descent) is not gameplay. */
25
+ const FREEFALL_SAMPLES = 3;
26
+ /**
27
+ * The player is in sustained freefall at the end of `samples`.
28
+ *
29
+ * Judged over the trailing samples that HAVE a player position; unobservable positions mean
30
+ * "cannot tell", never "falling". Exported because the final state snapshot reports the same
31
+ * judgement as `player.isFalling`.
32
+ */
33
+ export function isFreefalling(samples) {
34
+ const positioned = samples.filter((sample) => sample.playerY !== null);
35
+ if (positioned.length < FREEFALL_SAMPLES + 1)
36
+ return false;
37
+ const tail = positioned.slice(-(FREEFALL_SAMPLES + 1));
38
+ for (let i = 1; i < tail.length; i++) {
39
+ const drop = tail[i - 1].playerY - tail[i].playerY;
40
+ if (drop < FREEFALL_DROP_PER_SAMPLE)
41
+ return false;
42
+ }
43
+ return true;
44
+ }
45
+ /**
46
+ * True when the run has seen enough to stop settling early.
47
+ *
48
+ * Every condition must hold on the latest sample:
49
+ * - the minimum settle floor has passed;
50
+ * - gameplay is actually running — state 'playing' or 'end' (a run-to-completion minigame), or
51
+ * `hasStarted` when the state manager is not observable. Both unobservable → never settle
52
+ * early; the fixed timeout is the correct behaviour for an engine that cannot be asked;
53
+ * - the page has rendered enough frames to prove a live render loop;
54
+ * - the error counters have been flat for the quiet window;
55
+ * - the player is not falling out of the world.
56
+ */
57
+ export function settleVerdict(samples, config) {
58
+ if (samples.length === 0)
59
+ return 'keep-waiting';
60
+ const latest = samples[samples.length - 1];
61
+ if (latest.atMs < config.minSettleMs)
62
+ return 'keep-waiting';
63
+ const playing = latest.gameState === null
64
+ ? latest.hasStarted === true
65
+ : latest.gameState === 'playing' || latest.gameState === 'end';
66
+ if (!playing)
67
+ return 'keep-waiting';
68
+ if (latest.frameCount === null || latest.frameCount < config.minFrames)
69
+ return 'keep-waiting';
70
+ const quietWindowStart = latest.atMs - config.quietMs;
71
+ const windowSamples = samples.filter((sample) => sample.atMs >= quietWindowStart);
72
+ // The window must span actual time: right after the floor passes there may be only the latest
73
+ // sample in it, and "no errors between one sample and itself" proves nothing.
74
+ const earliest = windowSamples[0];
75
+ if (earliest === latest && samples.length > 1)
76
+ return 'keep-waiting';
77
+ if (windowSamples.some((sample) => sample.errorEventCount !== latest.errorEventCount)) {
78
+ return 'keep-waiting';
79
+ }
80
+ // Cumulative frames are not enough: a loop that rendered its 90 frames on the menu and froze
81
+ // at game start (measured under software WebGPU) would settle as "stable" on the count alone.
82
+ // Stable means frames are still being produced NOW — strictly more than at the window's start.
83
+ if (earliest !== latest && earliest.frameCount !== null && latest.frameCount <= earliest.frameCount) {
84
+ return 'keep-waiting';
85
+ }
86
+ if (isFreefalling(samples))
87
+ return 'keep-waiting';
88
+ return 'settled';
89
+ }
90
+ //# sourceMappingURL=settle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settle.js","sourceRoot":"","sources":["../../src/verify/settle.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitmagic/cli",
3
- "version": "0.1.36",
3
+ "version": "0.1.38",
4
4
  "description": "Build Bitmagic games from your own agent tool",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,8 +19,8 @@
19
19
  "citty": "^0.2.2",
20
20
  "playwright-core": "^1.59.1",
21
21
  "tar": "^7.4.3",
22
- "@bitmagic/world-forger": "0.1.3",
23
- "@bitmagic/asset-core": "0.1.2"
22
+ "@bitmagic/asset-core": "0.1.2",
23
+ "@bitmagic/world-forger": "0.1.4"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@eslint/js": "^9.38.0",
@@ -1,44 +0,0 @@
1
- /**
2
- * Terminal progress for the level bake.
3
- *
4
- * The bake is the one step that can legitimately run for many minutes, and its transport wait is
5
- * an IDLE timeout of 20 minutes (`VOXELIZE_TIMEOUT_MS`) — per-chunk progress re-arms the timer but
6
- * is otherwise consumed silently inside the transport. Without a printed line a creator watching a
7
- * still terminal concludes the command has hung and kills it mid-bake, losing the whole step. So
8
- * the command subscribes to the page's own `VOXELIZE_GLB_AS_LEVEL_PROGRESS` messages
9
- * (`game/src/engine/template/VoxelMessageHandlers.ts`) and prints them.
10
- *
11
- * Throttled, because the engine emits one message per chunk and a large level has thousands: an
12
- * unthrottled relay would bury every other line of output. The formatter and the throttle are pure
13
- * and separate from the subscription so both are testable without a browser.
14
- */
15
- /** The engine's per-chunk progress payload. `chunkIndex: -1` marks the post-bake upload phase. */
16
- export interface BakeProgress {
17
- chunkIndex: number;
18
- totalChunks: number;
19
- nonEmptyChunks?: number;
20
- label?: string;
21
- }
22
- export declare const BAKE_PROGRESS_TYPE = "VOXELIZE_GLB_AS_LEVEL_PROGRESS";
23
- /** Minimum gap between printed bake lines. Long enough not to flood, short enough that the
24
- * terminal never looks frozen. */
25
- export declare const BAKE_PROGRESS_MIN_INTERVAL_MS = 3000;
26
- /** Read a bake-progress payload off a page message, or null if it is not one. */
27
- export declare function asBakeProgress(message: unknown): BakeProgress | null;
28
- /**
29
- * The line to print for one progress message.
30
- *
31
- * `chunkIndex: -1` / `totalChunks: -1` is the engine's sentinel for the upload phase that follows
32
- * the chunk loop; it carries a label instead of a count, so it is rendered as that label rather
33
- * than as a nonsensical "chunk -1 of -1".
34
- */
35
- export declare function formatBakeProgress(progress: BakeProgress): string;
36
- /**
37
- * A printer that emits at most one line per `minIntervalMs` — except for the final chunk and the
38
- * label-only phases, which always print. The last chunk is what tells a creator the loop finished
39
- * rather than stalled, so dropping it to a throttle would remove the single most informative line.
40
- */
41
- export declare function createBakeProgressPrinter(log: (message: string) => void, options?: {
42
- minIntervalMs?: number;
43
- now?: () => number;
44
- }): (message: unknown) => void;
@@ -1,77 +0,0 @@
1
- /**
2
- * Terminal progress for the level bake.
3
- *
4
- * The bake is the one step that can legitimately run for many minutes, and its transport wait is
5
- * an IDLE timeout of 20 minutes (`VOXELIZE_TIMEOUT_MS`) — per-chunk progress re-arms the timer but
6
- * is otherwise consumed silently inside the transport. Without a printed line a creator watching a
7
- * still terminal concludes the command has hung and kills it mid-bake, losing the whole step. So
8
- * the command subscribes to the page's own `VOXELIZE_GLB_AS_LEVEL_PROGRESS` messages
9
- * (`game/src/engine/template/VoxelMessageHandlers.ts`) and prints them.
10
- *
11
- * Throttled, because the engine emits one message per chunk and a large level has thousands: an
12
- * unthrottled relay would bury every other line of output. The formatter and the throttle are pure
13
- * and separate from the subscription so both are testable without a browser.
14
- */
15
- export const BAKE_PROGRESS_TYPE = 'VOXELIZE_GLB_AS_LEVEL_PROGRESS';
16
- /** Minimum gap between printed bake lines. Long enough not to flood, short enough that the
17
- * terminal never looks frozen. */
18
- export const BAKE_PROGRESS_MIN_INTERVAL_MS = 3000;
19
- function isRecord(value) {
20
- return typeof value === 'object' && value !== null;
21
- }
22
- /** Read a bake-progress payload off a page message, or null if it is not one. */
23
- export function asBakeProgress(message) {
24
- if (!isRecord(message) || message.type !== BAKE_PROGRESS_TYPE)
25
- return null;
26
- const { chunkIndex, totalChunks } = message;
27
- if (typeof chunkIndex !== 'number' || typeof totalChunks !== 'number')
28
- return null;
29
- return {
30
- chunkIndex,
31
- totalChunks,
32
- ...(typeof message.nonEmptyChunks === 'number' ? { nonEmptyChunks: message.nonEmptyChunks } : {}),
33
- ...(typeof message.label === 'string' ? { label: message.label } : {}),
34
- };
35
- }
36
- /**
37
- * The line to print for one progress message.
38
- *
39
- * `chunkIndex: -1` / `totalChunks: -1` is the engine's sentinel for the upload phase that follows
40
- * the chunk loop; it carries a label instead of a count, so it is rendered as that label rather
41
- * than as a nonsensical "chunk -1 of -1".
42
- */
43
- export function formatBakeProgress(progress) {
44
- if (progress.totalChunks <= 0) {
45
- return ` bake: ${progress.label ?? 'working…'}`;
46
- }
47
- const percent = Math.floor(((progress.chunkIndex + 1) / progress.totalChunks) * 100);
48
- const filled = progress.nonEmptyChunks !== undefined ? `, ${progress.nonEmptyChunks} non-empty` : '';
49
- const label = progress.label ? ` — ${progress.label}` : '';
50
- return ` bake: chunk ${progress.chunkIndex + 1}/${progress.totalChunks} (${percent}%${filled})${label}`;
51
- }
52
- /**
53
- * A printer that emits at most one line per `minIntervalMs` — except for the final chunk and the
54
- * label-only phases, which always print. The last chunk is what tells a creator the loop finished
55
- * rather than stalled, so dropping it to a throttle would remove the single most informative line.
56
- */
57
- export function createBakeProgressPrinter(log, options = {}) {
58
- const minIntervalMs = options.minIntervalMs ?? BAKE_PROGRESS_MIN_INTERVAL_MS;
59
- const now = options.now ?? (() => Date.now());
60
- // -Infinity, not 0: the FIRST message must always print. A zero start throttles it whenever
61
- // `now()` begins near zero — which a monotonic clock does, and which is exactly the moment the
62
- // creator most needs to see that the bake has begun.
63
- let lastAt = -Infinity;
64
- return (message) => {
65
- const progress = asBakeProgress(message);
66
- if (progress === null)
67
- return;
68
- const isFinalChunk = progress.totalChunks > 0 && progress.chunkIndex + 1 >= progress.totalChunks;
69
- const isPhaseLabel = progress.totalChunks <= 0;
70
- const at = now();
71
- if (!isFinalChunk && !isPhaseLabel && at - lastAt < minIntervalMs)
72
- return;
73
- lastAt = at;
74
- log(formatBakeProgress(progress));
75
- };
76
- }
77
- //# sourceMappingURL=bake-progress.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bake-progress.js","sourceRoot":"","sources":["../../src/forge/bake-progress.ts"],"names":[],"mappings":"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"}