@playdrop/playdrop-cli 0.8.1 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/config/client-meta.json +1 -1
  2. package/dist/catalogue.js +1 -1
  3. package/dist/commands/create.d.ts +1 -0
  4. package/dist/commands/create.js +159 -9
  5. package/dist/commands/creations.d.ts +13 -0
  6. package/dist/commands/creations.js +141 -0
  7. package/dist/commands/upload.d.ts +1 -0
  8. package/dist/commands/upload.js +22 -0
  9. package/dist/index.js +29 -0
  10. package/node_modules/@playdrop/api-client/dist/client.d.ts +16 -1
  11. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  12. package/node_modules/@playdrop/api-client/dist/client.js +52 -0
  13. package/node_modules/@playdrop/api-client/dist/domains/game-ideas.d.ts +45 -0
  14. package/node_modules/@playdrop/api-client/dist/domains/game-ideas.d.ts.map +1 -0
  15. package/node_modules/@playdrop/api-client/dist/domains/game-ideas.js +166 -0
  16. package/node_modules/@playdrop/api-client/dist/index.d.ts +16 -1
  17. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  18. package/node_modules/@playdrop/api-client/dist/index.js +54 -0
  19. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js +1 -0
  20. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js.map +1 -1
  21. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js +1 -0
  22. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js.map +1 -1
  23. package/node_modules/@playdrop/config/client-meta.json +1 -1
  24. package/node_modules/@playdrop/types/dist/api.d.ts +98 -1
  25. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  26. package/node_modules/@playdrop/types/dist/api.js +9 -1
  27. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.8.1",
2
+ "version": "0.8.3",
3
3
  "runtimeSdkVersion": "0.7.21",
4
4
  "build": 1,
5
5
  "clients": {
package/dist/catalogue.js CHANGED
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ /* eslint-disable complexity, max-lines, max-lines-per-function */
2
3
  Object.defineProperty(exports, "__esModule", { value: true });
3
4
  exports.validateAppMetadata = validateAppMetadata;
4
5
  exports.resolveCatalogueTagGroups = resolveCatalogueTagGroups;
@@ -2187,7 +2188,6 @@ function buildAssetPackTasks(catalogues) {
2187
2188
  }
2188
2189
  return { tasks, warnings, errors };
2189
2190
  }
2190
- // eslint-disable-next-line complexity
2191
2191
  function resolveCatalogueTagGroups(rootDir) {
2192
2192
  const { files, errors: discoveryErrors } = walkCatalogueTree(rootDir);
2193
2193
  if (discoveryErrors.length > 0) {
@@ -1,6 +1,7 @@
1
1
  type CreateOptions = {
2
2
  template?: string;
3
3
  remix?: string;
4
+ gameIdea?: string;
4
5
  };
5
6
  export declare function createAssetSpecProject(name: string): Promise<void>;
6
7
  export declare function create(name: string, options?: CreateOptions): Promise<void>;
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ /* eslint-disable complexity, max-lines, max-lines-per-function */
2
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
5
  };
@@ -23,6 +24,8 @@ const clientInfo_1 = require("../clientInfo");
23
24
  const CATALOGUE_FILENAME = 'catalogue.json';
24
25
  const LEGACY_CATALOGUE_VERSION_KEY = ['schema', 'Version'].join('');
25
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;
26
29
  function buildArchiveDownloadHeaders(url, apiBase, token) {
27
30
  const targetUrl = new URL(url, apiBase);
28
31
  const apiOrigin = new URL(apiBase).origin;
@@ -1014,13 +1017,50 @@ function describeInitSummary(summary) {
1014
1017
  }
1015
1018
  return `Project bootstrap recap: ${parts.join('; ')}.`;
1016
1019
  }
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
+ }
1017
1055
  async function create(name, options = {}) {
1018
1056
  const file = (0, node_path_1.resolve)(`${name}.html`);
1019
1057
  const projectDirCandidate = (0, node_path_1.resolve)(name);
1020
- const templateOptionRaw = options.template?.trim();
1021
- const remixOptionRaw = options.remix?.trim();
1058
+ const gameIdeaRef = typeof options.gameIdea === 'string' ? options.gameIdea.trim() : '';
1059
+ let templateOptionRaw = options.template?.trim();
1060
+ let remixOptionRaw = options.remix?.trim();
1022
1061
  let parsedTemplate = null;
1023
1062
  let parsedRemix = null;
1063
+ let ctx = null;
1024
1064
  const existingLookup = (0, catalogue_1.resolveCatalogueEntries)(process.cwd(), { filterName: name });
1025
1065
  if (existingLookup.errors.length > 0) {
1026
1066
  for (const error of existingLookup.errors) {
@@ -1040,7 +1080,56 @@ async function create(name, options = {}) {
1040
1080
  return;
1041
1081
  }
1042
1082
  const existingTask = existingLookup.apps.length === 1 ? existingLookup.apps[0] : null;
1043
- const reuseExistingApp = existingTask !== null && !templateOptionRaw && !remixOptionRaw;
1083
+ if (templateOptionRaw && remixOptionRaw) {
1084
+ (0, messages_1.printErrorWithHelp)('Cannot use --template and --remix at the same time.', ['Choose one source: use "--remix app:creator/name@1.2.3" to remix an app version, or "--template {creator}/template/{name}" to start from a template.'], { command: 'project create app' });
1085
+ process.exitCode = 1;
1086
+ return;
1087
+ }
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;
1044
1133
  if (!reuseExistingApp && ((0, node_fs_1.existsSync)(file) || (0, node_fs_1.existsSync)(projectDirCandidate))) {
1045
1134
  (0, messages_1.printErrorWithHelp)(`Cannot create ${name} because a file or directory with that name already exists.`, [
1046
1135
  'Choose a different app name or remove the existing file before running "playdrop project create app" again.'
@@ -1048,11 +1137,6 @@ async function create(name, options = {}) {
1048
1137
  process.exitCode = 1;
1049
1138
  return;
1050
1139
  }
1051
- if (templateOptionRaw && remixOptionRaw) {
1052
- (0, messages_1.printErrorWithHelp)('Cannot use --template and --remix at the same time.', ['Choose one source: use "--remix app:creator/name@1.2.3" to remix an app version, or "--template {creator}/template/{name}" to start from a template.'], { command: 'project create app' });
1053
- process.exitCode = 1;
1054
- return;
1055
- }
1056
1140
  if (!templateOptionRaw && !remixOptionRaw) {
1057
1141
  if (!reuseExistingApp) {
1058
1142
  (0, messages_1.printErrorWithHelp)('Specify either --template or --remix when creating an app.', [
@@ -1087,7 +1171,7 @@ async function create(name, options = {}) {
1087
1171
  return;
1088
1172
  }
1089
1173
  }
1090
- const ctx = await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)('project create app', 'Creating an app', { workspacePath: process.cwd() });
1174
+ ctx = ctx ?? await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)('project create app', 'Creating an app', { workspacePath: process.cwd() });
1091
1175
  if (!ctx) {
1092
1176
  return;
1093
1177
  }
@@ -1386,6 +1470,7 @@ async function create(name, options = {}) {
1386
1470
  }
1387
1471
  registrationTask = selectedTask;
1388
1472
  }
1473
+ let gameIdeaAppRef = null;
1389
1474
  if (registrationTask) {
1390
1475
  const metadataWarning = (0, catalogue_1.formatMissingMetadataWarnings)(registrationTask);
1391
1476
  if (metadataWarning) {
@@ -1506,6 +1591,7 @@ async function create(name, options = {}) {
1506
1591
  throw error;
1507
1592
  }
1508
1593
  }
1594
+ gameIdeaAppRef = `${creatorUsername}/${name}`;
1509
1595
  }
1510
1596
  const projectDirLabel = projectDir ? ((0, node_path_1.relative)(process.cwd(), projectDir) || projectDir) : null;
1511
1597
  const entryPointLabel = (0, node_path_1.relative)(process.cwd(), htmlFilePath) || (0, node_path_1.basename)(htmlFilePath);
@@ -1543,6 +1629,70 @@ async function create(name, options = {}) {
1543
1629
  catalogueLabel = (0, node_path_1.relative)(process.cwd(), existingTask.catalogueAbsolutePath) || CATALOGUE_FILENAME;
1544
1630
  }
1545
1631
  }
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
+ }
1546
1696
  logNextSteps(name, {
1547
1697
  hasCatalogue,
1548
1698
  catalogueLabel,
@@ -5,6 +5,16 @@ 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
+ };
8
18
  type AppUpdateOptions = {
9
19
  creator?: string;
10
20
  displayName?: string;
@@ -28,6 +38,9 @@ type MutationOptions = {
28
38
  json?: boolean;
29
39
  };
30
40
  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>;
31
44
  export declare function updateCreationApp(nameArg: string | undefined, options?: AppUpdateOptions): Promise<void>;
32
45
  export declare function deleteCreationApp(nameArg: string | undefined, options?: MutationOptions): Promise<void>;
33
46
  export declare function setCurrentCreationAppVersion(nameArg: string | undefined, versionArg: string | undefined, options?: MutationOptions): Promise<void>;
@@ -1,6 +1,9 @@
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;
4
7
  exports.updateCreationApp = updateCreationApp;
5
8
  exports.deleteCreationApp = deleteCreationApp;
6
9
  exports.setCurrentCreationAppVersion = setCurrentCreationAppVersion;
@@ -26,6 +29,7 @@ const messages_1 = require("../messages");
26
29
  const output_1 = require("../output");
27
30
  const refs_1 = require("../refs");
28
31
  const MERGED_PAGE_SIZE = 100;
32
+ const IDEA_STATE_VALUES = new Set(['idea', 'started', 'shipped', 'all']);
29
33
  function parseBrowseKind(raw) {
30
34
  if (!raw || raw.trim().length === 0) {
31
35
  return 'all';
@@ -49,6 +53,19 @@ function parsePositiveInteger(raw, label, fallback) {
49
53
  }
50
54
  return parsed;
51
55
  }
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
+ }
52
69
  async function resolveCreator(client, rawCreator, command) {
53
70
  let creator;
54
71
  try {
@@ -294,6 +311,130 @@ async function browseCreations(options = {}) {
294
311
  }
295
312
  });
296
313
  }
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
+ }
297
438
  async function updateCreationApp(nameArg, options = {}) {
298
439
  const name = parseManagedName(nameArg, 'creations apps update');
299
440
  if (!name) {
@@ -3,5 +3,6 @@ export type UploadCommandOptions = {
3
3
  skipEcs?: boolean;
4
4
  skipReview?: boolean;
5
5
  clearTags?: boolean;
6
+ gameIdea?: string;
6
7
  };
7
8
  export declare function upload(pathOrName: string, options?: UploadCommandOptions): Promise<void>;
@@ -6,6 +6,7 @@ const node_path_1 = require("node:path");
6
6
  const semver_1 = require("semver");
7
7
  const types_1 = require("@playdrop/types");
8
8
  const apps_1 = require("../apps");
9
+ const registration_1 = require("../apps/registration");
9
10
  const commandContext_1 = require("../commandContext");
10
11
  const http_1 = require("../http");
11
12
  const catalogue_1 = require("../catalogue");
@@ -811,6 +812,15 @@ function appendTaskRelations(graphState, fromNodeId, relations, contextLabel) {
811
812
  });
812
813
  }
813
814
  async function uploadAppTask(state, task, taskCreator, options) {
815
+ const gameIdeaRef = typeof options?.gameIdea === 'string' ? options.gameIdea.trim() : '';
816
+ if (gameIdeaRef) {
817
+ await (0, registration_1.ensureRegisteredHostedAppShell)({
818
+ client: state.client,
819
+ creatorUsername: taskCreator,
820
+ task,
821
+ });
822
+ await state.client.startGameIdea(gameIdeaRef, { app: `${taskCreator}/${task.name}` });
823
+ }
814
824
  const { upload, warnings } = await (0, apps_1.runAppPipeline)(state.client, task, {
815
825
  skipEcs: options?.skipEcs,
816
826
  skipReview: options?.skipReview,
@@ -844,6 +854,9 @@ async function uploadAppTask(state, task, taskCreator, options) {
844
854
  };
845
855
  state.uploadedAppsByName.set(task.name, uploadedApp);
846
856
  (0, upload_graph_1.registerCanonicalNode)(state.graphState, appRef, uploadedApp.versionNodeId);
857
+ if (gameIdeaRef) {
858
+ await state.client.shipGameIdea(gameIdeaRef, { app: `${taskCreator}/${task.name}` });
859
+ }
847
860
  (0, upload_graph_1.registerLocalRef)(state.graphState.localAppNodeByName, state.graphState.ambiguousAppNames, task.name, uploadedApp.versionNodeId);
848
861
  appendTaskRelations(state.graphState, uploadedApp.versionNodeId, task.graph.relations, `[${task.cataloguePath}] app "${task.name}"`);
849
862
  const entry = {
@@ -1166,6 +1179,15 @@ async function upload(pathOrName, options) {
1166
1179
  }
1167
1180
  const warnings = selection.warnings;
1168
1181
  const tasks = selection.tasks;
1182
+ const gameIdeaRef = typeof options?.gameIdea === 'string' ? options.gameIdea.trim() : '';
1183
+ if (gameIdeaRef) {
1184
+ const appTasks = tasks.filter((task) => task.kind === 'app');
1185
+ if (appTasks.length !== 1) {
1186
+ (0, messages_1.printErrorWithHelp)('--game-idea can only be used when publishing exactly one app.', ['Publish a single app name or file, then retry with --game-idea.'], { command: 'project publish' });
1187
+ process.exitCode = 1;
1188
+ return;
1189
+ }
1190
+ }
1169
1191
  if (tagGroupLoad.groups.length > 0 && userInfo.role !== 'ADMIN') {
1170
1192
  (0, messages_1.printErrorWithHelp)('Publishing tagGroups requires an admin account.', [
1171
1193
  'Log in as an admin before publishing taxonomy changes.',
package/dist/index.js CHANGED
@@ -412,6 +412,32 @@ creations
412
412
  .action(async (opts) => {
413
413
  await (0, creations_1.browseCreations)(opts);
414
414
  });
415
+ const creationIdeas = creations.command('ideas').description('Manage game ideas');
416
+ creationIdeas
417
+ .command('browse')
418
+ .description('Browse your game ideas')
419
+ .option('--state <state>', 'idea, started, shipped, or all')
420
+ .option('--limit <number>', 'Maximum number of results to return')
421
+ .option('--offset <number>', 'Result offset')
422
+ .option('--json', 'Output JSON')
423
+ .action(async (opts) => {
424
+ await (0, creations_1.browseCreationIdeas)(opts);
425
+ });
426
+ creationIdeas
427
+ .command('start <idOrSlug>')
428
+ .description('Mark a game idea as started')
429
+ .option('--json', 'Output JSON')
430
+ .action(async (idOrSlug, opts) => {
431
+ await (0, creations_1.startCreationIdea)(idOrSlug, opts);
432
+ });
433
+ creationIdeas
434
+ .command('ship <idOrSlug>')
435
+ .description('Mark a game idea as shipped')
436
+ .requiredOption('--app <creator/name>', 'Created app ref')
437
+ .option('--json', 'Output JSON')
438
+ .action(async (idOrSlug, opts) => {
439
+ await (0, creations_1.shipCreationIdea)(idOrSlug, opts);
440
+ });
415
441
  const creationApps = creations.command('apps').description('Manage app creations');
416
442
  creationApps
417
443
  .command('update <name>')
@@ -680,6 +706,7 @@ projectCreate
680
706
  .description('Create a new app from a template or exact remix version')
681
707
  .option('--template <ref>', 'Template ref')
682
708
  .option('--remix <ref>', 'Canonical app version ref')
709
+ .option('--game-idea <idOrSlug>', 'Game idea id or slug to infer the source and mark started')
683
710
  .action(async (name, opts) => {
684
711
  await (0, create_1.create)(name, opts);
685
712
  });
@@ -822,6 +849,7 @@ project
822
849
  .option('--env <env>', 'Resolve this command against one logged-in environment')
823
850
  .option('--skip-ecs', 'Skip ecs.json and server.js during publish')
824
851
  .option('--clear-tags', 'Confirm clearing existing live tags when this publish removes them')
852
+ .option('--game-idea <idOrSlug>', 'Game idea id or slug to mark shipped')
825
853
  .addOption(new commander_1.Option('--skip-review', 'Mark uploaded app versions as NOT_REQUIRED review. Admin-only.').hideHelp())
826
854
  .action(async (target, opts) => {
827
855
  await (0, upload_1.upload)(target ?? '.', {
@@ -829,6 +857,7 @@ project
829
857
  skipEcs: opts.skipEcs,
830
858
  skipReview: opts.skipReview,
831
859
  clearTags: opts.clearTags,
860
+ gameIdea: opts.gameIdea,
832
861
  });
833
862
  });
834
863
  const documentation = program.command('documentation').description('Public Playdrop documentation');
@@ -1,4 +1,5 @@
1
- import type { AppleOAuthHandoffExchangeRequest, AppleOAuthHandoffExchangeResponse, AppleOAuthLinkStartRequest, AppleOAuthLinkStartResponse, ApplePushDeviceRequest, ApplePushDeviceStatusResponse, ApiKeyLoginRequest, CliApiKeyStatusResponse, CliLoginApproveRequest, CliLoginApproveResponse, CliLoginPollRequest, CliLoginPollResponse, CliLoginStartResponse, CliLoginStatusResponse, CliWebLaunchStartRequest, CliWebLaunchStartResponse, CompleteXSignupRequest, CompleteXSignupResponse, DeleteMyAccountRequest, DeleteMyAccountResponse, LoginRequest, LoginResponse, LogoutResponse, PrivacyExportResponse, RegisterRequest, RegisterResponse, StarterProfileAssetsResponse, CreateAppRequest, CreateAppResponse, UpdateAppRequest, AdminUpdateAppRequest, UpdateAppResponse, AppsListResponse, AppBrowseRequest, AppDetailRequest, AppDetailResponse, AppMoreCollectionKey, AppMoreCollectionPage, AppMoreCollectionRequest, AppMoreCollectionsRequest, AppMoreCollectionsResponse, CreatorAppsBrowseRequest, AppRuntimeAssetsResponse, ContentLicense, InitializeAppUploadRequest, InitializeAppUploadResponse, UploadAppSessionFileResponse, UploadAppSessionOwnedAssetResponse, FinalizeAppUploadResponse, AppUploadLaunchCheckPreviewResponse, AbortAppUploadResponse, RecordAppUploadLaunchCheckRequest, RecordAppUploadLaunchCheckResponse, MyAppsResponse, BootstrapResponse, SetProfileAssetRequest, SetProfileAssetResponse, MeResponse, UserDetailResponse, UsersListResponse, CreateUserRequest, CreateUserResponse, UpdateAdminUserRequest, RemixScaffoldResponse, TemplateScaffoldResponse, HomeResponse, HomeCollectionPageResponse, FetchHomeOptions, DeleteAppResponse, AppType, AppAccessTokenResponse, DevAppAccessTokenResponse, DevPlayerResetResponse, AppLogEntryRequest, AppLogEntryResponse, ClaimFreeCreditRewardResponse, SubmitFeedbackRequest, SubmitFeedbackResponse, FeedbackListResponse, AdminFeedbackListResponse, AdminFeedbackResponse, UpdateAdminFeedbackRequest, UpdateAdminFeedbackResponse, DeleteFeedbackResponse, FeedbackCategory, FeedbackClient, FeedbackStatus, ShopResponse, AdInterstitialShowResponse, AdLoadResponse, AdShowRequest, AdRewardedShowResponse, AssetPurchaseRequest, BoostActivationRequest, BoostBalanceResponse, BoostHistoryResponse, BoostPurchaseRequest, BoostPurchaseResponse, BoostReportQuery, BoostReportResponse, BoostStatusResponse, AdLoadRequest, CreditPackCheckoutSessionRequest, PurchaseResponse, CreditPackCheckoutSessionResponse, CreditPackCheckoutStatusResponse, CreatorEarningsResponse, TransactionsResponse, CreditPacksResponse, CreditPackResponse, PaymentProvider, PaymentStatus, AdminPaymentsListResponse, AdminPaymentDetailResponse, AdminRefundPaymentRequest, IapPurchaseRequest, IapPurchaseResponse, IapReceiptsResponse, IapReceiptListParams, IapReceiptResponse, AdminCreditTransactionsResponse, AiGenerationsResponse, AiGenerationDetailResponse, OwnedProfileAssetsResponse, SavedItemKind, CreateContentReportRequest, CreateCreatorReportRequest, CreateCommentReportRequest, CreatorBlockMutationResponse, LikedItemMutationResponse, DislikedItemMutationResponse, ModerationReportResponse, SavedItemMutationResponse, LibraryCategory, LibrarySort, LibraryResponse, TrackEngagementEventRequest, TrackEngagementEventResponse, CreatorFollowMutationResponse, ContentCommentsResponse, CreateContentCommentRequest, CreateContentCommentResponse, FollowingSort, FollowingCreatorsResponse, FollowingFeedSection, FollowingFeedResponse, DeleteContentCommentResponse, NotificationStatus, NotificationsResponse, NotificationReadResponse, PushSubscriptionMutationResponse, AdminUserResponse, DeleteAdminUserResponse, BatchCreateUsersResponse, UpsertWebPushSubscriptionRequest, UpdateProfileRequest, UpdateProfileResponse, WebPushConfigResponse, WebPushSubscriptionStatusResponse, ChangePasswordRequest, ChangePasswordResponse, UpgradeToCreatorResponse, AppleLinkRequest, AppleLinkResponse, AppleNativeChallengeResponse, ApplePendingSignupResponse, AppleSignInRequest, AppleSignInResponse, UnlinkXResponse, UnlinkGoogleResponse, GooglePendingSignupResponse, XPendingSignupResponse, GoogleOneTapExchangeRequest, GoogleOneTapExchangeResponse, UsernameAvailableResponse, CompleteAppleSignupRequest, CompleteAppleSignupResponse, CompleteGoogleSignupRequest, CompleteGoogleSignupResponse, GoogleAppleLinkRequest, GoogleAppleLinkResponse, GoogleAppleSignInRequest, GoogleAppleSignInResponse, AdminAppPlayerMetaResponse, AdminAppPlayerMetaUserResponse, AdminAppVersionReviewsListResponse, AdminClaimNextAppVersionReviewResponse, AdminMutateAchievementStateRequest, AdminMutateAchievementStateResponse, AdminMutateLeaderboardScoreRequest, AdminMutateLeaderboardScoreResponse, AdminRetirePlayerMetaDefinitionResponse, AdminUpdateAppVersionReviewRequest, AdminUpdateAppVersionReviewResponse, CreateCliApiKeyResponse, GetAchievementResponse, GetLeaderboardResponse, ListAchievementsResponse, ListLeaderboardsResponse, MyAchievementsResponse, MyLeaderboardsResponse, SetAchievementProgressRequest, SetAchievementProgressResponse, SubmitLeaderboardScoreRequest, SubmitLeaderboardScoreResponse, UnlockAchievementResponse, ReviewState, AppVersionVisibility, AppVersionsListResponse, AppVersionDetailResponse, AppSourceBundleResponse, UpdateVersionResponse, DeleteVersionResponse, AdminAppsListResponse, AdminGameCollectionCandidatesResponse, AdminGameCollectionResponse, AdminGameCollectionsResponse, AdminUpdateAppResponse, CreateAdminGameCollectionRequest, DeleteAdminGameCollectionResponse, UpdateAdminGameCollectionGamesRequest, UpdateAdminGameCollectionRequest, AssetBrowseKind, AssetCategory, AssetFormat, AssetVisibility, AssetSourceKind, AssetListSort, AssetListResponse, AssetCategoriesResponse, AssetDetailResponse, AssetVersionsListResponse, CreateAssetVersionResponse, DeleteOwnedCustomAssetResponse, CreateAssetSpecVersionResponse, ListOwnedCustomAssetsResponse, LoadOwnedCustomAssetResponse, MutateOwnedCustomAssetRequest, MutateOwnedCustomAssetResponse, UpdateAssetRequest, UpdateAssetResponse, UpdateAssetVersionRequest, UpdateAssetVersionResponse, UpdateAssetSpecVersionResponse, DeleteAssetVersionResponse, DeleteAssetResponse, DeleteAssetSpecVersionResponse, DeleteAssetSpecResponse, AssetSourceBundleResponse, AssetSpecAssetsListResponse, AssetSpecAppsListResponse, AssetSpecDetailResponse, AssetSpecListSort, AssetSpecListResponse, AssetSpecVersionResponseEnvelope, AssetSpecVersionsListResponse, AssetPackSourceBundleResponse, AssetPackListResponse, AssetPackDetailResponse, AssetPackVersionsListResponse, InitializeAssetPackUploadRequest, InitializeAssetPackUploadResponse, UploadAssetPackSessionAssetResponse, UploadAssetPackSessionMediaResponse, FinalizeAssetPackUploadResponse, AbortAssetPackUploadResponse, UpdateAssetPackRequest, UpdateAssetPackResponse, UpdateAssetPackVersionRequest, UpdateAssetPackVersionResponse, DeleteAssetPackVersionResponse, DeleteAssetPackResponse, BatchUpsertNodeRelationsRequest, BatchUpsertNodeRelationsResponse, ContentRemixRelationsResponse, SearchRequest, SearchResponse, SearchSuggestRequest, SearchSuggestResponse, AdminTagGroupResponse, AdminTagGroupsResponse, AdminTagResponse, CreateTagGroupRequest, CreateTagRequest, TagDirectoryResponse, TagDetailResponse, TagGroupDetailResponse, TagGroupSort, TagListResponse, UpdateTagGroupRequest, UpdateTagRequest, FreeCreditsResponse, InvitePreviewResponse, MyInviteCodeResponse } from '@playdrop/types';
1
+ import type { AppleOAuthHandoffExchangeRequest, AppleOAuthHandoffExchangeResponse, AppleOAuthLinkStartRequest, AppleOAuthLinkStartResponse, ApplePushDeviceRequest, ApplePushDeviceStatusResponse, FirebasePushDeviceRequest, FirebasePushDeviceStatusResponse, ApiKeyLoginRequest, CliApiKeyStatusResponse, CliLoginApproveRequest, CliLoginApproveResponse, CliLoginPollRequest, CliLoginPollResponse, CliLoginStartResponse, CliLoginStatusResponse, CliWebLaunchStartRequest, CliWebLaunchStartResponse, CompleteXSignupRequest, CompleteXSignupResponse, DeleteMyAccountRequest, DeleteMyAccountResponse, LoginRequest, LoginResponse, LogoutResponse, PrivacyExportResponse, RegisterRequest, RegisterResponse, StarterProfileAssetsResponse, CreateAppRequest, CreateAppResponse, UpdateAppRequest, AdminUpdateAppRequest, UpdateAppResponse, AppsListResponse, AppBrowseRequest, AppDetailRequest, AppDetailResponse, AppMoreCollectionKey, AppMoreCollectionPage, AppMoreCollectionRequest, AppMoreCollectionsRequest, AppMoreCollectionsResponse, CreatorAppsBrowseRequest, AppRuntimeAssetsResponse, ContentLicense, InitializeAppUploadRequest, InitializeAppUploadResponse, UploadAppSessionFileResponse, UploadAppSessionOwnedAssetResponse, FinalizeAppUploadResponse, AppUploadLaunchCheckPreviewResponse, AbortAppUploadResponse, RecordAppUploadLaunchCheckRequest, RecordAppUploadLaunchCheckResponse, MyAppsResponse, BootstrapResponse, SetProfileAssetRequest, SetProfileAssetResponse, MeResponse, UserDetailResponse, UsersListResponse, CreateUserRequest, CreateUserResponse, UpdateAdminUserRequest, RemixScaffoldResponse, TemplateScaffoldResponse, HomeResponse, HomeCollectionPageResponse, FetchHomeOptions, DeleteAppResponse, AppType, AppAccessTokenResponse, DevAppAccessTokenResponse, DevPlayerResetResponse, AppLogEntryRequest, AppLogEntryResponse, ClaimFreeCreditRewardResponse, SubmitFeedbackRequest, SubmitFeedbackResponse, FeedbackListResponse, AdminFeedbackListResponse, AdminFeedbackResponse, UpdateAdminFeedbackRequest, UpdateAdminFeedbackResponse, DeleteFeedbackResponse, FeedbackCategory, FeedbackClient, FeedbackStatus, ShopResponse, AdInterstitialShowResponse, AdLoadResponse, AdShowRequest, AdRewardedShowResponse, AssetPurchaseRequest, BoostActivationRequest, BoostBalanceResponse, BoostHistoryResponse, BoostPurchaseRequest, BoostPurchaseResponse, BoostReportQuery, BoostReportResponse, BoostStatusResponse, AdLoadRequest, CreditPackCheckoutSessionRequest, PurchaseResponse, CreditPackCheckoutSessionResponse, CreditPackCheckoutStatusResponse, CreatorEarningsResponse, TransactionsResponse, CreditPacksResponse, CreditPackResponse, PaymentProvider, PaymentStatus, AdminPaymentsListResponse, AdminPaymentDetailResponse, AdminRefundPaymentRequest, IapPurchaseRequest, IapPurchaseResponse, IapReceiptsResponse, IapReceiptListParams, IapReceiptResponse, AdminCreditTransactionsResponse, AiGenerationsResponse, AiGenerationDetailResponse, OwnedProfileAssetsResponse, SavedItemKind, CreateContentReportRequest, CreateCreatorReportRequest, CreateCommentReportRequest, CreatorBlockMutationResponse, LikedItemMutationResponse, DislikedItemMutationResponse, ModerationReportResponse, SavedItemMutationResponse, LibraryCategory, LibrarySort, LibraryResponse, TrackEngagementEventRequest, TrackEngagementEventResponse, CreatorFollowMutationResponse, ContentCommentsResponse, CreateContentCommentRequest, CreateContentCommentResponse, FollowingSort, FollowingCreatorsResponse, FollowingFeedSection, FollowingFeedResponse, DeleteContentCommentResponse, NotificationStatus, NotificationsResponse, NotificationReadResponse, PushSubscriptionMutationResponse, AdminUserResponse, DeleteAdminUserResponse, BatchCreateUsersResponse, UpsertWebPushSubscriptionRequest, UpdateProfileRequest, UpdateProfileResponse, WebPushConfigResponse, WebPushSubscriptionStatusResponse, ChangePasswordRequest, ChangePasswordResponse, UpgradeToCreatorResponse, AppleLinkRequest, AppleLinkResponse, AppleNativeChallengeResponse, ApplePendingSignupResponse, AppleSignInRequest, AppleSignInResponse, UnlinkXResponse, UnlinkGoogleResponse, GooglePendingSignupResponse, XPendingSignupResponse, GoogleOneTapExchangeRequest, GoogleOneTapExchangeResponse, UsernameAvailableResponse, CompleteAppleSignupRequest, CompleteAppleSignupResponse, CompleteGoogleSignupRequest, CompleteGoogleSignupResponse, GoogleAppleLinkRequest, GoogleAppleLinkResponse, GoogleAppleSignInRequest, GoogleAppleSignInResponse, AdminAppPlayerMetaResponse, AdminAppPlayerMetaUserResponse, AdminAppVersionReviewsListResponse, AdminClaimNextAppVersionReviewResponse, AdminMutateAchievementStateRequest, AdminMutateAchievementStateResponse, AdminMutateLeaderboardScoreRequest, AdminMutateLeaderboardScoreResponse, AdminRetirePlayerMetaDefinitionResponse, AdminUpdateAppVersionReviewRequest, AdminUpdateAppVersionReviewResponse, CreateCliApiKeyResponse, GetAchievementResponse, GetLeaderboardResponse, ListAchievementsResponse, ListLeaderboardsResponse, MyAchievementsResponse, MyLeaderboardsResponse, SetAchievementProgressRequest, SetAchievementProgressResponse, SubmitLeaderboardScoreRequest, SubmitLeaderboardScoreResponse, UnlockAchievementResponse, ReviewState, AppVersionVisibility, AppVersionsListResponse, AppVersionDetailResponse, AppSourceBundleResponse, UpdateVersionResponse, DeleteVersionResponse, AdminAppsListResponse, AdminGameCollectionCandidatesResponse, AdminGameCollectionResponse, AdminGameCollectionsResponse, AdminUpdateAppResponse, CreateAdminGameCollectionRequest, DeleteAdminGameCollectionResponse, UpdateAdminGameCollectionGamesRequest, UpdateAdminGameCollectionRequest, AssetBrowseKind, AssetCategory, AssetFormat, AssetVisibility, AssetSourceKind, AssetListSort, AssetListResponse, AssetCategoriesResponse, AssetDetailResponse, AssetVersionsListResponse, CreateAssetVersionResponse, DeleteOwnedCustomAssetResponse, CreateAssetSpecVersionResponse, ListOwnedCustomAssetsResponse, LoadOwnedCustomAssetResponse, MutateOwnedCustomAssetRequest, MutateOwnedCustomAssetResponse, UpdateAssetRequest, UpdateAssetResponse, UpdateAssetVersionRequest, UpdateAssetVersionResponse, UpdateAssetSpecVersionResponse, DeleteAssetVersionResponse, DeleteAssetResponse, DeleteAssetSpecVersionResponse, DeleteAssetSpecResponse, AssetSourceBundleResponse, AssetSpecAssetsListResponse, AssetSpecAppsListResponse, AssetSpecDetailResponse, AssetSpecListSort, AssetSpecListResponse, AssetSpecVersionResponseEnvelope, AssetSpecVersionsListResponse, AssetPackSourceBundleResponse, AssetPackListResponse, AssetPackDetailResponse, AssetPackVersionsListResponse, InitializeAssetPackUploadRequest, InitializeAssetPackUploadResponse, UploadAssetPackSessionAssetResponse, UploadAssetPackSessionMediaResponse, FinalizeAssetPackUploadResponse, AbortAssetPackUploadResponse, UpdateAssetPackRequest, UpdateAssetPackResponse, UpdateAssetPackVersionRequest, UpdateAssetPackVersionResponse, DeleteAssetPackVersionResponse, DeleteAssetPackResponse, BatchUpsertNodeRelationsRequest, BatchUpsertNodeRelationsResponse, ContentRemixRelationsResponse, SearchRequest, SearchResponse, SearchSuggestRequest, SearchSuggestResponse, AdminTagGroupResponse, AdminTagGroupsResponse, AdminTagResponse, CreateTagGroupRequest, CreateTagRequest, TagDirectoryResponse, TagDetailResponse, TagGroupDetailResponse, TagGroupSort, TagListResponse, UpdateTagGroupRequest, UpdateTagRequest, FreeCreditsResponse, InvitePreviewResponse, MyInviteCodeResponse, CreateGameIdeaResponse, GameIdeaStatusResponse, GameIdeasListRequest, GameIdeasListResponse, RemixCandidatesResponse, ShipGameIdeaRequest, ShipGameIdeaResponse, StartGameIdeaRequest, StartGameIdeaResponse, UpdateGameIdeaRequest, UpdateGameIdeaResponse } from '@playdrop/types';
2
+ import { type CreateGameIdeaOptions } from './domains/game-ideas.js';
2
3
  export interface HttpMethod {
3
4
  GET: 'GET';
4
5
  POST: 'POST';
@@ -131,6 +132,17 @@ export interface ApiClient {
131
132
  fetchInvitePreview(inviteCode: string): Promise<InvitePreviewResponse>;
132
133
  fetchMyInviteCode(): Promise<MyInviteCodeResponse>;
133
134
  submitMyInviteCode(inviteCode: string): Promise<MyInviteCodeResponse>;
135
+ createGameIdea(options: CreateGameIdeaOptions): Promise<CreateGameIdeaResponse>;
136
+ listGameIdeas(options?: GameIdeasListRequest): Promise<GameIdeasListResponse>;
137
+ getGameIdeaStatus(idOrSlug: string): Promise<GameIdeaStatusResponse>;
138
+ updateGameIdea(idOrSlug: string, body: UpdateGameIdeaRequest): Promise<UpdateGameIdeaResponse>;
139
+ startGameIdea(idOrSlug: string, body?: StartGameIdeaRequest): Promise<StartGameIdeaResponse>;
140
+ shipGameIdea(idOrSlug: string, body: ShipGameIdeaRequest): Promise<ShipGameIdeaResponse>;
141
+ fetchRemixCandidates(options?: {
142
+ q?: string;
143
+ limit?: number;
144
+ offset?: number;
145
+ }): Promise<RemixCandidatesResponse>;
134
146
  checkUsernameAvailable(username: string): Promise<UsernameAvailableResponse>;
135
147
  completeGoogleSignup(request: CompleteGoogleSignupRequest): Promise<CompleteGoogleSignupResponse>;
136
148
  completeXSignup(request: CompleteXSignupRequest): Promise<CompleteXSignupResponse>;
@@ -190,6 +202,9 @@ export interface ApiClient {
190
202
  fetchApplePushDeviceStatus(request: ApplePushDeviceRequest): Promise<ApplePushDeviceStatusResponse>;
191
203
  upsertApplePushDevice(request: ApplePushDeviceRequest): Promise<PushSubscriptionMutationResponse>;
192
204
  deleteApplePushDevice(request: ApplePushDeviceRequest): Promise<PushSubscriptionMutationResponse>;
205
+ fetchFirebasePushDeviceStatus(request: FirebasePushDeviceRequest): Promise<FirebasePushDeviceStatusResponse>;
206
+ upsertFirebasePushDevice(request: FirebasePushDeviceRequest): Promise<PushSubscriptionMutationResponse>;
207
+ deleteFirebasePushDevice(request: FirebasePushDeviceRequest): Promise<PushSubscriptionMutationResponse>;
193
208
  search(request: SearchRequest): Promise<SearchResponse>;
194
209
  suggestSearch(request: SearchSuggestRequest): Promise<SearchSuggestResponse>;
195
210
  fetchTagDirectory(options?: {