@playdrop/playdrop-cli 0.9.5 → 0.10.0

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.
Files changed (69) hide show
  1. package/config/client-meta.json +1 -2
  2. package/dist/apiClient.d.ts +2 -0
  3. package/dist/apiClient.js +27 -2
  4. package/dist/appUrls.d.ts +1 -0
  5. package/dist/appUrls.js +9 -0
  6. package/dist/apps/build.js +39 -28
  7. package/dist/apps/index.d.ts +1 -0
  8. package/dist/apps/index.js +2 -0
  9. package/dist/apps/launchCheck.d.ts +2 -0
  10. package/dist/apps/launchCheck.js +31 -6
  11. package/dist/apps/registration.d.ts +1 -0
  12. package/dist/apps/registration.js +1 -0
  13. package/dist/apps/upload.d.ts +1 -0
  14. package/dist/apps/upload.js +4 -17
  15. package/dist/captureRuntime.d.ts +1 -0
  16. package/dist/captureRuntime.js +308 -0
  17. package/dist/catalogue.d.ts +4 -2
  18. package/dist/catalogue.js +50 -7
  19. package/dist/commandContext.js +42 -3
  20. package/dist/commands/capture.d.ts +1 -0
  21. package/dist/commands/capture.js +30 -13
  22. package/dist/commands/create.d.ts +0 -1
  23. package/dist/commands/create.js +2 -151
  24. package/dist/commands/creations.d.ts +0 -13
  25. package/dist/commands/creations.js +0 -141
  26. package/dist/commands/dev.d.ts +2 -1
  27. package/dist/commands/dev.js +23 -6
  28. package/dist/commands/devServer.js +3 -1
  29. package/dist/commands/generation.d.ts +1 -0
  30. package/dist/commands/generation.js +274 -0
  31. package/dist/commands/upload.d.ts +27 -1
  32. package/dist/commands/upload.js +962 -21
  33. package/dist/commands/validate.js +5 -0
  34. package/dist/commands/worker/runtime.d.ts +69 -0
  35. package/dist/commands/worker/runtime.js +414 -0
  36. package/dist/commands/worker.d.ts +144 -0
  37. package/dist/commands/worker.js +2219 -0
  38. package/dist/config.d.ts +2 -0
  39. package/dist/config.js +23 -0
  40. package/dist/index.js +71 -30
  41. package/dist/shellProbe.d.ts +1 -1
  42. package/dist/shellProbe.js +3 -3
  43. package/dist/workspaceAuth.d.ts +1 -0
  44. package/dist/workspaceAuth.js +8 -0
  45. package/node_modules/@playdrop/api-client/dist/client.d.ts +31 -14
  46. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  47. package/node_modules/@playdrop/api-client/dist/client.js +2 -2
  48. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +3 -1
  49. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
  50. package/node_modules/@playdrop/api-client/dist/domains/admin.js +45 -0
  51. package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.d.ts +72 -0
  52. package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.d.ts.map +1 -0
  53. package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.js +442 -0
  54. package/node_modules/@playdrop/api-client/dist/index.d.ts +31 -14
  55. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  56. package/node_modules/@playdrop/api-client/dist/index.js +134 -38
  57. package/node_modules/@playdrop/config/client-meta.json +1 -2
  58. package/node_modules/@playdrop/types/dist/api.d.ts +501 -74
  59. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  60. package/node_modules/@playdrop/types/dist/api.js +90 -9
  61. package/node_modules/@playdrop/types/dist/app.d.ts +2 -0
  62. package/node_modules/@playdrop/types/dist/app.d.ts.map +1 -1
  63. package/node_modules/@playdrop/types/dist/app.js +3 -0
  64. package/node_modules/@playdrop/types/dist/version.d.ts +1 -0
  65. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  66. package/package.json +2 -1
  67. package/node_modules/@playdrop/api-client/dist/domains/game-ideas.d.ts +0 -46
  68. package/node_modules/@playdrop/api-client/dist/domains/game-ideas.d.ts.map +0 -1
  69. package/node_modules/@playdrop/api-client/dist/domains/game-ideas.js +0 -177
@@ -24,8 +24,6 @@ const clientInfo_1 = require("../clientInfo");
24
24
  const CATALOGUE_FILENAME = 'catalogue.json';
25
25
  const LEGACY_CATALOGUE_VERSION_KEY = ['schema', 'Version'].join('');
26
26
  const ALLOWED_CATALOGUE_TOP_LEVEL_KEYS = new Set(['apps', 'assetSpecs', 'assets', 'assetPacks']);
27
- const DEFAULT_GAME_IDEA_TEMPLATE = 'playdrop/template/typescript_template';
28
- const GAME_IDEA_LOOKUP_PAGE_SIZE = 200;
29
27
  function buildArchiveDownloadHeaders(url, apiBase, token) {
30
28
  const targetUrl = new URL(url, apiBase);
31
29
  const apiOrigin = new URL(apiBase).origin;
@@ -1017,50 +1015,13 @@ function describeInitSummary(summary) {
1017
1015
  }
1018
1016
  return `Project bootstrap recap: ${parts.join('; ')}.`;
1019
1017
  }
1020
- function gameIdeaMatchesRef(gameIdea, ref) {
1021
- return String(gameIdea.id) === ref || gameIdea.slug === ref;
1022
- }
1023
- async function findGameIdeaForCreate(client, gameIdeaRef) {
1024
- let offset = 0;
1025
- for (;;) {
1026
- const response = await client.listGameIdeas({
1027
- state: 'ALL',
1028
- limit: GAME_IDEA_LOOKUP_PAGE_SIZE,
1029
- offset,
1030
- });
1031
- const match = response.gameIdeas.find((gameIdea) => gameIdeaMatchesRef(gameIdea, gameIdeaRef));
1032
- if (match) {
1033
- return match;
1034
- }
1035
- if (!response.pagination.hasMore) {
1036
- throw new Error('game_idea_not_found');
1037
- }
1038
- offset += response.pagination.limit;
1039
- }
1040
- }
1041
- function inferCreateSourceFromGameIdea(gameIdea) {
1042
- if (gameIdea.inputMode !== 'REMIX') {
1043
- return { template: DEFAULT_GAME_IDEA_TEMPLATE };
1044
- }
1045
- const sourceAppRef = typeof gameIdea.inputData.sourceAppRef === 'string' ? gameIdea.inputData.sourceAppRef.trim() : '';
1046
- if (!sourceAppRef) {
1047
- throw new Error('game_idea_remix_source_missing');
1048
- }
1049
- return { remix: sourceAppRef };
1050
- }
1051
- async function inferCreateSourceOptionsForGameIdea(client, gameIdeaRef) {
1052
- const gameIdea = await findGameIdeaForCreate(client, gameIdeaRef);
1053
- return inferCreateSourceFromGameIdea(gameIdea);
1054
- }
1055
1018
  async function create(name, options = {}) {
1056
1019
  const file = (0, node_path_1.resolve)(`${name}.html`);
1057
1020
  const projectDirCandidate = (0, node_path_1.resolve)(name);
1058
- const gameIdeaRef = typeof options.gameIdea === 'string' ? options.gameIdea.trim() : '';
1059
1021
  let templateOptionRaw = options.template?.trim();
1060
1022
  let remixOptionRaw = options.remix?.trim();
1061
1023
  let parsedTemplate = null;
1062
1024
  let parsedRemix = null;
1063
- let ctx = null;
1064
1025
  const existingLookup = (0, catalogue_1.resolveCatalogueEntries)(process.cwd(), { filterName: name });
1065
1026
  if (existingLookup.errors.length > 0) {
1066
1027
  for (const error of existingLookup.errors) {
@@ -1085,51 +1046,7 @@ async function create(name, options = {}) {
1085
1046
  process.exitCode = 1;
1086
1047
  return;
1087
1048
  }
1088
- let reuseExistingApp = existingTask !== null && !templateOptionRaw && !remixOptionRaw;
1089
- if (!reuseExistingApp && !templateOptionRaw && !remixOptionRaw && gameIdeaRef) {
1090
- ctx = await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)('project create app', 'Creating an app', { workspacePath: process.cwd() });
1091
- if (!ctx) {
1092
- return;
1093
- }
1094
- try {
1095
- const inferredSource = await inferCreateSourceOptionsForGameIdea(ctx.client, gameIdeaRef);
1096
- templateOptionRaw = inferredSource.template;
1097
- remixOptionRaw = inferredSource.remix;
1098
- }
1099
- catch (error) {
1100
- if (error instanceof http_1.CLIUnsupportedClientError) {
1101
- process.exitCode = 1;
1102
- return;
1103
- }
1104
- if (error instanceof types_1.UnsupportedClientError) {
1105
- (0, http_1.handleUnsupportedError)(error, 'Resolve game idea');
1106
- process.exitCode = 1;
1107
- return;
1108
- }
1109
- if (error instanceof types_1.ApiError) {
1110
- (0, messages_1.printErrorWithHelp)(`Failed to load game idea ${gameIdeaRef} (status ${error.status}).`, ['Confirm the game idea id or slug, then retry with "playdrop creations ideas browse --state all".'], { command: 'project create app' });
1111
- process.exitCode = 1;
1112
- return;
1113
- }
1114
- if (error instanceof TypeError) {
1115
- (0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to load the game idea.', 'project create app');
1116
- process.exitCode = 1;
1117
- return;
1118
- }
1119
- if (error instanceof Error && error.message === 'game_idea_not_found') {
1120
- (0, messages_1.printErrorWithHelp)(`Could not find game idea ${gameIdeaRef}.`, ['Run "playdrop creations ideas browse --state all" and retry with the exact id or slug.'], { command: 'project create app' });
1121
- process.exitCode = 1;
1122
- return;
1123
- }
1124
- if (error instanceof Error && error.message === 'game_idea_remix_source_missing') {
1125
- (0, messages_1.printErrorWithHelp)(`Game idea ${gameIdeaRef} is a remix idea but does not include a source app ref.`, ['Create a new game idea from the web remix flow, then retry with that id or slug.'], { command: 'project create app' });
1126
- process.exitCode = 1;
1127
- return;
1128
- }
1129
- throw error;
1130
- }
1131
- }
1132
- reuseExistingApp = existingTask !== null && !templateOptionRaw && !remixOptionRaw;
1049
+ const reuseExistingApp = existingTask !== null && !templateOptionRaw && !remixOptionRaw;
1133
1050
  if (!reuseExistingApp && ((0, node_fs_1.existsSync)(file) || (0, node_fs_1.existsSync)(projectDirCandidate))) {
1134
1051
  (0, messages_1.printErrorWithHelp)(`Cannot create ${name} because a file or directory with that name already exists.`, [
1135
1052
  'Choose a different app name or remove the existing file before running "playdrop project create app" again.'
@@ -1171,7 +1088,7 @@ async function create(name, options = {}) {
1171
1088
  return;
1172
1089
  }
1173
1090
  }
1174
- ctx = ctx ?? await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)('project create app', 'Creating an app', { workspacePath: process.cwd() });
1091
+ const ctx = await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)('project create app', 'Creating an app', { workspacePath: process.cwd() });
1175
1092
  if (!ctx) {
1176
1093
  return;
1177
1094
  }
@@ -1470,7 +1387,6 @@ async function create(name, options = {}) {
1470
1387
  }
1471
1388
  registrationTask = selectedTask;
1472
1389
  }
1473
- let gameIdeaAppRef = null;
1474
1390
  if (registrationTask) {
1475
1391
  const metadataWarning = (0, catalogue_1.formatMissingMetadataWarnings)(registrationTask);
1476
1392
  if (metadataWarning) {
@@ -1591,7 +1507,6 @@ async function create(name, options = {}) {
1591
1507
  throw error;
1592
1508
  }
1593
1509
  }
1594
- gameIdeaAppRef = `${creatorUsername}/${name}`;
1595
1510
  }
1596
1511
  const projectDirLabel = projectDir ? ((0, node_path_1.relative)(process.cwd(), projectDir) || projectDir) : null;
1597
1512
  const entryPointLabel = (0, node_path_1.relative)(process.cwd(), htmlFilePath) || (0, node_path_1.basename)(htmlFilePath);
@@ -1629,70 +1544,6 @@ async function create(name, options = {}) {
1629
1544
  catalogueLabel = (0, node_path_1.relative)(process.cwd(), existingTask.catalogueAbsolutePath) || CATALOGUE_FILENAME;
1630
1545
  }
1631
1546
  }
1632
- if (gameIdeaRef) {
1633
- if (!gameIdeaAppRef) {
1634
- try {
1635
- const creatorUsername = await resolveAuthenticatedCreatorUsername(client);
1636
- gameIdeaAppRef = `${creatorUsername}/${name}`;
1637
- }
1638
- catch (error) {
1639
- if (error instanceof http_1.CLIUnsupportedClientError) {
1640
- process.exitCode = 1;
1641
- return;
1642
- }
1643
- if (error instanceof types_1.UnsupportedClientError) {
1644
- (0, http_1.handleUnsupportedError)(error, 'Resolve creator account');
1645
- process.exitCode = 1;
1646
- return;
1647
- }
1648
- if (error instanceof types_1.ApiError) {
1649
- (0, messages_1.printErrorWithHelp)(`Failed to resolve your creator account (status ${error.status}).`, ['Run "playdrop auth login" to refresh your session, then try again.'], { command: 'project create app' });
1650
- process.exitCode = 1;
1651
- return;
1652
- }
1653
- if (error instanceof TypeError) {
1654
- (0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to resolve your creator account.', 'project create app');
1655
- process.exitCode = 1;
1656
- return;
1657
- }
1658
- if (error instanceof Error && error.message === 'missing_creator_username') {
1659
- (0, messages_1.printErrorWithHelp)('The API did not return a creator username.', ['Run "playdrop auth login" again, then retry.'], { command: 'project create app' });
1660
- process.exitCode = 1;
1661
- return;
1662
- }
1663
- throw error;
1664
- }
1665
- }
1666
- if (!gameIdeaAppRef) {
1667
- throw new Error('missing_game_idea_app_ref');
1668
- }
1669
- try {
1670
- const response = await client.startGameIdea(gameIdeaRef, { app: gameIdeaAppRef });
1671
- console.log(`Marked game idea ${response.gameIdea.slug} as started.`);
1672
- }
1673
- catch (error) {
1674
- if (error instanceof http_1.CLIUnsupportedClientError) {
1675
- process.exitCode = 1;
1676
- return;
1677
- }
1678
- if (error instanceof types_1.UnsupportedClientError) {
1679
- (0, http_1.handleUnsupportedError)(error, 'Mark game idea started');
1680
- process.exitCode = 1;
1681
- return;
1682
- }
1683
- if (error instanceof types_1.ApiError) {
1684
- (0, messages_1.printErrorWithHelp)(`Failed to mark game idea ${gameIdeaRef} as started (status ${error.status}).`, ['Confirm the game idea id or slug, then retry with "playdrop creations ideas start <id-or-slug>".'], { command: 'project create app' });
1685
- process.exitCode = 1;
1686
- return;
1687
- }
1688
- if (error instanceof TypeError) {
1689
- (0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to mark the game idea started.', 'project create app');
1690
- process.exitCode = 1;
1691
- return;
1692
- }
1693
- throw error;
1694
- }
1695
- }
1696
1547
  logNextSteps(name, {
1697
1548
  hasCatalogue,
1698
1549
  catalogueLabel,
@@ -5,16 +5,6 @@ type BrowseCreationsOptions = {
5
5
  offset?: string | number;
6
6
  json?: boolean;
7
7
  };
8
- type BrowseIdeasOptions = {
9
- state?: string;
10
- limit?: string | number;
11
- offset?: string | number;
12
- json?: boolean;
13
- };
14
- type IdeaMutationOptions = {
15
- app?: string;
16
- json?: boolean;
17
- };
18
8
  type AppUpdateOptions = {
19
9
  creator?: string;
20
10
  displayName?: string;
@@ -38,9 +28,6 @@ type MutationOptions = {
38
28
  json?: boolean;
39
29
  };
40
30
  export declare function browseCreations(options?: BrowseCreationsOptions): Promise<void>;
41
- export declare function browseCreationIdeas(options?: BrowseIdeasOptions): Promise<void>;
42
- export declare function startCreationIdea(idOrSlug: string | undefined, options?: IdeaMutationOptions): Promise<void>;
43
- export declare function shipCreationIdea(idOrSlug: string | undefined, options?: IdeaMutationOptions): Promise<void>;
44
31
  export declare function updateCreationApp(nameArg: string | undefined, options?: AppUpdateOptions): Promise<void>;
45
32
  export declare function deleteCreationApp(nameArg: string | undefined, options?: MutationOptions): Promise<void>;
46
33
  export declare function setCurrentCreationAppVersion(nameArg: string | undefined, versionArg: string | undefined, options?: MutationOptions): Promise<void>;
@@ -1,9 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.browseCreations = browseCreations;
4
- exports.browseCreationIdeas = browseCreationIdeas;
5
- exports.startCreationIdea = startCreationIdea;
6
- exports.shipCreationIdea = shipCreationIdea;
7
4
  exports.updateCreationApp = updateCreationApp;
8
5
  exports.deleteCreationApp = deleteCreationApp;
9
6
  exports.setCurrentCreationAppVersion = setCurrentCreationAppVersion;
@@ -29,7 +26,6 @@ const messages_1 = require("../messages");
29
26
  const output_1 = require("../output");
30
27
  const refs_1 = require("../refs");
31
28
  const MERGED_PAGE_SIZE = 100;
32
- const IDEA_STATE_VALUES = new Set(['idea', 'started', 'shipped', 'all']);
33
29
  function parseBrowseKind(raw) {
34
30
  if (!raw || raw.trim().length === 0) {
35
31
  return 'all';
@@ -53,19 +49,6 @@ function parsePositiveInteger(raw, label, fallback) {
53
49
  }
54
50
  return parsed;
55
51
  }
56
- function parseIdeaState(raw) {
57
- if (!raw || raw.trim().length === 0) {
58
- return 'IDEA';
59
- }
60
- const normalized = raw.trim().toLowerCase();
61
- if (!IDEA_STATE_VALUES.has(normalized)) {
62
- return null;
63
- }
64
- if (normalized === 'all') {
65
- return 'ALL';
66
- }
67
- return normalized.toUpperCase();
68
- }
69
52
  async function resolveCreator(client, rawCreator, command) {
70
53
  let creator;
71
54
  try {
@@ -311,130 +294,6 @@ async function browseCreations(options = {}) {
311
294
  }
312
295
  });
313
296
  }
314
- async function browseCreationIdeas(options = {}) {
315
- const state = parseIdeaState(options.state);
316
- if (!state) {
317
- (0, messages_1.printErrorWithHelp)('The --state value is invalid.', ['Use one of: idea, started, shipped, all.'], { command: 'creations ideas browse' });
318
- process.exitCode = 1;
319
- return;
320
- }
321
- const limit = parsePositiveInteger(options.limit, 'limit', 20);
322
- if (limit === null) {
323
- (0, messages_1.printErrorWithHelp)('The --limit value must be a positive integer.', ['Example: --limit 20'], { command: 'creations ideas browse' });
324
- process.exitCode = 1;
325
- return;
326
- }
327
- const offset = parsePositiveInteger(options.offset, 'offset', 0);
328
- if (offset === null) {
329
- (0, messages_1.printErrorWithHelp)('The --offset value must be zero or a positive integer.', ['Example: --offset 20'], { command: 'creations ideas browse' });
330
- process.exitCode = 1;
331
- return;
332
- }
333
- await (0, commandContext_1.withEnvironment)('creations ideas browse', 'Browsing your game ideas', async ({ client }) => {
334
- try {
335
- const response = await client.listGameIdeas({ state, limit, offset });
336
- if (options.json) {
337
- (0, output_1.printJson)(response);
338
- return;
339
- }
340
- if (response.gameIdeas.length === 0) {
341
- console.log('No game ideas found.');
342
- console.log('Next: create one at https://playdrop.ai/create.');
343
- return;
344
- }
345
- console.log('Game ideas:\n');
346
- response.gameIdeas.forEach((idea, index) => {
347
- const tags = idea.tagRefs.length > 0 ? ` | ${idea.tagRefs.join(', ')}` : '';
348
- const app = idea.createdAppRef ? ` | app ${idea.createdAppRef}` : '';
349
- console.log(`${index + 1}. ${idea.slug} | ${idea.displayName} | ${idea.state.toLowerCase()}${tags}${app}`);
350
- });
351
- if (response.pagination.hasMore) {
352
- console.log(`\nNext page: playdrop creations ideas browse --state ${String(options.state ?? 'idea')} --offset ${response.pagination.offset + response.pagination.limit}`);
353
- }
354
- }
355
- catch (error) {
356
- const handled = (0, errors_1.handleCommandFailure)(error, 'creations ideas browse', 'Game idea lookup', {
357
- apiMessage: (apiError) => ({
358
- problem: `Game idea lookup failed with status ${apiError.status}.`,
359
- suggestions: ['Run "playdrop auth login" and retry.'],
360
- }),
361
- });
362
- if (!handled) {
363
- throw error;
364
- }
365
- }
366
- });
367
- }
368
- async function startCreationIdea(idOrSlug, options = {}) {
369
- const ref = typeof idOrSlug === 'string' ? idOrSlug.trim() : '';
370
- if (!ref) {
371
- (0, messages_1.printErrorWithHelp)('Provide a game idea id or slug.', ['Example: playdrop creations ideas start foxfire-kitchen'], { command: 'creations ideas start' });
372
- process.exitCode = 1;
373
- return;
374
- }
375
- await (0, commandContext_1.withEnvironment)('creations ideas start', 'Marking game idea started', async ({ client }) => {
376
- try {
377
- const response = await client.startGameIdea(ref);
378
- if (options.json) {
379
- (0, output_1.printJson)(response);
380
- return;
381
- }
382
- (0, output_1.printSuccess)(`Marked ${response.gameIdea.slug} as started.`, [
383
- `Next: build and publish the app, then run "playdrop creations ideas ship ${response.gameIdea.slug} --app <creator>/<name>".`,
384
- ]);
385
- }
386
- catch (error) {
387
- const handled = (0, errors_1.handleCommandFailure)(error, 'creations ideas start', 'Game idea update', {
388
- apiMessage: (apiError) => ({
389
- problem: `Game idea update failed with status ${apiError.status}.`,
390
- suggestions: ['Confirm the id or slug, then retry.'],
391
- }),
392
- });
393
- if (!handled) {
394
- throw error;
395
- }
396
- }
397
- });
398
- }
399
- async function shipCreationIdea(idOrSlug, options = {}) {
400
- const ref = typeof idOrSlug === 'string' ? idOrSlug.trim() : '';
401
- const appRef = typeof options.app === 'string' ? options.app.trim() : '';
402
- if (!ref) {
403
- (0, messages_1.printErrorWithHelp)('Provide a game idea id or slug.', ['Example: playdrop creations ideas ship foxfire-kitchen --app olivier/foxfire-kitchen'], { command: 'creations ideas ship' });
404
- process.exitCode = 1;
405
- return;
406
- }
407
- if (!appRef) {
408
- (0, messages_1.printErrorWithHelp)('Provide --app <creator>/<name>.', ['Example: --app olivier/foxfire-kitchen'], { command: 'creations ideas ship' });
409
- process.exitCode = 1;
410
- return;
411
- }
412
- await (0, commandContext_1.withEnvironment)('creations ideas ship', 'Marking game idea shipped', async ({ client }) => {
413
- try {
414
- const response = await client.shipGameIdea(ref, { app: appRef });
415
- if (options.json) {
416
- (0, output_1.printJson)(response);
417
- return;
418
- }
419
- (0, output_1.printSuccess)(`Marked ${response.gameIdea.slug} as shipped.`, [
420
- response.gameIdea.createdAppRef
421
- ? `Linked app: ${response.gameIdea.createdAppRef}.`
422
- : `Linked app: ${appRef}.`,
423
- ]);
424
- }
425
- catch (error) {
426
- const handled = (0, errors_1.handleCommandFailure)(error, 'creations ideas ship', 'Game idea update', {
427
- apiMessage: (apiError) => ({
428
- problem: `Game idea update failed with status ${apiError.status}.`,
429
- suggestions: ['Confirm the id or slug and --app value, then retry.'],
430
- }),
431
- });
432
- if (!handled) {
433
- throw error;
434
- }
435
- }
436
- });
437
- }
438
297
  async function updateCreationApp(nameArg, options = {}) {
439
298
  const name = parseManagedName(nameArg, 'creations apps update');
440
299
  if (!name) {
@@ -2,7 +2,8 @@ export declare function formatDevRuntimeAssetManifestFailure(error: unknown): {
2
2
  message: string;
3
3
  suggestions: string[];
4
4
  };
5
- export declare function dev(targetArg: string | undefined, _port?: number, appOption?: string, devOptions?: {
5
+ export declare function dev(targetArg: string | undefined, port?: number | string, appOption?: string, devOptions?: {
6
6
  devAuth?: string;
7
7
  player?: string | number;
8
8
  }): Promise<void>;
9
+ export declare function resolveDevRouterPort(value: number | string | null | undefined): number;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.formatDevRuntimeAssetManifestFailure = formatDevRuntimeAssetManifestFailure;
4
4
  exports.dev = dev;
5
+ exports.resolveDevRouterPort = resolveDevRouterPort;
5
6
  const node_fs_1 = require("node:fs");
6
7
  const node_path_1 = require("node:path");
7
8
  const types_1 = require("@playdrop/types");
@@ -133,7 +134,8 @@ function formatDevRuntimeAssetManifestFailure(error) {
133
134
  ],
134
135
  };
135
136
  }
136
- async function dev(targetArg, _port, appOption, devOptions = {}) {
137
+ async function dev(targetArg, port, appOption, devOptions = {}) {
138
+ const devRouterPort = resolveDevRouterPort(port ?? process.env.PLAYDROP_DEV_ROUTER_PORT);
137
139
  let resolvedTarget;
138
140
  try {
139
141
  resolvedTarget = (0, devShared_1.resolveDevTarget)(targetArg, appOption);
@@ -298,7 +300,7 @@ async function dev(targetArg, _port, appOption, devOptions = {}) {
298
300
  creatorUsername: currentUsername,
299
301
  appType: appTypeSlug,
300
302
  appName,
301
- port: devServer_1.DEV_ROUTER_PORT,
303
+ port: devRouterPort,
302
304
  });
303
305
  let runtimeAssetManifest = (0, devRuntimeAssets_1.createEmptyDevRuntimeAssetManifest)();
304
306
  if (taskLookup.task) {
@@ -367,7 +369,7 @@ async function dev(targetArg, _port, appOption, devOptions = {}) {
367
369
  appType: appTypeSlug,
368
370
  creatorUsername: currentUsername,
369
371
  htmlPath: filePath,
370
- port: devServer_1.DEV_ROUTER_PORT,
372
+ port: devRouterPort,
371
373
  projectInfo,
372
374
  runtimeAssetManifest,
373
375
  });
@@ -383,13 +385,13 @@ async function dev(targetArg, _port, appOption, devOptions = {}) {
383
385
  ], { command: 'project dev' });
384
386
  }
385
387
  else if (message.startsWith('dev_router_incompatible:')) {
386
- (0, messages_1.printErrorWithHelp)(`An incompatible shared dev router is already running on port ${devServer_1.DEV_ROUTER_PORT}.`, [
388
+ (0, messages_1.printErrorWithHelp)(`An incompatible shared dev router is already running on port ${devRouterPort}.`, [
387
389
  'Stop the existing dev router process, then retry "playdrop project dev".',
388
390
  `If needed, rebuild the local CLI first with "npm run build --workspace @playdrop/playdrop-cli" from /Users/oliviermichon/Documents/playdrop.`,
389
391
  ], { command: 'project dev' });
390
392
  }
391
393
  else {
392
- (0, messages_1.printErrorWithHelp)(error?.message || `Failed to start the shared dev router on port ${devServer_1.DEV_ROUTER_PORT}.`, [
394
+ (0, messages_1.printErrorWithHelp)(error?.message || `Failed to start the shared dev router on port ${devRouterPort}.`, [
393
395
  'Close the conflicting process or wait for the stale mount to exit.',
394
396
  'Ensure the app HTML file exists and is readable.',
395
397
  ], { command: 'project dev' });
@@ -425,7 +427,12 @@ async function dev(targetArg, _port, appOption, devOptions = {}) {
425
427
  }
426
428
  }
427
429
  if (webUrl) {
428
- const iframeUrl = (0, devAuth_1.applyHostedDevAuthSelectionToUrl)(`${webUrl}/creators/${encodeURIComponent(currentUsername)}/apps/${appTypeSlug}/${encodeURIComponent(appName)}/dev`, devAuthSelection);
430
+ const iframeUrl = (0, devAuth_1.applyHostedDevAuthSelectionToUrl)((0, appUrls_1.buildPlatformDevUrl)(webUrl, {
431
+ creatorUsername: currentUsername,
432
+ appType: appTypeSlug,
433
+ appName,
434
+ localDevPort: devRouterPort === devServer_1.DEV_ROUTER_PORT ? null : devRouterPort,
435
+ }), devAuthSelection);
429
436
  console.log('\nTest your app at:');
430
437
  console.log(` ${iframeUrl}`);
431
438
  console.log('\nMake something fun then share it with');
@@ -436,3 +443,13 @@ async function dev(targetArg, _port, appOption, devOptions = {}) {
436
443
  }
437
444
  }, { workspacePath });
438
445
  }
446
+ function resolveDevRouterPort(value) {
447
+ if (value === null || value === undefined || String(value).trim() === '') {
448
+ return devServer_1.DEV_ROUTER_PORT;
449
+ }
450
+ const parsed = typeof value === 'number' ? value : Number.parseInt(String(value), 10);
451
+ if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
452
+ throw new Error('invalid_dev_router_port');
453
+ }
454
+ return parsed;
455
+ }
@@ -413,11 +413,13 @@ async function ensureDevRouterRunning(port = exports.DEV_ROUTER_PORT) {
413
413
  throw new Error(`dev_router_incompatible:port_${port}`);
414
414
  }
415
415
  const cliEntrypoint = getCliEntrypointPath();
416
+ const childEnv = { ...process.env };
417
+ delete childEnv.PLAYDROP_WORKER_CONTEXT;
416
418
  const child = (0, node_child_process_1.spawn)(process.execPath, [cliEntrypoint, 'project', '_dev-router', 'serve'], {
417
419
  detached: true,
418
420
  stdio: 'ignore',
419
421
  env: {
420
- ...process.env,
422
+ ...childEnv,
421
423
  PLAYDROP_DEV_ROUTER_PORT: String(port),
422
424
  },
423
425
  });
@@ -24,6 +24,7 @@ type AiGenerateOptions = {
24
24
  texture?: boolean;
25
25
  timeoutSeconds?: string | number;
26
26
  pollIntervalMs?: string | number;
27
+ output?: string;
27
28
  json?: boolean;
28
29
  };
29
30
  type AiListOptions = {