@bitmagic/cli 0.1.65-dev.23 → 0.1.65-dev.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -137
- package/dist/assets/materials.d.ts +0 -9
- package/dist/assets/materials.js +2 -10
- package/dist/assets/materials.js.map +1 -1
- package/dist/cli.d.ts +0 -9
- package/dist/cli.js +0 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/forge.d.ts +0 -15
- package/dist/commands/forge.js +38 -143
- package/dist/commands/forge.js.map +1 -1
- package/dist/commands/generate.d.ts +13 -31
- package/dist/commands/generate.js +56 -48
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/trailer.js +1 -1
- package/dist/commands/trailer.js.map +1 -1
- package/dist/editor/animation-preview-page.js +1 -88
- package/dist/editor/animation-preview-page.js.map +1 -1
- package/dist/editor/asset-detail-panel.d.ts +2 -2
- package/dist/editor/asset-detail-panel.js +0 -21
- package/dist/editor/asset-detail-panel.js.map +1 -1
- package/dist/editor/asset-viewer.js +8 -31
- package/dist/editor/asset-viewer.js.map +1 -1
- package/dist/editor/shell-page.js +1 -63
- package/dist/editor/shell-page.js.map +1 -1
- package/dist/forge/run-pipeline.js +1 -5
- package/dist/forge/run-pipeline.js.map +1 -1
- package/dist/forge/stream.d.ts +1 -5
- package/dist/forge/stream.js +0 -4
- package/dist/forge/stream.js.map +1 -1
- package/dist/mobile/asset-hosts.d.ts +9 -7
- package/dist/mobile/asset-hosts.js +16 -7
- package/dist/mobile/asset-hosts.js.map +1 -1
- package/dist/scaffold/blender-level-skill-files.js +2 -3
- package/dist/scaffold/blender-level-skill-files.js.map +1 -1
- package/dist/scaffold/claude-settings.js +0 -4
- package/dist/scaffold/claude-settings.js.map +1 -1
- package/dist/scaffold/project-files.js +12 -18
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/scaffold/project.js +1 -4
- package/dist/scaffold/project.js.map +1 -1
- package/dist/scaffold/skill-files.d.ts +5 -32
- package/dist/scaffold/skill-files.js +63 -102
- package/dist/scaffold/skill-files.js.map +1 -1
- package/dist/telemetry/creator-prompt.d.ts +2 -18
- package/dist/telemetry/creator-prompt.js +3 -24
- package/dist/telemetry/creator-prompt.js.map +1 -1
- package/dist/telemetry/report.js +16 -31
- package/dist/telemetry/report.js.map +1 -1
- package/package.json +4 -4
- package/assets/skills/trailer-craft.md +0 -180
- package/dist/commands/character.d.ts +0 -18
- package/dist/commands/character.js +0 -268
- package/dist/commands/character.js.map +0 -1
- package/dist/forge/edit-target.d.ts +0 -30
- package/dist/forge/edit-target.js +0 -88
- package/dist/forge/edit-target.js.map +0 -1
- package/dist/forge/prune-store.d.ts +0 -46
- package/dist/forge/prune-store.js +0 -86
- package/dist/forge/prune-store.js.map +0 -1
- package/dist/forge/remote-store.d.ts +0 -36
- package/dist/forge/remote-store.js +0 -113
- package/dist/forge/remote-store.js.map +0 -1
- package/dist/generate/character-body.d.ts +0 -79
- package/dist/generate/character-body.js +0 -100
- package/dist/generate/character-body.js.map +0 -1
- package/dist/project/character-search.d.ts +0 -30
- package/dist/project/character-search.js +0 -82
- package/dist/project/character-search.js.map +0 -1
- package/dist/scaffold/asset-pipeline-skill-files.d.ts +0 -42
- package/dist/scaffold/asset-pipeline-skill-files.js +0 -207
- package/dist/scaffold/asset-pipeline-skill-files.js.map +0 -1
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `bitmagic forge --edit` — which level is being edited, and the request an
|
|
3
|
-
* edit job is frozen with.
|
|
4
|
-
*
|
|
5
|
-
* Pure over the project's world.json so the resolution rules are testable
|
|
6
|
-
* without a project on disk: `--level` names a level by id, by exact name, or
|
|
7
|
-
* by a unique case-insensitive name; omitted, it means the ONE level this
|
|
8
|
-
* project could edit, and anything else is refused with the candidates named.
|
|
9
|
-
* A level can be edited when its `.vwld` asset records the forge job that
|
|
10
|
-
* built it (or, for older levels, carries the job id in its source GLB key —
|
|
11
|
-
* see `parentForgeJobId`).
|
|
12
|
-
*/
|
|
13
|
-
import { parentForgeJobId } from '@bitmagic/world-forger/pipeline/index.js';
|
|
14
|
-
import { CliError } from '../errors.js';
|
|
15
|
-
import { listLevels } from '../levels/registry.js';
|
|
16
|
-
import { isJsonObject } from '../project/world-json.js';
|
|
17
|
-
/** The forge-built levels of a world: those whose asset names its job. */
|
|
18
|
-
function editableLevels(world) {
|
|
19
|
-
const assets = Array.isArray(world.assets) ? world.assets.filter(isJsonObject) : [];
|
|
20
|
-
return listLevels(world).flatMap((level) => {
|
|
21
|
-
const asset = assets.find((a) => a.id === level.vwldAssetId);
|
|
22
|
-
const parentJobId = asset === undefined ? null : parentForgeJobId(asset);
|
|
23
|
-
return parentJobId === null ? [] : [{ levelId: level.id, name: level.name, vwldAssetId: level.vwldAssetId, parentJobId }];
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
function describe(levels) {
|
|
27
|
-
return levels.map((l) => `${l.id ?? l.levelId} ("${l.name}")`).join(', ');
|
|
28
|
-
}
|
|
29
|
-
export function resolveEditTarget(world, levelArg) {
|
|
30
|
-
const all = listLevels(world);
|
|
31
|
-
const editable = editableLevels(world);
|
|
32
|
-
const wanted = levelArg?.trim();
|
|
33
|
-
if (wanted === undefined || wanted === '') {
|
|
34
|
-
if (editable.length === 1)
|
|
35
|
-
return editable[0];
|
|
36
|
-
if (editable.length === 0) {
|
|
37
|
-
throw new CliError(all.length === 0
|
|
38
|
-
? 'This game has no levels — `bitmagic forge --prompt "..."` creates one; --edit changes one that exists.'
|
|
39
|
-
: 'None of this game\'s levels was forged by a version that records its job, so there is nothing to edit — '
|
|
40
|
-
+ `re-forge the level once and edit it from then on. Levels: ${describe(all)}.`);
|
|
41
|
-
}
|
|
42
|
-
throw new CliError(`Pass --level to say which level to edit. Forged levels: ${describe(editable)}.`);
|
|
43
|
-
}
|
|
44
|
-
const byId = all.find((l) => l.id === wanted);
|
|
45
|
-
const byName = byId ? undefined : all.filter((l) => l.name === wanted);
|
|
46
|
-
const byLooseName = byId || (byName && byName.length > 0)
|
|
47
|
-
? undefined
|
|
48
|
-
: all.filter((l) => l.name.toLowerCase() === wanted.toLowerCase());
|
|
49
|
-
const matches = byId ? [byId] : byName && byName.length > 0 ? byName : byLooseName ?? [];
|
|
50
|
-
if (matches.length === 0) {
|
|
51
|
-
throw new CliError(`No level "${wanted}" in this project. `
|
|
52
|
-
+ (all.length > 0 ? `This game has: ${describe(all)}.` : 'This game has no levels.'));
|
|
53
|
-
}
|
|
54
|
-
if (matches.length > 1) {
|
|
55
|
-
throw new CliError(`"${wanted}" names ${matches.length} levels — pass the id instead: ${describe(matches)}.`);
|
|
56
|
-
}
|
|
57
|
-
const level = matches[0];
|
|
58
|
-
const target = editable.find((e) => e.levelId === level.id);
|
|
59
|
-
if (target === undefined) {
|
|
60
|
-
throw new CliError(`Level ${level.id} ("${level.name}") was not forged by a version that records its job, so it cannot be edited — `
|
|
61
|
-
+ 're-forge it once (`bitmagic forge --prompt "..."`) and edit it from then on.');
|
|
62
|
-
}
|
|
63
|
-
return target;
|
|
64
|
-
}
|
|
65
|
-
/** The frozen request of an edit job: the level's own name, the parent it edits, the level it replaces. */
|
|
66
|
-
export function buildEditJobInput(options) {
|
|
67
|
-
const editPrompt = options.editPrompt.trim();
|
|
68
|
-
return {
|
|
69
|
-
lane: 'threed',
|
|
70
|
-
name: options.target.name,
|
|
71
|
-
prompt: editPrompt,
|
|
72
|
-
editPrompt,
|
|
73
|
-
parentJobId: options.target.parentJobId,
|
|
74
|
-
assetId: options.target.vwldAssetId,
|
|
75
|
-
gameId: options.gameId,
|
|
76
|
-
...(options.groundPlane ? { groundPlane: true } : {}),
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
/** A plan that reuses nothing — what a terminal frame without one means. */
|
|
80
|
-
export function bakeEverythingPlan(artifact, reason) {
|
|
81
|
-
return {
|
|
82
|
-
levelBake: 'rebake',
|
|
83
|
-
archetypes: Object.fromEntries(artifact.archetypes.map((a) => [a.name, 'bake'])),
|
|
84
|
-
changedPaths: [],
|
|
85
|
-
reason,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
//# sourceMappingURL=edit-target.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"edit-target.js","sourceRoot":"","sources":["../../src/forge/edit-target.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Forge-store housekeeping — the job directories under `.bitmagic/forge` that
|
|
3
|
-
* nothing will read again are removed once a forge has landed.
|
|
4
|
-
*
|
|
5
|
-
* Every edit is a NEW job and the level keeps only its newest job's id, so a
|
|
6
|
-
* level edited a dozen times leaves a dozen finished job directories behind
|
|
7
|
-
* (dev-games/alien held 13, 6 MB, after one afternoon). A job is kept while
|
|
8
|
-
* anything could still want it:
|
|
9
|
-
*
|
|
10
|
-
* - an asset of the world names it as its forge job (`parentForgeJobId`, the
|
|
11
|
-
* same reading `--edit` uses) — that is the parent an edit reuses bakes from;
|
|
12
|
-
* - it is the job that just ran;
|
|
13
|
-
* - it never finished (no `persisted` record) — an unfinished job is what
|
|
14
|
-
* `--resume` continues, and its local bakes are the progress a resume keeps.
|
|
15
|
-
*
|
|
16
|
-
* The server's copy of every job is untouched: it is what a resume or an edit
|
|
17
|
-
* from another machine reads, and its lifetime is the server's call. The
|
|
18
|
-
* resolution is pure over world.json and a listing, so the rules are testable
|
|
19
|
-
* without a project on disk; the removal is best-effort, since a directory
|
|
20
|
-
* that will not delete must never fail a forge that has already landed.
|
|
21
|
-
*/
|
|
22
|
-
import { type JsonObject } from '../project/world-json.js';
|
|
23
|
-
export interface ForgeJobDir {
|
|
24
|
-
jobId: string;
|
|
25
|
-
/** The job wrote its `persisted` record: world.json has what it built. */
|
|
26
|
-
finished: boolean;
|
|
27
|
-
}
|
|
28
|
-
/** The job ids the world's assets name — the jobs an edit could reuse bakes from. */
|
|
29
|
-
export declare function referencedForgeJobs(world: JsonObject): Set<string>;
|
|
30
|
-
/** Of `jobs`, the finished ones neither the world nor `keep` refers to, in listing order. */
|
|
31
|
-
export declare function staleForgeJobs(world: JsonObject, jobs: ReadonlyArray<ForgeJobDir>, keep: ReadonlyArray<string>): string[];
|
|
32
|
-
/** `<root>/.bitmagic/forge/worlds/v3/forged-work` — the store's job directories, by the shared key layout. */
|
|
33
|
-
export declare function forgeJobsDir(projectRoot: string): string;
|
|
34
|
-
/** The store's job directories, finished or not; an absent store lists nothing. */
|
|
35
|
-
export declare function listForgeJobDirs(projectRoot: string): ForgeJobDir[];
|
|
36
|
-
/**
|
|
37
|
-
* Remove the finished jobs the project no longer refers to. One line when
|
|
38
|
-
* something was removed, a warning when something would not go; silent otherwise.
|
|
39
|
-
*/
|
|
40
|
-
export declare function pruneForgeStore(options: {
|
|
41
|
-
projectRoot: string;
|
|
42
|
-
world: JsonObject;
|
|
43
|
-
/** Jobs to keep whatever the world says — the one that just ran. */
|
|
44
|
-
keep: ReadonlyArray<string>;
|
|
45
|
-
log: (message: string) => void;
|
|
46
|
-
}): string[];
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Forge-store housekeeping — the job directories under `.bitmagic/forge` that
|
|
3
|
-
* nothing will read again are removed once a forge has landed.
|
|
4
|
-
*
|
|
5
|
-
* Every edit is a NEW job and the level keeps only its newest job's id, so a
|
|
6
|
-
* level edited a dozen times leaves a dozen finished job directories behind
|
|
7
|
-
* (dev-games/alien held 13, 6 MB, after one afternoon). A job is kept while
|
|
8
|
-
* anything could still want it:
|
|
9
|
-
*
|
|
10
|
-
* - an asset of the world names it as its forge job (`parentForgeJobId`, the
|
|
11
|
-
* same reading `--edit` uses) — that is the parent an edit reuses bakes from;
|
|
12
|
-
* - it is the job that just ran;
|
|
13
|
-
* - it never finished (no `persisted` record) — an unfinished job is what
|
|
14
|
-
* `--resume` continues, and its local bakes are the progress a resume keeps.
|
|
15
|
-
*
|
|
16
|
-
* The server's copy of every job is untouched: it is what a resume or an edit
|
|
17
|
-
* from another machine reads, and its lifetime is the server's call. The
|
|
18
|
-
* resolution is pure over world.json and a listing, so the rules are testable
|
|
19
|
-
* without a project on disk; the removal is best-effort, since a directory
|
|
20
|
-
* that will not delete must never fail a forge that has already landed.
|
|
21
|
-
*/
|
|
22
|
-
import * as fs from 'fs';
|
|
23
|
-
import * as path from 'path';
|
|
24
|
-
import { forgeArtifactKey, parentForgeJobId } from '@bitmagic/world-forger/pipeline/index.js';
|
|
25
|
-
import { FORGE_STORE_DIR } from './local-store.js';
|
|
26
|
-
import { isJsonObject } from '../project/world-json.js';
|
|
27
|
-
/** The job ids the world's assets name — the jobs an edit could reuse bakes from. */
|
|
28
|
-
export function referencedForgeJobs(world) {
|
|
29
|
-
const assets = Array.isArray(world.assets) ? world.assets.filter(isJsonObject) : [];
|
|
30
|
-
const jobs = new Set();
|
|
31
|
-
for (const asset of assets) {
|
|
32
|
-
const jobId = parentForgeJobId(asset);
|
|
33
|
-
if (jobId !== null)
|
|
34
|
-
jobs.add(jobId);
|
|
35
|
-
}
|
|
36
|
-
return jobs;
|
|
37
|
-
}
|
|
38
|
-
/** Of `jobs`, the finished ones neither the world nor `keep` refers to, in listing order. */
|
|
39
|
-
export function staleForgeJobs(world, jobs, keep) {
|
|
40
|
-
const referenced = referencedForgeJobs(world);
|
|
41
|
-
for (const jobId of keep)
|
|
42
|
-
referenced.add(jobId);
|
|
43
|
-
return jobs.filter((job) => job.finished && !referenced.has(job.jobId)).map((job) => job.jobId);
|
|
44
|
-
}
|
|
45
|
-
/** `<root>/.bitmagic/forge/worlds/v3/forged-work` — the store's job directories, by the shared key layout. */
|
|
46
|
-
export function forgeJobsDir(projectRoot) {
|
|
47
|
-
const oneJob = path.dirname(forgeArtifactKey('job', 'persisted'));
|
|
48
|
-
return path.join(projectRoot, FORGE_STORE_DIR, path.dirname(oneJob));
|
|
49
|
-
}
|
|
50
|
-
/** The store's job directories, finished or not; an absent store lists nothing. */
|
|
51
|
-
export function listForgeJobDirs(projectRoot) {
|
|
52
|
-
const dir = forgeJobsDir(projectRoot);
|
|
53
|
-
if (!fs.existsSync(dir))
|
|
54
|
-
return [];
|
|
55
|
-
return fs.readdirSync(dir, { withFileTypes: true })
|
|
56
|
-
.filter((entry) => entry.isDirectory())
|
|
57
|
-
.map((entry) => ({
|
|
58
|
-
jobId: entry.name,
|
|
59
|
-
finished: fs.existsSync(path.join(projectRoot, FORGE_STORE_DIR, forgeArtifactKey(entry.name, 'persisted'))),
|
|
60
|
-
}));
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Remove the finished jobs the project no longer refers to. One line when
|
|
64
|
-
* something was removed, a warning when something would not go; silent otherwise.
|
|
65
|
-
*/
|
|
66
|
-
export function pruneForgeStore(options) {
|
|
67
|
-
const { projectRoot, world, keep, log } = options;
|
|
68
|
-
const stale = staleForgeJobs(world, listForgeJobDirs(projectRoot), keep);
|
|
69
|
-
const removed = [];
|
|
70
|
-
for (const jobId of stale) {
|
|
71
|
-
const dir = path.join(forgeJobsDir(projectRoot), jobId);
|
|
72
|
-
try {
|
|
73
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
74
|
-
removed.push(jobId);
|
|
75
|
-
}
|
|
76
|
-
catch (error) {
|
|
77
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
78
|
-
log(`warning: could not remove the finished forge job ${jobId} from ${FORGE_STORE_DIR} (${detail}); it is safe to delete by hand.`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (removed.length > 0) {
|
|
82
|
-
log(`Removed ${removed.length} finished forge job${removed.length === 1 ? '' : 's'} from ${FORGE_STORE_DIR} that no level refers to any more.`);
|
|
83
|
-
}
|
|
84
|
-
return removed;
|
|
85
|
-
}
|
|
86
|
-
//# sourceMappingURL=prune-store.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"prune-store.js","sourceRoot":"","sources":["../../src/forge/prune-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The forge store with a server behind it.
|
|
3
|
-
*
|
|
4
|
-
* The CLI keeps every forge artifact in the project's `.bitmagic/forge` (see
|
|
5
|
-
* local-store.ts). This wraps that storage so the artifacts the BROWSER steps
|
|
6
|
-
* write are also PUT to api-server, and anything missing locally is fetched
|
|
7
|
-
* from it — so a job can be resumed, and a forged level edited, from a
|
|
8
|
-
* machine that never ran the original forge. The server owns `input`, `spec`
|
|
9
|
-
* and `forge` and refuses a client write of them; those are written locally
|
|
10
|
-
* only (they arrive on the forge's terminal frame anyway).
|
|
11
|
-
*
|
|
12
|
-
* The mirror is best-effort by design: the local write is the one the run
|
|
13
|
-
* depends on, and a server that cannot be reached must not fail a bake that
|
|
14
|
-
* has already been paid for. A failed mirror is said once, plainly, because
|
|
15
|
-
* its cost is real — the artifact will not be there for another machine.
|
|
16
|
-
* Reads that fall through to the server are cached locally, so a resumed job
|
|
17
|
-
* reads from disk from then on.
|
|
18
|
-
*/
|
|
19
|
-
import type { ForgeStorageEnv } from '@bitmagic/world-forger/pipeline/index.js';
|
|
20
|
-
import type { Environment } from '../config/environments.js';
|
|
21
|
-
export interface RemoteForgeStoreDeps {
|
|
22
|
-
environment: Environment;
|
|
23
|
-
/**
|
|
24
|
-
* Resolved on the FIRST remote call, not when the store is built: the store
|
|
25
|
-
* is created before the command's cheap pre-request checks, and a run that
|
|
26
|
-
* never leaves the machine (a fresh forge with every artifact local) must
|
|
27
|
-
* not demand a login it would not otherwise need until the request.
|
|
28
|
-
*/
|
|
29
|
-
accessToken: () => Promise<string>;
|
|
30
|
-
fetch: typeof globalThis.fetch;
|
|
31
|
-
/** Where a failed mirror or fetch is said. */
|
|
32
|
-
warn: (message: string) => void;
|
|
33
|
-
}
|
|
34
|
-
export declare function forgeArtifactUrl(environment: Environment, jobId: string, name: string): string;
|
|
35
|
-
/** Wrap a local storage env so forged-work artifacts are mirrored to, and fetched from, the server. */
|
|
36
|
-
export declare function createMirroredForgeStorage(local: ForgeStorageEnv, deps: RemoteForgeStoreDeps): ForgeStorageEnv;
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The forge store with a server behind it.
|
|
3
|
-
*
|
|
4
|
-
* The CLI keeps every forge artifact in the project's `.bitmagic/forge` (see
|
|
5
|
-
* local-store.ts). This wraps that storage so the artifacts the BROWSER steps
|
|
6
|
-
* write are also PUT to api-server, and anything missing locally is fetched
|
|
7
|
-
* from it — so a job can be resumed, and a forged level edited, from a
|
|
8
|
-
* machine that never ran the original forge. The server owns `input`, `spec`
|
|
9
|
-
* and `forge` and refuses a client write of them; those are written locally
|
|
10
|
-
* only (they arrive on the forge's terminal frame anyway).
|
|
11
|
-
*
|
|
12
|
-
* The mirror is best-effort by design: the local write is the one the run
|
|
13
|
-
* depends on, and a server that cannot be reached must not fail a bake that
|
|
14
|
-
* has already been paid for. A failed mirror is said once, plainly, because
|
|
15
|
-
* its cost is real — the artifact will not be there for another machine.
|
|
16
|
-
* Reads that fall through to the server are cached locally, so a resumed job
|
|
17
|
-
* reads from disk from then on.
|
|
18
|
-
*/
|
|
19
|
-
import { CLIENT_FORGE_ARTIFACTS, parseForgeArtifactKey } from '@bitmagic/world-forger/pipeline/index.js';
|
|
20
|
-
import { apiServerHeaders } from '../telemetry/request-headers.js';
|
|
21
|
-
export function forgeArtifactUrl(environment, jobId, name) {
|
|
22
|
-
return `${environment.apiUrl}/api/cli/v1/forge/jobs/${encodeURIComponent(jobId)}/artifacts/${encodeURIComponent(name)}`;
|
|
23
|
-
}
|
|
24
|
-
/** Wrap a local storage env so forged-work artifacts are mirrored to, and fetched from, the server. */
|
|
25
|
-
export function createMirroredForgeStorage(local, deps) {
|
|
26
|
-
const said = new Set();
|
|
27
|
-
const warnOnce = (key, message) => {
|
|
28
|
-
if (said.has(key))
|
|
29
|
-
return;
|
|
30
|
-
said.add(key);
|
|
31
|
-
deps.warn(message);
|
|
32
|
-
};
|
|
33
|
-
const headers = async () => ({
|
|
34
|
-
...apiServerHeaders(),
|
|
35
|
-
Authorization: `Bearer ${await deps.accessToken()}`,
|
|
36
|
-
});
|
|
37
|
-
const failure = (error) => (error instanceof Error ? error.message : String(error));
|
|
38
|
-
const client = {
|
|
39
|
-
async putObject(params) {
|
|
40
|
-
const written = await local.storageClient.putObject(params);
|
|
41
|
-
const parsed = parseForgeArtifactKey(params.key);
|
|
42
|
-
if (parsed === null || !CLIENT_FORGE_ARTIFACTS.includes(parsed.name))
|
|
43
|
-
return written;
|
|
44
|
-
const body = typeof params.body === 'string' ? params.body : new TextDecoder().decode(params.body);
|
|
45
|
-
try {
|
|
46
|
-
const response = await deps.fetch(forgeArtifactUrl(deps.environment, parsed.jobId, parsed.name), {
|
|
47
|
-
method: 'PUT',
|
|
48
|
-
headers: { ...(await headers()), 'Content-Type': 'application/json' },
|
|
49
|
-
body,
|
|
50
|
-
});
|
|
51
|
-
if (!response.ok) {
|
|
52
|
-
warnOnce(`put:${parsed.name}`, `Could not mirror the forge's "${parsed.name}" record to api-server (HTTP ${response.status}). The run continues from `
|
|
53
|
-
+ 'the local copy, but resuming or editing this level from another machine will re-bake it.');
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
warnOnce(`put:${parsed.name}`, `Could not mirror the forge's "${parsed.name}" record to api-server (${failure(error)}). The run continues from `
|
|
58
|
-
+ 'the local copy, but resuming or editing this level from another machine will re-bake it.');
|
|
59
|
-
}
|
|
60
|
-
return written;
|
|
61
|
-
},
|
|
62
|
-
async headObject(params) {
|
|
63
|
-
if (await local.storageClient.headObject(params))
|
|
64
|
-
return true;
|
|
65
|
-
const parsed = parseForgeArtifactKey(params.key);
|
|
66
|
-
if (parsed === null)
|
|
67
|
-
return false;
|
|
68
|
-
try {
|
|
69
|
-
const response = await deps.fetch(forgeArtifactUrl(deps.environment, parsed.jobId, parsed.name), {
|
|
70
|
-
method: 'HEAD',
|
|
71
|
-
headers: await headers(),
|
|
72
|
-
});
|
|
73
|
-
return response.ok;
|
|
74
|
-
}
|
|
75
|
-
catch (error) {
|
|
76
|
-
warnOnce('head', `Could not ask api-server for the job's saved state (${failure(error)}) — working from the local copy only.`);
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
async getObject(params) {
|
|
81
|
-
const here = await local.storageClient.getObject(params);
|
|
82
|
-
if (here !== null)
|
|
83
|
-
return here;
|
|
84
|
-
const parsed = parseForgeArtifactKey(params.key);
|
|
85
|
-
if (parsed === null)
|
|
86
|
-
return null;
|
|
87
|
-
try {
|
|
88
|
-
const response = await deps.fetch(forgeArtifactUrl(deps.environment, parsed.jobId, parsed.name), {
|
|
89
|
-
method: 'GET',
|
|
90
|
-
headers: await headers(),
|
|
91
|
-
});
|
|
92
|
-
if (response.status === 404)
|
|
93
|
-
return null;
|
|
94
|
-
if (!response.ok) {
|
|
95
|
-
warnOnce(`get:${parsed.name}`, `api-server refused the job's "${parsed.name}" record (HTTP ${response.status}) — working from the local copy only.`);
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
99
|
-
// Cached locally so the rest of the run, and the next one, read from disk.
|
|
100
|
-
await local.storageClient.putObject({
|
|
101
|
-
bucket: params.bucket, key: params.key, body: bytes, contentType: 'application/json', cacheControl: 'no-store, max-age=0',
|
|
102
|
-
});
|
|
103
|
-
return bytes;
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
warnOnce('get', `Could not fetch the job's saved state from api-server (${failure(error)}) — working from the local copy only.`);
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
return { ...local, storageClient: client };
|
|
112
|
-
}
|
|
113
|
-
//# sourceMappingURL=remote-store.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"remote-store.js","sourceRoot":"","sources":["../../src/forge/remote-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
export interface CharacterArgs {
|
|
2
|
-
name: string;
|
|
3
|
-
prompt: string;
|
|
4
|
-
applyToPlayer?: boolean;
|
|
5
|
-
headScale?: string;
|
|
6
|
-
/** `--fresh`: forge a new one, never substitute a stored body. */
|
|
7
|
-
fresh?: boolean;
|
|
8
|
-
/**
|
|
9
|
-
* citty's negative boolean: `--no-library` arrives as `false`; the flag has no positive form.
|
|
10
|
-
* The deprecated spelling of `--fresh`, kept working because every scaffolded AGENTS.md
|
|
11
|
-
* written before the rename teaches it.
|
|
12
|
-
*/
|
|
13
|
-
library?: boolean;
|
|
14
|
-
/** `--library-only`: take the closest stored body, never forge, never charge. */
|
|
15
|
-
libraryOnly?: boolean;
|
|
16
|
-
/** A row a prior search returned, which `bitmagic character add --id` adds verbatim. */
|
|
17
|
-
libraryPick?: {
|
|
18
|
-
sid: string;
|
|
19
|
-
assetUrl: string;
|
|
20
|
-
description?: string;
|
|
21
|
-
similarity: number;
|
|
22
|
-
};
|
|
23
|
-
/** Absent = the generator's default (true); the command fills it from the project's art style. */
|
|
24
|
-
voxelize?: boolean;
|
|
25
|
-
/** Asset Forger image-to-mesh model; absent = the Forger default. */
|
|
26
|
-
meshModel?: string;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* The character default a project's art style implies: a low-poly game keeps the textured rig.
|
|
30
|
-
* Read from the cwd's project like every other generate command; outside a project the answer
|
|
31
|
-
* is "declares nothing", so the generator's own default (voxelize) stands and `loadProjectContext`
|
|
32
|
-
* reports the missing project on the very next line.
|
|
33
|
-
*/
|
|
34
|
-
export declare function defaultVoxelizeForProject(): boolean | undefined;
|
|
35
|
-
export declare function buildCharacterBody(args: CharacterArgs): Record<string, unknown>;
|
|
36
|
-
export declare const NAME_ARG: {
|
|
37
|
-
readonly type: "string";
|
|
38
|
-
readonly required: true;
|
|
39
|
-
readonly description: "Unique name for the character.";
|
|
40
|
-
};
|
|
41
|
-
export declare const PROMPT_ARG: {
|
|
42
|
-
readonly type: "string";
|
|
43
|
-
readonly required: true;
|
|
44
|
-
readonly description: "Description of the character.";
|
|
45
|
-
};
|
|
46
|
-
export declare const APPLY_TO_PLAYER_ARG: {
|
|
47
|
-
readonly type: "boolean";
|
|
48
|
-
readonly description: "Set this character as the PLAYER. Omit to use the generator default (true).";
|
|
49
|
-
};
|
|
50
|
-
export declare const HEAD_SCALE_ARG: {
|
|
51
|
-
readonly type: "string";
|
|
52
|
-
readonly description: "Head size as a percentage of normal, 50-300. Omit to use the generator default.";
|
|
53
|
-
};
|
|
54
|
-
export declare const FRESH_ARG: {
|
|
55
|
-
readonly type: "boolean";
|
|
56
|
-
readonly description: string;
|
|
57
|
-
};
|
|
58
|
-
export declare const VOXELIZE_ARG: {
|
|
59
|
-
readonly type: "boolean";
|
|
60
|
-
readonly description: "Voxelize the rigged body. Omit to follow game.json artStyle: voxel games voxelize, low-poly games keep the textured rig.";
|
|
61
|
-
readonly negativeDescription: "Keep the textured low-poly rig instead of voxelizing it.";
|
|
62
|
-
};
|
|
63
|
-
export declare const MESH_MODEL_ARG: {
|
|
64
|
-
readonly type: "string";
|
|
65
|
-
readonly description: "Image-to-mesh model. Low-poly characters already default to 'runware-rodin-gen-2', which bills 60 sparks and takes ~3 min; pass 'unified-trellis' to put one back on the cheap lane at 15 sparks. Voxel characters ignore the default and use the Forger's.";
|
|
66
|
-
};
|
|
67
|
-
/**
|
|
68
|
-
* The deprecated spelling of `--fresh`.
|
|
69
|
-
*
|
|
70
|
-
* Kept declared because citty derives `--no-library` from an argument NAMED
|
|
71
|
-
* `library`, so there is no way to accept the old flag without it — and no way
|
|
72
|
-
* to hide it either: citty's `ArgDef` has no `hidden` field (only `CommandMeta`
|
|
73
|
-
* does). So it stays visible and says what it is.
|
|
74
|
-
*/
|
|
75
|
-
export declare const LIBRARY_ARG: {
|
|
76
|
-
readonly type: "boolean";
|
|
77
|
-
readonly description: "Deprecated and unnecessary — the library is consulted by default. Use --fresh to skip it.";
|
|
78
|
-
readonly negativeDescription: "Deprecated spelling of --fresh. Always makes a new character.";
|
|
79
|
-
};
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The character request: its flags, and the body they map onto.
|
|
3
|
-
*
|
|
4
|
-
* Lives here rather than in either command because TWO commands present it —
|
|
5
|
-
* `bitmagic character generate` and the deprecated `bitmagic generate character`
|
|
6
|
-
* it replaced — and a deprecated alias whose flags have drifted from the command
|
|
7
|
-
* it aliases is worse than no alias at all. One definition, imported by both.
|
|
8
|
-
*
|
|
9
|
-
* Field names are exactly what `characterParamsSchema`
|
|
10
|
-
* (shared/asset-core/src/generators/character.ts) expects, and a flag the
|
|
11
|
-
* creator did not pass is OMITTED rather than sent as `undefined` or as a
|
|
12
|
-
* restated default — the generator owns its defaults.
|
|
13
|
-
*/
|
|
14
|
-
import { findProjectRootOrNull } from '../project/context.js';
|
|
15
|
-
import { readArtStyleDeclaration } from '../project/art-style.js';
|
|
16
|
-
import { parseNumberFlag } from '../commands/number-flag.js';
|
|
17
|
-
/** Drops the keys an absent flag left `undefined`; `false` and `0` are deliberate values. */
|
|
18
|
-
function withoutAbsentFlags(fields) {
|
|
19
|
-
return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* The character default a project's art style implies: a low-poly game keeps the textured rig.
|
|
23
|
-
* Read from the cwd's project like every other generate command; outside a project the answer
|
|
24
|
-
* is "declares nothing", so the generator's own default (voxelize) stands and `loadProjectContext`
|
|
25
|
-
* reports the missing project on the very next line.
|
|
26
|
-
*/
|
|
27
|
-
export function defaultVoxelizeForProject() {
|
|
28
|
-
const root = findProjectRootOrNull();
|
|
29
|
-
if (root === null)
|
|
30
|
-
return undefined;
|
|
31
|
-
return readArtStyleDeclaration(root).artStyle === 'low-poly' ? false : undefined;
|
|
32
|
-
}
|
|
33
|
-
export function buildCharacterBody(args) {
|
|
34
|
-
return withoutAbsentFlags({
|
|
35
|
-
name: args.name,
|
|
36
|
-
prompt: args.prompt,
|
|
37
|
-
applyToPlayer: args.applyToPlayer,
|
|
38
|
-
headScale: parseNumberFlag('head-scale', args.headScale, { min: 50, max: 300 }),
|
|
39
|
-
// `--fresh` and the deprecated `--no-library` are the same request. Neither
|
|
40
|
-
// passed leaves the field absent, so the generator's default (search first)
|
|
41
|
-
// stands rather than being restated here.
|
|
42
|
-
useLibrary: args.fresh === true ? false : args.library,
|
|
43
|
-
libraryOnly: args.libraryOnly,
|
|
44
|
-
libraryPick: args.libraryPick,
|
|
45
|
-
voxelize: args.voxelize,
|
|
46
|
-
meshModel: args.meshModel,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
// ============================================================================
|
|
50
|
-
// Flag definitions, shared by `character generate` and the deprecated alias
|
|
51
|
-
// ============================================================================
|
|
52
|
-
export const NAME_ARG = {
|
|
53
|
-
type: 'string',
|
|
54
|
-
required: true,
|
|
55
|
-
description: 'Unique name for the character.',
|
|
56
|
-
};
|
|
57
|
-
export const PROMPT_ARG = {
|
|
58
|
-
type: 'string',
|
|
59
|
-
required: true,
|
|
60
|
-
description: 'Description of the character.',
|
|
61
|
-
};
|
|
62
|
-
export const APPLY_TO_PLAYER_ARG = {
|
|
63
|
-
type: 'boolean',
|
|
64
|
-
description: 'Set this character as the PLAYER. Omit to use the generator default (true).',
|
|
65
|
-
};
|
|
66
|
-
export const HEAD_SCALE_ARG = {
|
|
67
|
-
type: 'string',
|
|
68
|
-
description: 'Head size as a percentage of normal, 50-300. Omit to use the generator default.',
|
|
69
|
-
};
|
|
70
|
-
export const FRESH_ARG = {
|
|
71
|
-
type: 'boolean',
|
|
72
|
-
description: 'Forge a new character even when the library has a close match. '
|
|
73
|
-
+ 'Use it when you want this exact brief made rather than the nearest ready-made body.',
|
|
74
|
-
};
|
|
75
|
-
export const VOXELIZE_ARG = {
|
|
76
|
-
type: 'boolean',
|
|
77
|
-
description: 'Voxelize the rigged body. Omit to follow game.json artStyle: voxel games voxelize, low-poly games keep the textured rig.',
|
|
78
|
-
negativeDescription: 'Keep the textured low-poly rig instead of voxelizing it.',
|
|
79
|
-
};
|
|
80
|
-
export const MESH_MODEL_ARG = {
|
|
81
|
-
type: 'string',
|
|
82
|
-
description: "Image-to-mesh model. Low-poly characters already default to 'runware-rodin-gen-2', which bills 60 sparks and takes ~3 min; pass 'unified-trellis' to put one back on the cheap lane at 15 sparks. Voxel characters ignore the default and use the Forger's.",
|
|
83
|
-
};
|
|
84
|
-
/**
|
|
85
|
-
* The deprecated spelling of `--fresh`.
|
|
86
|
-
*
|
|
87
|
-
* Kept declared because citty derives `--no-library` from an argument NAMED
|
|
88
|
-
* `library`, so there is no way to accept the old flag without it — and no way
|
|
89
|
-
* to hide it either: citty's `ArgDef` has no `hidden` field (only `CommandMeta`
|
|
90
|
-
* does). So it stays visible and says what it is.
|
|
91
|
-
*/
|
|
92
|
-
export const LIBRARY_ARG = {
|
|
93
|
-
type: 'boolean',
|
|
94
|
-
// Both forms get a description, because citty renders a positive row for any
|
|
95
|
-
// boolean arg whether or not the flag is meant to be used. Left undescribed it
|
|
96
|
-
// was a blank line in `--help` sitting next to `--fresh`.
|
|
97
|
-
description: 'Deprecated and unnecessary — the library is consulted by default. Use --fresh to skip it.',
|
|
98
|
-
negativeDescription: 'Deprecated spelling of --fresh. Always makes a new character.',
|
|
99
|
-
};
|
|
100
|
-
//# sourceMappingURL=character-body.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"character-body.js","sourceRoot":"","sources":["../../src/generate/character-body.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,6FAA6F;AAC7F,SAAS,kBAAkB,CAAC,MAA+B;IACzD,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC;AAC/F,CAAC;AAyBD;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB;IACvC,MAAM,IAAI,GAAG,qBAAqB,EAAE,CAAC;IACrC,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACpC,OAAO,uBAAuB,CAAC,IAAI,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAmB;IACpD,OAAO,kBAAkB,CAAC;QACxB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,SAAS,EAAE,eAAe,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;QAC/E,4EAA4E;QAC5E,4EAA4E;QAC5E,0CAA0C;QAC1C,UAAU,EAAE,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO;QACtD,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC,CAAC;AACL,CAAC;AAED,+EAA+E;AAC/E,4EAA4E;AAC5E,+EAA+E;AAE/E,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,IAAI;IACd,WAAW,EAAE,gCAAgC;CACrC,CAAC;AAEX,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,IAAI;IACd,WAAW,EAAE,+BAA+B;CACpC,CAAC;AAEX,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,6EAA6E;CAClF,CAAC;AAEX,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,iFAAiF;CACtF,CAAC;AAEX,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,iEAAiE;UAC1E,qFAAqF;CACjF,CAAC;AAEX,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,0HAA0H;IACvI,mBAAmB,EAAE,0DAA0D;CACvE,CAAC;AAEX,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,6PAA6P;CAClQ,CAAC;AAEX;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,IAAI,EAAE,SAAS;IACf,6EAA6E;IAC7E,+EAA+E;IAC/E,0DAA0D;IAC1D,WAAW,EAAE,2FAA2F;IACxG,mBAAmB,EAAE,+DAA+D;CAC5E,CAAC"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export declare function characterSearchPath(root: string): string;
|
|
2
|
-
/** One row as the search displayed it. The shape `libraryPick` needs, plus the caption. */
|
|
3
|
-
export interface CharacterCandidate {
|
|
4
|
-
sid: string;
|
|
5
|
-
assetUrl: string;
|
|
6
|
-
description: string;
|
|
7
|
-
subject: string | null;
|
|
8
|
-
similarity: number;
|
|
9
|
-
}
|
|
10
|
-
export interface CharacterSearchRecord {
|
|
11
|
-
/** The prompt that produced this list — `add --id` reuses it as the asset's brief. */
|
|
12
|
-
prompt: string;
|
|
13
|
-
/** Which shelf was searched. `add` sends the matching `voxelize` so the media cannot cross. */
|
|
14
|
-
representation: 'voxel' | 'lowpoly';
|
|
15
|
-
/** The floor automatic substitution would have applied, for the "auto" column. */
|
|
16
|
-
threshold: number;
|
|
17
|
-
searchedAt: string;
|
|
18
|
-
candidates: CharacterCandidate[];
|
|
19
|
-
}
|
|
20
|
-
/** The last search, or null when there is none to trust. */
|
|
21
|
-
export declare function readCharacterSearch(root: string): CharacterSearchRecord | null;
|
|
22
|
-
export declare function writeCharacterSearch(root: string, record: CharacterSearchRecord): void;
|
|
23
|
-
/**
|
|
24
|
-
* The cached row with this id, or null.
|
|
25
|
-
*
|
|
26
|
-
* Case-insensitive on the id because a creator reads it off a table and retypes
|
|
27
|
-
* it, and the corpus uses one case throughout — so a capital letter is a typo
|
|
28
|
-
* with an obvious intent, not a different character.
|
|
29
|
-
*/
|
|
30
|
-
export declare function findCachedCandidate(record: CharacterSearchRecord, sid: string): CharacterCandidate | null;
|