@hypersoniclabs/helix-cli 0.1.27-helix3.550 → 0.1.27-helix3.551
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/city/convert/command.d.ts +2 -0
- package/dist/city/convert/command.js +98 -0
- package/dist/city/convert/command.js.map +1 -0
- package/dist/city/convert/config.d.ts +56 -0
- package/dist/city/convert/config.js +78 -0
- package/dist/city/convert/config.js.map +1 -0
- package/dist/city/convert/exec.d.ts +5 -0
- package/dist/city/convert/exec.js +36 -0
- package/dist/city/convert/exec.js.map +1 -0
- package/dist/city/convert/landscapeSource.d.ts +6 -0
- package/dist/city/convert/landscapeSource.js +19 -0
- package/dist/city/convert/landscapeSource.js.map +1 -0
- package/dist/city/convert/optimizations.d.ts +8 -0
- package/dist/city/convert/optimizations.js +41 -0
- package/dist/city/convert/optimizations.js.map +1 -0
- package/dist/city/convert/receipt.d.ts +44 -0
- package/dist/city/convert/receipt.js +60 -0
- package/dist/city/convert/receipt.js.map +1 -0
- package/dist/city/convert/run.d.ts +20 -0
- package/dist/city/convert/run.js +82 -0
- package/dist/city/convert/run.js.map +1 -0
- package/dist/city/convert/spawnPick.d.ts +25 -0
- package/dist/city/convert/spawnPick.js +193 -0
- package/dist/city/convert/spawnPick.js.map +1 -0
- package/dist/city/convert/stages.d.ts +94 -0
- package/dist/city/convert/stages.js +335 -0
- package/dist/city/convert/stages.js.map +1 -0
- package/dist/city/convert/tierArgs.d.ts +10 -0
- package/dist/city/convert/tierArgs.js +21 -0
- package/dist/city/convert/tierArgs.js.map +1 -0
- package/dist/index.js +12 -10
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/scene-create.md +2 -0
- package/package.json +1 -1
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_abs.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_audio.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_engine.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_expression.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_jbeam.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_materials.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_stock_events.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/fmod_studio.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/fsb5.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/sim_audio.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_ac.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_beamng.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_fixtures.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/test_beamng_engine.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/test_sim_audio.cpython-312.pyc +0 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// `helix city convert cs2`: the one way to turn a Cities: Skylines II capture into a publish-ready World (docs/city-convert-cs2.md).
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.registerCityConvert = registerCityConvert;
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const lib_1 = require("../../lib");
|
|
7
|
+
const config_1 = require("./config");
|
|
8
|
+
const exec_1 = require("./exec");
|
|
9
|
+
const optimizations_1 = require("./optimizations");
|
|
10
|
+
const run_1 = require("./run");
|
|
11
|
+
function parseTiers(value) {
|
|
12
|
+
const tiers = [...new Set(value.split(',').map((t) => t.trim()).filter(Boolean))];
|
|
13
|
+
if (!tiers.length || tiers.some((t) => t !== 'desktop' && t !== 'mobile'))
|
|
14
|
+
throw new Error('--tiers must be desktop, mobile or desktop,mobile');
|
|
15
|
+
return tiers;
|
|
16
|
+
}
|
|
17
|
+
function parsePair(value, flag) {
|
|
18
|
+
const parts = value.split(',').map(Number);
|
|
19
|
+
if (parts.length !== 2 || parts.some((n) => !Number.isFinite(n)))
|
|
20
|
+
throw new Error(`${flag} must be "x,z" in metres`);
|
|
21
|
+
return { x: parts[0], z: parts[1] };
|
|
22
|
+
}
|
|
23
|
+
function registerCityConvert(city, fail) {
|
|
24
|
+
const convert = city.command('convert').description('One-shot conversion of a captured city into a publish-ready World (docs/city-convert-cs2.md)');
|
|
25
|
+
convert
|
|
26
|
+
.command('cs2')
|
|
27
|
+
.description('A Cities: Skylines II source-v2 bundle to desktop and phone World roots with every shared optimization applied, collision completed, both tiers perf-gated, and a receipt that proves which ran')
|
|
28
|
+
.argument('[bundle]', 'the CS2 source-v2 bundle folder (terrain/, roads-v3/, placements/, meshes-v2/, areas/, water/, legacy/city/)')
|
|
29
|
+
.requiredOption('--name <slug>', 'World slug and scene root name (lowercase letters, digits, dashes)')
|
|
30
|
+
.option('--out <dir>', 'folder to write everything into (default ./convert-<name>)')
|
|
31
|
+
.option('--title <title>', 'the World title (default: the name, capitalised)')
|
|
32
|
+
.option('--description <text>', 'the World description')
|
|
33
|
+
.option('--config <file>', 'a helix.city-convert/1 file: spawn and the few knobs a map may need; every field has a default')
|
|
34
|
+
.option('--spawn <x,z>', 'open the World here (metres); default: a clear street-level point beside the densest downtown')
|
|
35
|
+
.option('--yaw <deg>', 'spawn heading in degrees (with --spawn); default along the street')
|
|
36
|
+
.option('--tiers <list>', 'desktop, mobile or desktop,mobile (default both)')
|
|
37
|
+
.option('--bounds <minX,minZ,maxX,maxZ>', 'convert only this window of the CS2 world, in scene metres')
|
|
38
|
+
.option('--masses', 'draw buildings as procedural masses even when the bundle has prefab meshes')
|
|
39
|
+
.option('--jobs <n>', 'parallel prototype shards (default 4)')
|
|
40
|
+
.option('--from <stage>', 'rerun this stage and everything after it')
|
|
41
|
+
.option('--stop-after <stage>', 'stop after this stage')
|
|
42
|
+
.option('--force', 'ignore the receipt and run every stage')
|
|
43
|
+
.option('--unlisted', 'the publish plan says unlisted (the default)')
|
|
44
|
+
.option('--public', 'the publish plan says public')
|
|
45
|
+
.option('--private', 'the publish plan says private')
|
|
46
|
+
.option('--list-stages', 'print the stages and the optimizations each applies, then exit')
|
|
47
|
+
.option('--json', 'print the receipt as JSON')
|
|
48
|
+
.action(async (bundle, opts) => {
|
|
49
|
+
try {
|
|
50
|
+
const overrides = {};
|
|
51
|
+
if (opts.title)
|
|
52
|
+
overrides.title = opts.title;
|
|
53
|
+
if (opts.description)
|
|
54
|
+
overrides.description = opts.description;
|
|
55
|
+
if (opts.tiers)
|
|
56
|
+
overrides.tiers = parseTiers(opts.tiers);
|
|
57
|
+
if (opts.spawn)
|
|
58
|
+
overrides.spawn = { ...parsePair(opts.spawn, '--spawn'), ...(opts.yaw !== undefined ? { yawDeg: Number(opts.yaw) } : {}) };
|
|
59
|
+
if (opts.bounds) {
|
|
60
|
+
const b = opts.bounds.split(',').map(Number);
|
|
61
|
+
if (b.length !== 4 || b.some((n) => !Number.isFinite(n)))
|
|
62
|
+
throw new Error('--bounds must be "minX,minZ,maxX,maxZ"');
|
|
63
|
+
overrides.bounds = [b[0], b[1], b[2], b[3]];
|
|
64
|
+
}
|
|
65
|
+
const config = (0, config_1.resolveConvertConfig)(opts.name, opts.config ? (0, node_path_1.resolve)(opts.config) : undefined, overrides);
|
|
66
|
+
if (opts.masses)
|
|
67
|
+
config.buildings.meshes = 'masses';
|
|
68
|
+
const visibility = opts.public ? 'public' : opts.private ? 'private' : opts.unlisted ? 'unlisted' : undefined;
|
|
69
|
+
if (visibility)
|
|
70
|
+
config.publish.visibility = visibility;
|
|
71
|
+
if (opts.listStages) {
|
|
72
|
+
for (const stage of (0, run_1.listStages)(config))
|
|
73
|
+
console.log(`${stage.id.padEnd(22)} ${stage.title}${stage.optimizations.length ? `\n${' '.repeat(23)}applies: ${stage.optimizations.join(', ')}` : ''}`);
|
|
74
|
+
console.log(`\n${optimizations_1.OPTIMIZATIONS.length} named optimizations; runtime (engine) fixes reach every World from the runtime range.`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!bundle)
|
|
78
|
+
throw new Error('give the bundle folder: helix city convert cs2 <bundle> --name <slug> --out <dir>');
|
|
79
|
+
const out = (0, node_path_1.resolve)(opts.out ?? `convert-${opts.name}`);
|
|
80
|
+
const receipt = await (0, run_1.runConvert)({
|
|
81
|
+
bundle: (0, node_path_1.resolve)(bundle), out, config, exec: (0, exec_1.childExec)({ out }), cliVersion: (0, lib_1.cliVersion)(), log: (line) => process.stderr.write(`${line}\n`),
|
|
82
|
+
...(opts.jobs ? { jobs: Math.max(1, Number(opts.jobs)) } : {}), ...(opts.from ? { from: opts.from } : {}), ...(opts.stopAfter ? { stopAfter: opts.stopAfter } : {}), ...(opts.force ? { force: true } : {}),
|
|
83
|
+
});
|
|
84
|
+
if (opts.json) {
|
|
85
|
+
console.log(JSON.stringify(receipt, null, 2));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
console.log(`✔ ${config.title}: ${receipt.stages.length} stages, ${receipt.applied.length} optimizations applied -> ${out}`);
|
|
89
|
+
for (const stage of receipt.stages)
|
|
90
|
+
console.log(` ${stage.id.padEnd(22)} ${(stage.ms / 1000).toFixed(1)} s`);
|
|
91
|
+
console.log(` receipt: ${(0, node_path_1.resolve)(out, 'convert-receipt.json')}\n publish plan: ${(0, node_path_1.resolve)(out, 'publish', 'publish.json')}`);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
fail(`✖ ${error instanceof Error ? error.message : String(error)}`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../../src/city/convert/command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export declare const CONVERT_CONFIG_SCHEMA: "helix.city-convert/1";
|
|
2
|
+
export type ConvertTier = 'desktop' | 'mobile';
|
|
3
|
+
export interface ConvertSpawn {
|
|
4
|
+
x: number;
|
|
5
|
+
z: number;
|
|
6
|
+
/** Rotation about Y in degrees (0 faces -z). Omitted: along the street. */
|
|
7
|
+
yawDeg?: number;
|
|
8
|
+
/** Metres around the spawn kept free of trees. */
|
|
9
|
+
clearM?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface ConvertConfig {
|
|
12
|
+
schema: typeof CONVERT_CONFIG_SCHEMA;
|
|
13
|
+
/** Scene root name and World slug base (`leonida` gives `leonida-c1` and `leonida-mobile-c1`). */
|
|
14
|
+
name: string;
|
|
15
|
+
title: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
/** City id written into the city source (default `city.cs2-<name>`). */
|
|
18
|
+
cityId?: string;
|
|
19
|
+
/** Import only this window of the CS2 world, in scene metres. */
|
|
20
|
+
bounds?: [number, number, number, number];
|
|
21
|
+
/** Omitted: the stage `spawn` picks a clear street-level point near the densest downtown. */
|
|
22
|
+
spawn?: ConvertSpawn;
|
|
23
|
+
tiers: ConvertTier[];
|
|
24
|
+
terrain: {
|
|
25
|
+
/** 4 m is what the engine's stride ladder (4/8/16/32 m) is cut from. */
|
|
26
|
+
metresPerSample: 1 | 2 | 4;
|
|
27
|
+
/** Copernicus margin ring: real-world terrain, wrong for a fictional CS2 map, so off. */
|
|
28
|
+
marginM: number;
|
|
29
|
+
/** `natural`: a CS2 map only maps its lots, so unmapped land is grass and shore sand, not pavement. */
|
|
30
|
+
ground: 'natural' | 'urban';
|
|
31
|
+
/** CS2 height maps carry bogus land along the border that the coarse layer extrudes as a hard rectangle. */
|
|
32
|
+
demEdgeRepair: number;
|
|
33
|
+
};
|
|
34
|
+
vegetation: {
|
|
35
|
+
/** `source`: every tree the map has (the default 16,000 cap read as bare on a 292,028-tree map). */
|
|
36
|
+
maxTrees: 'source' | number;
|
|
37
|
+
};
|
|
38
|
+
buildings: {
|
|
39
|
+
/** `auto`: real prefab meshes when the bundle has them, else procedural masses. */
|
|
40
|
+
meshes: 'auto' | 'real' | 'masses';
|
|
41
|
+
/** Prototype builds run as this many parallel processes per tier. */
|
|
42
|
+
shards: number;
|
|
43
|
+
};
|
|
44
|
+
colliders: {
|
|
45
|
+
/** Building boxes inline in the root document (a document near its memory cap cannot afford the mount). */
|
|
46
|
+
inline: boolean;
|
|
47
|
+
};
|
|
48
|
+
publish: {
|
|
49
|
+
visibility: 'private' | 'unlisted' | 'public';
|
|
50
|
+
world: string | null;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export declare function defaultConvertConfig(name: string): ConvertConfig;
|
|
54
|
+
export declare function validateConvertConfig(config: ConvertConfig, where?: string): ConvertConfig;
|
|
55
|
+
/** Defaults, then the file on top (one level of merge per group), then validation. */
|
|
56
|
+
export declare function resolveConvertConfig(name: string, file?: string, overrides?: Partial<Pick<ConvertConfig, 'title' | 'description' | 'cityId' | 'bounds' | 'spawn' | 'tiers'>>): ConvertConfig;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// `helix.city-convert/1`: the one small file a CS2 conversion reads for what only a person knows (a name, optionally a spawn).
|
|
3
|
+
// Everything else has a default that every converted map gets, so two maps converted with the same CLI differ only in what the
|
|
4
|
+
// map itself says. A field here is never a place to hand-apply an optimisation: those are stages (`stages.ts`).
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CONVERT_CONFIG_SCHEMA = void 0;
|
|
7
|
+
exports.defaultConvertConfig = defaultConvertConfig;
|
|
8
|
+
exports.validateConvertConfig = validateConvertConfig;
|
|
9
|
+
exports.resolveConvertConfig = resolveConvertConfig;
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
11
|
+
exports.CONVERT_CONFIG_SCHEMA = 'helix.city-convert/1';
|
|
12
|
+
function defaultConvertConfig(name) {
|
|
13
|
+
const title = name.replace(/[-_]+/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
14
|
+
return {
|
|
15
|
+
schema: exports.CONVERT_CONFIG_SCHEMA,
|
|
16
|
+
name,
|
|
17
|
+
title,
|
|
18
|
+
tiers: ['desktop', 'mobile'],
|
|
19
|
+
terrain: { metresPerSample: 4, marginM: 0, ground: 'natural', demEdgeRepair: 5 },
|
|
20
|
+
vegetation: { maxTrees: 'source' },
|
|
21
|
+
buildings: { meshes: 'auto', shards: 8 },
|
|
22
|
+
colliders: { inline: true },
|
|
23
|
+
publish: { visibility: 'unlisted', world: null },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
27
|
+
function validateConvertConfig(config, where = 'convert config') {
|
|
28
|
+
if (config.schema !== exports.CONVERT_CONFIG_SCHEMA)
|
|
29
|
+
throw new Error(`${where}: schema must be ${exports.CONVERT_CONFIG_SCHEMA}`);
|
|
30
|
+
if (!SLUG.test(config.name))
|
|
31
|
+
throw new Error(`${where}: name must be a lowercase slug (letters, digits, dashes), got '${config.name}'`);
|
|
32
|
+
if (!config.tiers.length || config.tiers.some((t) => t !== 'desktop' && t !== 'mobile'))
|
|
33
|
+
throw new Error(`${where}: tiers must list desktop and/or mobile`);
|
|
34
|
+
if (config.bounds && (config.bounds.length !== 4 || config.bounds.some((n) => !Number.isFinite(n))))
|
|
35
|
+
throw new Error(`${where}: bounds must be [minX, minZ, maxX, maxZ] in metres`);
|
|
36
|
+
if (config.spawn && !(Number.isFinite(config.spawn.x) && Number.isFinite(config.spawn.z)))
|
|
37
|
+
throw new Error(`${where}: spawn needs numeric x and z`);
|
|
38
|
+
if (![1, 2, 4].includes(config.terrain.metresPerSample))
|
|
39
|
+
throw new Error(`${where}: terrain.metresPerSample must be 1, 2 or 4`);
|
|
40
|
+
if (!Number.isInteger(config.terrain.demEdgeRepair) || config.terrain.demEdgeRepair < 0 || config.terrain.demEdgeRepair > 16)
|
|
41
|
+
throw new Error(`${where}: terrain.demEdgeRepair must be an integer from 0 to 16`);
|
|
42
|
+
if (config.terrain.ground !== 'natural' && config.terrain.ground !== 'urban')
|
|
43
|
+
throw new Error(`${where}: terrain.ground must be natural or urban`);
|
|
44
|
+
if (config.vegetation.maxTrees !== 'source' && !(Number.isInteger(config.vegetation.maxTrees) && config.vegetation.maxTrees >= 0))
|
|
45
|
+
throw new Error(`${where}: vegetation.maxTrees must be 'source' or a count`);
|
|
46
|
+
if (!['auto', 'real', 'masses'].includes(config.buildings.meshes))
|
|
47
|
+
throw new Error(`${where}: buildings.meshes must be auto, real or masses`);
|
|
48
|
+
if (!Number.isInteger(config.buildings.shards) || config.buildings.shards < 1 || config.buildings.shards > 32)
|
|
49
|
+
throw new Error(`${where}: buildings.shards must be 1 to 32`);
|
|
50
|
+
if (!['private', 'unlisted', 'public'].includes(config.publish.visibility))
|
|
51
|
+
throw new Error(`${where}: publish.visibility must be private, unlisted or public`);
|
|
52
|
+
return config;
|
|
53
|
+
}
|
|
54
|
+
/** Defaults, then the file on top (one level of merge per group), then validation. */
|
|
55
|
+
function resolveConvertConfig(name, file, overrides = {}) {
|
|
56
|
+
const base = defaultConvertConfig(name);
|
|
57
|
+
let given = {};
|
|
58
|
+
if (file) {
|
|
59
|
+
if (!(0, node_fs_1.existsSync)(file))
|
|
60
|
+
throw new Error(`convert config ${file} does not exist`);
|
|
61
|
+
given = JSON.parse((0, node_fs_1.readFileSync)(file, 'utf8'));
|
|
62
|
+
}
|
|
63
|
+
const merged = {
|
|
64
|
+
...base, ...given,
|
|
65
|
+
terrain: { ...base.terrain, ...given.terrain },
|
|
66
|
+
vegetation: { ...base.vegetation, ...given.vegetation },
|
|
67
|
+
buildings: { ...base.buildings, ...given.buildings },
|
|
68
|
+
colliders: { ...base.colliders, ...given.colliders },
|
|
69
|
+
publish: { ...base.publish, ...given.publish },
|
|
70
|
+
};
|
|
71
|
+
if (given.name === undefined)
|
|
72
|
+
merged.name = name;
|
|
73
|
+
for (const [key, value] of Object.entries(overrides))
|
|
74
|
+
if (value !== undefined)
|
|
75
|
+
merged[key] = value;
|
|
76
|
+
return validateConvertConfig(merged, file ?? 'convert config');
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/city/convert/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Runs `helix <args>` as a child process of this very CLI (same node, same entry), with the output in a log file per step. A child
|
|
3
|
+
// per heavy step keeps each step's memory separate (the landscape of a 14 km map wants a 14 GiB heap, a validate does not).
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.childExec = childExec;
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
const TAIL_BYTES = 6000;
|
|
10
|
+
function tailOf(file) {
|
|
11
|
+
try {
|
|
12
|
+
const size = (0, node_fs_1.statSync)(file).size;
|
|
13
|
+
const text = (0, node_fs_1.readFileSync)(file, 'utf8');
|
|
14
|
+
return size > TAIL_BYTES ? text.slice(-TAIL_BYTES) : text;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function childExec(options) {
|
|
21
|
+
const entry = options.entry ?? process.argv[1];
|
|
22
|
+
const logs = (0, node_path_1.join)(options.out, 'logs');
|
|
23
|
+
(0, node_fs_1.mkdirSync)(logs, { recursive: true });
|
|
24
|
+
return (args, { id, memMiB }) => new Promise((resolve) => {
|
|
25
|
+
const log = (0, node_path_1.join)(logs, `${id}.log`);
|
|
26
|
+
const fd = (0, node_fs_1.openSync)(log, 'w');
|
|
27
|
+
const child = (0, node_child_process_1.spawn)(process.execPath, [...(memMiB ? [`--max-old-space-size=${memMiB}`] : []), entry, ...args], { stdio: ['ignore', fd, fd], env: process.env });
|
|
28
|
+
const done = (code) => { try {
|
|
29
|
+
(0, node_fs_1.closeSync)(fd);
|
|
30
|
+
}
|
|
31
|
+
catch { /* already closed */ } resolve({ code, tail: tailOf(log) }); };
|
|
32
|
+
child.on('error', () => done(127));
|
|
33
|
+
child.on('close', (code) => done(code ?? 1));
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../../src/city/convert/exec.ts"],"names":[],"mappings":";AAAA,mIAAmI;AACnI,4HAA4H;;AAiB5H,8BAYC;AA3BD,2DAA2C;AAC3C,qCAAiF;AACjF,yCAAiC;AAGjC,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,SAAS,MAAM,CAAC,IAAY;IAC1B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAA,kBAAQ,EAAC,IAAI,CAAC,CAAC,IAAI,CAAC;QACjC,MAAM,IAAI,GAAG,IAAA,sBAAY,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACxC,OAAO,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC;IAAC,CAAC;AACxB,CAAC;AAED,SAAgB,SAAS,CAAC,OAAwC;IAChE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,IAAA,gBAAI,EAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACvC,IAAA,mBAAS,EAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,IAAI,OAAO,CAAa,CAAC,OAAO,EAAE,EAAE;QACnE,MAAM,GAAG,GAAG,IAAA,gBAAI,EAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QACpC,MAAM,EAAE,GAAG,IAAA,kBAAQ,EAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC9B,MAAM,KAAK,GAAG,IAAA,0BAAK,EAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAChK,MAAM,IAAI,GAAG,CAAC,IAAY,EAAQ,EAAE,GAAG,IAAI,CAAC;YAAC,IAAA,mBAAS,EAAC,EAAE,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACrI,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACnC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// A landscape-only copy of a city source: same frame, roads, areas, trees and terrain, no buildings. `scene create city` builds the
|
|
3
|
+
// landscape root from it and the real-mesh building parts are mounted on top (docs/cs2-mesh-buildings.md). Until now this was a
|
|
4
|
+
// hand script per map (Leonida's `masses.py`); it is a stage so every CS2 map gets the same landscape.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.deriveLandscapeSource = deriveLandscapeSource;
|
|
7
|
+
const source_1 = require("../source");
|
|
8
|
+
async function deriveLandscapeSource(sourceDir, outDir) {
|
|
9
|
+
const source = await (0, source_1.readCitySource)(sourceDir);
|
|
10
|
+
const { manifest, ...layers } = source;
|
|
11
|
+
const draft = {
|
|
12
|
+
...layers,
|
|
13
|
+
manifest: { ...manifest, provenance: [...manifest.provenance, { source: 'helix-city-convert', note: 'landscape-only: buildings removed, real-mesh parts are mounted on this root' }] },
|
|
14
|
+
buildings: [],
|
|
15
|
+
};
|
|
16
|
+
const written = await (0, source_1.writeCitySource)(outDir, draft);
|
|
17
|
+
return { dir: written.dir, buildingsRemoved: source.buildings?.length ?? 0, manifestSha256: written.manifestSha256 };
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=landscapeSource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"landscapeSource.js","sourceRoot":"","sources":["../../../src/city/convert/landscapeSource.ts"],"names":[],"mappings":";AAAA,oIAAoI;AACpI,gIAAgI;AAChI,uGAAuG;;AAMvG,sDAUC;AAdD,sCAAkF;AAI3E,KAAK,UAAU,qBAAqB,CAAC,SAAiB,EAAE,MAAc;IAC3E,MAAM,MAAM,GAAG,MAAM,IAAA,uBAAc,EAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC;IACvC,MAAM,KAAK,GAAoB;QAC7B,GAAG,MAAM;QACT,QAAQ,EAAE,EAAE,GAAG,QAAQ,EAAE,UAAU,EAAE,CAAC,GAAG,QAAQ,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,oBAAoB,EAAE,IAAI,EAAE,6EAA6E,EAAE,CAAC,EAAE;QACtL,SAAS,EAAE,EAAE;KACd,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,IAAA,wBAAe,EAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACrD,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,gBAAgB,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC;AACvH,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Every improvement a converted CS2 map receives after the raw import, named once. A stage claims the ids it applies and the
|
|
3
|
+
// run receipt lists the ones a run actually applied, so "does this map have the Granit fixes" is a lookup, not an investigation.
|
|
4
|
+
// `ref` is the PR or doc that introduced it. Runtime fixes (ENGINE) are not here: every World gets them from the runtime range.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.optimizationById = exports.OPTIMIZATIONS = void 0;
|
|
7
|
+
exports.OPTIMIZATIONS = [
|
|
8
|
+
{ id: 'import.repeated-points', title: 'Consecutive repeated centreline points dropped', ref: 'creator-cli #482', stage: 'import' },
|
|
9
|
+
{ id: 'import.scanner-chunk', title: 'Road scanner keeps a value that opens on a read-chunk boundary', ref: 'creator-cli #481', stage: 'import' },
|
|
10
|
+
{ id: 'import.sea-from-water-field', title: 'Sea level taken from the water field when water.json cannot reach the terrain', ref: 'creator-cli #483', stage: 'import' },
|
|
11
|
+
{ id: 'spawn.clear-street', title: 'Spawn auto-picked on a clear, dry, street-level street beside the densest downtown, facing along it', ref: 'cs2-pipeline-unify', stage: 'spawn' },
|
|
12
|
+
{ id: 'spawn.exact-and-tree-clearing', title: 'Exact spawn and yaw, trees cleared around it', ref: 'creator-cli #488', stage: 'landscape' },
|
|
13
|
+
{ id: 'landscape.landscape-only-source', title: 'Landscape-only source so real-mesh building parts can be mounted', ref: 'creator-cli #485', stage: 'land-source' },
|
|
14
|
+
{ id: 'terrain.natural-ground', title: 'Unmapped land off the streets is grass and shore sand, not pavement', ref: 'creator-cli #486', stage: 'landscape' },
|
|
15
|
+
{ id: 'terrain.dem-edge-repair', title: 'Bogus border land of the CS2 height map repaired', ref: 'creator-cli #488', stage: 'landscape' },
|
|
16
|
+
{ id: 'terrain.conform-under-roads', title: 'Terrain cut and filled under the roads, banks and crisp ground classes', ref: 'creator-cli #423 #426 #427 #439 #488', stage: 'landscape' },
|
|
17
|
+
{ id: 'terrain.no-copernicus-margin', title: 'No real-world Copernicus margin ring around a fictional map', ref: 'cs2-pipeline-unify', stage: 'landscape' },
|
|
18
|
+
{ id: 'sea.water-volume', title: 'Open sea emitted as a water volume from the water layer', ref: 'creator-cli #484', stage: 'landscape' },
|
|
19
|
+
{ id: 'vegetation.all-source-trees', title: 'Every tree of the map kept (cap raised to the source count), street trees, distant impostors', ref: 'creator-cli #439 #458 #486', stage: 'landscape' },
|
|
20
|
+
{ id: 'props.street-kit', title: 'Street-furniture kit and street life placed from the Vault', ref: 'creator-cli #428 #433 #439', stage: 'landscape' },
|
|
21
|
+
{ id: 'roads.generated', title: 'Roads: junctions, ramps with gores, deck profile, portals, turn arrows, legible markings, city materials', ref: 'creator-cli #423 #426 #427', stage: 'landscape' },
|
|
22
|
+
{ id: 'buildings.real-prefab-meshes', title: 'Real CS2 prefab meshes as shared prototypes in building part Scenes under one World root', ref: 'creator-cli #485', stage: 'prototypes' },
|
|
23
|
+
{ id: 'buildings.material-fixes', title: 'Prototype materials: normals decoded, ORM, control-mask recolour, window alpha over glass, wide prefabs cut in tiles', ref: 'creator-cli #485 #486 #431', stage: 'prototypes' },
|
|
24
|
+
{ id: 'tier.phone-profile', title: 'Phone data profile: small prototypes, small groups, 20-draw groups, 128 px HLOD tiles in 1,024 m cells', ref: 'creator-cli #486 / tier profiles', stage: 'prototypes' },
|
|
25
|
+
{ id: 'parts.stream-groups', title: 'Building parts cut into stream groups by median cut under the memory, side, triangle and draw caps', ref: 'creator-cli #485 #486', stage: 'parts' },
|
|
26
|
+
{ id: 'hlod.complete-bake', title: 'Complete HLOD over every part: the whole skyline is always drawn', ref: 'creator-cli #477 #485', stage: 'bake' },
|
|
27
|
+
{ id: 'compose.tier-roots', title: 'Per-tier World root mounting the baked parts (desktop 128 m near, phone 48 m near / 384 m stream)', ref: 'creator-cli #485', stage: 'compose' },
|
|
28
|
+
{ id: 'colliders.decks', title: 'Collider cells for elevated decks, ramps and rails', ref: 'creator-cli #478', stage: 'colliders' },
|
|
29
|
+
{ id: 'colliders.building-boxes', title: 'One collision box per building, inline in the root', ref: 'creator-cli #478 #488', stage: 'colliders' },
|
|
30
|
+
{ id: 'colliders.lips', title: 'Kerb and deck-seam lips bevelled so wheels roll up', ref: 'creator-cli #487', stage: 'colliders' },
|
|
31
|
+
{ id: 'perf.gate-both-tiers', title: 'scene validate --perf on the final desktop and phone roots; a refusal fails the run', ref: 'creator-cli #419', stage: 'validate' },
|
|
32
|
+
{ id: 'publish.layout', title: 'Publish tree laid out (scene/<root>/ plus part folders) and a publish plan with the exact shell command', ref: 'cs2-pipeline-unify', stage: 'finish' },
|
|
33
|
+
];
|
|
34
|
+
const optimizationById = (id) => {
|
|
35
|
+
const found = exports.OPTIMIZATIONS.find((o) => o.id === id);
|
|
36
|
+
if (!found)
|
|
37
|
+
throw new Error(`unknown optimization '${id}'`);
|
|
38
|
+
return found;
|
|
39
|
+
};
|
|
40
|
+
exports.optimizationById = optimizationById;
|
|
41
|
+
//# sourceMappingURL=optimizations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimizations.js","sourceRoot":"","sources":["../../../src/city/convert/optimizations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export declare const CONVERT_RECEIPT_SCHEMA: "helix.city-convert-receipt/1";
|
|
2
|
+
export declare const RECEIPT_FILE = "convert-receipt.json";
|
|
3
|
+
export interface StageReceipt {
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
status: 'ok' | 'failed';
|
|
7
|
+
signature: string;
|
|
8
|
+
startedAt: string;
|
|
9
|
+
ms: number;
|
|
10
|
+
commands: string[][];
|
|
11
|
+
outputs: Record<string, string>;
|
|
12
|
+
optimizations: string[];
|
|
13
|
+
facts?: Record<string, unknown>;
|
|
14
|
+
log?: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ConvertReceipt {
|
|
18
|
+
schema: typeof CONVERT_RECEIPT_SCHEMA;
|
|
19
|
+
cliVersion: string;
|
|
20
|
+
bundle: string;
|
|
21
|
+
out: string;
|
|
22
|
+
config: unknown;
|
|
23
|
+
stages: StageReceipt[];
|
|
24
|
+
/** Optimizations applied by the stages that finished ok. */
|
|
25
|
+
applied: Array<{
|
|
26
|
+
id: string;
|
|
27
|
+
title: string;
|
|
28
|
+
ref: string;
|
|
29
|
+
stage: string;
|
|
30
|
+
}>;
|
|
31
|
+
outcome: 'running' | 'ok' | 'failed';
|
|
32
|
+
}
|
|
33
|
+
export declare const sha256Json: (value: unknown) => string;
|
|
34
|
+
export declare function newReceipt(init: {
|
|
35
|
+
cliVersion: string;
|
|
36
|
+
bundle: string;
|
|
37
|
+
out: string;
|
|
38
|
+
config: unknown;
|
|
39
|
+
}): ConvertReceipt;
|
|
40
|
+
export declare function readReceipt(out: string): ConvertReceipt | null;
|
|
41
|
+
export declare function recordStage(receipt: ConvertReceipt, stage: StageReceipt): void;
|
|
42
|
+
/** The optimizations named by the stages that finished ok, in first-applied order. */
|
|
43
|
+
export declare function refreshApplied(receipt: ConvertReceipt): void;
|
|
44
|
+
export declare function writeReceipt(out: string, receipt: ConvertReceipt): void;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// The conversion receipt: which stages ran, with what, how long, and which named optimizations each applied. Written after every
|
|
3
|
+
// stage, so an interrupted run resumes from it and a finished run proves what it did.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.sha256Json = exports.RECEIPT_FILE = exports.CONVERT_RECEIPT_SCHEMA = void 0;
|
|
6
|
+
exports.newReceipt = newReceipt;
|
|
7
|
+
exports.readReceipt = readReceipt;
|
|
8
|
+
exports.recordStage = recordStage;
|
|
9
|
+
exports.refreshApplied = refreshApplied;
|
|
10
|
+
exports.writeReceipt = writeReceipt;
|
|
11
|
+
const node_crypto_1 = require("node:crypto");
|
|
12
|
+
const node_fs_1 = require("node:fs");
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
const optimizations_1 = require("./optimizations");
|
|
15
|
+
exports.CONVERT_RECEIPT_SCHEMA = 'helix.city-convert-receipt/1';
|
|
16
|
+
exports.RECEIPT_FILE = 'convert-receipt.json';
|
|
17
|
+
const sha256Json = (value) => (0, node_crypto_1.createHash)('sha256').update(JSON.stringify(value)).digest('hex');
|
|
18
|
+
exports.sha256Json = sha256Json;
|
|
19
|
+
function newReceipt(init) {
|
|
20
|
+
return { schema: exports.CONVERT_RECEIPT_SCHEMA, ...init, stages: [], applied: [], outcome: 'running' };
|
|
21
|
+
}
|
|
22
|
+
function readReceipt(out) {
|
|
23
|
+
const file = (0, node_path_1.join)(out, exports.RECEIPT_FILE);
|
|
24
|
+
if (!(0, node_fs_1.existsSync)(file))
|
|
25
|
+
return null;
|
|
26
|
+
const receipt = JSON.parse((0, node_fs_1.readFileSync)(file, 'utf8'));
|
|
27
|
+
return receipt.schema === exports.CONVERT_RECEIPT_SCHEMA ? receipt : null;
|
|
28
|
+
}
|
|
29
|
+
function recordStage(receipt, stage) {
|
|
30
|
+
const at = receipt.stages.findIndex((s) => s.id === stage.id);
|
|
31
|
+
if (at >= 0)
|
|
32
|
+
receipt.stages[at] = stage;
|
|
33
|
+
else
|
|
34
|
+
receipt.stages.push(stage);
|
|
35
|
+
refreshApplied(receipt);
|
|
36
|
+
}
|
|
37
|
+
/** The optimizations named by the stages that finished ok, in first-applied order. */
|
|
38
|
+
function refreshApplied(receipt) {
|
|
39
|
+
const seen = new Set();
|
|
40
|
+
receipt.applied = [];
|
|
41
|
+
for (const s of receipt.stages) {
|
|
42
|
+
if (s.status !== 'ok')
|
|
43
|
+
continue;
|
|
44
|
+
for (const id of s.optimizations) {
|
|
45
|
+
if (seen.has(id))
|
|
46
|
+
continue;
|
|
47
|
+
seen.add(id);
|
|
48
|
+
const { title, ref, stage: owner } = (0, optimizations_1.optimizationById)(id);
|
|
49
|
+
receipt.applied.push({ id, title, ref, stage: owner });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function writeReceipt(out, receipt) {
|
|
54
|
+
const file = (0, node_path_1.join)(out, exports.RECEIPT_FILE);
|
|
55
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(file), { recursive: true });
|
|
56
|
+
const tmp = `${file}.tmp`;
|
|
57
|
+
(0, node_fs_1.writeFileSync)(tmp, `${JSON.stringify(receipt, null, 2)}\n`);
|
|
58
|
+
(0, node_fs_1.renameSync)(tmp, file);
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=receipt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.js","sourceRoot":"","sources":["../../../src/city/convert/receipt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ConvertConfig } from './config';
|
|
2
|
+
import { type ConvertReceipt } from './receipt';
|
|
3
|
+
import { type Exec, type Facts, type StageDef } from './stages';
|
|
4
|
+
export interface RunOptions {
|
|
5
|
+
bundle: string;
|
|
6
|
+
out: string;
|
|
7
|
+
config: ConvertConfig;
|
|
8
|
+
exec: Exec;
|
|
9
|
+
cliVersion: string;
|
|
10
|
+
log?: (line: string) => void;
|
|
11
|
+
jobs?: number;
|
|
12
|
+
/** Rerun this stage and everything after it. */
|
|
13
|
+
from?: string;
|
|
14
|
+
/** Stop after this stage (it still counts as run). */
|
|
15
|
+
stopAfter?: string;
|
|
16
|
+
/** Ignore the receipt; run every stage. */
|
|
17
|
+
force?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function listStages(config: ConvertConfig, meshMode?: Facts['meshMode'] | 'unknown'): StageDef[];
|
|
20
|
+
export declare function runConvert(options: RunOptions): Promise<ConvertReceipt>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// The conversion runner: stages in dependency order, a receipt after each, resume from the receipt, stop on the first failure.
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.listStages = listStages;
|
|
5
|
+
exports.runConvert = runConvert;
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
const receipt_1 = require("./receipt");
|
|
9
|
+
const stages_1 = require("./stages");
|
|
10
|
+
function listStages(config, meshMode = 'unknown') {
|
|
11
|
+
return (0, stages_1.buildStages)(config, meshMode);
|
|
12
|
+
}
|
|
13
|
+
async function runConvert(options) {
|
|
14
|
+
const { config, out } = options;
|
|
15
|
+
const log = options.log ?? (() => undefined);
|
|
16
|
+
(0, node_fs_1.mkdirSync)(out, { recursive: true });
|
|
17
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(out, 'convert.json'), `${JSON.stringify(config, null, 2)}\n`);
|
|
18
|
+
const paths = (0, stages_1.convertPaths)(options.bundle, out, config.name);
|
|
19
|
+
const ctx = { config, paths, exec: options.exec, log, jobs: options.jobs ?? 4 };
|
|
20
|
+
const prior = options.force ? null : (0, receipt_1.readReceipt)(out);
|
|
21
|
+
const receipt = (0, receipt_1.newReceipt)({ cliVersion: options.cliVersion, bundle: options.bundle, out, config });
|
|
22
|
+
if (prior && prior.bundle === options.bundle) {
|
|
23
|
+
receipt.stages = prior.stages.filter((s) => s.status === 'ok');
|
|
24
|
+
(0, receipt_1.refreshApplied)(receipt);
|
|
25
|
+
}
|
|
26
|
+
let meshMode = 'unknown';
|
|
27
|
+
let stages = (0, stages_1.buildStages)(config, meshMode);
|
|
28
|
+
if (options.from && !stages.some((s) => s.id === options.from))
|
|
29
|
+
throw new Error(`--from ${options.from}: no such stage (${stages.map((s) => s.id).join(', ')})`);
|
|
30
|
+
if (options.stopAfter && !stages.some((s) => s.id === options.stopAfter))
|
|
31
|
+
throw new Error(`--stop-after ${options.stopAfter}: no such stage (${stages.map((s) => s.id).join(', ')})`);
|
|
32
|
+
let chain = (0, receipt_1.sha256Json)({ bundle: options.bundle, cli: options.cliVersion });
|
|
33
|
+
let rerun = Boolean(options.force);
|
|
34
|
+
for (let index = 0; index < stages.length; index += 1) {
|
|
35
|
+
const stage = stages[index];
|
|
36
|
+
if (stage.id === options.from)
|
|
37
|
+
rerun = true;
|
|
38
|
+
const signature = (0, receipt_1.sha256Json)({ chain, id: stage.id, own: stage.signature(ctx) });
|
|
39
|
+
chain = signature;
|
|
40
|
+
const done = receipt.stages.find((s) => s.id === stage.id);
|
|
41
|
+
const upToDate = !rerun && done !== undefined && done.signature === signature && stage.outputs(ctx).every((p) => (0, node_fs_1.existsSync)(p));
|
|
42
|
+
if (upToDate) {
|
|
43
|
+
log(`${stage.id}: up to date`);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
rerun = true;
|
|
47
|
+
log(`${stage.id}: ${stage.title}`);
|
|
48
|
+
const startedAt = new Date().toISOString();
|
|
49
|
+
const t0 = performance.now();
|
|
50
|
+
try {
|
|
51
|
+
const result = await stage.run(ctx);
|
|
52
|
+
(0, receipt_1.recordStage)(receipt, {
|
|
53
|
+
id: stage.id, title: stage.title, status: 'ok', signature, startedAt, ms: Math.round(performance.now() - t0), commands: result.commands,
|
|
54
|
+
outputs: result.outputs ?? {}, optimizations: stage.optimizations, ...(result.facts ? { facts: result.facts } : {}), log: (0, node_path_1.join)(out, 'logs'),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
59
|
+
(0, receipt_1.recordStage)(receipt, {
|
|
60
|
+
id: stage.id, title: stage.title, status: 'failed', signature, startedAt, ms: Math.round(performance.now() - t0), commands: [], outputs: {}, optimizations: [], error: message, log: (0, node_path_1.join)(out, 'logs'),
|
|
61
|
+
});
|
|
62
|
+
receipt.outcome = 'failed';
|
|
63
|
+
(0, receipt_1.writeReceipt)(out, receipt);
|
|
64
|
+
throw new Error(`stage ${stage.id} failed: ${message}`);
|
|
65
|
+
}
|
|
66
|
+
(0, receipt_1.writeReceipt)(out, receipt);
|
|
67
|
+
}
|
|
68
|
+
if (stage.id === 'facts') {
|
|
69
|
+
meshMode = (0, stages_1.loadFacts)(paths).meshMode;
|
|
70
|
+
const next = (0, stages_1.buildStages)(config, meshMode);
|
|
71
|
+
stages = next;
|
|
72
|
+
}
|
|
73
|
+
if (stage.id === options.stopAfter) {
|
|
74
|
+
(0, receipt_1.writeReceipt)(out, receipt);
|
|
75
|
+
return receipt;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
receipt.outcome = 'ok';
|
|
79
|
+
(0, receipt_1.writeReceipt)(out, receipt);
|
|
80
|
+
return receipt;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=run.js.map
|