@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
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deciding *when* the browser should reload.
|
|
3
|
+
*
|
|
4
|
+
* The hard part is not noticing a change — `fs.watch` does that — it is not reloading at the wrong
|
|
5
|
+
* moment. Three wrong moments, each of which this file exists to avoid:
|
|
6
|
+
*
|
|
7
|
+
* 1. **Mid-burst.** An agent editing five files produces five reloads unless the writes are let
|
|
8
|
+
* go quiet first. Hence the debounce.
|
|
9
|
+
* 2. **Mid-compile.** A `.ts` edit changes `src/` long before `tsc --watch` finishes emitting
|
|
10
|
+
* `dist/`, and every module the project loads resolves into `dist/`. Reloading on the source
|
|
11
|
+
* write shows the creator the *old* build and reads as "my change did nothing". Hence the
|
|
12
|
+
* compile gate: reloads are held while a compile is open and released when it lands clean.
|
|
13
|
+
* 3. **After a failed compile.** `dist/` still holds the last good emit, so a reload would show
|
|
14
|
+
* working code for broken source — the most confusing outcome of the three. The pending reload
|
|
15
|
+
* is dropped and the shell is told the build failed instead.
|
|
16
|
+
*
|
|
17
|
+
* `ReloadCoordinator` holds all of that and touches no I/O, so it is testable with fake timers and
|
|
18
|
+
* no filesystem. `watchProject` and `pipeTscWatch` are the thin I/O shells that feed it.
|
|
19
|
+
*/
|
|
20
|
+
import { spawn } from 'child_process';
|
|
21
|
+
import * as fs from 'fs';
|
|
22
|
+
import * as path from 'path';
|
|
23
|
+
/**
|
|
24
|
+
* How long the writes must stay quiet before a reload fires.
|
|
25
|
+
*
|
|
26
|
+
* Long enough that `tsc --watch` (which has its own ~250 ms debounce before it reports
|
|
27
|
+
* `File change detected`) has almost always opened its compile by the time this elapses, so the
|
|
28
|
+
* gate below actually gets to hold the reload. Too short and a `.ts` edit produces two reloads —
|
|
29
|
+
* one stale, one correct — instead of one.
|
|
30
|
+
*/
|
|
31
|
+
export const RELOAD_QUIET_MS = 700;
|
|
32
|
+
/**
|
|
33
|
+
* How long after one of our own writes to ignore file events.
|
|
34
|
+
*
|
|
35
|
+
* `/api/scene/save` and the HQ generation both write `world.json`, and a reload fired at the
|
|
36
|
+
* creator's own gizmo drag would throw away the drag that caused it. `fs.watch` delivers
|
|
37
|
+
* asynchronously but promptly, so this only has to outlast the delivery, not the write.
|
|
38
|
+
*/
|
|
39
|
+
export const OWN_WRITE_GRACE_MS = 400;
|
|
40
|
+
export class ReloadCoordinator {
|
|
41
|
+
options;
|
|
42
|
+
timer = null;
|
|
43
|
+
pendingReason = null;
|
|
44
|
+
compiling = false;
|
|
45
|
+
suppressUntil = 0;
|
|
46
|
+
stopped = false;
|
|
47
|
+
constructor(options) {
|
|
48
|
+
this.options = { quietMs: RELOAD_QUIET_MS, ...options };
|
|
49
|
+
}
|
|
50
|
+
/** A file under a watched directory changed. Arms — or re-arms — the quiet window. */
|
|
51
|
+
noteFileChange(reason) {
|
|
52
|
+
if (this.stopped)
|
|
53
|
+
return;
|
|
54
|
+
if (Date.now() < this.suppressUntil)
|
|
55
|
+
return;
|
|
56
|
+
this.pendingReason = reason;
|
|
57
|
+
if (this.timer)
|
|
58
|
+
clearTimeout(this.timer);
|
|
59
|
+
this.timer = setTimeout(() => {
|
|
60
|
+
this.timer = null;
|
|
61
|
+
this.release();
|
|
62
|
+
}, this.options.quietMs);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Reload as soon as it is safe to — now, or the moment the compile in flight lands clean.
|
|
66
|
+
*
|
|
67
|
+
* The wait is the whole point for `bitmagic reload`: an agent's stop hook fires immediately after
|
|
68
|
+
* its last edit, which is precisely when `tsc` is still compiling it. Returns whether the reload
|
|
69
|
+
* was queued behind a compile rather than sent, so the command can say which happened.
|
|
70
|
+
*/
|
|
71
|
+
requestNow(reason) {
|
|
72
|
+
if (this.stopped)
|
|
73
|
+
return { queued: false };
|
|
74
|
+
if (this.timer) {
|
|
75
|
+
clearTimeout(this.timer);
|
|
76
|
+
this.timer = null;
|
|
77
|
+
}
|
|
78
|
+
this.pendingReason = reason;
|
|
79
|
+
this.release();
|
|
80
|
+
return { queued: this.pendingReason !== null };
|
|
81
|
+
}
|
|
82
|
+
/** The sidecar wrote `world.json` itself; the resulting file events are not news. */
|
|
83
|
+
noteOwnWrite() {
|
|
84
|
+
this.suppressUntil = Date.now() + OWN_WRITE_GRACE_MS;
|
|
85
|
+
}
|
|
86
|
+
noteCompileStart() {
|
|
87
|
+
if (this.stopped || this.compiling)
|
|
88
|
+
return;
|
|
89
|
+
this.compiling = true;
|
|
90
|
+
this.options.onBuild('building');
|
|
91
|
+
}
|
|
92
|
+
noteCompileEnd(errorCount) {
|
|
93
|
+
if (this.stopped)
|
|
94
|
+
return;
|
|
95
|
+
this.compiling = false;
|
|
96
|
+
if (errorCount > 0) {
|
|
97
|
+
// Dropped, not deferred: `dist/` holds the last good emit, so reloading would show working
|
|
98
|
+
// code for broken source. The shell says so instead, and the next clean compile reloads.
|
|
99
|
+
this.pendingReason = null;
|
|
100
|
+
this.options.onBuild('failed', `${errorCount} TypeScript error${errorCount === 1 ? '' : 's'} — the game was not rebuilt.`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
this.options.onBuild('ok');
|
|
104
|
+
this.release();
|
|
105
|
+
}
|
|
106
|
+
stop() {
|
|
107
|
+
this.stopped = true;
|
|
108
|
+
if (this.timer)
|
|
109
|
+
clearTimeout(this.timer);
|
|
110
|
+
this.timer = null;
|
|
111
|
+
this.pendingReason = null;
|
|
112
|
+
}
|
|
113
|
+
/** Send the pending reload, unless a compile is still open — then it waits for `noteCompileEnd`. */
|
|
114
|
+
release() {
|
|
115
|
+
if (this.pendingReason === null || this.compiling)
|
|
116
|
+
return;
|
|
117
|
+
const reason = this.pendingReason;
|
|
118
|
+
this.pendingReason = null;
|
|
119
|
+
this.options.onReload(reason);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** Path segments that never mean "the project changed". */
|
|
123
|
+
const IGNORED_SEGMENTS = new Set(['node_modules', '.bitmagic', '.vite-cache', '.git']);
|
|
124
|
+
/**
|
|
125
|
+
* Editors write through temporary siblings — vim's `4913`/`~`, JetBrains' `___jb_tmp___`, an
|
|
126
|
+
* atomic-rename `.tmp`. Reloading on those means reloading on a file that no longer exists.
|
|
127
|
+
*/
|
|
128
|
+
function isIgnoredPath(relative) {
|
|
129
|
+
const segments = relative.split(path.sep);
|
|
130
|
+
if (segments.some((segment) => IGNORED_SEGMENTS.has(segment)))
|
|
131
|
+
return true;
|
|
132
|
+
const name = segments[segments.length - 1] ?? '';
|
|
133
|
+
return name.startsWith('.') || name.endsWith('~') || name.includes('___jb_') || name.endsWith('.tmp');
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Watch the two directories that decide what the browser shows: `src/` (what the agent and the
|
|
137
|
+
* creator edit — including `work/world.json`) and `dist/` (what the page actually loads).
|
|
138
|
+
*
|
|
139
|
+
* `dist/` is watched as well as `src/` even though the compile gate already covers the normal
|
|
140
|
+
* `tsc --watch` path, because it is what a build run outside this process — a manual `tsc`, an
|
|
141
|
+
* editor's own compiler — lands in, and that should reach the browser too.
|
|
142
|
+
*
|
|
143
|
+
* A directory that cannot be watched is skipped with a note rather than failing the command: a
|
|
144
|
+
* missing `dist/` means "not built yet", and losing auto-reload is a smaller loss than losing
|
|
145
|
+
* `bitmagic dev`.
|
|
146
|
+
*/
|
|
147
|
+
export function watchProject(root, coordinator, log) {
|
|
148
|
+
const watchers = [];
|
|
149
|
+
for (const dir of ['src', 'dist']) {
|
|
150
|
+
const absolute = path.join(root, dir);
|
|
151
|
+
if (!fs.existsSync(absolute))
|
|
152
|
+
continue;
|
|
153
|
+
try {
|
|
154
|
+
const watcher = fs.watch(absolute, { recursive: true }, (_event, filename) => {
|
|
155
|
+
const relative = filename === null ? '' : String(filename);
|
|
156
|
+
if (relative !== '' && isIgnoredPath(relative))
|
|
157
|
+
return;
|
|
158
|
+
coordinator.noteFileChange(path.posix.join(dir, relative.split(path.sep).join('/')));
|
|
159
|
+
});
|
|
160
|
+
watcher.on('error', (error) => {
|
|
161
|
+
log(`[dev] stopped watching ${dir}/: ${error.message}`);
|
|
162
|
+
});
|
|
163
|
+
watchers.push(watcher);
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
167
|
+
log(`[dev] could not watch ${dir}/ (${message}) — reload on change is off for it.`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
close: () => {
|
|
172
|
+
for (const watcher of watchers)
|
|
173
|
+
watcher.close();
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/** tsc colourises when it thinks it is on a terminal; the markers below must match either way. */
|
|
178
|
+
// eslint-disable-next-line no-control-regex
|
|
179
|
+
const ANSI = /\u001B\[[0-9;]*m/g;
|
|
180
|
+
const COMPILE_STARTED = /(File change detected|Starting compilation in watch mode)/;
|
|
181
|
+
const COMPILE_FINISHED = /Found (\d+) error/;
|
|
182
|
+
/**
|
|
183
|
+
* Feed one line of `tsc --watch` output to the coordinator. Exported for its test — the markers are
|
|
184
|
+
* tsc's own wording, and a version that changes them would otherwise break auto-reload silently.
|
|
185
|
+
*/
|
|
186
|
+
export function noteTscLine(line, coordinator) {
|
|
187
|
+
const plain = line.replace(ANSI, '');
|
|
188
|
+
const finished = COMPILE_FINISHED.exec(plain);
|
|
189
|
+
if (finished) {
|
|
190
|
+
coordinator.noteCompileEnd(Number(finished[1]));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (COMPILE_STARTED.test(plain))
|
|
194
|
+
coordinator.noteCompileStart();
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Spawn `tsc --watch` with its output piped through us instead of inherited.
|
|
198
|
+
*
|
|
199
|
+
* Piping is what makes the compile gate possible — the only signal that a build finished, and
|
|
200
|
+
* whether it finished clean, is tsc's own `Found N errors` line. Every line is re-echoed verbatim,
|
|
201
|
+
* so the creator sees exactly what an inherited stdio would have shown, and `--pretty` is forced
|
|
202
|
+
* back on when our own stdout is a terminal (tsc drops colour the moment it is piped).
|
|
203
|
+
*/
|
|
204
|
+
export function pipeTscWatch(tsc, cwd, coordinator) {
|
|
205
|
+
const args = ['--watch', '--preserveWatchOutput'];
|
|
206
|
+
if (process.stdout.isTTY)
|
|
207
|
+
args.push('--pretty');
|
|
208
|
+
const child = spawn(tsc, args, { cwd, stdio: ['inherit', 'pipe', 'pipe'] });
|
|
209
|
+
const consume = (stream, echo) => {
|
|
210
|
+
if (!stream)
|
|
211
|
+
return;
|
|
212
|
+
let buffered = '';
|
|
213
|
+
stream.setEncoding('utf-8');
|
|
214
|
+
stream.on('data', (chunk) => {
|
|
215
|
+
echo.write(chunk);
|
|
216
|
+
buffered += chunk;
|
|
217
|
+
const lines = buffered.split('\n');
|
|
218
|
+
// The last element is whatever came after the final newline — an incomplete line, held over.
|
|
219
|
+
buffered = lines.pop() ?? '';
|
|
220
|
+
for (const line of lines)
|
|
221
|
+
noteTscLine(line, coordinator);
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
consume(child.stdout, process.stdout);
|
|
225
|
+
consume(child.stderr, process.stderr);
|
|
226
|
+
return child;
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=watch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/editor/watch.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,11 +6,24 @@ export interface GenerationOutcome {
|
|
|
6
6
|
message: string;
|
|
7
7
|
patches: WorldPatch[];
|
|
8
8
|
/**
|
|
9
|
-
* Only the `prop`
|
|
10
|
-
* the caller voxelizes it in a browser before there is anything to write — so
|
|
11
|
-
* not `patches`, is the result that matters. Absent for every other type.
|
|
9
|
+
* Only the `prop` and `vehicle` types set this. Those generators produce a mesh rather than a
|
|
10
|
+
* finished asset — the caller voxelizes it in a browser before there is anything to write — so
|
|
11
|
+
* for them this, not `patches`, is the result that matters. Absent for every other type.
|
|
12
12
|
*/
|
|
13
13
|
glbUrl?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Only the `vehicle` type sets this: the bake settings its GLB was authored for. `minVoxelSize`
|
|
16
|
+
* is derived from the vehicle's own proportions rather than fixed, so the CLI cannot pick it —
|
|
17
|
+
* it has to come back from the server with the mesh.
|
|
18
|
+
*/
|
|
19
|
+
vehicle?: VehicleOutcome;
|
|
20
|
+
}
|
|
21
|
+
export interface VehicleOutcome {
|
|
22
|
+
name: string;
|
|
23
|
+
minVoxelSize: number;
|
|
24
|
+
maxVoxelSize: number;
|
|
25
|
+
/** Cosmetic repairs and dropped wrap layers — worth showing, never failures. */
|
|
26
|
+
notes: string[];
|
|
14
27
|
}
|
|
15
28
|
export interface StreamDeps {
|
|
16
29
|
fetch: typeof globalThis.fetch;
|
package/dist/generate/stream.js
CHANGED
|
@@ -15,18 +15,39 @@ function asProgressMessage(data) {
|
|
|
15
15
|
* `WorldPatch[]` contract is re-validated field-by-field later by
|
|
16
16
|
* `apply-patches.ts`'s `validateWorldPatch` before anything is written to disk.
|
|
17
17
|
*/
|
|
18
|
+
/**
|
|
19
|
+
* Structural, like the rest of this parser: a frame carrying a malformed `vehicle` is treated as
|
|
20
|
+
* carrying none, so a server that ever changed the shape degrades into "no bake settings" (a clear
|
|
21
|
+
* failure at the voxelize step) rather than into a NaN voxel size.
|
|
22
|
+
*/
|
|
23
|
+
function asVehicleOutcome(value) {
|
|
24
|
+
if (!isRecord(value))
|
|
25
|
+
return null;
|
|
26
|
+
const { name, minVoxelSize, maxVoxelSize, notes } = value;
|
|
27
|
+
if (typeof name !== 'string' || typeof minVoxelSize !== 'number' || typeof maxVoxelSize !== 'number') {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
name,
|
|
32
|
+
minVoxelSize,
|
|
33
|
+
maxVoxelSize,
|
|
34
|
+
notes: Array.isArray(notes) ? notes.filter((n) => typeof n === 'string') : [],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
18
37
|
function asGenerationOutcome(data) {
|
|
19
38
|
if (!isRecord(data))
|
|
20
39
|
return null;
|
|
21
|
-
const { success, message, patches, glbUrl } = data;
|
|
40
|
+
const { success, message, patches, glbUrl, vehicle } = data;
|
|
22
41
|
if (typeof success !== 'boolean' || typeof message !== 'string' || !Array.isArray(patches)) {
|
|
23
42
|
return null;
|
|
24
43
|
}
|
|
44
|
+
const vehicleOutcome = asVehicleOutcome(vehicle);
|
|
25
45
|
return {
|
|
26
46
|
success,
|
|
27
47
|
message,
|
|
28
48
|
patches: patches,
|
|
29
49
|
...(typeof glbUrl === 'string' ? { glbUrl } : {}),
|
|
50
|
+
...(vehicleOutcome ? { vehicle: vehicleOutcome } : {}),
|
|
30
51
|
};
|
|
31
52
|
}
|
|
32
53
|
async function readJsonBody(response) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/generate/stream.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAQtD,gGAAgG;AAChG,gGAAgG;AAChG,qCAAqC;AACrC,OAAO,EAAE,cAAc,EAAiB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/generate/stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Environment } from '../config/environments.js';
|
|
2
|
+
import type { ProjectContext } from '../project/context.js';
|
|
3
|
+
export interface VehicleVoxelizeOptions {
|
|
4
|
+
context: ProjectContext;
|
|
5
|
+
environment: Environment;
|
|
6
|
+
token: string;
|
|
7
|
+
/** Asset name — the name game code spawns by. */
|
|
8
|
+
name: string;
|
|
9
|
+
glbUrl: string;
|
|
10
|
+
minVoxelSize: number;
|
|
11
|
+
maxVoxelSize: number;
|
|
12
|
+
/** What the vehicle was asked for, used as the asset description when the GLB carries none. */
|
|
13
|
+
description: string;
|
|
14
|
+
log: (message: string) => void;
|
|
15
|
+
}
|
|
16
|
+
export interface GenerateAndInstallVehicleOptions {
|
|
17
|
+
context: ProjectContext;
|
|
18
|
+
environment: Environment;
|
|
19
|
+
token: string;
|
|
20
|
+
prompt?: string;
|
|
21
|
+
presetName?: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
/** Skip the paid design and voxelize this GLB instead — how a failed run is retried. */
|
|
24
|
+
glbUrl?: string;
|
|
25
|
+
/** Required alongside `glbUrl`: the bake resolution is not recoverable from the other flags. */
|
|
26
|
+
minVoxelSize?: number;
|
|
27
|
+
fetchImpl?: typeof globalThis.fetch;
|
|
28
|
+
log: (message: string) => void;
|
|
29
|
+
}
|
|
30
|
+
export interface VehicleInstallResult {
|
|
31
|
+
assetId: string;
|
|
32
|
+
assetName: string;
|
|
33
|
+
assetUrl?: string;
|
|
34
|
+
/**
|
|
35
|
+
* False when the engine registered the asset without physics fitment — it will not drive. Not an
|
|
36
|
+
* error: the asset is real and placeable, so this is reported rather than thrown.
|
|
37
|
+
*/
|
|
38
|
+
drivable: boolean;
|
|
39
|
+
/** Cosmetic repairs and dropped wrap layers from the build. */
|
|
40
|
+
notes: string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Steps 1-3. Everything the caller must resolve first (project, environment, token) is a parameter,
|
|
44
|
+
* so this stays free of CLI output plumbing and of how the token was obtained.
|
|
45
|
+
*/
|
|
46
|
+
export declare function generateAndInstallVehicle(options: GenerateAndInstallVehicleOptions): Promise<VehicleInstallResult & {
|
|
47
|
+
glbUrl: string;
|
|
48
|
+
}>;
|
|
49
|
+
/**
|
|
50
|
+
* Steps 2 and 3. Split from the command so the browser work is callable directly on a retry that
|
|
51
|
+
* already has a GLB, and so the command file stays about flags and output.
|
|
52
|
+
*/
|
|
53
|
+
export declare function voxelizeAndInstallVehicle(options: VehicleVoxelizeOptions): Promise<VehicleInstallResult>;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bitmagic generate vehicle` — design a drivable vehicle and register it as an asset.
|
|
3
|
+
*
|
|
4
|
+
* Three steps, the same shape `generate prop` uses:
|
|
5
|
+
*
|
|
6
|
+
* 1. Ask api-server to design + build the vehicle (`POST /api/cli/v1/assets/vehicle/stream`).
|
|
7
|
+
* The designer LLM and the Asset Forger credentials live there and never reach this machine.
|
|
8
|
+
* 2. Voxelize the GLB in a browser. Only the engine can do this, so the CLI drives its own
|
|
9
|
+
* headless Chrome — the same arrangement `bitmagic forge` and `generate prop` use.
|
|
10
|
+
* 3. Upsert the finished asset into `assets[]`.
|
|
11
|
+
*
|
|
12
|
+
* Where this deliberately differs from `prop`, which upgrades an EXISTING asset in place:
|
|
13
|
+
*
|
|
14
|
+
* - No asset is looked up first, and none is required. A vehicle is a NEW asset; requiring a
|
|
15
|
+
* placeholder to already exist would make the command unusable.
|
|
16
|
+
* - No `fitBox` and no `assetId` are sent. The engine derives the vehicle's physics fitment from
|
|
17
|
+
* the GLB's own `bmVehicle` scene extras and mints the id. Passing a fitBox would rescale the
|
|
18
|
+
* body off its authored ride stance and silently produce wrong physics.
|
|
19
|
+
* - The bake resolution comes from the server frame, not a constant: it is derived from the
|
|
20
|
+
* vehicle's own proportions (a semi tractor and a go-kart do not bake at the same voxel size).
|
|
21
|
+
*
|
|
22
|
+
* The GLB survives a failure in steps 2-3: it is reported in the error so a retry can be pointed at
|
|
23
|
+
* it with `--glb-url` rather than paying for the design twice.
|
|
24
|
+
*/
|
|
25
|
+
import { spawn } from 'child_process';
|
|
26
|
+
import { CliError } from '../errors.js';
|
|
27
|
+
import { applyModificationsToWorld } from '../forge/apply-modifications.js';
|
|
28
|
+
import { ForgeBrowserHost, readProjectGameData } from '../forge/browser-host.js';
|
|
29
|
+
import { projectWorldJsonPath } from '../forge/run-pipeline.js';
|
|
30
|
+
import { startUploadProxy } from '../forge/upload-proxy.js';
|
|
31
|
+
import { requestGeneration } from './stream.js';
|
|
32
|
+
import { reserveCorsAllowedPort } from '../local-port.js';
|
|
33
|
+
import { projectBin } from '../project/context.js';
|
|
34
|
+
import { waitForServer } from '../verify/wait-for-server.js';
|
|
35
|
+
/** Voxelizing a dense mesh is minutes of work in the browser, not seconds. */
|
|
36
|
+
const VOXELIZE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
37
|
+
/**
|
|
38
|
+
* A vehicle body is a closed shell around a cabin; baked hollow it reads as a shell the moment the
|
|
39
|
+
* camera clips inside, and every preset is well under the size where a solid fill gets expensive.
|
|
40
|
+
* Matches what the agent lane's vehicle pipeline has always passed.
|
|
41
|
+
*/
|
|
42
|
+
const FILL_INTERIOR = true;
|
|
43
|
+
function isRecord(value) {
|
|
44
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Steps 1-3. Everything the caller must resolve first (project, environment, token) is a parameter,
|
|
48
|
+
* so this stays free of CLI output plumbing and of how the token was obtained.
|
|
49
|
+
*/
|
|
50
|
+
export async function generateAndInstallVehicle(options) {
|
|
51
|
+
const { context, environment, token, prompt, presetName, name, log } = options;
|
|
52
|
+
if (options.glbUrl !== undefined) {
|
|
53
|
+
if (options.minVoxelSize === undefined) {
|
|
54
|
+
throw new CliError('--glb-url needs --voxel-size too: the bake resolution is derived from the vehicle\'s own '
|
|
55
|
+
+ 'proportions during the design step, so it cannot be recovered from the other flags. '
|
|
56
|
+
+ 'The failed run printed the exact command to retry with.');
|
|
57
|
+
}
|
|
58
|
+
if (name === undefined) {
|
|
59
|
+
throw new CliError('--glb-url needs --name too, so the asset can be registered under the right name.');
|
|
60
|
+
}
|
|
61
|
+
log(`Reusing the vehicle at ${options.glbUrl} — skipping the design step.`);
|
|
62
|
+
const result = await voxelizeAndInstallVehicle({
|
|
63
|
+
context,
|
|
64
|
+
environment,
|
|
65
|
+
token,
|
|
66
|
+
name,
|
|
67
|
+
glbUrl: options.glbUrl,
|
|
68
|
+
minVoxelSize: options.minVoxelSize,
|
|
69
|
+
// Unlike minVoxelSize this is a fixed LOD ceiling, so a retry can restate it.
|
|
70
|
+
maxVoxelSize: 0.5,
|
|
71
|
+
description: prompt ?? presetName ?? name,
|
|
72
|
+
log,
|
|
73
|
+
});
|
|
74
|
+
return { ...result, glbUrl: options.glbUrl };
|
|
75
|
+
}
|
|
76
|
+
const outcome = await requestGeneration(environment, token, 'vehicle', {
|
|
77
|
+
gameId: context.metadata.gameId,
|
|
78
|
+
...(prompt !== undefined ? { prompt } : {}),
|
|
79
|
+
...(presetName !== undefined ? { presetName } : {}),
|
|
80
|
+
...(name !== undefined ? { name } : {}),
|
|
81
|
+
}, { fetch: options.fetchImpl ?? globalThis.fetch, onProgress: log });
|
|
82
|
+
if (!outcome.success)
|
|
83
|
+
throw new CliError(`${outcome.message} world.json is unchanged.`);
|
|
84
|
+
if (!outcome.glbUrl || !outcome.vehicle) {
|
|
85
|
+
throw new CliError('The generator reported success but returned no vehicle mesh. world.json is unchanged.');
|
|
86
|
+
}
|
|
87
|
+
const glbUrl = outcome.glbUrl;
|
|
88
|
+
const { vehicle } = outcome;
|
|
89
|
+
log(`Vehicle built: ${glbUrl}`);
|
|
90
|
+
for (const note of vehicle.notes)
|
|
91
|
+
log(` note: ${note}`);
|
|
92
|
+
const result = await voxelizeAndInstallVehicle({
|
|
93
|
+
context,
|
|
94
|
+
environment,
|
|
95
|
+
token,
|
|
96
|
+
name: vehicle.name,
|
|
97
|
+
glbUrl,
|
|
98
|
+
minVoxelSize: vehicle.minVoxelSize,
|
|
99
|
+
maxVoxelSize: vehicle.maxVoxelSize,
|
|
100
|
+
description: prompt ?? presetName ?? vehicle.name,
|
|
101
|
+
log,
|
|
102
|
+
});
|
|
103
|
+
return { ...result, glbUrl, notes: [...vehicle.notes, ...result.notes] };
|
|
104
|
+
}
|
|
105
|
+
/** How a failed voxelize tells the caller to retry without paying for the design again. */
|
|
106
|
+
function retryHint(name, glbUrl, minVoxelSize) {
|
|
107
|
+
return `The vehicle mesh is kept — retry the browser step with:\n`
|
|
108
|
+
+ ` bitmagic generate vehicle --glb-url ${glbUrl} --voxel-size ${minVoxelSize} --name "${name}"`;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Steps 2 and 3. Split from the command so the browser work is callable directly on a retry that
|
|
112
|
+
* already has a GLB, and so the command file stays about flags and output.
|
|
113
|
+
*/
|
|
114
|
+
export async function voxelizeAndInstallVehicle(options) {
|
|
115
|
+
const { context, environment, token, name, glbUrl, minVoxelSize, maxVoxelSize, description, log } = options;
|
|
116
|
+
const worldPath = projectWorldJsonPath(context.root);
|
|
117
|
+
const port = await reserveCorsAllowedPort();
|
|
118
|
+
if (port === null) {
|
|
119
|
+
throw new CliError('No free port between 3000 and 3199, which is the only range the asset CDN serves. '
|
|
120
|
+
+ `Stop something in that range and retry.\n${retryHint(name, glbUrl, minVoxelSize)}`);
|
|
121
|
+
}
|
|
122
|
+
const vite = projectBin(context.root, 'vite');
|
|
123
|
+
let server;
|
|
124
|
+
let host;
|
|
125
|
+
let uploadProxy;
|
|
126
|
+
try {
|
|
127
|
+
server = spawn(vite, ['--port', String(port), '--strictPort'], {
|
|
128
|
+
cwd: context.root,
|
|
129
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
130
|
+
});
|
|
131
|
+
let viteStderr = '';
|
|
132
|
+
server.stderr?.on('data', (chunk) => { viteStderr += String(chunk); });
|
|
133
|
+
try {
|
|
134
|
+
await waitForServer(port, 30_000);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
throw new CliError(`The project's dev server did not come up on port ${port}, so the vehicle cannot be voxelized.`
|
|
138
|
+
+ (viteStderr.trim() ? `\n${viteStderr.trim()}` : '')
|
|
139
|
+
+ `\n${retryHint(name, glbUrl, minVoxelSize)}`);
|
|
140
|
+
}
|
|
141
|
+
// The engine uploads the baked .vxl itself, to a presigned URL it asks for at a hardcoded
|
|
142
|
+
// path with no credentials. The proxy translates that into the authenticated api-server route
|
|
143
|
+
// and keeps the token out of the page — see forge/upload-proxy.ts.
|
|
144
|
+
uploadProxy = await startUploadProxy({
|
|
145
|
+
apiUrl: environment.apiUrl,
|
|
146
|
+
token,
|
|
147
|
+
gameId: context.metadata.gameId,
|
|
148
|
+
log,
|
|
149
|
+
});
|
|
150
|
+
host = new ForgeBrowserHost({
|
|
151
|
+
gamePort: port,
|
|
152
|
+
gameId: context.metadata.gameId,
|
|
153
|
+
gameData: readProjectGameData(context.root, context.metadata),
|
|
154
|
+
agentUrl: uploadProxy.url,
|
|
155
|
+
log,
|
|
156
|
+
});
|
|
157
|
+
await host.launch();
|
|
158
|
+
await host.loadGame();
|
|
159
|
+
log(`Voxelizing "${name}" at ${minVoxelSize}m…`);
|
|
160
|
+
const result = await host.transport.sendAndWait({
|
|
161
|
+
type: 'CREATE_ASSET_FROM_GLB_URL',
|
|
162
|
+
requestId: `vehicle-${port}`,
|
|
163
|
+
name,
|
|
164
|
+
glbUrl,
|
|
165
|
+
// No `assetId`: the engine mints one, and no `fitBox`: fitment comes from the GLB's own
|
|
166
|
+
// bmVehicle extras. See this file's header for why both matter.
|
|
167
|
+
options: {
|
|
168
|
+
minVoxelSize,
|
|
169
|
+
maxVoxelSize,
|
|
170
|
+
fillInterior: FILL_INTERIOR,
|
|
171
|
+
placeholder: false,
|
|
172
|
+
},
|
|
173
|
+
}, 'ASSET_FROM_GLB_URL_RESULT', VOXELIZE_TIMEOUT_MS, { maxRetries: 1 });
|
|
174
|
+
if (!result) {
|
|
175
|
+
throw new CliError(`Voxelizing "${name}" timed out.\n${retryHint(name, glbUrl, minVoxelSize)}`);
|
|
176
|
+
}
|
|
177
|
+
if (result.success === false || !isRecord(result.asset)) {
|
|
178
|
+
const reason = typeof result.error === 'string' ? result.error : 'unknown error';
|
|
179
|
+
throw new CliError(`Voxelizing "${name}" failed: ${reason}.\n${retryHint(name, glbUrl, minVoxelSize)}`);
|
|
180
|
+
}
|
|
181
|
+
const engineAsset = result.asset;
|
|
182
|
+
const assetId = typeof engineAsset.id === 'string' ? engineAsset.id : '';
|
|
183
|
+
if (assetId === '') {
|
|
184
|
+
throw new CliError(`The engine registered "${name}" but returned no asset id, so it cannot be written to `
|
|
185
|
+
+ `world.json.\n${retryHint(name, glbUrl, minVoxelSize)}`);
|
|
186
|
+
}
|
|
187
|
+
const asset = {
|
|
188
|
+
...engineAsset,
|
|
189
|
+
description: typeof engineAsset.description === 'string' && engineAsset.description.trim() !== ''
|
|
190
|
+
? engineAsset.description
|
|
191
|
+
: description,
|
|
192
|
+
};
|
|
193
|
+
const modification = {
|
|
194
|
+
type: 'upsertRoot',
|
|
195
|
+
path: ['assets'],
|
|
196
|
+
predicate: (item) => isRecord(item) && item.id === assetId,
|
|
197
|
+
value: asset,
|
|
198
|
+
};
|
|
199
|
+
applyModificationsToWorld(worldPath, [modification]);
|
|
200
|
+
// No fitment means the asset registered but will not drive. Reported rather than thrown: the
|
|
201
|
+
// asset is real, and the usual cause is a project whose vendored engine predates fitment
|
|
202
|
+
// derivation — which `bitmagic upgrade` fixes without regenerating anything.
|
|
203
|
+
const drivable = isRecord(engineAsset.vehicleFitment);
|
|
204
|
+
const notes = drivable
|
|
205
|
+
? []
|
|
206
|
+
: ['The engine returned no vehicleFitment, so this asset will not drive. '
|
|
207
|
+
+ 'Run `bitmagic upgrade` to update the project\'s vendored engine, then regenerate.'];
|
|
208
|
+
return {
|
|
209
|
+
assetId,
|
|
210
|
+
assetName: name,
|
|
211
|
+
...(typeof asset.url === 'string' ? { assetUrl: asset.url } : {}),
|
|
212
|
+
drivable,
|
|
213
|
+
notes,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
finally {
|
|
217
|
+
// Ordered: the page first (it may still be mid-upload), then the proxy it was uploading
|
|
218
|
+
// through, then the server underneath both.
|
|
219
|
+
if (host)
|
|
220
|
+
await host.close();
|
|
221
|
+
if (uploadProxy)
|
|
222
|
+
await uploadProxy.close();
|
|
223
|
+
if (server)
|
|
224
|
+
server.kill('SIGTERM');
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=vehicle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vehicle.js","sourceRoot":"","sources":["../../src/generate/vehicle.ts"],"names":[],"mappings":"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"}
|
package/dist/local-port.d.ts
CHANGED
|
@@ -16,6 +16,16 @@
|
|
|
16
16
|
*/
|
|
17
17
|
export declare const CORS_ALLOWED_PORT_MIN = 3000;
|
|
18
18
|
export declare const CORS_ALLOWED_PORT_MAX = 3199;
|
|
19
|
+
/**
|
|
20
|
+
* A `--port`-style flag parsed and checked against the window above.
|
|
21
|
+
*
|
|
22
|
+
* Every port `bitmagic dev` binds goes through here, the game port for the reason documented above
|
|
23
|
+
* and the shell port because the two are adjacent by default: a creator who moves one will move the
|
|
24
|
+
* other, and being told the rule once, at the point of the mistake, beats discovering it as a
|
|
25
|
+
* terrain-less world. (The shell itself fetches nothing from the CDN, so its own check is purely
|
|
26
|
+
* that kindness.)
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseLocalPort(value: string | undefined, fallback: number, flag: string): number;
|
|
19
29
|
/**
|
|
20
30
|
* A free port the CDN will serve assets to, or `null` when the whole window is taken.
|
|
21
31
|
*
|