@bitmagic/cli 0.1.21 → 0.1.24
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 +83 -20
- package/dist/cli.d.ts +10 -6
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/dev.d.ts +5 -1
- package/dist/commands/dev.js +82 -13
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/generate.d.ts +20 -0
- package/dist/commands/generate.js +100 -2
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/reload.d.ts +10 -0
- package/dist/commands/reload.js +82 -0
- package/dist/commands/reload.js.map +1 -0
- package/dist/commands/upgrade.js +6 -0
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/editor/journal.js +2 -2
- package/dist/editor/reload-bus.d.ts +38 -0
- package/dist/editor/reload-bus.js +66 -0
- package/dist/editor/reload-bus.js.map +1 -0
- package/dist/editor/server.d.ts +7 -0
- package/dist/editor/server.js +51 -9
- package/dist/editor/server.js.map +1 -1
- package/dist/editor/shell-page.d.ts +24 -5
- package/dist/editor/shell-page.js +295 -36
- package/dist/editor/shell-page.js.map +1 -1
- package/dist/editor/watch.d.ts +101 -0
- package/dist/editor/watch.js +228 -0
- package/dist/editor/watch.js.map +1 -0
- package/dist/generate/stream.d.ts +16 -3
- package/dist/generate/stream.js +22 -1
- package/dist/generate/stream.js.map +1 -1
- package/dist/generate/vehicle.d.ts +53 -0
- package/dist/generate/vehicle.js +227 -0
- package/dist/generate/vehicle.js.map +1 -0
- package/dist/local-port.d.ts +10 -0
- package/dist/local-port.js +23 -0
- package/dist/local-port.js.map +1 -1
- package/dist/project/dev-handle.d.ts +14 -0
- package/dist/project/dev-handle.js +53 -0
- package/dist/project/dev-handle.js.map +1 -0
- package/dist/scaffold/claude-settings.d.ts +41 -0
- package/dist/scaffold/claude-settings.js +98 -0
- package/dist/scaffold/claude-settings.js.map +1 -0
- package/dist/scaffold/project-files.js +36 -10
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/scaffold/project.js +5 -0
- package/dist/scaffold/project.js.map +1 -1
- package/dist/scaffold/upgrade-project.d.ts +6 -0
- package/dist/scaffold/upgrade-project.js +19 -1
- package/dist/scaffold/upgrade-project.js.map +1 -1
- package/dist/verify/browser.js +3 -1
- package/dist/verify/browser.js.map +1 -1
- package/package.json +1 -1
- package/dist/commands/edit.d.ts +0 -10
- package/dist/commands/edit.js +0 -148
- package/dist/commands/edit.js.map +0 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ bitmagic init my-game # scaffold a new project into ./my-game
|
|
|
20
20
|
cd my-game
|
|
21
21
|
bitmagic cover # generate cover art from GAME-DESIGN.md
|
|
22
22
|
bitmagic check # typecheck against the vendored engine
|
|
23
|
-
bitmagic dev # build, then serve
|
|
23
|
+
bitmagic dev # build, then serve the game + editor, and leave it running
|
|
24
24
|
bitmagic verify # boot the game in a real browser and report what broke
|
|
25
25
|
bitmagic publish # verify-gate, build, and ship — private by default
|
|
26
26
|
```
|
|
@@ -34,13 +34,13 @@ bitmagic publish # verify-gate, build, and ship — private by defa
|
|
|
34
34
|
| `bitmagic whoami [--env <env>]` | Show the identity the CLI is authenticated as. |
|
|
35
35
|
| `bitmagic init [dir] [--template <id>] [--name <name>] [--idea "<pitch>"] [--env <env>] [--force]` | Scaffold a new project: mints a game, downloads the vendored engine, writes `AGENTS.md`, `GAME-DESIGN.md` and templates. `--idea` seeds the design doc's pitch. |
|
|
36
36
|
| `bitmagic check` | Typecheck (`tsc --noEmit`) against the vendored engine. Fast; does not prove the game runs. |
|
|
37
|
-
| `bitmagic dev [--port <port>]` | Build, then serve
|
|
38
|
-
| `bitmagic
|
|
37
|
+
| `bitmagic dev [--port <port>] [--editor-port <port>]` | Build, then serve everything and keep it running: `tsc --watch`, `vite` on 3010, and a **Game / Editor** view on 3011 that reloads itself when the project changes. The one command to leave open. See **The dev view** below. |
|
|
38
|
+
| `bitmagic reload [--port <port>]` | Tell a running `bitmagic dev` to reload the browser now. For agents: run it when you finish a round of edits. Exits 0 and does nothing when no dev server is running. |
|
|
39
39
|
| `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). |
|
|
40
40
|
| `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. |
|
|
41
41
|
| `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.** |
|
|
42
42
|
| `bitmagic upgrade [--engine <version>] [--force]` | Refresh vendored platform files and the CLI's own skills to the currently published engine. Refuses a dirty git tree unless `--force`. |
|
|
43
|
-
| `bitmagic generate <skybox\|sound\|image\|character\|animation\|prop> ...` | Generate one asset directly into `src/work/world.json`. Costs sparks. `prop` upgrades an existing placeholder asset in place — see below. |
|
|
43
|
+
| `bitmagic generate <skybox\|sound\|image\|character\|animation\|prop\|vehicle> ...` | Generate one asset directly into `src/work/world.json`. Costs sparks. `prop` upgrades an existing placeholder asset in place, `vehicle` adds a new drivable one — see below. |
|
|
44
44
|
| `bitmagic cover [--prompt "..."] [--force] [--no-start-screen]` | Generate the game's cover art from `GAME-DESIGN.md`: sets the start screen, saves `.bitmagic/cover.webp`, becomes the publish thumbnail. Costs sparks. |
|
|
45
45
|
| `bitmagic forge --prompt "..." [--city\|--dungeon\|--platformer\|--freeform] [--name <name>] [--resume <jobId>]` | Design and bake a whole playable level into `world.json`. Slow and expensive; supports `--resume`. |
|
|
46
46
|
|
|
@@ -60,10 +60,14 @@ my-game/
|
|
|
60
60
|
engine/ # vendored engine, read-only, replaced wholesale by `bitmagic upgrade`
|
|
61
61
|
AGENTS.md # guidance for the agent editing this project
|
|
62
62
|
GAME-DESIGN.md # what the game is — yours to write; bitmagic cover reads it
|
|
63
|
+
.claude/
|
|
64
|
+
settings.json # yours; seeded with a Stop hook that runs `bitmagic reload`
|
|
65
|
+
skills/ # generating-assets is shipped by the CLI; your own are untouched
|
|
63
66
|
.bitmagic/
|
|
64
67
|
cover.webp # bitmagic cover's artwork — the publish thumbnail when present
|
|
65
68
|
cover.json # its URL and the design hash it was generated from
|
|
66
69
|
AGENTS.md.latest # the current AGENTS.md template, when upgrade kept your edited one
|
|
70
|
+
dev.json # the running `bitmagic dev`'s ports, so `bitmagic reload` finds it
|
|
67
71
|
build/
|
|
68
72
|
index.html # bitmagic build's output bundle
|
|
69
73
|
manifest.json # engineVersion, fingerprint, bytes, sha256, builtAt
|
|
@@ -89,33 +93,59 @@ Re-running on an unchanged design generates nothing and costs nothing — it rep
|
|
|
89
93
|
cover. Edit the design and run again, or pass `--force`, to replace it. Because it writes
|
|
90
94
|
`world.json`, it invalidates a passing verify: run it before `bitmagic verify`, not after.
|
|
91
95
|
|
|
92
|
-
##
|
|
96
|
+
## The dev view
|
|
93
97
|
|
|
94
|
-
|
|
95
|
-
local editor on a second port:
|
|
98
|
+
`bitmagic dev` is meant to be started once and left running, beside your agent:
|
|
96
99
|
|
|
97
100
|
```bash
|
|
98
|
-
bitmagic
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
+
bitmagic dev
|
|
102
|
+
# Open http://localhost:3011/ ← game + editor
|
|
103
|
+
#
|
|
104
|
+
# Game only http://localhost:3010/
|
|
105
|
+
# Editor actions .bitmagic/edit/events.jsonl
|
|
101
106
|
```
|
|
102
107
|
|
|
103
|
-
Open the
|
|
104
|
-
|
|
105
|
-
inside `engine/`. Click an object to select it, then move, rotate, scale, snap it to the ground,
|
|
106
|
-
edit its numbers in the inspector, delete it, or right-click to add one from the project's assets.
|
|
108
|
+
Open the 3011 URL in your normal browser and put it beside your agent's window. It is one page with
|
|
109
|
+
two tabs over the same running game, so switching costs nothing and does not restart anything:
|
|
107
110
|
|
|
108
|
-
**
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
- **Game** — your game, playing. Click into it to capture the mouse, exactly as a player would.
|
|
112
|
+
- **Editor** — the same world, paused, with a free-flying camera, the scene hierarchy and the
|
|
113
|
+
transform gizmo. This is the editor the web Creator uses, which already ships inside `engine/`.
|
|
114
|
+
Click an object to select it, then move, rotate, scale, snap it to the ground, edit its numbers in
|
|
115
|
+
the inspector, delete it, or right-click to add one from the project's assets.
|
|
111
116
|
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
3010 still serves the game on its own, for a second window or a bookmark. Both ports must stay
|
|
118
|
+
inside 3000–3199: the asset CDN allowlists only those origins, and outside that window the world
|
|
119
|
+
loads with no terrain.
|
|
120
|
+
|
|
121
|
+
**Every change in the Editor tab is saved to `src/work/world.json` as you make it.** There is no
|
|
122
|
+
Save button and no unsaved state: your agent reads the same file, and `git diff` is the record of
|
|
123
|
+
what you moved. Saves are surgical — one entry per object touched, nothing else in the file is
|
|
124
|
+
rewritten.
|
|
125
|
+
|
|
126
|
+
### Auto-reload
|
|
127
|
+
|
|
128
|
+
The browser reloads itself when the project changes, so you watch your agent's work appear instead
|
|
129
|
+
of reaching for F5. Three things drive it:
|
|
130
|
+
|
|
131
|
+
- **The file watcher.** `src/` and `dist/` are watched, and a reload fires once the writes go quiet.
|
|
132
|
+
- **The build gate.** A reload waits for `tsc --watch` to finish and land clean. If the build fails
|
|
133
|
+
the reload is dropped and the page says so — reloading would show you the previous build's code
|
|
134
|
+
running against your new source, which is the most confusing outcome available.
|
|
135
|
+
- **`bitmagic reload`.** The watcher has to guess when an agent is finished, because an agent pauses
|
|
136
|
+
to think. This command replaces the guess. `bitmagic init` wires it to Claude Code's `Stop` hook in
|
|
137
|
+
`.claude/settings.json`, so Claude fires it at the end of every turn; any other agent can run it
|
|
138
|
+
directly. `bitmagic upgrade` adds the hook to an existing project without touching anything else in
|
|
139
|
+
that file.
|
|
140
|
+
|
|
141
|
+
Turn it off with the **Auto-reload** checkbox in the top bar — the setting sticks, and you get the
|
|
142
|
+
old "changed on disk, click to reload" banner instead. It also falls back to the banner on its own
|
|
143
|
+
while you are mid-edit in the Editor tab, rather than reloading a drag away.
|
|
114
144
|
|
|
115
145
|
### What your agent sees
|
|
116
146
|
|
|
117
147
|
Every editor action is appended to `.bitmagic/edit/events.jsonl`, one JSON object per line, and
|
|
118
|
-
printed to the command's own output — so the agent that launched `bitmagic
|
|
148
|
+
printed to the command's own output — so the agent that launched `bitmagic dev` can tell what you
|
|
119
149
|
did without inferring it from a diff:
|
|
120
150
|
|
|
121
151
|
```
|
|
@@ -151,6 +181,35 @@ engine's terrain tools, but saving them needs an asset upload this command does
|
|
|
151
181
|
editor warns you as soon as you change a voxel and offers to discard it. Use the web Creator for
|
|
152
182
|
terrain work.
|
|
153
183
|
|
|
184
|
+
## Drivable vehicles
|
|
185
|
+
|
|
186
|
+
`bitmagic generate vehicle` designs a vehicle and registers it as a **new** asset — unlike
|
|
187
|
+
`generate prop`, which upgrades an existing placeholder in place. An AI designer authors the shape,
|
|
188
|
+
paint and wrap art (including generated side art like "a poodle mural on a grooming van"); a
|
|
189
|
+
parametric builder produces the model; the engine voxelizes it and derives its physics fitment.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
bitmagic generate vehicle --prompt "a rusty orange pickup with flame decals"
|
|
193
|
+
bitmagic generate vehicle --preset VAN # a standard vehicle: skips the designer, free
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Then spawn it from game code by name:
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
engine.getVehicleSpawner().spawnAndEnterFromAsset({ x: 0, z: 0 }, '<assetName>', playerController);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Like `prop` it needs Chrome and a free port between 3000 and 3199 — only the engine can voxelize,
|
|
203
|
+
so the CLI drives its own headless browser. Headlights, taillights and emergency beacons are built
|
|
204
|
+
and lit automatically; there is no need to ask for them.
|
|
205
|
+
|
|
206
|
+
Costs sparks based on what the designer actually spends, so a retrying design costs more than a
|
|
207
|
+
clean one and `--preset` costs nothing. If it fails after the model exists, the error prints the
|
|
208
|
+
exact `--glb-url … --voxel-size …` command to retry the browser step without paying again.
|
|
209
|
+
|
|
210
|
+
If the finished asset reports that it will not drive, the project's vendored engine predates
|
|
211
|
+
vehicle-fitment derivation: run `bitmagic upgrade`, then regenerate.
|
|
212
|
+
|
|
154
213
|
## Publishing
|
|
155
214
|
|
|
156
215
|
`bitmagic publish` is two gated steps in one command:
|
|
@@ -209,6 +268,10 @@ its own reviewable diff.
|
|
|
209
268
|
What is yours and never touched: `src/`, `world.json`, `game.json`, `package.json`, `CLAUDE.md`,
|
|
210
269
|
`GAME-DESIGN.md`, `.gitignore`, and any skill you wrote yourself.
|
|
211
270
|
|
|
271
|
+
`.claude/settings.json` is merged rather than rewritten — it is where your permissions and your own
|
|
272
|
+
hooks live. An upgrade adds the `bitmagic reload` Stop hook if it is missing and changes nothing
|
|
273
|
+
else; if it cannot parse the file it leaves it entirely alone and says so.
|
|
274
|
+
|
|
212
275
|
`AGENTS.md` is the one conditional case, and it can never lose your work. The CLI records a hash of
|
|
213
276
|
the `AGENTS.md` it writes. On a later upgrade:
|
|
214
277
|
|
package/dist/cli.d.ts
CHANGED
|
@@ -92,17 +92,21 @@ declare const rawCommands: {
|
|
|
92
92
|
dev: CommandDef<{
|
|
93
93
|
readonly port: {
|
|
94
94
|
readonly type: "string";
|
|
95
|
-
readonly description: "Port to serve on (default 3010).";
|
|
95
|
+
readonly description: "Port to serve the game on (default 3010).";
|
|
96
|
+
};
|
|
97
|
+
readonly 'editor-port': {
|
|
98
|
+
readonly type: "string";
|
|
99
|
+
readonly description: "Port to serve the Game/Editor view on (default 3011).";
|
|
96
100
|
};
|
|
97
101
|
}>;
|
|
98
|
-
|
|
102
|
+
reload: CommandDef<{
|
|
99
103
|
readonly port: {
|
|
100
104
|
readonly type: "string";
|
|
101
|
-
readonly description: "Port
|
|
105
|
+
readonly description: "Port the dev view is on. Defaults to the running server, then 3011.";
|
|
102
106
|
};
|
|
103
|
-
readonly
|
|
104
|
-
readonly type: "
|
|
105
|
-
readonly description: "
|
|
107
|
+
readonly json: {
|
|
108
|
+
readonly type: "boolean";
|
|
109
|
+
readonly description: "Print the result as JSON on stdout; all progress goes to stderr.";
|
|
106
110
|
};
|
|
107
111
|
}>;
|
|
108
112
|
verify: CommandDef<{
|
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import { initCommand } from './commands/init.js';
|
|
|
8
8
|
import { upgradeCommand } from './commands/upgrade.js';
|
|
9
9
|
import { checkCommand } from './commands/check.js';
|
|
10
10
|
import { devCommand } from './commands/dev.js';
|
|
11
|
-
import {
|
|
11
|
+
import { reloadCommand } from './commands/reload.js';
|
|
12
12
|
import { verifyCommand } from './commands/verify.js';
|
|
13
13
|
import { buildCommand } from './commands/build.js';
|
|
14
14
|
import { generateCommand } from './commands/generate.js';
|
|
@@ -103,7 +103,7 @@ const rawCommands = {
|
|
|
103
103
|
upgrade: upgradeCommand,
|
|
104
104
|
check: checkCommand,
|
|
105
105
|
dev: devCommand,
|
|
106
|
-
|
|
106
|
+
reload: reloadCommand,
|
|
107
107
|
verify: verifyCommand,
|
|
108
108
|
build: buildCommand,
|
|
109
109
|
generate: generateCommand,
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/dev.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export declare const devCommand: import("citty").CommandDef<{
|
|
2
2
|
readonly port: {
|
|
3
3
|
readonly type: "string";
|
|
4
|
-
readonly description: "Port to serve on (default 3010).";
|
|
4
|
+
readonly description: "Port to serve the game on (default 3010).";
|
|
5
|
+
};
|
|
6
|
+
readonly 'editor-port': {
|
|
7
|
+
readonly type: "string";
|
|
8
|
+
readonly description: "Port to serve the Game/Editor view on (default 3011).";
|
|
5
9
|
};
|
|
6
10
|
}>;
|
package/dist/commands/dev.js
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bitmagic dev` — the whole local loop, in one command that stays running.
|
|
3
|
+
*
|
|
4
|
+
* It builds, watches, serves the game, serves a shell page with a **Game** tab and an **Editor**
|
|
5
|
+
* tab, and reloads the browser when the project changes. There is deliberately no second command:
|
|
6
|
+
* the workflow this exists for is an agent window beside a browser window, and a creator should
|
|
7
|
+
* never have to stop one server and start another to go from playing their game to nudging an
|
|
8
|
+
* object in it. (`bitmagic edit` was that second command, and folding it in is why it is gone.)
|
|
9
|
+
*
|
|
10
|
+
* Four processes make that up, all owned here:
|
|
11
|
+
* tsc --watch piped, not inherited — its `Found N errors` line is the only signal that tells
|
|
12
|
+
* auto-reload whether the emit in dist/ is worth showing (see editor/watch.ts)
|
|
13
|
+
* vite serves the project at --port
|
|
14
|
+
* the sidecar serves the shell and the scene-save API at --editor-port (see editor/server.ts)
|
|
15
|
+
* the watcher src/ and dist/, debounced, feeding the same coordinator as tsc
|
|
16
|
+
*/
|
|
1
17
|
import { defineCommand } from 'citty';
|
|
2
18
|
import { spawn, spawnSync } from 'child_process';
|
|
19
|
+
import * as path from 'path';
|
|
3
20
|
import { CliError } from '../errors.js';
|
|
4
21
|
import { loadProjectContext, projectBin } from '../project/context.js';
|
|
22
|
+
import { removeDevHandle, writeDevHandle } from '../project/dev-handle.js';
|
|
23
|
+
import { startEditorServer } from '../editor/server.js';
|
|
24
|
+
import { ReloadBus } from '../editor/reload-bus.js';
|
|
25
|
+
import { ReloadCoordinator, pipeTscWatch, watchProject } from '../editor/watch.js';
|
|
26
|
+
import { parseLocalPort } from '../local-port.js';
|
|
5
27
|
import { waitForServer } from '../verify/wait-for-server.js';
|
|
6
28
|
import { engineUpdateNotice } from '../project/engine-update.js';
|
|
7
29
|
/**
|
|
@@ -34,17 +56,29 @@ async function notifyIfEngineOutdated(projectEngineVersion, log) {
|
|
|
34
56
|
// See above: no login, no network, or no manifest all mean "no hint", never "dev failed".
|
|
35
57
|
}
|
|
36
58
|
}
|
|
37
|
-
const
|
|
59
|
+
const DEFAULT_GAME_PORT = 3010;
|
|
60
|
+
const DEFAULT_EDITOR_PORT = 3011;
|
|
38
61
|
export const devCommand = defineCommand({
|
|
39
|
-
meta: {
|
|
62
|
+
meta: {
|
|
63
|
+
name: 'dev',
|
|
64
|
+
description: 'Build the game, then serve it with live rebuilds, a scene editor and auto-reload.',
|
|
65
|
+
},
|
|
40
66
|
args: {
|
|
41
|
-
port: { type: 'string', description: `Port to serve on (default ${
|
|
67
|
+
port: { type: 'string', description: `Port to serve the game on (default ${DEFAULT_GAME_PORT}).` },
|
|
68
|
+
'editor-port': {
|
|
69
|
+
type: 'string',
|
|
70
|
+
description: `Port to serve the Game/Editor view on (default ${DEFAULT_EDITOR_PORT}).`,
|
|
71
|
+
},
|
|
42
72
|
},
|
|
43
73
|
async run({ args }) {
|
|
44
74
|
const context = loadProjectContext();
|
|
45
75
|
const tsc = projectBin(context.root, 'tsc');
|
|
46
76
|
const vite = projectBin(context.root, 'vite');
|
|
47
|
-
const
|
|
77
|
+
const gamePort = parseLocalPort(args.port, DEFAULT_GAME_PORT, '--port');
|
|
78
|
+
const editorPort = parseLocalPort(args['editor-port'], DEFAULT_EDITOR_PORT, '--editor-port');
|
|
79
|
+
if (gamePort === editorPort) {
|
|
80
|
+
throw new CliError('`--port` and `--editor-port` must differ — the game and the shell are two servers.');
|
|
81
|
+
}
|
|
48
82
|
// Build to completion first. Every alias resolves into dist/, so serving before the first
|
|
49
83
|
// emit 404s every module — a failure that reads as "the engine is broken", not "not built".
|
|
50
84
|
console.log('Building…');
|
|
@@ -57,16 +91,33 @@ export const devCommand = defineCommand({
|
|
|
57
91
|
}
|
|
58
92
|
// Fire and forget: the nudge must never gate or delay the server coming up.
|
|
59
93
|
void notifyIfEngineOutdated(context.metadata.engineVersion, (m) => console.log(m));
|
|
94
|
+
// Bus first, then the coordinator that feeds it, then the servers that feed the coordinator.
|
|
95
|
+
const bus = new ReloadBus();
|
|
96
|
+
const coordinator = new ReloadCoordinator({
|
|
97
|
+
onReload: (reason) => bus.reload(reason),
|
|
98
|
+
onBuild: (status, message) => bus.build(status, message),
|
|
99
|
+
});
|
|
100
|
+
// Before the children, so a port clash fails fast rather than after vite has claimed its port.
|
|
101
|
+
const editor = await startEditorServer({
|
|
102
|
+
root: context.root,
|
|
103
|
+
metadata: context.metadata,
|
|
104
|
+
gameId: context.metadata.gameId,
|
|
105
|
+
gamePort,
|
|
106
|
+
port: editorPort,
|
|
107
|
+
bus,
|
|
108
|
+
coordinator,
|
|
109
|
+
log: (message) => console.log(message),
|
|
110
|
+
});
|
|
60
111
|
const children = [
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
process: spawn(tsc, ['--watch', '--preserveWatchOutput'], { cwd: context.root, stdio: 'inherit' }),
|
|
64
|
-
},
|
|
112
|
+
// Piped rather than inherited — see the file header and editor/watch.ts.
|
|
113
|
+
{ name: 'tsc --watch', process: pipeTscWatch(tsc, context.root, coordinator) },
|
|
65
114
|
{
|
|
66
115
|
name: 'vite',
|
|
67
|
-
process: spawn(vite, ['--port',
|
|
116
|
+
process: spawn(vite, ['--port', String(gamePort), '--strictPort'], { cwd: context.root, stdio: 'inherit' }),
|
|
68
117
|
},
|
|
69
118
|
];
|
|
119
|
+
const watcher = watchProject(context.root, coordinator, (m) => console.log(m));
|
|
120
|
+
writeDevHandle(context.root, { editorPort, gamePort, pid: process.pid });
|
|
70
121
|
let shuttingDown = false;
|
|
71
122
|
let requestedByUser = false;
|
|
72
123
|
const shutdown = () => {
|
|
@@ -75,6 +126,11 @@ export const devCommand = defineCommand({
|
|
|
75
126
|
shuttingDown = true;
|
|
76
127
|
for (const child of children)
|
|
77
128
|
child.process.kill('SIGTERM');
|
|
129
|
+
coordinator.stop();
|
|
130
|
+
watcher.close();
|
|
131
|
+
bus.close();
|
|
132
|
+
removeDevHandle(context.root);
|
|
133
|
+
void editor.close();
|
|
78
134
|
};
|
|
79
135
|
process.on('SIGINT', () => {
|
|
80
136
|
requestedByUser = true;
|
|
@@ -84,10 +140,20 @@ export const devCommand = defineCommand({
|
|
|
84
140
|
requestedByUser = true;
|
|
85
141
|
shutdown();
|
|
86
142
|
});
|
|
87
|
-
// Print the
|
|
88
|
-
// startup banner — that output is vite's to change, this is ours to keep stable.
|
|
89
|
-
|
|
90
|
-
|
|
143
|
+
// Print the URLs ourselves once vite actually answers, rather than relying on vite's own
|
|
144
|
+
// startup banner — that output is vite's to change, this is ours to keep stable. The shell URL
|
|
145
|
+
// leads because it is the one thing to open: the raw game below it is the same game without
|
|
146
|
+
// the tabs, kept addressable for a second window or a bookmark.
|
|
147
|
+
void waitForServer(gamePort, 30_000)
|
|
148
|
+
.then(() => {
|
|
149
|
+
console.log(`\n Open ${editor.url} ← game + editor`);
|
|
150
|
+
console.log(`\n Game only http://localhost:${gamePort}/`);
|
|
151
|
+
// Named on startup so the agent that launched this command knows where to look without
|
|
152
|
+
// being told — the journal is the only record of intentions the editor cannot carry out
|
|
153
|
+
// itself, and of who changed what.
|
|
154
|
+
console.log(` Editor actions ${path.relative(context.root, editor.journalPath)}`);
|
|
155
|
+
console.log('\nThe browser reloads itself when the project changes. `bitmagic reload` forces it.\n');
|
|
156
|
+
})
|
|
91
157
|
.catch(() => {
|
|
92
158
|
// The child-exit watcher below reports a vite that never comes up; this poll simply has
|
|
93
159
|
// nothing to announce in that case.
|
|
@@ -114,6 +180,9 @@ export const devCommand = defineCommand({
|
|
|
114
180
|
});
|
|
115
181
|
}
|
|
116
182
|
});
|
|
183
|
+
// Both children are gone by here — via a signal handler or via the failure path above, each of
|
|
184
|
+
// which has already run shutdown(). This awaits the close that shutdown() could only start.
|
|
185
|
+
await editor.close();
|
|
117
186
|
if (failure) {
|
|
118
187
|
throw new CliError(`${failure.name} ${failure.detail}. \`bitmagic dev\` stopped.`);
|
|
119
188
|
}
|
package/dist/commands/dev.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,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,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE;;;;;;;;;;GAUG;AACH,KAAK,UAAU,sBAAsB,CAAC,oBAA4B,EAAE,GAAwB;IAC1F,IAAI,CAAC;QACH,MAAM,CAAC,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,sBAAsB,EAAE,EAAE,EAAE,mBAAmB,EAAE,CAAC,GAC3H,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,MAAM,CAAC,2BAA2B,CAAC;YACnC,MAAM,CAAC,oBAAoB,CAAC;YAC5B,MAAM,CAAC,0BAA0B,CAAC;YAClC,MAAM,CAAC,gCAAgC,CAAC;SACzC,CAAC,CAAC;QACL,MAAM,WAAW,GAAG,kBAAkB,CAAC,EAAE,aAAa,EAAE,sBAAsB,EAAE,EAAE,CAAC,CAAC;QACpF,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,WAAW,EAAE,oBAAoB,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QACpL,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,WAAW,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC;QAC5F,MAAM,MAAM,GAAG,kBAAkB,CAAC,oBAAoB,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC1E,IAAI,MAAM,KAAK,IAAI;YAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,0FAA0F;IAC5F,CAAC;AACH,CAAC;AAED,MAAM,
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type GenerationOutcome } from '../generate/stream.js';
|
|
2
2
|
import { type PropVoxelizeResult } from '../generate/prop.js';
|
|
3
|
+
import { type VehicleInstallResult } from '../generate/vehicle.js';
|
|
3
4
|
export interface SkyboxArgs {
|
|
4
5
|
description: string;
|
|
5
6
|
referenceImageUrl?: string;
|
|
@@ -84,4 +85,23 @@ export declare function runPropGeneration(options: {
|
|
|
84
85
|
json: boolean;
|
|
85
86
|
deps?: GenerateRunDeps;
|
|
86
87
|
}): Promise<PropVoxelizeResult>;
|
|
88
|
+
/**
|
|
89
|
+
* `generate vehicle`, like `generate prop`, cannot go through {@link runAssetGeneration}: the
|
|
90
|
+
* generator returns a GLB rather than patches, so there is nothing to apply until the CLI has
|
|
91
|
+
* voxelized it in a browser.
|
|
92
|
+
*
|
|
93
|
+
* Unlike prop there is nothing to validate up front — a vehicle creates a NEW asset, so there is no
|
|
94
|
+
* existing one to look up. The one thing that would waste a paid design is asking for neither a
|
|
95
|
+
* prompt nor a preset, which is refused here before anything is spent.
|
|
96
|
+
*/
|
|
97
|
+
export declare function runVehicleGeneration(options: {
|
|
98
|
+
prompt?: string;
|
|
99
|
+
presetName?: string;
|
|
100
|
+
name?: string;
|
|
101
|
+
/** Skip the paid design and voxelize this GLB instead — how a failed run is retried. */
|
|
102
|
+
glbUrl?: string;
|
|
103
|
+
minVoxelSize?: number;
|
|
104
|
+
json: boolean;
|
|
105
|
+
deps?: GenerateRunDeps;
|
|
106
|
+
}): Promise<VehicleInstallResult>;
|
|
87
107
|
export declare const generateCommand: import("citty").CommandDef<import("citty").ArgsDef>;
|
|
@@ -10,6 +10,7 @@ import { getAccessToken } from '../auth/session.js';
|
|
|
10
10
|
import { requestGeneration } from '../generate/stream.js';
|
|
11
11
|
import { applyPatchesToWorld } from '../generate/apply-patches.js';
|
|
12
12
|
import { generateAndInstallProp } from '../generate/prop.js';
|
|
13
|
+
import { generateAndInstallVehicle } from '../generate/vehicle.js';
|
|
13
14
|
function parseNumberFlag(flagName, raw, options = {}) {
|
|
14
15
|
if (raw === undefined)
|
|
15
16
|
return undefined;
|
|
@@ -181,7 +182,7 @@ export async function runAssetGeneration(type, label, body, json = false, deps)
|
|
|
181
182
|
}
|
|
182
183
|
runDeps.log(`world.json updated (${applyResult.applied} patch${applyResult.applied === 1 ? '' : 'es'}).`);
|
|
183
184
|
output.result({ ok: true, kind: label, message: outcome.message, patchesApplied: applyResult.applied });
|
|
184
|
-
runDeps.log('
|
|
185
|
+
runDeps.log('A running `bitmagic dev` reloads the browser by itself to show the change.');
|
|
185
186
|
return outcome;
|
|
186
187
|
}
|
|
187
188
|
/**
|
|
@@ -217,7 +218,60 @@ export async function runPropGeneration(options) {
|
|
|
217
218
|
// `result` already carries glbUrl, so a retry after a later failure can find the paid-for mesh
|
|
218
219
|
// in the JSON document rather than only in the error text.
|
|
219
220
|
output.result({ ok: true, kind: 'prop', ...result });
|
|
220
|
-
runDeps.log('
|
|
221
|
+
runDeps.log('A running `bitmagic dev` reloads the browser by itself to show it.');
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* `generate vehicle`, like `generate prop`, cannot go through {@link runAssetGeneration}: the
|
|
226
|
+
* generator returns a GLB rather than patches, so there is nothing to apply until the CLI has
|
|
227
|
+
* voxelized it in a browser.
|
|
228
|
+
*
|
|
229
|
+
* Unlike prop there is nothing to validate up front — a vehicle creates a NEW asset, so there is no
|
|
230
|
+
* existing one to look up. The one thing that would waste a paid design is asking for neither a
|
|
231
|
+
* prompt nor a preset, which is refused here before anything is spent.
|
|
232
|
+
*/
|
|
233
|
+
export async function runVehicleGeneration(options) {
|
|
234
|
+
if (options.prompt === undefined && options.presetName === undefined && options.glbUrl === undefined) {
|
|
235
|
+
throw new CliError('Provide --prompt to design a vehicle, or --preset to build a standard one. '
|
|
236
|
+
+ 'Run `bitmagic generate vehicle --preset ?` to see the preset names.');
|
|
237
|
+
}
|
|
238
|
+
const output = createOutput(options.json);
|
|
239
|
+
const runDeps = options.deps ?? defaultDeps(options.json);
|
|
240
|
+
const context = loadProjectContext(runDeps.startDir);
|
|
241
|
+
const environment = resolveEnvironment({ storedDefault: readDefaultEnvironment(runDeps.baseDir) });
|
|
242
|
+
const token = await getAccessToken(environment, resolveAuth0ClientId(environment), { fetch: runDeps.fetch, sleep: runDeps.sleep, now: runDeps.now }, runDeps.baseDir);
|
|
243
|
+
let result;
|
|
244
|
+
try {
|
|
245
|
+
result = await generateAndInstallVehicle({
|
|
246
|
+
context,
|
|
247
|
+
environment,
|
|
248
|
+
token,
|
|
249
|
+
...(options.prompt !== undefined ? { prompt: options.prompt } : {}),
|
|
250
|
+
...(options.presetName !== undefined ? { presetName: options.presetName } : {}),
|
|
251
|
+
...(options.name !== undefined ? { name: options.name } : {}),
|
|
252
|
+
...(options.glbUrl !== undefined ? { glbUrl: options.glbUrl } : {}),
|
|
253
|
+
...(options.minVoxelSize !== undefined ? { minVoxelSize: options.minVoxelSize } : {}),
|
|
254
|
+
fetchImpl: runDeps.fetch,
|
|
255
|
+
log: runDeps.log,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
throw decoratedError(error, (message) => message);
|
|
260
|
+
}
|
|
261
|
+
for (const note of result.notes)
|
|
262
|
+
runDeps.log(`note: ${note}`);
|
|
263
|
+
if (result.drivable) {
|
|
264
|
+
runDeps.log(`Added "${result.assetName}". Spawn it from game code with:\n`
|
|
265
|
+
+ ` engine.getVehicleSpawner().spawnAndEnterFromAsset({ x: 0, z: 0 }, `
|
|
266
|
+
+ `'${result.assetName}', playerController);`);
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
runDeps.log(`Added "${result.assetName}", but it will not drive — see the note above.`);
|
|
270
|
+
}
|
|
271
|
+
// `result` already carries glbUrl, so a retry after a later failure can find the paid-for vehicle
|
|
272
|
+
// in the JSON document rather than only in the error text.
|
|
273
|
+
output.result({ ok: true, kind: 'vehicle', ...result });
|
|
274
|
+
runDeps.log('A running `bitmagic dev` reloads the browser by itself to show it.');
|
|
221
275
|
return result;
|
|
222
276
|
}
|
|
223
277
|
// ============================================================================
|
|
@@ -455,6 +509,49 @@ const propCommand = defineCommand({
|
|
|
455
509
|
});
|
|
456
510
|
},
|
|
457
511
|
});
|
|
512
|
+
const vehicleCommand = defineCommand({
|
|
513
|
+
meta: {
|
|
514
|
+
name: 'vehicle',
|
|
515
|
+
description: 'Design a drivable vehicle and register it as a new asset.',
|
|
516
|
+
},
|
|
517
|
+
args: {
|
|
518
|
+
prompt: {
|
|
519
|
+
type: 'string',
|
|
520
|
+
description: 'What to design ("a rusty orange pickup with flame decals"). Required unless --preset is given.',
|
|
521
|
+
},
|
|
522
|
+
preset: {
|
|
523
|
+
type: 'string',
|
|
524
|
+
description: 'Build a standard vehicle instead of designing one — faster and free. Run without it to see the valid names.',
|
|
525
|
+
},
|
|
526
|
+
name: {
|
|
527
|
+
type: 'string',
|
|
528
|
+
description: 'Asset name override; defaults to the designed name. This is the name game code spawns by.',
|
|
529
|
+
},
|
|
530
|
+
'glb-url': {
|
|
531
|
+
type: 'string',
|
|
532
|
+
description: 'Voxelize this existing vehicle instead of designing one — how to retry for free. Needs --voxel-size and --name.',
|
|
533
|
+
},
|
|
534
|
+
'voxel-size': {
|
|
535
|
+
type: 'string',
|
|
536
|
+
description: 'Bake resolution, only with --glb-url. The failed run prints the exact value to use.',
|
|
537
|
+
},
|
|
538
|
+
json: {
|
|
539
|
+
type: 'boolean',
|
|
540
|
+
description: 'Print the result as JSON on stdout; all progress goes to stderr.',
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
async run({ args }) {
|
|
544
|
+
const minVoxelSize = parseNumberFlag('voxel-size', args['voxel-size'], { min: 0.01, max: 1 });
|
|
545
|
+
await runVehicleGeneration({
|
|
546
|
+
...(args.prompt ? { prompt: args.prompt } : {}),
|
|
547
|
+
...(args.preset ? { presetName: args.preset } : {}),
|
|
548
|
+
...(args.name ? { name: args.name } : {}),
|
|
549
|
+
...(args['glb-url'] ? { glbUrl: args['glb-url'] } : {}),
|
|
550
|
+
...(minVoxelSize !== undefined ? { minVoxelSize } : {}),
|
|
551
|
+
json: args.json === true,
|
|
552
|
+
});
|
|
553
|
+
},
|
|
554
|
+
});
|
|
458
555
|
export const generateCommand = defineCommand({
|
|
459
556
|
meta: {
|
|
460
557
|
name: 'generate',
|
|
@@ -467,6 +564,7 @@ export const generateCommand = defineCommand({
|
|
|
467
564
|
character: characterCommand,
|
|
468
565
|
animation: animationCommand,
|
|
469
566
|
prop: propCommand,
|
|
567
|
+
vehicle: vehicleCommand,
|
|
470
568
|
},
|
|
471
569
|
});
|
|
472
570
|
//# sourceMappingURL=generate.js.map
|