@bitmagic/cli 0.1.36 → 0.1.37
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/dist/commands/forge.js +39 -26
- package/dist/commands/forge.js.map +1 -1
- package/dist/editor/shell-page.js +123 -7
- package/dist/editor/shell-page.js.map +1 -1
- package/dist/forge/browser-host.d.ts +0 -10
- package/dist/forge/browser-host.js +0 -12
- package/dist/forge/browser-host.js.map +1 -1
- package/dist/forge/progress.d.ts +80 -0
- package/dist/forge/progress.js +158 -0
- package/dist/forge/progress.js.map +1 -0
- package/dist/forge/run-pipeline.d.ts +11 -2
- package/dist/forge/run-pipeline.js +9 -7
- package/dist/forge/run-pipeline.js.map +1 -1
- package/dist/forge/stream.d.ts +14 -3
- package/dist/forge/stream.js +62 -9
- package/dist/forge/stream.js.map +1 -1
- package/dist/forge/transport.js +14 -3
- package/dist/forge/transport.js.map +1 -1
- package/dist/project/jobs.d.ts +22 -1
- package/dist/project/jobs.js +60 -3
- package/dist/project/jobs.js.map +1 -1
- package/package.json +3 -3
- package/dist/forge/bake-progress.d.ts +0 -44
- package/dist/forge/bake-progress.js +0 -77
- package/dist/forge/bake-progress.js.map +0 -1
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The forge's five-step progress model, assembled from both halves of the run.
|
|
3
|
+
*
|
|
4
|
+
* A forge is split across two machines — steps 1-2 on api-server, arriving as SSE frames, and
|
|
5
|
+
* steps 3-5 in this process, arriving through `ForgeDeps.onProgress` — and a creator watching it
|
|
6
|
+
* does not care which. So both feed ONE tracker, which owns the whole picture: which step is
|
|
7
|
+
* running, how far through it is, and what the level being built contains.
|
|
8
|
+
*
|
|
9
|
+
* The tracker has two outputs and they are deliberately different:
|
|
10
|
+
*
|
|
11
|
+
* - `log` — the terminal. Lines, appended, throttled so a thousand-chunk bake does not bury
|
|
12
|
+
* everything else.
|
|
13
|
+
* - `report` — the job record `bitmagic dev` reads (`project/jobs.ts`). A SNAPSHOT, rewritten in
|
|
14
|
+
* place, because a panel showing five live rows wants the current state of all five, not the
|
|
15
|
+
* last sentence anyone said.
|
|
16
|
+
*
|
|
17
|
+
* Pure: no filesystem, no browser, no network, no clock it does not accept as a parameter. That
|
|
18
|
+
* matters here more than anywhere else in the command — this is the display logic for a
|
|
19
|
+
* thirty-minute, expensive, hard-to-reproduce operation, so it has to be exercisable in
|
|
20
|
+
* milliseconds.
|
|
21
|
+
*
|
|
22
|
+
* This replaced `bake-progress.ts`, which printed the level bake's chunk counts from a separate
|
|
23
|
+
* subscription to the browser page. That subscription existed only because the transport dropped
|
|
24
|
+
* progress messages after re-arming its idle timer; it now forwards them
|
|
25
|
+
* (`ForgeTransportOptions.onProgress`), so the counts arrive as ordinary step events and the
|
|
26
|
+
* second, parallel path is gone.
|
|
27
|
+
*/
|
|
28
|
+
import { FORGE_STEPS, } from '@bitmagic/world-forger/pipeline/index.js';
|
|
29
|
+
/**
|
|
30
|
+
* Minimum gap between printed `update` lines. The bake is already throttled at its source
|
|
31
|
+
* (`voxelize-level.ts`), but the archetype step is not: a city forge can carry hundreds of
|
|
32
|
+
* archetypes and each one emits, so without this the step list scrolls away.
|
|
33
|
+
*/
|
|
34
|
+
export const PROGRESS_LOG_MIN_INTERVAL_MS = 3000;
|
|
35
|
+
/**
|
|
36
|
+
* Minimum gap between job-record writes. Every write is a temp-file-and-rename that the dev
|
|
37
|
+
* sidecar's directory watcher then picks up, so this is the one throttle that protects somebody
|
|
38
|
+
* else's process rather than this one's output. Shorter than the log interval because the panel
|
|
39
|
+
* re-renders at 1 Hz anyway and a stale bar looks broken in a way a missing log line does not.
|
|
40
|
+
*/
|
|
41
|
+
export const PROGRESS_REPORT_MIN_INTERVAL_MS = 1000;
|
|
42
|
+
/** `[3/5]`, from the step's position in {@link FORGE_STEPS}. */
|
|
43
|
+
function stepBanner(index) {
|
|
44
|
+
return `[${index + 1}/${FORGE_STEPS.length}]`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Merge a step's summary into the accumulated one.
|
|
48
|
+
*
|
|
49
|
+
* Later steps ADD fields rather than replace the object: the design step knows the approach and
|
|
50
|
+
* the size, the forge step knows the objects, the placements and the bands. Both omit the keys
|
|
51
|
+
* they cannot speak to (never sending an explicit `undefined`), so a plain spread is exactly the
|
|
52
|
+
* right merge — the forge step's summary does not erase the approach the design step reported.
|
|
53
|
+
*/
|
|
54
|
+
function mergeSummary(previous, next) {
|
|
55
|
+
if (!next)
|
|
56
|
+
return previous;
|
|
57
|
+
return { ...previous, ...next };
|
|
58
|
+
}
|
|
59
|
+
export function createForgeProgressTracker(options) {
|
|
60
|
+
const now = options.now ?? (() => Date.now());
|
|
61
|
+
const logIntervalMs = options.logIntervalMs ?? PROGRESS_LOG_MIN_INTERVAL_MS;
|
|
62
|
+
const reportIntervalMs = options.reportIntervalMs ?? PROGRESS_REPORT_MIN_INTERVAL_MS;
|
|
63
|
+
const steps = FORGE_STEPS.map((step) => ({
|
|
64
|
+
key: step.key,
|
|
65
|
+
label: step.label,
|
|
66
|
+
status: 'pending',
|
|
67
|
+
}));
|
|
68
|
+
const startedAt = new Map();
|
|
69
|
+
let summary;
|
|
70
|
+
// -Infinity so the first line and the first report always go out, whatever the clock reads.
|
|
71
|
+
let lastLogAt = -Infinity;
|
|
72
|
+
let lastReportAt = -Infinity;
|
|
73
|
+
const indexOf = (key) => steps.findIndex((step) => step.key === key);
|
|
74
|
+
const snapshot = () => ({
|
|
75
|
+
steps: steps.map((step) => ({ ...step })),
|
|
76
|
+
...(summary ? { summary } : {}),
|
|
77
|
+
});
|
|
78
|
+
const report = (force, at) => {
|
|
79
|
+
if (!options.report)
|
|
80
|
+
return;
|
|
81
|
+
if (!force && at - lastReportAt < reportIntervalMs)
|
|
82
|
+
return;
|
|
83
|
+
lastReportAt = at;
|
|
84
|
+
options.report(snapshot());
|
|
85
|
+
};
|
|
86
|
+
const print = (line, force, at) => {
|
|
87
|
+
if (!force && at - lastLogAt < logIntervalMs)
|
|
88
|
+
return;
|
|
89
|
+
lastLogAt = at;
|
|
90
|
+
options.log(line);
|
|
91
|
+
};
|
|
92
|
+
const apply = (event) => {
|
|
93
|
+
const index = indexOf(event.step);
|
|
94
|
+
// An unknown step key means this CLI is older than the server or the package it is talking to.
|
|
95
|
+
// The sentence is still worth printing — it is what the new step is saying about itself — but
|
|
96
|
+
// there is no row to fold it into, so it does not touch the model.
|
|
97
|
+
if (index < 0) {
|
|
98
|
+
const at = now();
|
|
99
|
+
print(event.message, event.phase !== 'update', at);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const step = steps[index];
|
|
103
|
+
const at = now();
|
|
104
|
+
if (!startedAt.has(event.step))
|
|
105
|
+
startedAt.set(event.step, at);
|
|
106
|
+
// Every earlier step is finished by definition once a later one speaks. This is what keeps the
|
|
107
|
+
// list honest when a step produces no events of its own — a resume that skipped it, or a step
|
|
108
|
+
// running against a server too old to report it.
|
|
109
|
+
for (let i = 0; i < index; i++) {
|
|
110
|
+
const earlier = steps[i];
|
|
111
|
+
if (earlier.status !== 'done') {
|
|
112
|
+
earlier.status = 'done';
|
|
113
|
+
earlier.detail = undefined;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
summary = mergeSummary(summary, event.summary);
|
|
117
|
+
if (event.phase === 'done') {
|
|
118
|
+
step.status = 'done';
|
|
119
|
+
step.detail = undefined;
|
|
120
|
+
step.durationMs = at - (startedAt.get(event.step) ?? at);
|
|
121
|
+
if (step.total !== undefined)
|
|
122
|
+
step.completed = step.total;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
step.status = 'running';
|
|
126
|
+
if (event.total !== undefined)
|
|
127
|
+
step.total = event.total;
|
|
128
|
+
if (event.completed !== undefined)
|
|
129
|
+
step.completed = event.completed;
|
|
130
|
+
step.detail = event.detail;
|
|
131
|
+
}
|
|
132
|
+
const terminal = event.phase !== 'update';
|
|
133
|
+
const suffix = event.detail !== undefined && !event.message.includes(event.detail)
|
|
134
|
+
? ` — ${event.detail}`
|
|
135
|
+
: '';
|
|
136
|
+
print(`${stepBanner(index)} ${event.message}${suffix}`, terminal, at);
|
|
137
|
+
report(terminal, at);
|
|
138
|
+
};
|
|
139
|
+
const skip = (key, message) => {
|
|
140
|
+
const index = indexOf(key);
|
|
141
|
+
if (index < 0)
|
|
142
|
+
return;
|
|
143
|
+
const step = steps[index];
|
|
144
|
+
step.status = 'done';
|
|
145
|
+
step.resumed = true;
|
|
146
|
+
step.detail = undefined;
|
|
147
|
+
for (let i = 0; i < index; i++) {
|
|
148
|
+
const earlier = steps[i];
|
|
149
|
+
earlier.status = 'done';
|
|
150
|
+
earlier.detail = undefined;
|
|
151
|
+
}
|
|
152
|
+
const at = now();
|
|
153
|
+
print(`${stepBanner(index)} ${message}`, true, at);
|
|
154
|
+
report(true, at);
|
|
155
|
+
};
|
|
156
|
+
return { apply, skip, snapshot };
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/forge/progress.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type ForgeArtifact, type ForgeDeps, type ForgeJobInput, type ForgeLevelSummary, type ForgeLogger, type ForgeStorageEnv, type ForgeTransport, type ForgeWorldJsonSource, type ForgedWorkStore } from '@bitmagic/world-forger/pipeline/index.js';
|
|
1
|
+
import { type ForgeArtifact, type ForgeDeps, type ForgeJobInput, type ForgeLevelSummary, type ForgeLogger, type ForgeProgressEvent, type ForgeStorageEnv, type ForgeTransport, type ForgeWorldJsonSource, type ForgedWorkStore } from '@bitmagic/world-forger/pipeline/index.js';
|
|
2
|
+
import { type ForgeProgressTracker } from './progress.js';
|
|
2
3
|
/** The project's one world.json — the file the engine loads and the forge writes back to. */
|
|
3
4
|
export declare function projectWorldJsonPath(projectRoot: string): string;
|
|
4
5
|
/**
|
|
@@ -55,6 +56,8 @@ export declare function buildCliForgeDeps(options: {
|
|
|
55
56
|
storage: ForgeStorageEnv;
|
|
56
57
|
transport: ForgeTransport;
|
|
57
58
|
logger: ForgeLogger;
|
|
59
|
+
/** Where steps 3-5 report their progress. Steps 1-2's arrives over SSE; both feed one tracker. */
|
|
60
|
+
onProgress?: (event: ForgeProgressEvent) => void;
|
|
58
61
|
}): ForgeDeps;
|
|
59
62
|
/**
|
|
60
63
|
* Put the upstream artifacts into the local store so steps 3-5 can read them.
|
|
@@ -81,8 +84,14 @@ export interface RunPipelineOptions {
|
|
|
81
84
|
projectRoot: string;
|
|
82
85
|
store: ForgedWorkStore;
|
|
83
86
|
deps: ForgeDeps;
|
|
84
|
-
/** Progress printer for the
|
|
87
|
+
/** Progress printer for the lines that are not step events (the sidecar write, the warnings). */
|
|
85
88
|
log: (message: string) => void;
|
|
89
|
+
/**
|
|
90
|
+
* The run's step model. `commands/forge.ts` passes the SAME tracker it fed steps 1-2's SSE frames
|
|
91
|
+
* into, which is the whole point: one five-step picture across both machines. Omitted (in tests)
|
|
92
|
+
* a local one is built over `log`, so the step banners print exactly as they always did.
|
|
93
|
+
*/
|
|
94
|
+
tracker?: ForgeProgressTracker;
|
|
86
95
|
}
|
|
87
96
|
export interface RunPipelineResult {
|
|
88
97
|
summary: ForgeLevelSummary;
|
|
@@ -18,6 +18,7 @@ import { FORGED_HEIGHTFIELD_FILE } from '@bitmagic/world-forger/terrain-height-g
|
|
|
18
18
|
import { CliError } from '../errors.js';
|
|
19
19
|
import { aliasSourceDir } from '../scaffold/aliases.js';
|
|
20
20
|
import { applyModificationsToWorld } from './apply-modifications.js';
|
|
21
|
+
import { createForgeProgressTracker } from './progress.js';
|
|
21
22
|
/** The project's one world.json — the file the engine loads and the forge writes back to. */
|
|
22
23
|
export function projectWorldJsonPath(projectRoot) {
|
|
23
24
|
return path.join(projectRoot, aliasSourceDir('work'), 'world.json');
|
|
@@ -119,6 +120,7 @@ export function buildCliForgeDeps(options) {
|
|
|
119
120
|
transport: options.transport,
|
|
120
121
|
worldJson: createProjectWorldJsonSource(options.projectRoot),
|
|
121
122
|
logger: options.logger,
|
|
123
|
+
...(options.onProgress ? { onProgress: options.onProgress } : {}),
|
|
122
124
|
model: {
|
|
123
125
|
async generateText() {
|
|
124
126
|
throw new CliError('The CLI hosts steps 3-5 of the forge, which never call a language model — '
|
|
@@ -204,6 +206,7 @@ async function warnIfPlatformerMovementMissing(store, log) {
|
|
|
204
206
|
*/
|
|
205
207
|
export async function runForgePipeline(options) {
|
|
206
208
|
const { projectRoot, store, deps, log } = options;
|
|
209
|
+
const tracker = options.tracker ?? createForgeProgressTracker({ log });
|
|
207
210
|
if (await store.has('persisted')) {
|
|
208
211
|
const cached = await store.get('persisted');
|
|
209
212
|
if (cached) {
|
|
@@ -211,24 +214,23 @@ export async function runForgePipeline(options) {
|
|
|
211
214
|
return { summary: cached, applied: false, changes: [] };
|
|
212
215
|
}
|
|
213
216
|
}
|
|
217
|
+
// The steps announce their OWN start and finish through `deps.onProgress` (see the shared
|
|
218
|
+
// package's `emitForgeProgress`), so the only thing left to say here is what did NOT run: a
|
|
219
|
+
// skipped step emits nothing at all, and a five-step view with a silently missing row reads as
|
|
220
|
+
// a step that hung.
|
|
214
221
|
if (await store.has('archetype-assets')) {
|
|
215
|
-
|
|
222
|
+
tracker.skip('archetypes', 'Objects already created — skipping (resumed).');
|
|
216
223
|
}
|
|
217
224
|
else {
|
|
218
|
-
log('[3/5] Creating the level\'s objects in the browser…');
|
|
219
225
|
await createLevelArchetypes(store, deps);
|
|
220
|
-
log('[3/5] Objects created.');
|
|
221
226
|
}
|
|
222
227
|
if (await store.has('level-bake')) {
|
|
223
|
-
|
|
228
|
+
tracker.skip('voxelize', 'Level already baked — skipping (resumed).');
|
|
224
229
|
}
|
|
225
230
|
else {
|
|
226
|
-
log('[4/5] Baking the level (this is the long one — progress follows)…');
|
|
227
231
|
await voxelizeLevel(store, deps);
|
|
228
|
-
log('[4/5] Level baked.');
|
|
229
232
|
}
|
|
230
233
|
await warnIfPlatformerMovementMissing(store, log);
|
|
231
|
-
log('[5/5] Placing instances and writing world.json…');
|
|
232
234
|
const { summary, modifications, terrainHeightGrid } = await placeAndPersistLevel(store, deps);
|
|
233
235
|
const worldPath = projectWorldJsonPath(projectRoot);
|
|
234
236
|
let outcome;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-pipeline.js","sourceRoot":"","sources":["../../src/forge/run-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,aAAa,
|
|
1
|
+
{"version":3,"file":"run-pipeline.js","sourceRoot":"","sources":["../../src/forge/run-pipeline.ts"],"names":[],"mappings":"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"}
|
package/dist/forge/stream.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* `{ success, message, patches }` — and so is what a failure means to the creator, which is what
|
|
11
11
|
* {@link classifyForgeFailure} is for.
|
|
12
12
|
*/
|
|
13
|
-
import type { ForgeArtifact } from '@bitmagic/world-forger/pipeline/index.js';
|
|
13
|
+
import type { ForgeArtifact, ForgeProgressEvent } from '@bitmagic/world-forger/pipeline/index.js';
|
|
14
14
|
import type { Environment } from '../config/environments.js';
|
|
15
15
|
export interface ForgeSuccess {
|
|
16
16
|
success: true;
|
|
@@ -38,6 +38,12 @@ export interface ForgeFailure {
|
|
|
38
38
|
timedOut: boolean;
|
|
39
39
|
/** Every progress line the server sent, in order — the input to {@link classifyForgeFailure}. */
|
|
40
40
|
progress: string[];
|
|
41
|
+
/**
|
|
42
|
+
* The same frames, structured, for the servers that send them. Preferred by
|
|
43
|
+
* {@link classifyForgeFailure}; empty against a server deployed before the fields existed, which
|
|
44
|
+
* is exactly when {@link progress} is still the only answer.
|
|
45
|
+
*/
|
|
46
|
+
events: ForgeProgressEvent[];
|
|
41
47
|
}
|
|
42
48
|
export type ForgeOutcome = ForgeSuccess | ForgeFailure;
|
|
43
49
|
/**
|
|
@@ -73,7 +79,7 @@ export declare function forgeDeadlineAdvice(jobId: string | null): string;
|
|
|
73
79
|
* terminal one: api-server puts it on every progress frame precisely so this case has an answer.
|
|
74
80
|
*/
|
|
75
81
|
export declare function forgeTruncationMessage(jobId: string | null): string;
|
|
76
|
-
export declare function classifyForgeFailure(progress: readonly string[]): ForgeFailureStage;
|
|
82
|
+
export declare function classifyForgeFailure(progress: readonly string[], events?: readonly ForgeProgressEvent[]): ForgeFailureStage;
|
|
77
83
|
/**
|
|
78
84
|
* The resume advice for a failure, as a paragraph appended to the server's own message. Every
|
|
79
85
|
* branch says explicitly that world.json is unchanged, because a creator who does not know has to
|
|
@@ -92,7 +98,12 @@ export declare function forgeFailureAdvice(stage: ForgeFailureStage, jobId: stri
|
|
|
92
98
|
export declare function describeForgeFailure(failure: ForgeFailure): string;
|
|
93
99
|
export interface ForgeStreamDeps {
|
|
94
100
|
fetch: typeof globalThis.fetch;
|
|
95
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Called for every progress frame. `event` is the structured form when the server sent one and
|
|
103
|
+
* null otherwise — the caller decides what to do with an unstructured frame (`commands/forge.ts`
|
|
104
|
+
* prints it as a plain line, which is what the CLI did for every frame before this existed).
|
|
105
|
+
*/
|
|
106
|
+
onProgress: (message: string, event: ForgeProgressEvent | null) => void;
|
|
96
107
|
}
|
|
97
108
|
/**
|
|
98
109
|
* POSTs to `/api/cli/v1/forge/stream` and consumes the SSE response.
|
package/dist/forge/stream.js
CHANGED
|
@@ -66,14 +66,31 @@ export function forgeTruncationMessage(jobId) {
|
|
|
66
66
|
* before `steps.design(...)` is awaited, one after it returns — so "did a design-completed line
|
|
67
67
|
* arrive before the error frame?" is an exact answer to "did step 1 succeed in this request?".
|
|
68
68
|
*
|
|
69
|
-
* This
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
69
|
+
* This USED to be the only answer, because there was no step key on the wire. There is one now
|
|
70
|
+
* (`step` / `phase` on every progress frame), and the classifier prefers it — but the CLI and
|
|
71
|
+
* api-server ship independently, so these strings remain the fallback for a CLI carrying this code
|
|
72
|
+
* against a server deployed before the fields existed. api-server keeps them verbatim for the same
|
|
73
|
+
* reason; its own comment says so.
|
|
74
|
+
*
|
|
75
|
+
* The coupling is bounded either way: `classifyForgeFailure` degrades to `unknown` (the both-cases
|
|
76
|
+
* message) the moment any of these stops matching, so a drift costs precision, never correctness.
|
|
73
77
|
*/
|
|
74
78
|
const DESIGN_STARTED_MARKER = 'Designing the level scene';
|
|
75
79
|
const DESIGN_FINISHED_MARKERS = ['Scene design complete', 'Design already complete'];
|
|
76
|
-
|
|
80
|
+
/** Steps whose completion means step 1 is behind us — the geometry step is what failed. */
|
|
81
|
+
const POST_DESIGN_STEPS = new Set(['forge', 'archetypes', 'voxelize', 'persist']);
|
|
82
|
+
export function classifyForgeFailure(progress, events = []) {
|
|
83
|
+
// Structural first: a `design`/`done` frame is the server saying the step finished, which is the
|
|
84
|
+
// fact the string matching below was only ever approximating.
|
|
85
|
+
if (events.length > 0) {
|
|
86
|
+
const designFinished = events.some((event) => (event.step === 'design' && event.phase === 'done') || POST_DESIGN_STEPS.has(event.step));
|
|
87
|
+
if (designFinished)
|
|
88
|
+
return 'forge';
|
|
89
|
+
if (events.some((event) => event.step === 'design'))
|
|
90
|
+
return 'design';
|
|
91
|
+
// Frames arrived but none named a step we know — fall through to the strings rather than
|
|
92
|
+
// report `unknown`, since an older-vocabulary server still writes the sentences.
|
|
93
|
+
}
|
|
77
94
|
const designFinished = progress.some((line) => DESIGN_FINISHED_MARKERS.some((marker) => line.includes(marker)));
|
|
78
95
|
if (designFinished)
|
|
79
96
|
return 'forge';
|
|
@@ -119,7 +136,7 @@ export function forgeFailureAdvice(stage, jobId) {
|
|
|
119
136
|
export function describeForgeFailure(failure) {
|
|
120
137
|
const advice = isDeadlineFailure(failure)
|
|
121
138
|
? forgeDeadlineAdvice(failure.jobId)
|
|
122
|
-
: forgeFailureAdvice(classifyForgeFailure(failure.progress), failure.jobId);
|
|
139
|
+
: forgeFailureAdvice(classifyForgeFailure(failure.progress, failure.events), failure.jobId);
|
|
123
140
|
const glb = failure.glbUrl
|
|
124
141
|
? ` The forged GLB is still available at ${failure.glbUrl} and can be voxelized by hand.`
|
|
125
142
|
: '';
|
|
@@ -177,6 +194,36 @@ async function toCliError(response, gameId) {
|
|
|
177
194
|
+ (serverMessage ? `: ${serverMessage}.` : '.')
|
|
178
195
|
+ ' world.json is unchanged.');
|
|
179
196
|
}
|
|
197
|
+
const FORGE_STEP_KEYS = new Set(['design', 'forge', 'archetypes', 'voxelize', 'persist']);
|
|
198
|
+
const FORGE_PHASES = new Set(['start', 'update', 'done']);
|
|
199
|
+
/**
|
|
200
|
+
* Read a structured progress event off a `progress` frame, or null when the server did not send
|
|
201
|
+
* one (an older api-server, whose frames carry `{ jobId, message }` and nothing else).
|
|
202
|
+
*
|
|
203
|
+
* An unrecognised `step` is rejected rather than passed through: the fields exist so a client can
|
|
204
|
+
* drive a step list, and a key with no row is worse than no key at all — {@link ForgeStepKey} is
|
|
205
|
+
* the contract, and a server that grows a sixth step needs a CLI that knows about it.
|
|
206
|
+
*
|
|
207
|
+
* `summary` is taken as-is, for the same reason `asForgeSuccess` trusts `artifact` structurally:
|
|
208
|
+
* it is display-only, produced by the very package this CLI depends on, and a field-by-field
|
|
209
|
+
* re-validation would give false confidence without catching anything a renderer would not.
|
|
210
|
+
*/
|
|
211
|
+
function asProgressEvent(data, message) {
|
|
212
|
+
const { step, phase } = data;
|
|
213
|
+
if (typeof step !== 'string' || !FORGE_STEP_KEYS.has(step))
|
|
214
|
+
return null;
|
|
215
|
+
if (typeof phase !== 'string' || !FORGE_PHASES.has(phase))
|
|
216
|
+
return null;
|
|
217
|
+
return {
|
|
218
|
+
step: step,
|
|
219
|
+
phase: phase,
|
|
220
|
+
message,
|
|
221
|
+
...(typeof data.completed === 'number' ? { completed: data.completed } : {}),
|
|
222
|
+
...(typeof data.total === 'number' ? { total: data.total } : {}),
|
|
223
|
+
...(typeof data.detail === 'string' ? { detail: data.detail } : {}),
|
|
224
|
+
...(isRecord(data.summary) ? { summary: data.summary } : {}),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
180
227
|
/** The terminal `result` frame: `{ success: true, jobId, artifact, platformerMovement? }`. */
|
|
181
228
|
function asForgeSuccess(data) {
|
|
182
229
|
if (!isRecord(data) || data.success !== true)
|
|
@@ -232,6 +279,7 @@ export async function requestForge(environment, accessToken, body, deps) {
|
|
|
232
279
|
throw new CliError('The server accepted the forge but returned no stream body. world.json is unchanged.');
|
|
233
280
|
}
|
|
234
281
|
const progress = [];
|
|
282
|
+
const events = [];
|
|
235
283
|
let outcome = null;
|
|
236
284
|
/**
|
|
237
285
|
* The job id from the LAST frame that carried one, whatever kind of frame that was.
|
|
@@ -248,10 +296,14 @@ export async function requestForge(environment, accessToken, body, deps) {
|
|
|
248
296
|
lastJobId = stringField(event.data.jobId) ?? lastJobId;
|
|
249
297
|
}
|
|
250
298
|
if (event.event === 'progress') {
|
|
251
|
-
const
|
|
252
|
-
|
|
299
|
+
const data = isRecord(event.data) ? event.data : null;
|
|
300
|
+
const message = data ? stringField(data.message) : null;
|
|
301
|
+
if (data !== null && message !== null) {
|
|
253
302
|
progress.push(message);
|
|
254
|
-
|
|
303
|
+
const parsed = asProgressEvent(data, message);
|
|
304
|
+
if (parsed)
|
|
305
|
+
events.push(parsed);
|
|
306
|
+
deps.onProgress(message, parsed);
|
|
255
307
|
}
|
|
256
308
|
return false;
|
|
257
309
|
}
|
|
@@ -272,6 +324,7 @@ export async function requestForge(environment, accessToken, body, deps) {
|
|
|
272
324
|
...(stringField(data.glbUrl) !== null ? { glbUrl: stringField(data.glbUrl) } : {}),
|
|
273
325
|
timedOut: data.timedOut === true,
|
|
274
326
|
progress,
|
|
327
|
+
events,
|
|
275
328
|
};
|
|
276
329
|
return true;
|
|
277
330
|
}
|
package/dist/forge/stream.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/forge/stream.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/forge/stream.ts"],"names":[],"mappings":"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"}
|
package/dist/forge/transport.js
CHANGED
|
@@ -51,7 +51,7 @@ function matchesAwaited(message, expected) {
|
|
|
51
51
|
* One send + wait. Resolves the matching message, or null on timeout / a failed post — never
|
|
52
52
|
* rejects, because callers distinguish "no answer" from "answered with an error".
|
|
53
53
|
*/
|
|
54
|
-
function singleAttempt(bridge, command, requestId, resultType, timeoutMs, progressType, log) {
|
|
54
|
+
function singleAttempt(bridge, command, requestId, resultType, timeoutMs, progressType, onProgress, log) {
|
|
55
55
|
return new Promise((resolve) => {
|
|
56
56
|
let settled = false;
|
|
57
57
|
let timer;
|
|
@@ -78,9 +78,20 @@ function singleAttempt(bridge, command, requestId, resultType, timeoutMs, progre
|
|
|
78
78
|
}, timeoutMs);
|
|
79
79
|
};
|
|
80
80
|
unsubscribe = bridge.onMessage((message) => {
|
|
81
|
-
// Progress for THIS request keeps the attempt alive and resets the idle timer
|
|
81
|
+
// Progress for THIS request keeps the attempt alive and resets the idle timer — and is now
|
|
82
|
+
// also reported on. Swallowed rather than propagated: a reporter that throws must not fail
|
|
83
|
+
// the twenty-minute bake it is describing, and this callback runs inside the page's message
|
|
84
|
+
// listener where a throw would take the subscription with it.
|
|
82
85
|
if (progressType && matchesAwaited(message, progressType) && (!requestId || message.requestId === requestId)) {
|
|
83
86
|
armTimer();
|
|
87
|
+
if (onProgress) {
|
|
88
|
+
try {
|
|
89
|
+
onProgress(message);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// See above.
|
|
93
|
+
}
|
|
94
|
+
}
|
|
84
95
|
return;
|
|
85
96
|
}
|
|
86
97
|
if (!matchesAwaited(message, resultType))
|
|
@@ -129,7 +140,7 @@ export function createForgePageTransport(bridge, options) {
|
|
|
129
140
|
const envelope = { type, data: payload };
|
|
130
141
|
const maxRetries = waitOptions?.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
131
142
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
132
|
-
const result = await singleAttempt(bridge, envelope, requestId, resultType, timeoutMs, waitOptions?.progressType, log);
|
|
143
|
+
const result = await singleAttempt(bridge, envelope, requestId, resultType, timeoutMs, waitOptions?.progressType, waitOptions?.onProgress, log);
|
|
133
144
|
if (result !== null)
|
|
134
145
|
return result;
|
|
135
146
|
if (attempt < maxRetries) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/forge/transport.ts"],"names":[],"mappings":"AAoDA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAA2B;IACrD,mBAAmB,EAAE,oBAAoB;IACzC,0BAA0B,EAAE,0BAA0B;IACtD,qBAAqB,EAAE,qBAAqB;IAC5C,oBAAoB,EAAE,qBAAqB;IAC3C,iBAAiB,EAAE,oBAAoB;IACvC,wBAAwB,EAAE,2BAA2B;IACrD,4BAA4B,EAAE,8BAA8B;IAC5D,8BAA8B,EAAE,gCAAgC;CACjE,CAAC;AAEF,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAC9B,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAEpC,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAS,cAAc,CAAC,OAAgC,EAAE,QAAgB;IACxE,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IACzB,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC1C,4FAA4F;IAC5F,gFAAgF;IAChF,OAAO,GAAG,KAAK,QAAQ,IAAI,eAAe,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CACpB,MAAuB,EACvB,OAAoB,EACpB,SAA6B,EAC7B,UAAkB,EAClB,SAAiB,EACjB,YAAgC,EAChC,GAA8B;IAE9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,KAAgD,CAAC;QACrD,IAAI,WAAqC,CAAC;QAE1C,MAAM,MAAM,GAAG,CAAC,MAAsC,EAAQ,EAAE;YAC9D,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,IAAI,KAAK;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;YAC/B,WAAW,EAAE,EAAE,CAAC;YAChB,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC,CAAC;QAEF,yFAAyF;QACzF,wFAAwF;QACxF,MAAM,QAAQ,GAAG,GAAS,EAAE;YAC1B,IAAI,OAAO;gBAAE,OAAO;YACpB,IAAI,KAAK;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;YAC/B,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBACtB,GAAG,CAAC,uBAAuB,UAAU,WAAW,SAAS,eAAe,CAAC,CAAC;gBAC1E,MAAM,CAAC,IAAI,CAAC,CAAC;YACf,CAAC,EAAE,SAAS,CAAC,CAAC;QAChB,CAAC,CAAC;QAEF,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE;YACzC
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/forge/transport.ts"],"names":[],"mappings":"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"}
|
package/dist/project/jobs.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ForgeProgressSnapshot } from '../forge/progress.js';
|
|
1
2
|
/** Which command is running. Shown as-is in the dev view, so these are creator-facing words. */
|
|
2
3
|
export type JobKind = 'skybox' | 'sound' | 'image' | 'character' | 'animation' | 'prop' | 'vehicle' | 'forge' | 'cover' | 'hq';
|
|
3
4
|
export interface JobRecord {
|
|
@@ -10,6 +11,16 @@ export interface JobRecord {
|
|
|
10
11
|
status: 'running' | 'failed';
|
|
11
12
|
/** The generation's most recent progress line. See `withJobRecord`. */
|
|
12
13
|
message: string;
|
|
14
|
+
/**
|
|
15
|
+
* Structured progress, for the one kind that has any: a forge's five steps, how far through the
|
|
16
|
+
* current one is, and what the level being built contains.
|
|
17
|
+
*
|
|
18
|
+
* Optional because it is a forge-only enrichment of {@link message}, never a replacement for it —
|
|
19
|
+
* every other kind has a single sentence and nothing more to say, and a reader (a `bitmagic dev`
|
|
20
|
+
* from an older install, or the creator's agent reading this directory by hand) must still make
|
|
21
|
+
* sense of a record without it.
|
|
22
|
+
*/
|
|
23
|
+
progress?: ForgeProgressSnapshot;
|
|
13
24
|
pid: number;
|
|
14
25
|
startedAt: string;
|
|
15
26
|
updatedAt: string;
|
|
@@ -48,7 +59,11 @@ export interface JobHandle {
|
|
|
48
59
|
readonly jobId: string;
|
|
49
60
|
/** Record the latest progress line. */
|
|
50
61
|
update(message: string): void;
|
|
62
|
+
/** Record structured progress alongside the line. Only a forge has any; see {@link JobRecord}. */
|
|
63
|
+
setProgress(progress: ForgeProgressSnapshot): void;
|
|
51
64
|
}
|
|
65
|
+
/** What a body reports structured progress through. A no-op when the record could not be started. */
|
|
66
|
+
export type JobProgressReporter = (progress: ForgeProgressSnapshot) => void;
|
|
52
67
|
export interface StartJobOptions {
|
|
53
68
|
root: string;
|
|
54
69
|
kind: JobKind;
|
|
@@ -65,6 +80,12 @@ export declare function startJob(options: StartJobOptions): JobHandle;
|
|
|
65
80
|
* way since it shipped, and it means a command gains live progress in the dev view without a single
|
|
66
81
|
* new call site inside it.
|
|
67
82
|
*
|
|
83
|
+
* The SECOND argument is for the one command that has more than a sentence to say. A forge runs
|
|
84
|
+
* for half an hour across five steps on two machines, and a line-at-a-time record can only ever
|
|
85
|
+
* show the most recent one — so `forge` also reports a snapshot of all five (see
|
|
86
|
+
* `forge/progress.ts`). Every other caller ignores this parameter, which is the point: the log tap
|
|
87
|
+
* above stays the default, and structured progress is opt-in for the kind that needs it.
|
|
88
|
+
*
|
|
68
89
|
* **Never fails the generation it is watching.** Bookkeeping that breaks a paid, minutes-long
|
|
69
90
|
* generation would be a far worse bug than the missing progress line it is trying to provide, so
|
|
70
91
|
* every filesystem error here is swallowed. The body's own errors propagate untouched, after being
|
|
@@ -72,7 +93,7 @@ export declare function startJob(options: StartJobOptions): JobHandle;
|
|
|
72
93
|
*/
|
|
73
94
|
export declare function withJobRecord<T>(options: StartJobOptions & {
|
|
74
95
|
log: (message: string) => void;
|
|
75
|
-
}, body: (log: (message: string) => void) => Promise<T>): Promise<T>;
|
|
96
|
+
}, body: (log: (message: string) => void, report: JobProgressReporter) => Promise<T>): Promise<T>;
|
|
76
97
|
/** Success: the asset is in world.json, and that is the record. */
|
|
77
98
|
export declare function removeJob(root: string, jobId: string): void;
|
|
78
99
|
/** Failure: nothing else records it, so the record stays and carries the reason. */
|