lpc-forge 1.0.0 → 1.0.1
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/assets/manager.d.ts +69 -0
- package/dist/assets/manager.js +452 -0
- package/dist/assets/manager.js.map +1 -0
- package/dist/cli.js +61 -8
- package/package.json +3 -2
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset manager — downloads, caches, and verifies LPC sprite assets
|
|
3
|
+
* for npm-installed CLI users who don't have the full repo checkout.
|
|
4
|
+
*/
|
|
5
|
+
export interface AssetManifest {
|
|
6
|
+
version: string;
|
|
7
|
+
generatedAt: string;
|
|
8
|
+
chunks: AssetChunk[];
|
|
9
|
+
totalSize: number;
|
|
10
|
+
}
|
|
11
|
+
export interface AssetChunk {
|
|
12
|
+
name: string;
|
|
13
|
+
filename: string;
|
|
14
|
+
url: string;
|
|
15
|
+
size: number;
|
|
16
|
+
sha256: string;
|
|
17
|
+
required: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** Platform-aware asset cache directory */
|
|
20
|
+
export declare function getAssetDir(): string;
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the root directory where `spritesheets/` and `sheet_definitions/` live.
|
|
23
|
+
*
|
|
24
|
+
* Priority:
|
|
25
|
+
* 1. LPC_FORGE_ASSETS env var
|
|
26
|
+
* 2. Local repo checkout (spritesheets/ next to package root)
|
|
27
|
+
* 3. Cached asset directory (~/.lpc-forge/assets or platform equivalent)
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveAssetRoot(packageRoot: string): string;
|
|
30
|
+
/** Check if assets are installed and match the expected version */
|
|
31
|
+
export declare function isAssetsInstalled(version?: string): Promise<{
|
|
32
|
+
installed: boolean;
|
|
33
|
+
version?: string;
|
|
34
|
+
chunks?: string[];
|
|
35
|
+
path: string;
|
|
36
|
+
}>;
|
|
37
|
+
/** Fetch the remote asset manifest from the latest GitHub release */
|
|
38
|
+
export declare function fetchRemoteManifest(version?: string): Promise<AssetManifest>;
|
|
39
|
+
export interface DownloadOptions {
|
|
40
|
+
/** Only download required chunks (body + definitions) */
|
|
41
|
+
minimal?: boolean;
|
|
42
|
+
/** Custom asset directory (overrides default) */
|
|
43
|
+
path?: string;
|
|
44
|
+
/** Callback for progress updates */
|
|
45
|
+
onProgress?: (state: DownloadProgress) => void;
|
|
46
|
+
/** Skip interactive prompts */
|
|
47
|
+
nonInteractive?: boolean;
|
|
48
|
+
}
|
|
49
|
+
export interface DownloadProgress {
|
|
50
|
+
phase: 'fetching-manifest' | 'downloading' | 'verifying' | 'extracting' | 'done';
|
|
51
|
+
currentChunk?: string;
|
|
52
|
+
chunkIndex?: number;
|
|
53
|
+
totalChunks?: number;
|
|
54
|
+
bytesDownloaded?: number;
|
|
55
|
+
bytesTotal?: number | null;
|
|
56
|
+
overallDownloaded?: number;
|
|
57
|
+
overallTotal?: number;
|
|
58
|
+
}
|
|
59
|
+
export declare function downloadAssets(options?: DownloadOptions): Promise<void>;
|
|
60
|
+
export declare function cleanAssets(assetDir?: string): Promise<void>;
|
|
61
|
+
/** Ask user yes/no in the terminal. Returns true for yes. */
|
|
62
|
+
export declare function promptYesNo(question: string): Promise<boolean>;
|
|
63
|
+
/**
|
|
64
|
+
* Ensures assets are available. If missing, prompts the user to download them.
|
|
65
|
+
* Returns the resolved asset root path.
|
|
66
|
+
* Throws if assets are unavailable and user declines download.
|
|
67
|
+
*/
|
|
68
|
+
export declare function ensureAssets(packageRoot: string): Promise<string>;
|
|
69
|
+
export declare function downloadWithProgress(options?: DownloadOptions): Promise<void>;
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset manager — downloads, caches, and verifies LPC sprite assets
|
|
3
|
+
* for npm-installed CLI users who don't have the full repo checkout.
|
|
4
|
+
*/
|
|
5
|
+
import { createHash } from 'node:crypto';
|
|
6
|
+
import { existsSync } from 'node:fs';
|
|
7
|
+
import { mkdir, readFile, writeFile, rm, } from 'node:fs/promises';
|
|
8
|
+
import { get as httpsGet } from 'node:https';
|
|
9
|
+
import { get as httpGet } from 'node:http';
|
|
10
|
+
import { homedir, platform } from 'node:os';
|
|
11
|
+
import { join, resolve } from 'node:path';
|
|
12
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
13
|
+
import { pipeline } from 'node:stream/promises';
|
|
14
|
+
import { createInterface } from 'node:readline';
|
|
15
|
+
import { execFile } from 'node:child_process';
|
|
16
|
+
import { promisify } from 'node:util';
|
|
17
|
+
const execFileAsync = promisify(execFile);
|
|
18
|
+
// ──────────────────────────────────────────────
|
|
19
|
+
// Constants
|
|
20
|
+
// ──────────────────────────────────────────────
|
|
21
|
+
const GITHUB_REPO = 'LiberatedPixelCup/Universal-LPC-Spritesheet-Character-Generator';
|
|
22
|
+
const MANIFEST_FILENAME = 'assets-manifest.json';
|
|
23
|
+
const LOCAL_MANIFEST = '.lpc-forge-manifest.json';
|
|
24
|
+
// ──────────────────────────────────────────────
|
|
25
|
+
// Asset directory resolution
|
|
26
|
+
// ──────────────────────────────────────────────
|
|
27
|
+
/** Platform-aware asset cache directory */
|
|
28
|
+
export function getAssetDir() {
|
|
29
|
+
if (process.env.LPC_FORGE_ASSETS) {
|
|
30
|
+
return resolve(process.env.LPC_FORGE_ASSETS);
|
|
31
|
+
}
|
|
32
|
+
const p = platform();
|
|
33
|
+
if (p === 'darwin') {
|
|
34
|
+
return join(homedir(), 'Library', 'Application Support', 'lpc-forge', 'assets');
|
|
35
|
+
}
|
|
36
|
+
if (p === 'win32') {
|
|
37
|
+
return join(process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local'), 'lpc-forge', 'assets');
|
|
38
|
+
}
|
|
39
|
+
// Linux / other — respect XDG
|
|
40
|
+
return join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), 'lpc-forge', 'assets');
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the root directory where `spritesheets/` and `sheet_definitions/` live.
|
|
44
|
+
*
|
|
45
|
+
* Priority:
|
|
46
|
+
* 1. LPC_FORGE_ASSETS env var
|
|
47
|
+
* 2. Local repo checkout (spritesheets/ next to package root)
|
|
48
|
+
* 3. Cached asset directory (~/.lpc-forge/assets or platform equivalent)
|
|
49
|
+
*/
|
|
50
|
+
export function resolveAssetRoot(packageRoot) {
|
|
51
|
+
if (process.env.LPC_FORGE_ASSETS) {
|
|
52
|
+
return resolve(process.env.LPC_FORGE_ASSETS);
|
|
53
|
+
}
|
|
54
|
+
// Check if running from a git clone / dev checkout
|
|
55
|
+
if (existsSync(join(packageRoot, 'spritesheets'))) {
|
|
56
|
+
return packageRoot;
|
|
57
|
+
}
|
|
58
|
+
return getAssetDir();
|
|
59
|
+
}
|
|
60
|
+
// ──────────────────────────────────────────────
|
|
61
|
+
// Status checks
|
|
62
|
+
// ──────────────────────────────────────────────
|
|
63
|
+
/** Check if assets are installed and match the expected version */
|
|
64
|
+
export async function isAssetsInstalled(version) {
|
|
65
|
+
const assetDir = getAssetDir();
|
|
66
|
+
const manifestPath = join(assetDir, LOCAL_MANIFEST);
|
|
67
|
+
if (!existsSync(manifestPath)) {
|
|
68
|
+
return { installed: false, path: assetDir };
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const raw = await readFile(manifestPath, 'utf-8');
|
|
72
|
+
const manifest = JSON.parse(raw);
|
|
73
|
+
// Check if critical directories actually exist
|
|
74
|
+
const hasSpritesheets = existsSync(join(assetDir, 'spritesheets'));
|
|
75
|
+
const hasDefinitions = existsSync(join(assetDir, 'sheet_definitions'));
|
|
76
|
+
if (!hasSpritesheets || !hasDefinitions) {
|
|
77
|
+
return { installed: false, path: assetDir };
|
|
78
|
+
}
|
|
79
|
+
// Version mismatch check
|
|
80
|
+
if (version && manifest.version !== version) {
|
|
81
|
+
return {
|
|
82
|
+
installed: false,
|
|
83
|
+
version: manifest.version,
|
|
84
|
+
chunks: manifest.chunks,
|
|
85
|
+
path: assetDir,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
installed: true,
|
|
90
|
+
version: manifest.version,
|
|
91
|
+
chunks: manifest.chunks,
|
|
92
|
+
path: assetDir,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return { installed: false, path: assetDir };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// ──────────────────────────────────────────────
|
|
100
|
+
// Download helpers
|
|
101
|
+
// ──────────────────────────────────────────────
|
|
102
|
+
function followRedirects(url, maxRedirects = 5) {
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
if (maxRedirects <= 0) {
|
|
105
|
+
reject(new Error('Too many redirects'));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const getter = url.startsWith('https') ? httpsGet : httpGet;
|
|
109
|
+
const req = getter(url, {
|
|
110
|
+
headers: { 'User-Agent': 'lpc-forge-cli' },
|
|
111
|
+
}, (res) => {
|
|
112
|
+
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
113
|
+
res.resume(); // drain the response
|
|
114
|
+
followRedirects(res.headers.location, maxRedirects - 1).then(resolve, reject);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
118
|
+
res.resume();
|
|
119
|
+
reject(new Error(`HTTP ${res.statusCode} fetching ${url}`));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
resolve(res);
|
|
123
|
+
});
|
|
124
|
+
req.on('error', reject);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
async function downloadToFile(url, destPath, onProgress) {
|
|
128
|
+
const res = await followRedirects(url);
|
|
129
|
+
const total = res.headers['content-length'] ? parseInt(res.headers['content-length'], 10) : null;
|
|
130
|
+
await mkdir(join(destPath, '..'), { recursive: true });
|
|
131
|
+
const tmpPath = destPath + '.tmp';
|
|
132
|
+
const ws = createWriteStream(tmpPath);
|
|
133
|
+
let downloaded = 0;
|
|
134
|
+
res.on('data', (chunk) => {
|
|
135
|
+
downloaded += chunk.length;
|
|
136
|
+
onProgress?.(downloaded, total);
|
|
137
|
+
});
|
|
138
|
+
try {
|
|
139
|
+
await pipeline(res, ws);
|
|
140
|
+
// Atomic rename from tmp to final
|
|
141
|
+
const { rename } = await import('node:fs/promises');
|
|
142
|
+
await rename(tmpPath, destPath);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
// Clean up partial file
|
|
146
|
+
await rm(tmpPath, { force: true });
|
|
147
|
+
throw err;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async function computeSha256(filePath) {
|
|
151
|
+
const hash = createHash('sha256');
|
|
152
|
+
const stream = createReadStream(filePath);
|
|
153
|
+
for await (const chunk of stream) {
|
|
154
|
+
hash.update(chunk);
|
|
155
|
+
}
|
|
156
|
+
return hash.digest('hex');
|
|
157
|
+
}
|
|
158
|
+
// ──────────────────────────────────────────────
|
|
159
|
+
// Manifest fetching
|
|
160
|
+
// ──────────────────────────────────────────────
|
|
161
|
+
async function fetchJson(url) {
|
|
162
|
+
const res = await followRedirects(url);
|
|
163
|
+
const chunks = [];
|
|
164
|
+
for await (const chunk of res) {
|
|
165
|
+
chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
|
|
166
|
+
}
|
|
167
|
+
return JSON.parse(Buffer.concat(chunks).toString('utf-8'));
|
|
168
|
+
}
|
|
169
|
+
/** Fetch the remote asset manifest from the latest GitHub release */
|
|
170
|
+
export async function fetchRemoteManifest(version) {
|
|
171
|
+
const tag = version || 'latest';
|
|
172
|
+
// For 'latest', use the GitHub API to resolve the actual tag
|
|
173
|
+
if (tag === 'latest') {
|
|
174
|
+
const releaseUrl = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
|
|
175
|
+
const release = await fetchJson(releaseUrl);
|
|
176
|
+
const manifestAsset = release.assets.find(a => a.name === MANIFEST_FILENAME);
|
|
177
|
+
if (!manifestAsset) {
|
|
178
|
+
throw new Error(`No ${MANIFEST_FILENAME} found in latest release (${release.tag_name}). ` +
|
|
179
|
+
`The release may not have been packaged correctly.`);
|
|
180
|
+
}
|
|
181
|
+
return fetchJson(manifestAsset.browser_download_url);
|
|
182
|
+
}
|
|
183
|
+
const url = `https://github.com/${GITHUB_REPO}/releases/download/${tag}/${MANIFEST_FILENAME}`;
|
|
184
|
+
return fetchJson(url);
|
|
185
|
+
}
|
|
186
|
+
export async function downloadAssets(options = {}) {
|
|
187
|
+
const assetDir = options.path ? resolve(options.path) : getAssetDir();
|
|
188
|
+
// Check available disk space (best-effort)
|
|
189
|
+
await checkDiskSpace(assetDir);
|
|
190
|
+
// 1. Fetch manifest
|
|
191
|
+
options.onProgress?.({ phase: 'fetching-manifest' });
|
|
192
|
+
const manifest = await fetchRemoteManifest();
|
|
193
|
+
// 2. Filter chunks
|
|
194
|
+
let chunks = manifest.chunks;
|
|
195
|
+
if (options.minimal) {
|
|
196
|
+
chunks = chunks.filter(c => c.required);
|
|
197
|
+
}
|
|
198
|
+
const totalSize = chunks.reduce((s, c) => s + c.size, 0);
|
|
199
|
+
let overallDownloaded = 0;
|
|
200
|
+
// 3. Download and extract each chunk
|
|
201
|
+
await mkdir(assetDir, { recursive: true });
|
|
202
|
+
const downloadedChunks = [];
|
|
203
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
204
|
+
const chunk = chunks[i];
|
|
205
|
+
const archivePath = join(assetDir, chunk.filename);
|
|
206
|
+
// Download
|
|
207
|
+
options.onProgress?.({
|
|
208
|
+
phase: 'downloading',
|
|
209
|
+
currentChunk: chunk.name,
|
|
210
|
+
chunkIndex: i,
|
|
211
|
+
totalChunks: chunks.length,
|
|
212
|
+
bytesDownloaded: 0,
|
|
213
|
+
bytesTotal: chunk.size,
|
|
214
|
+
overallDownloaded,
|
|
215
|
+
overallTotal: totalSize,
|
|
216
|
+
});
|
|
217
|
+
await downloadToFile(chunk.url, archivePath, (downloaded, total) => {
|
|
218
|
+
options.onProgress?.({
|
|
219
|
+
phase: 'downloading',
|
|
220
|
+
currentChunk: chunk.name,
|
|
221
|
+
chunkIndex: i,
|
|
222
|
+
totalChunks: chunks.length,
|
|
223
|
+
bytesDownloaded: downloaded,
|
|
224
|
+
bytesTotal: total ?? chunk.size,
|
|
225
|
+
overallDownloaded: overallDownloaded + downloaded,
|
|
226
|
+
overallTotal: totalSize,
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
overallDownloaded += chunk.size;
|
|
230
|
+
// Verify checksum
|
|
231
|
+
options.onProgress?.({
|
|
232
|
+
phase: 'verifying',
|
|
233
|
+
currentChunk: chunk.name,
|
|
234
|
+
chunkIndex: i,
|
|
235
|
+
totalChunks: chunks.length,
|
|
236
|
+
});
|
|
237
|
+
const actualHash = await computeSha256(archivePath);
|
|
238
|
+
if (actualHash !== chunk.sha256) {
|
|
239
|
+
await rm(archivePath, { force: true });
|
|
240
|
+
throw new Error(`Checksum mismatch for ${chunk.filename}. ` +
|
|
241
|
+
`Expected ${chunk.sha256}, got ${actualHash}. ` +
|
|
242
|
+
`The file may be corrupted — try again.`);
|
|
243
|
+
}
|
|
244
|
+
// Extract
|
|
245
|
+
options.onProgress?.({
|
|
246
|
+
phase: 'extracting',
|
|
247
|
+
currentChunk: chunk.name,
|
|
248
|
+
chunkIndex: i,
|
|
249
|
+
totalChunks: chunks.length,
|
|
250
|
+
});
|
|
251
|
+
await execFileAsync('tar', ['xzf', archivePath, '-C', assetDir]);
|
|
252
|
+
// Remove archive after extraction to save space
|
|
253
|
+
await rm(archivePath, { force: true });
|
|
254
|
+
downloadedChunks.push(chunk.name);
|
|
255
|
+
}
|
|
256
|
+
// 4. Write local manifest
|
|
257
|
+
const localManifest = {
|
|
258
|
+
version: manifest.version,
|
|
259
|
+
installedAt: new Date().toISOString(),
|
|
260
|
+
chunks: downloadedChunks,
|
|
261
|
+
};
|
|
262
|
+
await writeFile(join(assetDir, LOCAL_MANIFEST), JSON.stringify(localManifest, null, 2));
|
|
263
|
+
options.onProgress?.({ phase: 'done' });
|
|
264
|
+
}
|
|
265
|
+
// ──────────────────────────────────────────────
|
|
266
|
+
// Disk space check (best-effort)
|
|
267
|
+
// ──────────────────────────────────────────────
|
|
268
|
+
async function checkDiskSpace(dir) {
|
|
269
|
+
// Ensure parent exists for statvfs
|
|
270
|
+
await mkdir(dir, { recursive: true });
|
|
271
|
+
try {
|
|
272
|
+
// Use df on Unix-like systems
|
|
273
|
+
if (platform() !== 'win32') {
|
|
274
|
+
const { stdout } = await execFileAsync('df', ['-k', dir]);
|
|
275
|
+
const lines = stdout.trim().split('\n');
|
|
276
|
+
if (lines.length >= 2) {
|
|
277
|
+
const parts = lines[1].split(/\s+/);
|
|
278
|
+
const availKB = parseInt(parts[3], 10);
|
|
279
|
+
if (!isNaN(availKB)) {
|
|
280
|
+
const availMB = availKB / 1024;
|
|
281
|
+
if (availMB < 2000) { // Less than 2GB available
|
|
282
|
+
throw new Error(`Insufficient disk space: ${Math.round(availMB)} MB available, ` +
|
|
283
|
+
`but assets require ~1.3 GB. Free up space or use --path to specify a different location.`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
// If it's our own insufficient space error, re-throw
|
|
291
|
+
if (err instanceof Error && err.message.includes('Insufficient disk space')) {
|
|
292
|
+
throw err;
|
|
293
|
+
}
|
|
294
|
+
// Otherwise ignore — best-effort check
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// ──────────────────────────────────────────────
|
|
298
|
+
// Clean up
|
|
299
|
+
// ──────────────────────────────────────────────
|
|
300
|
+
export async function cleanAssets(assetDir) {
|
|
301
|
+
const dir = assetDir || getAssetDir();
|
|
302
|
+
if (existsSync(dir)) {
|
|
303
|
+
await rm(dir, { recursive: true, force: true });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// ──────────────────────────────────────────────
|
|
307
|
+
// Interactive prompt
|
|
308
|
+
// ──────────────────────────────────────────────
|
|
309
|
+
/** Ask user yes/no in the terminal. Returns true for yes. */
|
|
310
|
+
export async function promptYesNo(question) {
|
|
311
|
+
// Non-interactive / CI environments
|
|
312
|
+
if (process.env.LPC_FORGE_AUTO_DOWNLOAD === 'true') {
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
if (!process.stdin.isTTY) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
319
|
+
return new Promise((resolve) => {
|
|
320
|
+
rl.question(question, (answer) => {
|
|
321
|
+
rl.close();
|
|
322
|
+
const a = answer.trim().toLowerCase();
|
|
323
|
+
resolve(a === '' || a === 'y' || a === 'yes');
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
// ──────────────────────────────────────────────
|
|
328
|
+
// Ensure assets (called before sprite commands)
|
|
329
|
+
// ──────────────────────────────────────────────
|
|
330
|
+
/**
|
|
331
|
+
* Ensures assets are available. If missing, prompts the user to download them.
|
|
332
|
+
* Returns the resolved asset root path.
|
|
333
|
+
* Throws if assets are unavailable and user declines download.
|
|
334
|
+
*/
|
|
335
|
+
export async function ensureAssets(packageRoot) {
|
|
336
|
+
const assetRoot = resolveAssetRoot(packageRoot);
|
|
337
|
+
// If we found a local checkout with spritesheets, we're good
|
|
338
|
+
if (existsSync(join(assetRoot, 'spritesheets')) && existsSync(join(assetRoot, 'sheet_definitions'))) {
|
|
339
|
+
return assetRoot;
|
|
340
|
+
}
|
|
341
|
+
// Assets not found — need to download
|
|
342
|
+
const chalk = (await import('chalk')).default;
|
|
343
|
+
console.log('');
|
|
344
|
+
console.log(chalk.yellow('⚠️ LPC Forge assets not found.') + ' These are required for sprite generation.');
|
|
345
|
+
console.log('');
|
|
346
|
+
console.log(` Total download: ${chalk.bold('~1.3 GB')} (305,680 sprite files)`);
|
|
347
|
+
console.log(` Install location: ${chalk.gray(getAssetDir())}`);
|
|
348
|
+
console.log('');
|
|
349
|
+
const proceed = await promptYesNo(` Download now? (Y/n): `);
|
|
350
|
+
if (!proceed) {
|
|
351
|
+
console.log('');
|
|
352
|
+
console.log(chalk.gray(' Run ') + chalk.cyan('lpc-forge setup') + chalk.gray(' when ready to download.'));
|
|
353
|
+
console.log(chalk.gray(' Run ') + chalk.cyan('lpc-forge setup --help') + chalk.gray(' for more options.'));
|
|
354
|
+
console.log('');
|
|
355
|
+
process.exit(1);
|
|
356
|
+
}
|
|
357
|
+
// Download with progress display
|
|
358
|
+
await downloadWithProgress();
|
|
359
|
+
return getAssetDir();
|
|
360
|
+
}
|
|
361
|
+
// ──────────────────────────────────────────────
|
|
362
|
+
// Progress display
|
|
363
|
+
// ──────────────────────────────────────────────
|
|
364
|
+
function formatBytes(bytes) {
|
|
365
|
+
if (bytes < 1024)
|
|
366
|
+
return `${bytes} B`;
|
|
367
|
+
if (bytes < 1024 * 1024)
|
|
368
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
369
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
370
|
+
return `${(bytes / (1024 * 1024)).toFixed(0)} MB`;
|
|
371
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
372
|
+
}
|
|
373
|
+
function progressBar(fraction, width = 20) {
|
|
374
|
+
const filled = Math.round(fraction * width);
|
|
375
|
+
const empty = width - filled;
|
|
376
|
+
return '[' + '#'.repeat(filled) + '-'.repeat(empty) + ']';
|
|
377
|
+
}
|
|
378
|
+
export async function downloadWithProgress(options = {}) {
|
|
379
|
+
const chalk = (await import('chalk')).default;
|
|
380
|
+
const ora = (await import('ora')).default;
|
|
381
|
+
let manifest;
|
|
382
|
+
const manifestSpinner = ora('Fetching asset manifest...').start();
|
|
383
|
+
try {
|
|
384
|
+
manifest = await fetchRemoteManifest();
|
|
385
|
+
manifestSpinner.succeed('Asset manifest fetched');
|
|
386
|
+
}
|
|
387
|
+
catch (err) {
|
|
388
|
+
manifestSpinner.fail('Failed to fetch asset manifest');
|
|
389
|
+
throw err;
|
|
390
|
+
}
|
|
391
|
+
let chunks = manifest.chunks;
|
|
392
|
+
if (options.minimal) {
|
|
393
|
+
chunks = chunks.filter(c => c.required);
|
|
394
|
+
}
|
|
395
|
+
const totalSize = chunks.reduce((s, c) => s + c.size, 0);
|
|
396
|
+
const assetDir = options.path ? resolve(options.path) : getAssetDir();
|
|
397
|
+
console.log('');
|
|
398
|
+
console.log(chalk.bold('📦 Downloading LPC Forge assets...'));
|
|
399
|
+
console.log('');
|
|
400
|
+
await mkdir(assetDir, { recursive: true });
|
|
401
|
+
await checkDiskSpace(assetDir);
|
|
402
|
+
const downloadedChunks = [];
|
|
403
|
+
let overallDownloaded = 0;
|
|
404
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
405
|
+
const chunk = chunks[i];
|
|
406
|
+
const archivePath = join(assetDir, chunk.filename);
|
|
407
|
+
// Show which chunk we're downloading
|
|
408
|
+
const spinner = ora({
|
|
409
|
+
text: `${chunk.name} (${formatBytes(chunk.size)})`,
|
|
410
|
+
prefixText: ' ',
|
|
411
|
+
}).start();
|
|
412
|
+
try {
|
|
413
|
+
// Download
|
|
414
|
+
await downloadToFile(chunk.url, archivePath, (downloaded, total) => {
|
|
415
|
+
const t = total ?? chunk.size;
|
|
416
|
+
const pct = Math.round((downloaded / t) * 100);
|
|
417
|
+
spinner.text = `${chunk.name} (${formatBytes(chunk.size)}) ${progressBar(downloaded / t)} ${pct}%`;
|
|
418
|
+
});
|
|
419
|
+
// Verify
|
|
420
|
+
spinner.text = `${chunk.name} — verifying checksum...`;
|
|
421
|
+
const actualHash = await computeSha256(archivePath);
|
|
422
|
+
if (actualHash !== chunk.sha256) {
|
|
423
|
+
await rm(archivePath, { force: true });
|
|
424
|
+
spinner.fail(`${chunk.name} — checksum mismatch`);
|
|
425
|
+
throw new Error(`Checksum mismatch for ${chunk.filename}. ` +
|
|
426
|
+
`Expected ${chunk.sha256}, got ${actualHash}.`);
|
|
427
|
+
}
|
|
428
|
+
// Extract
|
|
429
|
+
spinner.text = `${chunk.name} — extracting...`;
|
|
430
|
+
await execFileAsync('tar', ['xzf', archivePath, '-C', assetDir]);
|
|
431
|
+
await rm(archivePath, { force: true });
|
|
432
|
+
overallDownloaded += chunk.size;
|
|
433
|
+
spinner.succeed(`${chunk.name} (${formatBytes(chunk.size)})`);
|
|
434
|
+
downloadedChunks.push(chunk.name);
|
|
435
|
+
}
|
|
436
|
+
catch (err) {
|
|
437
|
+
spinner.fail(`${chunk.name} — failed`);
|
|
438
|
+
throw err;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// Write local manifest
|
|
442
|
+
const localManifest = {
|
|
443
|
+
version: manifest.version,
|
|
444
|
+
installedAt: new Date().toISOString(),
|
|
445
|
+
chunks: downloadedChunks,
|
|
446
|
+
};
|
|
447
|
+
await writeFile(join(assetDir, LOCAL_MANIFEST), JSON.stringify(localManifest, null, 2));
|
|
448
|
+
console.log('');
|
|
449
|
+
console.log(chalk.green(`✅ Assets installed (${formatBytes(overallDownloaded)}) → ${assetDir}`));
|
|
450
|
+
console.log('');
|
|
451
|
+
}
|
|
452
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/assets/manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EACL,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,GAC/B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,GAAG,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,GAAG,IAAI,OAAO,EAAwB,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AA4B1C,iDAAiD;AACjD,YAAY;AACZ,iDAAiD;AAEjD,MAAM,WAAW,GAAG,iEAAiE,CAAC;AACtF,MAAM,iBAAiB,GAAG,sBAAsB,CAAC;AACjD,MAAM,cAAc,GAAG,0BAA0B,CAAC;AAElD,iDAAiD;AACjD,6BAA6B;AAC7B,iDAAiD;AAEjD,2CAA2C;AAC3C,MAAM,UAAU,WAAW;IACzB,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACjC,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;IACrB,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,qBAAqB,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;IAClF,CAAC;IACD,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;IACtG,CAAC;IACD,8BAA8B;IAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;AACtG,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACjC,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC/C,CAAC;IAED,mDAAmD;IACnD,IAAI,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;QAClD,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,OAAO,WAAW,EAAE,CAAC;AACvB,CAAC;AAED,iDAAiD;AACjD,gBAAgB;AAChB,iDAAiD;AAEjD,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAgB;IAMtD,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IAEpD,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC9C,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAkB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAEhD,+CAA+C;QAC/C,MAAM,eAAe,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC;QACnE,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC;QAEvE,IAAI,CAAC,eAAe,IAAI,CAAC,cAAc,EAAE,CAAC;YACxC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC9C,CAAC;QAED,yBAAyB;QACzB,IAAI,OAAO,IAAI,QAAQ,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;YAC5C,OAAO;gBACL,SAAS,EAAE,KAAK;gBAChB,OAAO,EAAE,QAAQ,CAAC,OAAO;gBACzB,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,IAAI,EAAE,QAAQ;aACf,CAAC;QACJ,CAAC;QAED,OAAO;YACL,SAAS,EAAE,IAAI;YACf,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,IAAI,EAAE,QAAQ;SACf,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC9C,CAAC;AACH,CAAC;AAED,iDAAiD;AACjD,mBAAmB;AACnB,iDAAiD;AAEjD,SAAS,eAAe,CAAC,GAAW,EAAE,YAAY,GAAG,CAAC;IACpD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;YACtB,MAAM,CAAC,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;YACxC,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5D,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE;YACtB,OAAO,EAAE,EAAE,YAAY,EAAE,eAAe,EAAE;SAC3C,EAAE,CAAC,GAAG,EAAE,EAAE;YACT,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;gBAC5F,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,qBAAqB;gBACnC,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC9E,OAAO;YACT,CAAC;YACD,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,EAAE,CAAC;gBAC5C,GAAG,CAAC,MAAM,EAAE,CAAC;gBACb,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,UAAU,aAAa,GAAG,EAAE,CAAC,CAAC,CAAC;gBAC5D,OAAO;YACT,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,CAAC;QACf,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,cAAc,CAC3B,GAAW,EACX,QAAgB,EAChB,UAA+D;IAE/D,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEjG,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEvD,MAAM,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClC,MAAM,EAAE,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAEtC,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;QAC/B,UAAU,IAAI,KAAK,CAAC,MAAM,CAAC;QAC3B,UAAU,EAAE,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACxB,kCAAkC;QAClC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QACpD,MAAM,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,wBAAwB;QACxB,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACnC,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,QAAgB;IAC3C,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,iDAAiD;AACjD,oBAAoB;AACpB,iDAAiD;AAEjD,KAAK,UAAU,SAAS,CAAI,GAAW;IACrC,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAM,CAAC;AAClE,CAAC;AAED,qEAAqE;AACrE,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAAgB;IACxD,MAAM,GAAG,GAAG,OAAO,IAAI,QAAQ,CAAC;IAChC,6DAA6D;IAC7D,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;QACrB,MAAM,UAAU,GAAG,gCAAgC,WAAW,kBAAkB,CAAC;QACjF,MAAM,OAAO,GAAG,MAAM,SAAS,CAAiF,UAAU,CAAC,CAAC;QAE5H,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,iBAAiB,CAAC,CAAC;QAC7E,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CACb,MAAM,iBAAiB,6BAA6B,OAAO,CAAC,QAAQ,KAAK;gBACzE,mDAAmD,CACpD,CAAC;QACJ,CAAC;QACD,OAAO,SAAS,CAAgB,aAAa,CAAC,oBAAoB,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,GAAG,GAAG,sBAAsB,WAAW,sBAAsB,GAAG,IAAI,iBAAiB,EAAE,CAAC;IAC9F,OAAO,SAAS,CAAgB,GAAG,CAAC,CAAC;AACvC,CAAC;AA4BD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,UAA2B,EAAE;IAChE,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAEtE,2CAA2C;IAC3C,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;IAE/B,oBAAoB;IACpB,OAAO,CAAC,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,MAAM,mBAAmB,EAAE,CAAC;IAE7C,mBAAmB;IACnB,IAAI,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC7B,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACzD,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAE1B,qCAAqC;IACrC,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,gBAAgB,GAAa,EAAE,CAAC;IAEtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAEnD,WAAW;QACX,OAAO,CAAC,UAAU,EAAE,CAAC;YACnB,KAAK,EAAE,aAAa;YACpB,YAAY,EAAE,KAAK,CAAC,IAAI;YACxB,UAAU,EAAE,CAAC;YACb,WAAW,EAAE,MAAM,CAAC,MAAM;YAC1B,eAAe,EAAE,CAAC;YAClB,UAAU,EAAE,KAAK,CAAC,IAAI;YACtB,iBAAiB;YACjB,YAAY,EAAE,SAAS;SACxB,CAAC,CAAC;QAEH,MAAM,cAAc,CAAC,KAAK,CAAC,GAAG,EAAE,WAAW,EAAE,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE;YACjE,OAAO,CAAC,UAAU,EAAE,CAAC;gBACnB,KAAK,EAAE,aAAa;gBACpB,YAAY,EAAE,KAAK,CAAC,IAAI;gBACxB,UAAU,EAAE,CAAC;gBACb,WAAW,EAAE,MAAM,CAAC,MAAM;gBAC1B,eAAe,EAAE,UAAU;gBAC3B,UAAU,EAAE,KAAK,IAAI,KAAK,CAAC,IAAI;gBAC/B,iBAAiB,EAAE,iBAAiB,GAAG,UAAU;gBACjD,YAAY,EAAE,SAAS;aACxB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,iBAAiB,IAAI,KAAK,CAAC,IAAI,CAAC;QAEhC,kBAAkB;QAClB,OAAO,CAAC,UAAU,EAAE,CAAC;YACnB,KAAK,EAAE,WAAW;YAClB,YAAY,EAAE,KAAK,CAAC,IAAI;YACxB,UAAU,EAAE,CAAC;YACb,WAAW,EAAE,MAAM,CAAC,MAAM;SAC3B,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,WAAW,CAAC,CAAC;QACpD,IAAI,UAAU,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;YAChC,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACvC,MAAM,IAAI,KAAK,CACb,yBAAyB,KAAK,CAAC,QAAQ,IAAI;gBAC3C,YAAY,KAAK,CAAC,MAAM,SAAS,UAAU,IAAI;gBAC/C,wCAAwC,CACzC,CAAC;QACJ,CAAC;QAED,UAAU;QACV,OAAO,CAAC,UAAU,EAAE,CAAC;YACnB,KAAK,EAAE,YAAY;YACnB,YAAY,EAAE,KAAK,CAAC,IAAI;YACxB,UAAU,EAAE,CAAC;YACb,WAAW,EAAE,MAAM,CAAC,MAAM;SAC3B,CAAC,CAAC;QAEH,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;QAEjE,gDAAgD;QAChD,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAEvC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,0BAA0B;IAC1B,MAAM,aAAa,GAAkB;QACnC,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,MAAM,EAAE,gBAAgB;KACzB,CAAC;IACF,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAExF,OAAO,CAAC,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,iDAAiD;AACjD,iCAAiC;AACjC,iDAAiD;AAEjD,KAAK,UAAU,cAAc,CAAC,GAAW;IACvC,mCAAmC;IACnC,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEtC,IAAI,CAAC;QACH,8BAA8B;QAC9B,IAAI,QAAQ,EAAE,KAAK,OAAO,EAAE,CAAC;YAC3B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;YAC1D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBACtB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACpC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACvC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;oBACpB,MAAM,OAAO,GAAG,OAAO,GAAG,IAAI,CAAC;oBAC/B,IAAI,OAAO,GAAG,IAAI,EAAE,CAAC,CAAC,0BAA0B;wBAC9C,MAAM,IAAI,KAAK,CACb,4BAA4B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,iBAAiB;4BAChE,0FAA0F,CAC3F,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,qDAAqD;QACrD,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAC,EAAE,CAAC;YAC5E,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,uCAAuC;IACzC,CAAC;AACH,CAAC;AAED,iDAAiD;AACjD,WAAW;AACX,iDAAiD;AAEjD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,QAAiB;IACjD,MAAM,GAAG,GAAG,QAAQ,IAAI,WAAW,EAAE,CAAC;IACtC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;AACH,CAAC;AAED,iDAAiD;AACjD,qBAAqB;AACrB,iDAAiD;AAEjD,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,QAAgB;IAChD,oCAAoC;IACpC,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,MAAM,EAAE,CAAC;QACnD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7E,OAAO,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;QACtC,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;YAC/B,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACtC,OAAO,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,iDAAiD;AACjD,gDAAgD;AAChD,iDAAiD;AAEjD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,WAAmB;IACpD,MAAM,SAAS,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAEhD,6DAA6D;IAC7D,IAAI,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC;QACpG,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,sCAAsC;IACtC,MAAM,KAAK,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IAE9C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,iCAAiC,CAAC,GAAG,4CAA4C,CAAC,CAAC;IAC5G,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,sBAAsB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;IAClF,OAAO,CAAC,GAAG,CAAC,wBAAwB,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC;IACjE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,0BAA0B,CAAC,CAAC;IAE9D,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC;QAC5G,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC;QAC7G,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,iCAAiC;IACjC,MAAM,oBAAoB,EAAE,CAAC;IAE7B,OAAO,WAAW,EAAE,CAAC;AACvB,CAAC;AAED,iDAAiD;AACjD,mBAAmB;AACnB,iDAAiD;AAEjD,SAAS,WAAW,CAAC,KAAa;IAChC,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,GAAG,KAAK,IAAI,CAAC;IACtC,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAClE,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAClF,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AAC3D,CAAC;AAED,SAAS,WAAW,CAAC,QAAgB,EAAE,KAAK,GAAG,EAAE;IAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC7B,OAAO,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,UAA2B,EAAE;IACtE,MAAM,KAAK,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IAC9C,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;IAE1C,IAAI,QAAuB,CAAC;IAC5B,MAAM,eAAe,GAAG,GAAG,CAAC,4BAA4B,CAAC,CAAC,KAAK,EAAE,CAAC;IAClE,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,mBAAmB,EAAE,CAAC;QACvC,eAAe,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,eAAe,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;QACvD,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,IAAI,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC7B,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACzD,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAEtE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;IAE/B,MAAM,gBAAgB,GAAa,EAAE,CAAC;IACtC,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAE1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAEnD,qCAAqC;QACrC,MAAM,OAAO,GAAG,GAAG,CAAC;YAClB,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG;YAClD,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC,KAAK,EAAE,CAAC;QAEX,IAAI,CAAC;YACH,WAAW;YACX,MAAM,cAAc,CAAC,KAAK,CAAC,GAAG,EAAE,WAAW,EAAE,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE;gBACjE,MAAM,CAAC,GAAG,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC;gBAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;gBAC/C,OAAO,CAAC,IAAI,GAAG,GAAG,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,WAAW,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC;YACvG,CAAC,CAAC,CAAC;YAEH,SAAS;YACT,OAAO,CAAC,IAAI,GAAG,GAAG,KAAK,CAAC,IAAI,0BAA0B,CAAC;YACvD,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,WAAW,CAAC,CAAC;YACpD,IAAI,UAAU,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;gBAChC,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBACvC,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,sBAAsB,CAAC,CAAC;gBAClD,MAAM,IAAI,KAAK,CACb,yBAAyB,KAAK,CAAC,QAAQ,IAAI;oBAC3C,YAAY,KAAK,CAAC,MAAM,SAAS,UAAU,GAAG,CAC/C,CAAC;YACJ,CAAC;YAED,UAAU;YACV,OAAO,CAAC,IAAI,GAAG,GAAG,KAAK,CAAC,IAAI,kBAAkB,CAAC;YAC/C,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;YACjE,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAEvC,iBAAiB,IAAI,KAAK,CAAC,IAAI,CAAC;YAChC,OAAO,CAAC,OAAO,CACb,GAAG,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAC7C,CAAC;YACF,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,WAAW,CAAC,CAAC;YACvC,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,MAAM,aAAa,GAAkB;QACnC,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,MAAM,EAAE,gBAAgB;KACzB,CAAC;IACF,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAExF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,uBAAuB,WAAW,CAAC,iBAAiB,CAAC,OAAO,QAAQ,EAAE,CAAC,CAAC,CAAC;IACjG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -3,8 +3,10 @@ import { Command } from 'commander';
|
|
|
3
3
|
import { resolve, join } from 'node:path';
|
|
4
4
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { resolveAssetRoot, ensureAssets } from './assets/manager.js';
|
|
6
7
|
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
7
|
-
const
|
|
8
|
+
const PACKAGE_ROOT = resolve(__dirname, '..');
|
|
9
|
+
const REPO_ROOT = resolveAssetRoot(PACKAGE_ROOT);
|
|
8
10
|
const program = new Command();
|
|
9
11
|
program
|
|
10
12
|
.name('lpc-forge')
|
|
@@ -29,8 +31,9 @@ program
|
|
|
29
31
|
const chalk = (await import('chalk')).default;
|
|
30
32
|
const ora = (await import('ora')).default;
|
|
31
33
|
if (opts.listLayers) {
|
|
34
|
+
const assetRoot = await ensureAssets(PACKAGE_ROOT);
|
|
32
35
|
const { loadDefinitions, listLayers } = await import('./character/definitions.js');
|
|
33
|
-
const registry = await loadDefinitions(
|
|
36
|
+
const registry = await loadDefinitions(assetRoot);
|
|
34
37
|
const layers = listLayers(registry);
|
|
35
38
|
for (const [category, items] of Object.entries(layers)) {
|
|
36
39
|
console.log(chalk.bold.cyan(`\n${category}`));
|
|
@@ -40,6 +43,7 @@ program
|
|
|
40
43
|
}
|
|
41
44
|
return;
|
|
42
45
|
}
|
|
46
|
+
const assetRoot = await ensureAssets(PACKAGE_ROOT);
|
|
43
47
|
const spinner = ora('Composing character...').start();
|
|
44
48
|
try {
|
|
45
49
|
const { composeCharacter } = await import('./character/composer.js');
|
|
@@ -76,7 +80,7 @@ program
|
|
|
76
80
|
layers,
|
|
77
81
|
};
|
|
78
82
|
}
|
|
79
|
-
const buffer = await composeCharacter(spec,
|
|
83
|
+
const buffer = await composeCharacter(spec, assetRoot);
|
|
80
84
|
const outputDir = resolve(opts.output);
|
|
81
85
|
await mkdir(outputDir, { recursive: true });
|
|
82
86
|
await writeFile(join(outputDir, 'spritesheet.png'), buffer);
|
|
@@ -111,8 +115,9 @@ program
|
|
|
111
115
|
const chalk = (await import('chalk')).default;
|
|
112
116
|
const ora = (await import('ora')).default;
|
|
113
117
|
const { runBatch } = await import('./character/batch.js');
|
|
118
|
+
const assetRoot = await ensureAssets(PACKAGE_ROOT);
|
|
114
119
|
const spinner = ora('Running batch generation...').start();
|
|
115
|
-
const results = await runBatch(resolve(configPath),
|
|
120
|
+
const results = await runBatch(resolve(configPath), assetRoot, resolve(opts.output));
|
|
116
121
|
const succeeded = results.filter(r => r.success).length;
|
|
117
122
|
const failed = results.filter(r => !r.success).length;
|
|
118
123
|
spinner.succeed(`Batch complete: ${succeeded} succeeded, ${failed} failed`);
|
|
@@ -134,8 +139,9 @@ program
|
|
|
134
139
|
.option('--json', 'Output as JSON')
|
|
135
140
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
136
141
|
.action(async (opts) => {
|
|
142
|
+
const assetRoot = await ensureAssets(PACKAGE_ROOT);
|
|
137
143
|
const { loadDefinitions, listLayers } = await import('./character/definitions.js');
|
|
138
|
-
const registry = await loadDefinitions(
|
|
144
|
+
const registry = await loadDefinitions(assetRoot);
|
|
139
145
|
const layers = listLayers(registry);
|
|
140
146
|
if (opts.category) {
|
|
141
147
|
const filtered = {};
|
|
@@ -335,7 +341,8 @@ program
|
|
|
335
341
|
charSpinner.fail(`Unknown preset: ${opts.character}`);
|
|
336
342
|
process.exit(1);
|
|
337
343
|
}
|
|
338
|
-
const
|
|
344
|
+
const assetRoot = await ensureAssets(PACKAGE_ROOT);
|
|
345
|
+
const charBuffer = await composeCharacter(preset.spec, assetRoot);
|
|
339
346
|
await exportCharacterToGodot(charBuffer, outputDir, opts.character);
|
|
340
347
|
charSpinner.succeed(`${preset.name} character generated`);
|
|
341
348
|
// 3. Generate map
|
|
@@ -420,7 +427,7 @@ program
|
|
|
420
427
|
let enemyCount = 0;
|
|
421
428
|
for (const enemyPreset of enemyPresets) {
|
|
422
429
|
if (PRESETS[enemyPreset]) {
|
|
423
|
-
const enemyBuffer = await composeCharacter(PRESETS[enemyPreset].spec,
|
|
430
|
+
const enemyBuffer = await composeCharacter(PRESETS[enemyPreset].spec, assetRoot);
|
|
424
431
|
await exportCharacterToGodot(enemyBuffer, outputDir, enemyPreset, { isPlayer: false });
|
|
425
432
|
enemyCount++;
|
|
426
433
|
}
|
|
@@ -432,7 +439,7 @@ program
|
|
|
432
439
|
let npcCount = 0;
|
|
433
440
|
for (const npcPreset of npcPresets) {
|
|
434
441
|
if (PRESETS[npcPreset] && npcPreset !== 'guard') {
|
|
435
|
-
const npcBuffer = await composeCharacter(PRESETS[npcPreset].spec,
|
|
442
|
+
const npcBuffer = await composeCharacter(PRESETS[npcPreset].spec, assetRoot);
|
|
436
443
|
await exportCharacterToGodot(npcBuffer, outputDir, `npc_${npcPreset}`, { isPlayer: false });
|
|
437
444
|
npcCount++;
|
|
438
445
|
}
|
|
@@ -934,5 +941,51 @@ program
|
|
|
934
941
|
process.exit(1);
|
|
935
942
|
}
|
|
936
943
|
});
|
|
944
|
+
// === SETUP COMMAND ===
|
|
945
|
+
program
|
|
946
|
+
.command('setup')
|
|
947
|
+
.description('Download and install LPC sprite assets (~1.3 GB)')
|
|
948
|
+
.option('--minimal', 'Download only required assets (body + definitions, ~200 MB)')
|
|
949
|
+
.option('--check', 'Check if assets are installed and up to date')
|
|
950
|
+
.option('--clean', 'Remove cached assets')
|
|
951
|
+
.option('--path <dir>', 'Custom asset directory (also set via LPC_FORGE_ASSETS env var)')
|
|
952
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
953
|
+
.action(async (opts) => {
|
|
954
|
+
const chalk = (await import('chalk')).default;
|
|
955
|
+
const { isAssetsInstalled, getAssetDir, cleanAssets, downloadWithProgress, } = await import('./assets/manager.js');
|
|
956
|
+
if (opts.check) {
|
|
957
|
+
const status = await isAssetsInstalled();
|
|
958
|
+
if (status.installed) {
|
|
959
|
+
console.log(chalk.green('✅ Assets installed'));
|
|
960
|
+
console.log(chalk.gray(` Version: ${status.version}`));
|
|
961
|
+
console.log(chalk.gray(` Location: ${status.path}`));
|
|
962
|
+
if (status.chunks) {
|
|
963
|
+
console.log(chalk.gray(` Chunks: ${status.chunks.join(', ')}`));
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
else {
|
|
967
|
+
console.log(chalk.yellow('⚠️ Assets not installed'));
|
|
968
|
+
console.log(chalk.gray(` Expected at: ${status.path}`));
|
|
969
|
+
if (status.version) {
|
|
970
|
+
console.log(chalk.gray(` Found version: ${status.version} (may need update)`));
|
|
971
|
+
}
|
|
972
|
+
console.log(chalk.gray('\n Run: lpc-forge setup'));
|
|
973
|
+
}
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
if (opts.clean) {
|
|
977
|
+
const dir = opts.path || getAssetDir();
|
|
978
|
+
const ora = (await import('ora')).default;
|
|
979
|
+
const spinner = ora('Removing cached assets...').start();
|
|
980
|
+
await cleanAssets(dir);
|
|
981
|
+
spinner.succeed(`Assets removed from ${dir}`);
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
// Download
|
|
985
|
+
await downloadWithProgress({
|
|
986
|
+
minimal: opts.minimal,
|
|
987
|
+
path: opts.path,
|
|
988
|
+
});
|
|
989
|
+
});
|
|
937
990
|
program.parse();
|
|
938
991
|
//# sourceMappingURL=cli.js.map
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lpc-forge",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Complete 2D RPG asset pipeline — character compositor, procedural map generator, and Godot 4.6 exporter. Built on Liberated Pixel Cup sprites.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"lpc-forge": "
|
|
7
|
+
"lpc-forge": "dist/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist/cli.js",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"dist/utils/**",
|
|
17
17
|
"dist/license.js",
|
|
18
18
|
"dist/license.d.ts",
|
|
19
|
+
"dist/assets/**",
|
|
19
20
|
"assets/**",
|
|
20
21
|
"README.md",
|
|
21
22
|
"LICENSE",
|