@playdrop/playdrop-cli 0.3.8-build.3 → 0.3.10
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 +43 -1
- package/config/client-meta.json +5 -6
- package/dist/apps/build.js +43 -38
- package/dist/catalogue-utils.js +30 -18
- package/dist/catalogue.d.ts +0 -3
- package/dist/catalogue.js +80 -49
- package/dist/clientInfo.js +16 -2
- package/dist/commands/browse.js +10 -1
- package/dist/commands/capture.js +3 -2
- package/dist/commands/create.js +153 -54
- package/dist/commands/createRemixContent.js +61 -46
- package/dist/commands/creations.js +10 -1
- package/dist/commands/devServer.d.ts +1 -1
- package/dist/commands/devShared.js +1 -1
- package/dist/commands/generation.js +91 -74
- package/dist/commands/gettingStarted.js +1 -1
- package/dist/commands/init.js +30 -2
- package/dist/commands/search.js +10 -1
- package/dist/commands/upload-content.d.ts +70 -0
- package/dist/commands/upload-content.js +627 -0
- package/dist/commands/upload-graph.d.ts +23 -0
- package/dist/commands/upload-graph.js +108 -0
- package/dist/commands/upload.js +264 -543
- package/dist/http.d.ts +1 -1
- package/dist/playwright.d.ts +12 -4
- package/dist/proxyFetch.js +3 -2
- package/dist/taskSelection.js +2 -2
- package/node_modules/@playdrop/ai-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/ai-client/dist/index.js +74 -54
- package/node_modules/@playdrop/api-client/dist/client.d.ts +20 -12
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/client.js +6 -8
- package/node_modules/@playdrop/api-client/dist/core/errors.js +11 -11
- package/node_modules/@playdrop/api-client/dist/core/request.d.ts +2 -0
- package/node_modules/@playdrop/api-client/dist/core/request.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/core/request.js +10 -3
- package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +12 -10
- package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/admin.js +33 -30
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +1 -0
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.js +127 -128
- package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts +9 -5
- package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/asset-packs.js +151 -88
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +1 -0
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/assets.js +150 -115
- package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts +3 -1
- package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/auth.js +21 -0
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +1 -0
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/payments.js +10 -0
- package/node_modules/@playdrop/api-client/dist/index.d.ts +34 -31
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +19 -9
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js +2 -0
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/builder.js +95 -75
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/builder.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/scanner.d.ts +2 -3
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/scanner.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/face-map.js +4 -4
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/face-map.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.d.ts +5 -5
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/textured-to-voxel.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/types.d.ts +25 -25
- package/node_modules/@playdrop/boxel-core/dist/src/validation.js +2 -1
- package/node_modules/@playdrop/boxel-core/dist/src/validation.js.map +1 -1
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.js +5 -0
- package/node_modules/@playdrop/config/client-meta.json +5 -6
- package/node_modules/@playdrop/config/dist/src/index.js +6 -6
- package/node_modules/@playdrop/config/dist/test/validateClientEnvironment.test.js +15 -2
- package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +54 -9
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +15 -8
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts +105 -11
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/asset-pack.js +2 -0
- package/node_modules/@playdrop/types/dist/asset.d.ts +18 -50
- package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/ecs.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/ecs.js +10 -6
- package/node_modules/@playdrop/types/dist/entity.d.ts +5 -10
- package/node_modules/@playdrop/types/dist/entity.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/entity.js +40 -23
- package/node_modules/@playdrop/types/dist/graph.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/graph.js +13 -5
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.js +7 -3
- package/node_modules/@playdrop/vox-three/dist/src/vox.d.ts +1 -0
- package/node_modules/@playdrop/vox-three/dist/src/vox.js +15 -6
- package/node_modules/@playdrop/vox-three/dist/src/vox.js.map +1 -1
- package/node_modules/@playdrop/vox-three/dist/test/vox.test.js +16 -0
- package/node_modules/@playdrop/vox-three/dist/test/vox.test.js.map +1 -1
- package/package.json +23 -2
package/dist/commands/create.js
CHANGED
|
@@ -21,6 +21,8 @@ const app_1 = require("@playdrop/types/app");
|
|
|
21
21
|
const catalogue_utils_1 = require("../catalogue-utils");
|
|
22
22
|
const catalogue_1 = require("../catalogue");
|
|
23
23
|
const CATALOGUE_FILENAME = 'catalogue.json';
|
|
24
|
+
const LEGACY_CATALOGUE_VERSION_KEY = ['schema', 'Version'].join('');
|
|
25
|
+
const ALLOWED_CATALOGUE_TOP_LEVEL_KEYS = new Set(['apps', 'assets', 'assetPacks']);
|
|
24
26
|
async function downloadArchive(url) {
|
|
25
27
|
const response = await fetch(url);
|
|
26
28
|
if (!response.ok) {
|
|
@@ -147,6 +149,14 @@ function runNpmInstall(projectDir) {
|
|
|
147
149
|
});
|
|
148
150
|
return result.status === 0;
|
|
149
151
|
}
|
|
152
|
+
function findUnsupportedCatalogueTopLevelKey(parsed) {
|
|
153
|
+
for (const key of Object.keys(parsed)) {
|
|
154
|
+
if (!ALLOWED_CATALOGUE_TOP_LEVEL_KEYS.has(key) && key !== 'games') {
|
|
155
|
+
return key;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
150
160
|
function extractEntryPointFromMetadata(metadata) {
|
|
151
161
|
if (!metadata) {
|
|
152
162
|
return null;
|
|
@@ -265,9 +275,6 @@ function buildCatalogueEntry(name, metadata, sourceInfo, relativeFilePath) {
|
|
|
265
275
|
function readCatalogue(path) {
|
|
266
276
|
try {
|
|
267
277
|
const raw = (0, node_fs_1.readFileSync)(path, 'utf8');
|
|
268
|
-
if (!raw.trim()) {
|
|
269
|
-
return { schemaVersion: 2, apps: [] };
|
|
270
|
-
}
|
|
271
278
|
const parsed = JSON.parse(raw);
|
|
272
279
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
273
280
|
return null;
|
|
@@ -278,14 +285,39 @@ function readCatalogue(path) {
|
|
|
278
285
|
return null;
|
|
279
286
|
}
|
|
280
287
|
}
|
|
288
|
+
function hasNestedCatalogueFiles(rootDir) {
|
|
289
|
+
const rootCataloguePath = (0, node_path_1.join)(rootDir, CATALOGUE_FILENAME);
|
|
290
|
+
const stack = [rootDir];
|
|
291
|
+
while (stack.length > 0) {
|
|
292
|
+
const current = stack.pop();
|
|
293
|
+
const entries = (0, node_fs_1.readdirSync)(current, { withFileTypes: true });
|
|
294
|
+
for (const entry of entries) {
|
|
295
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules') {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
const absolutePath = (0, node_path_1.join)(current, entry.name);
|
|
299
|
+
if (entry.isDirectory()) {
|
|
300
|
+
stack.push(absolutePath);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (entry.isFile() && entry.name === CATALOGUE_FILENAME && absolutePath !== rootCataloguePath) {
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
281
310
|
function ensureCatalogueEntry(name, metadata, cataloguePath, sourceInfo, htmlFilePath) {
|
|
282
311
|
const path = (0, node_path_1.resolve)(cataloguePath);
|
|
283
312
|
const catalogueDir = (0, node_path_1.dirname)(path);
|
|
313
|
+
if (hasNestedCatalogueFiles(catalogueDir)) {
|
|
314
|
+
return { path, createdFile: false, addedEntry: false, error: 'workspace_root_marker_required' };
|
|
315
|
+
}
|
|
284
316
|
const relativeFilePath = (0, node_path_1.relative)(catalogueDir, (0, node_path_1.resolve)(htmlFilePath)) || `${name}.html`;
|
|
285
317
|
const normalizedFilePath = relativeFilePath.replace(/\\/g, '/');
|
|
286
318
|
const entry = buildCatalogueEntry(name, metadata, sourceInfo, normalizedFilePath);
|
|
287
319
|
if (!(0, node_fs_1.existsSync)(path)) {
|
|
288
|
-
const catalogue = {
|
|
320
|
+
const catalogue = { apps: [entry] };
|
|
289
321
|
(0, node_fs_1.writeFileSync)(path, `${JSON.stringify(catalogue, null, 2)}\n`);
|
|
290
322
|
return { path, createdFile: true, addedEntry: true };
|
|
291
323
|
}
|
|
@@ -293,9 +325,13 @@ function ensureCatalogueEntry(name, metadata, cataloguePath, sourceInfo, htmlFil
|
|
|
293
325
|
if (!parsed) {
|
|
294
326
|
return { path, createdFile: false, addedEntry: false, error: 'invalid_json' };
|
|
295
327
|
}
|
|
296
|
-
if (parsed
|
|
328
|
+
if (Object.prototype.hasOwnProperty.call(parsed, LEGACY_CATALOGUE_VERSION_KEY)) {
|
|
297
329
|
return { path, createdFile: false, addedEntry: false, error: 'invalid_schema_version' };
|
|
298
330
|
}
|
|
331
|
+
const unsupportedKey = findUnsupportedCatalogueTopLevelKey(parsed);
|
|
332
|
+
if (unsupportedKey) {
|
|
333
|
+
return { path, createdFile: false, addedEntry: false, error: 'unsupported_top_level_key', unsupportedKey };
|
|
334
|
+
}
|
|
299
335
|
if (!Array.isArray(parsed.apps) && Array.isArray(parsed.games)) {
|
|
300
336
|
return { path, createdFile: false, addedEntry: false, error: 'legacy_games_property' };
|
|
301
337
|
}
|
|
@@ -325,7 +361,17 @@ function logCatalogueOutcome(result, name) {
|
|
|
325
361
|
return false;
|
|
326
362
|
}
|
|
327
363
|
if (result.error === 'invalid_schema_version') {
|
|
328
|
-
(0, messages_1.printErrorWithHelp)(`${label} must
|
|
364
|
+
(0, messages_1.printErrorWithHelp)(`${label} must not define the legacy top-level version field.`, ['Remove the legacy top-level version field from the file and rerun `playdrop project create app`.'], { command: 'project create app' });
|
|
365
|
+
process.exitCode = process.exitCode || 1;
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
if (result.error === 'unsupported_top_level_key') {
|
|
369
|
+
(0, messages_1.printErrorWithHelp)(`${label} contains unsupported top-level key "${result.unsupportedKey || 'unknown'}".`, ['Keep catalogue.json limited to apps, assets, and assetPacks, then rerun `playdrop project create app`.'], { command: 'project create app' });
|
|
370
|
+
process.exitCode = process.exitCode || 1;
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
if (result.error === 'workspace_root_marker_required') {
|
|
374
|
+
(0, messages_1.printErrorWithHelp)(`${label} must stay {} because nested catalogue.json files already exist below this directory.`, ['Create the app inside its own project folder instead of writing entries into the workspace root catalogue.json.'], { command: 'project create app' });
|
|
329
375
|
process.exitCode = process.exitCode || 1;
|
|
330
376
|
return false;
|
|
331
377
|
}
|
|
@@ -344,13 +390,20 @@ function logCatalogueOutcome(result, name) {
|
|
|
344
390
|
}
|
|
345
391
|
function updateExtractedProjectCatalogue(name, metadata, projectCataloguePath, sourceInfo, htmlFilePath) {
|
|
346
392
|
const path = (0, node_path_1.resolve)(projectCataloguePath);
|
|
393
|
+
if (hasNestedCatalogueFiles((0, node_path_1.dirname)(path))) {
|
|
394
|
+
return { path, updated: false, error: 'workspace_root_marker_required' };
|
|
395
|
+
}
|
|
347
396
|
const parsed = readCatalogue(path);
|
|
348
397
|
if (!parsed) {
|
|
349
398
|
return { path, updated: false, error: 'invalid_json' };
|
|
350
399
|
}
|
|
351
|
-
if (parsed
|
|
400
|
+
if (Object.prototype.hasOwnProperty.call(parsed, LEGACY_CATALOGUE_VERSION_KEY)) {
|
|
352
401
|
return { path, updated: false, error: 'invalid_schema_version' };
|
|
353
402
|
}
|
|
403
|
+
const unsupportedKey = findUnsupportedCatalogueTopLevelKey(parsed);
|
|
404
|
+
if (unsupportedKey) {
|
|
405
|
+
return { path, updated: false, error: 'unsupported_top_level_key', unsupportedKey };
|
|
406
|
+
}
|
|
354
407
|
if (!Array.isArray(parsed.apps) && Array.isArray(parsed.games)) {
|
|
355
408
|
return { path, updated: false, error: 'legacy_games_property' };
|
|
356
409
|
}
|
|
@@ -436,7 +489,17 @@ function logProjectCatalogueOutcome(result, name) {
|
|
|
436
489
|
return false;
|
|
437
490
|
}
|
|
438
491
|
if (result.error === 'invalid_schema_version') {
|
|
439
|
-
(0, messages_1.printErrorWithHelp)(`${label} must
|
|
492
|
+
(0, messages_1.printErrorWithHelp)(`${label} must not define the legacy top-level version field.`, ['Remove the legacy top-level version field from the file and rerun `playdrop project create app`.'], { command: 'project create app' });
|
|
493
|
+
process.exitCode = process.exitCode || 1;
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
if (result.error === 'unsupported_top_level_key') {
|
|
497
|
+
(0, messages_1.printErrorWithHelp)(`${label} contains unsupported top-level key "${result.unsupportedKey || 'unknown'}".`, ['Keep catalogue.json limited to apps, assets, and assetPacks, then rerun `playdrop project create app`.'], { command: 'project create app' });
|
|
498
|
+
process.exitCode = process.exitCode || 1;
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
if (result.error === 'workspace_root_marker_required') {
|
|
502
|
+
(0, messages_1.printErrorWithHelp)(`${label} must stay {} because nested catalogue.json files already exist below this directory.`, ['Create the app inside its own project folder instead of writing entries into the workspace root catalogue.json.'], { command: 'project create app' });
|
|
440
503
|
process.exitCode = process.exitCode || 1;
|
|
441
504
|
return false;
|
|
442
505
|
}
|
|
@@ -602,55 +665,91 @@ function parseRemixRef(value) {
|
|
|
602
665
|
ref: (0, types_1.formatContentVersionRef)(parsed),
|
|
603
666
|
};
|
|
604
667
|
}
|
|
668
|
+
function parseRemixScaffoldResponse(response, version) {
|
|
669
|
+
const remixMode = typeof response?.remixMode === 'string' ? response.remixMode : '';
|
|
670
|
+
const archiveUrl = typeof response?.archiveUrl === 'string' && response.archiveUrl.trim().length > 0
|
|
671
|
+
? response.archiveUrl.trim()
|
|
672
|
+
: null;
|
|
673
|
+
const htmlContent = typeof response?.html === 'string' ? response.html : null;
|
|
674
|
+
const externalListingUrl = typeof response?.externalListingUrl === 'string' && response.externalListingUrl.trim().length > 0
|
|
675
|
+
? response.externalListingUrl.trim()
|
|
676
|
+
: null;
|
|
677
|
+
const metadata = response.metadata;
|
|
678
|
+
const entryPoint = extractEntryPointFromMetadata(metadata);
|
|
679
|
+
if (remixMode === 'archive') {
|
|
680
|
+
if (!archiveUrl) {
|
|
681
|
+
throw new Error('Remix response missing archiveUrl');
|
|
682
|
+
}
|
|
683
|
+
return {
|
|
684
|
+
html: null,
|
|
685
|
+
metadata,
|
|
686
|
+
archiveUrl,
|
|
687
|
+
entryPoint,
|
|
688
|
+
sourceVersion: version,
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
if (remixMode === 'inline-html') {
|
|
692
|
+
if (!htmlContent) {
|
|
693
|
+
throw new Error('Remix response missing html');
|
|
694
|
+
}
|
|
695
|
+
return {
|
|
696
|
+
html: htmlContent,
|
|
697
|
+
metadata,
|
|
698
|
+
entryPoint,
|
|
699
|
+
sourceVersion: null,
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
if (remixMode === 'external-metadata-only') {
|
|
703
|
+
if (!externalListingUrl) {
|
|
704
|
+
throw new Error('Remix response missing externalListingUrl');
|
|
705
|
+
}
|
|
706
|
+
const externalMetadata = parseExternalRemixMetadata(response.metadata);
|
|
707
|
+
return {
|
|
708
|
+
html: null,
|
|
709
|
+
metadata: externalMetadata,
|
|
710
|
+
externalListingUrl,
|
|
711
|
+
entryPoint,
|
|
712
|
+
sourceVersion: null,
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
throw new Error(`Unsupported remix mode: ${remixMode || 'missing'}`);
|
|
716
|
+
}
|
|
717
|
+
function parseExternalRemixMetadata(value) {
|
|
718
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
719
|
+
throw new Error('Remix response missing external metadata');
|
|
720
|
+
}
|
|
721
|
+
const record = value;
|
|
722
|
+
const name = typeof record.name === 'string' ? record.name.trim() : '';
|
|
723
|
+
const displayName = typeof record.displayName === 'string' ? record.displayName.trim() : '';
|
|
724
|
+
const description = typeof record.description === 'string' ? record.description.trim() : '';
|
|
725
|
+
const creatorUsername = typeof record.creatorUsername === 'string' ? record.creatorUsername.trim() : '';
|
|
726
|
+
const type = (0, app_1.parseAppType)(record.type) ?? '';
|
|
727
|
+
const emoji = record.emoji;
|
|
728
|
+
const color = record.color;
|
|
729
|
+
if (!name || !displayName || !description || !creatorUsername || !type) {
|
|
730
|
+
throw new Error('Remix response missing required external metadata fields');
|
|
731
|
+
}
|
|
732
|
+
if (emoji !== null && typeof emoji !== 'string') {
|
|
733
|
+
throw new Error('Remix response has invalid external metadata emoji');
|
|
734
|
+
}
|
|
735
|
+
if (color !== null && typeof color !== 'string') {
|
|
736
|
+
throw new Error('Remix response has invalid external metadata color');
|
|
737
|
+
}
|
|
738
|
+
return {
|
|
739
|
+
name,
|
|
740
|
+
displayName,
|
|
741
|
+
description,
|
|
742
|
+
creatorUsername,
|
|
743
|
+
type,
|
|
744
|
+
emoji,
|
|
745
|
+
color,
|
|
746
|
+
surfaceTargets: (0, catalogue_utils_1.parseSurfaceTargets)(record.surfaceTargets).list,
|
|
747
|
+
};
|
|
748
|
+
}
|
|
605
749
|
async function fetchRemixScaffold(client, creator, app, version) {
|
|
606
750
|
try {
|
|
607
751
|
const response = await client.fetchRemixScaffold(creator, app, version);
|
|
608
|
-
|
|
609
|
-
const archiveUrl = typeof response?.archiveUrl === 'string' && response.archiveUrl.trim().length > 0
|
|
610
|
-
? response.archiveUrl.trim()
|
|
611
|
-
: null;
|
|
612
|
-
const htmlContent = typeof response?.html === 'string' ? response.html : null;
|
|
613
|
-
const externalListingUrl = typeof response?.externalListingUrl === 'string' && response.externalListingUrl.trim().length > 0
|
|
614
|
-
? response.externalListingUrl.trim()
|
|
615
|
-
: null;
|
|
616
|
-
const metadata = response.metadata;
|
|
617
|
-
const entryPoint = extractEntryPointFromMetadata(metadata);
|
|
618
|
-
if (remixMode === 'archive') {
|
|
619
|
-
if (!archiveUrl) {
|
|
620
|
-
throw new Error('Remix response missing archiveUrl');
|
|
621
|
-
}
|
|
622
|
-
return {
|
|
623
|
-
html: null,
|
|
624
|
-
metadata,
|
|
625
|
-
archiveUrl,
|
|
626
|
-
entryPoint,
|
|
627
|
-
sourceVersion: version,
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
if (remixMode === 'inline-html') {
|
|
631
|
-
if (!htmlContent) {
|
|
632
|
-
throw new Error('Remix response missing html');
|
|
633
|
-
}
|
|
634
|
-
return {
|
|
635
|
-
html: htmlContent,
|
|
636
|
-
metadata,
|
|
637
|
-
entryPoint,
|
|
638
|
-
sourceVersion: null,
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
if (remixMode === 'external-metadata-only') {
|
|
642
|
-
if (!externalListingUrl) {
|
|
643
|
-
throw new Error('Remix response missing externalListingUrl');
|
|
644
|
-
}
|
|
645
|
-
return {
|
|
646
|
-
html: null,
|
|
647
|
-
metadata,
|
|
648
|
-
externalListingUrl,
|
|
649
|
-
entryPoint,
|
|
650
|
-
sourceVersion: null,
|
|
651
|
-
};
|
|
652
|
-
}
|
|
653
|
-
throw new Error(`Unsupported remix mode: ${remixMode || 'missing'}`);
|
|
752
|
+
return parseRemixScaffoldResponse(response, version);
|
|
654
753
|
}
|
|
655
754
|
catch (unknownError) {
|
|
656
755
|
if (unknownError instanceof types_1.UnsupportedClientError) {
|
|
@@ -13,6 +13,33 @@ const http_1 = require("../http");
|
|
|
13
13
|
const messages_1 = require("../messages");
|
|
14
14
|
const init_1 = require("./init");
|
|
15
15
|
const CATALOGUE_FILENAME = 'catalogue.json';
|
|
16
|
+
const LEGACY_CATALOGUE_VERSION_KEY = ['schema', 'Version'].join('');
|
|
17
|
+
const ALLOWED_CATALOGUE_TOP_LEVEL_KEYS = new Set(['apps', 'assets', 'assetPacks']);
|
|
18
|
+
const MIME_TYPE_TO_EXTENSION = {
|
|
19
|
+
'image/png': '.png',
|
|
20
|
+
'image/jpeg': '.jpg',
|
|
21
|
+
'image/webp': '.webp',
|
|
22
|
+
'image/gif': '.gif',
|
|
23
|
+
'audio/mpeg': '.mp3',
|
|
24
|
+
'audio/wav': '.wav',
|
|
25
|
+
'audio/ogg': '.ogg',
|
|
26
|
+
'video/mp4': '.mp4',
|
|
27
|
+
'video/webm': '.webm',
|
|
28
|
+
'model/gltf+json': '.gltf',
|
|
29
|
+
'model/gltf-binary': '.glb',
|
|
30
|
+
'application/json': '.json',
|
|
31
|
+
};
|
|
32
|
+
function inferExtensionFromCandidate(candidate) {
|
|
33
|
+
try {
|
|
34
|
+
if (candidate.startsWith('http://') || candidate.startsWith('https://')) {
|
|
35
|
+
return (0, node_path_1.extname)(new URL(candidate).pathname);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return (0, node_path_1.extname)(candidate);
|
|
40
|
+
}
|
|
41
|
+
return (0, node_path_1.extname)(candidate);
|
|
42
|
+
}
|
|
16
43
|
async function ensureWorkspaceCataloguePath() {
|
|
17
44
|
const path = (0, node_path_1.resolve)(process.cwd(), CATALOGUE_FILENAME);
|
|
18
45
|
if ((0, node_fs_1.existsSync)(path) && (0, node_fs_1.statSync)(path).isFile()) {
|
|
@@ -42,14 +69,40 @@ function readWorkspaceCatalogue(path) {
|
|
|
42
69
|
throw new Error(`Invalid catalogue.json at ${path}.`);
|
|
43
70
|
}
|
|
44
71
|
const catalogue = parsed;
|
|
45
|
-
if (catalogue
|
|
46
|
-
throw new Error(`catalogue.json at ${path} must
|
|
72
|
+
if (Object.prototype.hasOwnProperty.call(catalogue, LEGACY_CATALOGUE_VERSION_KEY)) {
|
|
73
|
+
throw new Error(`catalogue.json at ${path} must not define the legacy top-level version field.`);
|
|
74
|
+
}
|
|
75
|
+
const unsupportedKey = Object.keys(catalogue).find((key) => !ALLOWED_CATALOGUE_TOP_LEVEL_KEYS.has(key));
|
|
76
|
+
if (unsupportedKey) {
|
|
77
|
+
throw new Error(`catalogue.json at ${path} contains unsupported top-level key "${unsupportedKey}".`);
|
|
47
78
|
}
|
|
48
79
|
return catalogue;
|
|
49
80
|
}
|
|
50
81
|
function writeWorkspaceCatalogue(path, catalogue) {
|
|
51
82
|
(0, node_fs_1.writeFileSync)(path, `${JSON.stringify(catalogue, null, 2)}\n`);
|
|
52
83
|
}
|
|
84
|
+
function assertWorkspaceRootCanOwnEntries(cataloguePath) {
|
|
85
|
+
const rootDir = (0, node_path_1.resolve)(cataloguePath, '..');
|
|
86
|
+
const rootCataloguePath = (0, node_path_1.join)(rootDir, CATALOGUE_FILENAME);
|
|
87
|
+
const stack = [rootDir];
|
|
88
|
+
while (stack.length > 0) {
|
|
89
|
+
const current = stack.pop();
|
|
90
|
+
const entries = (0, node_fs_1.readdirSync)(current, { withFileTypes: true });
|
|
91
|
+
for (const entry of entries) {
|
|
92
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules') {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const absolutePath = (0, node_path_1.join)(current, entry.name);
|
|
96
|
+
if (entry.isDirectory()) {
|
|
97
|
+
stack.push(absolutePath);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (entry.isFile() && entry.name === CATALOGUE_FILENAME && absolutePath !== rootCataloguePath) {
|
|
101
|
+
throw new Error(`catalogue.json at ${cataloguePath} must stay {} because nested catalogue.json files already exist below this directory.`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
53
106
|
async function createAuthenticatedClient(commandLabel) {
|
|
54
107
|
const cfg = (0, config_1.loadConfig)();
|
|
55
108
|
const envName = cfg.env;
|
|
@@ -100,53 +153,13 @@ function parseAssetManifestFiles(value) {
|
|
|
100
153
|
function inferExtensionFromManifestEntry(entry) {
|
|
101
154
|
const candidates = [entry.key, entry.url].filter((value) => typeof value === 'string' && value.length > 0);
|
|
102
155
|
for (const candidate of candidates) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (extension) {
|
|
107
|
-
return extension;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
const extension = (0, node_path_1.extname)(candidate);
|
|
112
|
-
if (extension) {
|
|
113
|
-
return extension;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
catch {
|
|
118
|
-
const extension = (0, node_path_1.extname)(candidate);
|
|
119
|
-
if (extension) {
|
|
120
|
-
return extension;
|
|
121
|
-
}
|
|
156
|
+
const extension = inferExtensionFromCandidate(candidate);
|
|
157
|
+
if (extension) {
|
|
158
|
+
return extension;
|
|
122
159
|
}
|
|
123
160
|
}
|
|
124
161
|
const mimeType = typeof entry.contentType === 'string' ? entry.contentType.trim().toLowerCase() : '';
|
|
125
|
-
|
|
126
|
-
return '.png';
|
|
127
|
-
if (mimeType === 'image/jpeg')
|
|
128
|
-
return '.jpg';
|
|
129
|
-
if (mimeType === 'image/webp')
|
|
130
|
-
return '.webp';
|
|
131
|
-
if (mimeType === 'image/gif')
|
|
132
|
-
return '.gif';
|
|
133
|
-
if (mimeType === 'audio/mpeg')
|
|
134
|
-
return '.mp3';
|
|
135
|
-
if (mimeType === 'audio/wav')
|
|
136
|
-
return '.wav';
|
|
137
|
-
if (mimeType === 'audio/ogg')
|
|
138
|
-
return '.ogg';
|
|
139
|
-
if (mimeType === 'video/mp4')
|
|
140
|
-
return '.mp4';
|
|
141
|
-
if (mimeType === 'video/webm')
|
|
142
|
-
return '.webm';
|
|
143
|
-
if (mimeType === 'model/gltf+json')
|
|
144
|
-
return '.gltf';
|
|
145
|
-
if (mimeType === 'model/gltf-binary')
|
|
146
|
-
return '.glb';
|
|
147
|
-
if (mimeType === 'application/json')
|
|
148
|
-
return '.json';
|
|
149
|
-
return '';
|
|
162
|
+
return MIME_TYPE_TO_EXTENSION[mimeType] ?? '';
|
|
150
163
|
}
|
|
151
164
|
function buildLocalFilename(entry, usedNames) {
|
|
152
165
|
const sourceName = typeof entry.key === 'string' && entry.key.length > 0
|
|
@@ -193,6 +206,7 @@ async function createAssetRemix(name, remixRef) {
|
|
|
193
206
|
const client = await createAuthenticatedClient('project create asset');
|
|
194
207
|
const cataloguePath = await ensureWorkspaceCataloguePath();
|
|
195
208
|
const catalogue = readWorkspaceCatalogue(cataloguePath);
|
|
209
|
+
assertWorkspaceRootCanOwnEntries(cataloguePath);
|
|
196
210
|
ensureUniqueEntryName(catalogue, 'asset', name);
|
|
197
211
|
const detail = await client.fetchAssetBySlug(parsedRef.creatorUsername, parsedRef.name);
|
|
198
212
|
const versions = await client.listAssetVersions(parsedRef.creatorUsername, parsedRef.name, { limit: 200, offset: 0 });
|
|
@@ -238,6 +252,7 @@ async function createPackRemix(name, remixRef) {
|
|
|
238
252
|
const client = await createAuthenticatedClient('project create pack');
|
|
239
253
|
const cataloguePath = await ensureWorkspaceCataloguePath();
|
|
240
254
|
const catalogue = readWorkspaceCatalogue(cataloguePath);
|
|
255
|
+
assertWorkspaceRootCanOwnEntries(cataloguePath);
|
|
241
256
|
ensureUniqueEntryName(catalogue, 'pack', name);
|
|
242
257
|
const detail = await client.fetchAssetPackBySlug(parsedRef.creatorUsername, parsedRef.name);
|
|
243
258
|
const versions = await client.listAssetPackVersions(parsedRef.creatorUsername, parsedRef.name, { limit: 200, offset: 0 });
|
|
@@ -99,7 +99,16 @@ function formatCountValue(value) {
|
|
|
99
99
|
}
|
|
100
100
|
function buildCountsSuffix(item) {
|
|
101
101
|
const parts = [];
|
|
102
|
-
if ('playCount' in item.item && typeof item.item.playCount === 'number') {
|
|
102
|
+
if (item.kind === 'app' && 'playCount' in item.item && typeof item.item.playCount === 'number') {
|
|
103
|
+
if (typeof item.item.viewCount === 'number') {
|
|
104
|
+
parts.push(`view ${formatCountValue(item.item.viewCount)}`);
|
|
105
|
+
}
|
|
106
|
+
parts.push(`launch ${formatCountValue(item.item.playCount)}`);
|
|
107
|
+
}
|
|
108
|
+
if (item.kind === 'asset' && 'playCount' in item.item && typeof item.item.playCount === 'number') {
|
|
109
|
+
if (typeof item.item.viewCount === 'number') {
|
|
110
|
+
parts.push(`view ${formatCountValue(item.item.viewCount)}`);
|
|
111
|
+
}
|
|
103
112
|
parts.push(`play ${formatCountValue(item.item.playCount)}`);
|
|
104
113
|
}
|
|
105
114
|
if (typeof item.item.likeCount === 'number') {
|
|
@@ -63,7 +63,7 @@ async function assertAppRegistered(client, creatorUsername, appName) {
|
|
|
63
63
|
}
|
|
64
64
|
function findNearestCatalogue(startDir) {
|
|
65
65
|
let current = (0, node_path_1.resolve)(startDir);
|
|
66
|
-
while (true) {
|
|
66
|
+
while (true) {
|
|
67
67
|
const candidate = (0, node_path_1.join)(current, 'catalogue.json');
|
|
68
68
|
if ((0, node_fs_1.existsSync)(candidate)) {
|
|
69
69
|
return candidate;
|
|
@@ -36,6 +36,95 @@ const LOCAL_IMAGE_MIME_BY_EXTENSION = {
|
|
|
36
36
|
'.jpeg': 'image/jpeg',
|
|
37
37
|
'.webp': 'image/webp',
|
|
38
38
|
};
|
|
39
|
+
function handleGenerateBuildRequestError(message) {
|
|
40
|
+
const errorByCode = {
|
|
41
|
+
missing_type: {
|
|
42
|
+
title: 'A generation type is required.',
|
|
43
|
+
details: ['Use one of: image, music, sfx, video, model_3d.'],
|
|
44
|
+
},
|
|
45
|
+
missing_prompt: {
|
|
46
|
+
title: 'A prompt is required.',
|
|
47
|
+
details: ['Example: playdrop ai create image "A pixel art hero portrait"'],
|
|
48
|
+
},
|
|
49
|
+
invalid_type: {
|
|
50
|
+
title: 'The generation type is invalid.',
|
|
51
|
+
details: ['Use one of: image, music, sfx, video, model_3d.'],
|
|
52
|
+
},
|
|
53
|
+
invalid_duration: {
|
|
54
|
+
title: 'The --duration value is invalid.',
|
|
55
|
+
details: ['Use a positive value like 20000, 20s, or 20000ms.'],
|
|
56
|
+
},
|
|
57
|
+
invalid_video_duration: {
|
|
58
|
+
title: 'The --duration value for video is invalid.',
|
|
59
|
+
details: ['Use 4s or 8s for video generation.'],
|
|
60
|
+
},
|
|
61
|
+
invalid_aspect_ratio: {
|
|
62
|
+
title: 'The --ratio value is invalid.',
|
|
63
|
+
details: ['Image: 1:1, 3:4, 9:16, 4:3, 16:9. Video: 16:9, 9:16.'],
|
|
64
|
+
},
|
|
65
|
+
missing_model3d_images: {
|
|
66
|
+
title: 'Model 3D IMAGE source mode requires at least one reference image.',
|
|
67
|
+
details: ['Provide --image1 <url> or --image2 <url>, or switch to --source-mode TEXT.'],
|
|
68
|
+
},
|
|
69
|
+
model3d_text_mode_disallows_images: {
|
|
70
|
+
title: 'Model 3D TEXT source mode cannot include reference images.',
|
|
71
|
+
details: ['Remove --image1/--image2 or switch to --source-mode IMAGE.'],
|
|
72
|
+
},
|
|
73
|
+
invalid_asset_subcategory: {
|
|
74
|
+
title: 'The --subcategory value is invalid.',
|
|
75
|
+
details: ['Use a lowercase slug such as generic, music, sfx, or avatar.'],
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
const entry = errorByCode[message];
|
|
79
|
+
if (!entry) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
(0, messages_1.printErrorWithHelp)(entry.title, entry.details, { command: 'ai create' });
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
function handleGenerateImageReferenceError(message) {
|
|
87
|
+
const imageRefHandlers = [
|
|
88
|
+
{
|
|
89
|
+
prefix: 'image_ref_not_found:',
|
|
90
|
+
title: (optionName) => `The ${optionName || 'image'} reference file was not found.`,
|
|
91
|
+
details: (path) => [path ? `Missing file: ${path}` : 'Provide a valid local file path or HTTPS URL.'],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
prefix: 'image_ref_not_file:',
|
|
95
|
+
title: (optionName) => `The ${optionName || 'image'} reference is not a file.`,
|
|
96
|
+
details: (path) => [path ? `Path must point to a file: ${path}` : 'Provide a valid local file path.'],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
prefix: 'image_ref_invalid_scheme:',
|
|
100
|
+
title: (optionName) => `The ${optionName || 'image'} reference URL uses an unsupported scheme.`,
|
|
101
|
+
details: (scheme) => [`Use an HTTPS URL or a local file path. Received: ${scheme || 'unknown'}`],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
prefix: 'image_ref_signature_mismatch:',
|
|
105
|
+
title: (optionName) => `The ${optionName || 'image'} reference file extension does not match its image signature.`,
|
|
106
|
+
details: (path) => [path ? `Fix file type mismatch for: ${path}` : 'Ensure extension and file signature match (.png, .jpg, .jpeg, .webp).'],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
prefix: 'image_ref_unsupported_type:',
|
|
110
|
+
title: (optionName) => `The ${optionName || 'image'} reference file type is unsupported.`,
|
|
111
|
+
details: (path) => [
|
|
112
|
+
path ? `Unsupported file: ${path}` : 'Provide a supported local file.',
|
|
113
|
+
'Supported local image types: .png, .jpg, .jpeg, .webp.',
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
for (const handler of imageRefHandlers) {
|
|
118
|
+
if (!message.startsWith(handler.prefix)) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const [, optionName, value] = message.split(':');
|
|
122
|
+
(0, messages_1.printErrorWithHelp)(handler.title(optionName), handler.details(value), { command: 'ai create' });
|
|
123
|
+
process.exitCode = 1;
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
39
128
|
function sleep(ms) {
|
|
40
129
|
return new Promise((resolve) => {
|
|
41
130
|
setTimeout(resolve, ms);
|
|
@@ -442,82 +531,10 @@ async function generate(typeInput, promptInput, options = {}) {
|
|
|
442
531
|
}
|
|
443
532
|
catch (error) {
|
|
444
533
|
const message = typeof error?.message === 'string' ? error.message : 'invalid_input';
|
|
445
|
-
if (message
|
|
446
|
-
(0, messages_1.printErrorWithHelp)('A generation type is required.', ['Use one of: image, music, sfx, video, model_3d.'], { command: 'ai create' });
|
|
447
|
-
process.exitCode = 1;
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
if (message === 'missing_prompt') {
|
|
451
|
-
(0, messages_1.printErrorWithHelp)('A prompt is required.', ['Example: playdrop ai create image "A pixel art hero portrait"'], { command: 'ai create' });
|
|
452
|
-
process.exitCode = 1;
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
if (message === 'invalid_type') {
|
|
456
|
-
(0, messages_1.printErrorWithHelp)('The generation type is invalid.', ['Use one of: image, music, sfx, video, model_3d.'], { command: 'ai create' });
|
|
457
|
-
process.exitCode = 1;
|
|
534
|
+
if (handleGenerateBuildRequestError(message)) {
|
|
458
535
|
return;
|
|
459
536
|
}
|
|
460
|
-
if (message
|
|
461
|
-
(0, messages_1.printErrorWithHelp)('The --duration value is invalid.', ['Use a positive value like 20000, 20s, or 20000ms.'], { command: 'ai create' });
|
|
462
|
-
process.exitCode = 1;
|
|
463
|
-
return;
|
|
464
|
-
}
|
|
465
|
-
if (message === 'invalid_video_duration') {
|
|
466
|
-
(0, messages_1.printErrorWithHelp)('The --duration value for video is invalid.', ['Use 4s or 8s for video generation.'], { command: 'ai create' });
|
|
467
|
-
process.exitCode = 1;
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
|
-
if (message === 'invalid_aspect_ratio') {
|
|
471
|
-
(0, messages_1.printErrorWithHelp)('The --ratio value is invalid.', ['Image: 1:1, 3:4, 9:16, 4:3, 16:9. Video: 16:9, 9:16.'], { command: 'ai create' });
|
|
472
|
-
process.exitCode = 1;
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
if (message === 'missing_model3d_images') {
|
|
476
|
-
(0, messages_1.printErrorWithHelp)('Model 3D IMAGE source mode requires at least one reference image.', ['Provide --image1 <url> or --image2 <url>, or switch to --source-mode TEXT.'], { command: 'ai create' });
|
|
477
|
-
process.exitCode = 1;
|
|
478
|
-
return;
|
|
479
|
-
}
|
|
480
|
-
if (message === 'model3d_text_mode_disallows_images') {
|
|
481
|
-
(0, messages_1.printErrorWithHelp)('Model 3D TEXT source mode cannot include reference images.', ['Remove --image1/--image2 or switch to --source-mode IMAGE.'], { command: 'ai create' });
|
|
482
|
-
process.exitCode = 1;
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
if (message === 'invalid_asset_subcategory') {
|
|
486
|
-
(0, messages_1.printErrorWithHelp)('The --subcategory value is invalid.', ['Use a lowercase slug such as generic, music, sfx, or avatar.'], { command: 'ai create' });
|
|
487
|
-
process.exitCode = 1;
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
if (message.startsWith('image_ref_not_found:')) {
|
|
491
|
-
const [, optionName, path] = message.split(':');
|
|
492
|
-
(0, messages_1.printErrorWithHelp)(`The ${optionName || 'image'} reference file was not found.`, [path ? `Missing file: ${path}` : 'Provide a valid local file path or HTTPS URL.'], { command: 'ai create' });
|
|
493
|
-
process.exitCode = 1;
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
if (message.startsWith('image_ref_not_file:')) {
|
|
497
|
-
const [, optionName, path] = message.split(':');
|
|
498
|
-
(0, messages_1.printErrorWithHelp)(`The ${optionName || 'image'} reference is not a file.`, [path ? `Path must point to a file: ${path}` : 'Provide a valid local file path.'], { command: 'ai create' });
|
|
499
|
-
process.exitCode = 1;
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
if (message.startsWith('image_ref_invalid_scheme:')) {
|
|
503
|
-
const [, optionName, scheme] = message.split(':');
|
|
504
|
-
(0, messages_1.printErrorWithHelp)(`The ${optionName || 'image'} reference URL uses an unsupported scheme.`, [`Use an HTTPS URL or a local file path. Received: ${scheme || 'unknown'}`], { command: 'ai create' });
|
|
505
|
-
process.exitCode = 1;
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
if (message.startsWith('image_ref_signature_mismatch:')) {
|
|
509
|
-
const [, optionName, path] = message.split(':');
|
|
510
|
-
(0, messages_1.printErrorWithHelp)(`The ${optionName || 'image'} reference file extension does not match its image signature.`, [path ? `Fix file type mismatch for: ${path}` : 'Ensure extension and file signature match (.png, .jpg, .jpeg, .webp).'], { command: 'ai create' });
|
|
511
|
-
process.exitCode = 1;
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
if (message.startsWith('image_ref_unsupported_type:')) {
|
|
515
|
-
const [, optionName, path] = message.split(':');
|
|
516
|
-
(0, messages_1.printErrorWithHelp)(`The ${optionName || 'image'} reference file type is unsupported.`, [
|
|
517
|
-
path ? `Unsupported file: ${path}` : 'Provide a supported local file.',
|
|
518
|
-
'Supported local image types: .png, .jpg, .jpeg, .webp.',
|
|
519
|
-
], { command: 'ai create' });
|
|
520
|
-
process.exitCode = 1;
|
|
537
|
+
if (handleGenerateImageReferenceError(message)) {
|
|
521
538
|
return;
|
|
522
539
|
}
|
|
523
540
|
(0, messages_1.printErrorWithHelp)(`Invalid AI create input: ${message}.`, ['Run "playdrop help ai create" for valid options.'], {
|
|
@@ -10,7 +10,7 @@ function printGettingStarted() {
|
|
|
10
10
|
console.log(' playdrop project init .');
|
|
11
11
|
console.log('');
|
|
12
12
|
console.log('3. Create an app');
|
|
13
|
-
console.log(' playdrop project create app my-app --template playdrop/template/
|
|
13
|
+
console.log(' playdrop project create app my-app --template playdrop/template/typescript_template');
|
|
14
14
|
console.log('');
|
|
15
15
|
console.log('4. Preview locally');
|
|
16
16
|
console.log(' playdrop project dev my-app');
|