@playdrop/playdrop-cli 0.3.4-build.1 → 0.3.5-build.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +60 -23
  2. package/config/client-meta.json +5 -5
  3. package/dist/apps/upload.js +5 -3
  4. package/dist/assets/model-artifacts.js +1 -1
  5. package/dist/catalogue.d.ts +6 -0
  6. package/dist/catalogue.js +38 -1
  7. package/dist/commandContext.d.ts +1 -0
  8. package/dist/commandContext.js +45 -15
  9. package/dist/commands/browse.d.ts +16 -0
  10. package/dist/commands/browse.js +370 -0
  11. package/dist/commands/build.js +4 -9
  12. package/dist/commands/capture.js +24 -24
  13. package/dist/commands/captureRemote.d.ts +11 -0
  14. package/dist/commands/captureRemote.js +90 -0
  15. package/dist/commands/comments.d.ts +14 -0
  16. package/dist/commands/comments.js +189 -0
  17. package/dist/commands/create.js +112 -72
  18. package/dist/commands/creations.d.ts +49 -0
  19. package/dist/commands/creations.js +657 -0
  20. package/dist/commands/credits.d.ts +10 -0
  21. package/dist/commands/credits.js +91 -0
  22. package/dist/commands/detail.d.ts +2 -2
  23. package/dist/commands/detail.js +148 -290
  24. package/dist/commands/dev.js +24 -24
  25. package/dist/commands/devShared.js +2 -2
  26. package/dist/commands/documentation.d.ts +4 -1
  27. package/dist/commands/documentation.js +79 -104
  28. package/dist/commands/feedback.d.ts +12 -9
  29. package/dist/commands/feedback.js +125 -257
  30. package/dist/commands/format.js +6 -13
  31. package/dist/commands/generation.d.ts +11 -0
  32. package/dist/commands/generation.js +204 -42
  33. package/dist/commands/gettingStarted.d.ts +1 -0
  34. package/dist/commands/gettingStarted.js +26 -0
  35. package/dist/commands/init.js +26 -24
  36. package/dist/commands/login.js +9 -8
  37. package/dist/commands/logout.js +2 -1
  38. package/dist/commands/notifications.d.ts +14 -0
  39. package/dist/commands/notifications.js +179 -0
  40. package/dist/commands/search.d.ts +13 -0
  41. package/dist/commands/search.js +198 -0
  42. package/dist/commands/upload.js +20 -17
  43. package/dist/commands/validate.js +15 -1
  44. package/dist/commands/versionsBrowse.d.ts +7 -0
  45. package/dist/commands/versionsBrowse.js +209 -0
  46. package/dist/commands/whoami.js +9 -8
  47. package/dist/errors.d.ts +9 -0
  48. package/dist/errors.js +52 -0
  49. package/dist/externalAssetPackValidation.d.ts +2 -0
  50. package/dist/externalAssetPackValidation.js +115 -0
  51. package/dist/http.js +1 -1
  52. package/dist/index.js +570 -630
  53. package/dist/messages.js +11 -11
  54. package/dist/output.d.ts +5 -0
  55. package/dist/output.js +45 -0
  56. package/dist/playwright.js +1 -1
  57. package/dist/refs.d.ts +18 -0
  58. package/dist/refs.js +105 -0
  59. package/node_modules/@playdrop/ai-client/dist/index.d.ts +42 -15
  60. package/node_modules/@playdrop/ai-client/dist/index.d.ts.map +1 -1
  61. package/node_modules/@playdrop/ai-client/package.json +1 -0
  62. package/node_modules/@playdrop/api-client/dist/client.d.ts +39 -27
  63. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  64. package/node_modules/@playdrop/api-client/dist/client.js +280 -1669
  65. package/node_modules/@playdrop/api-client/dist/core/errors.d.ts +9 -0
  66. package/node_modules/@playdrop/api-client/dist/core/errors.d.ts.map +1 -0
  67. package/node_modules/@playdrop/api-client/dist/core/errors.js +46 -0
  68. package/node_modules/@playdrop/api-client/dist/core/request.d.ts +27 -0
  69. package/node_modules/@playdrop/api-client/dist/core/request.d.ts.map +1 -0
  70. package/node_modules/@playdrop/api-client/dist/core/request.js +122 -0
  71. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +75 -0
  72. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -0
  73. package/node_modules/@playdrop/api-client/dist/domains/admin.js +282 -0
  74. package/node_modules/@playdrop/api-client/dist/domains/ai.d.ts +22 -0
  75. package/node_modules/@playdrop/api-client/dist/domains/ai.d.ts.map +1 -0
  76. package/node_modules/@playdrop/api-client/dist/domains/ai.js +15 -0
  77. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +60 -0
  78. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -0
  79. package/node_modules/@playdrop/api-client/dist/domains/apps.js +301 -0
  80. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts +59 -0
  81. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts.map +1 -0
  82. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.js +297 -0
  83. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +62 -0
  84. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -0
  85. package/node_modules/@playdrop/api-client/dist/domains/assets.js +297 -0
  86. package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts +28 -0
  87. package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts.map +1 -0
  88. package/node_modules/@playdrop/api-client/dist/domains/auth.js +78 -0
  89. package/node_modules/@playdrop/api-client/dist/domains/comments.d.ts +29 -0
  90. package/node_modules/@playdrop/api-client/dist/domains/comments.d.ts.map +1 -0
  91. package/node_modules/@playdrop/api-client/dist/domains/comments.js +65 -0
  92. package/node_modules/@playdrop/api-client/dist/domains/me.d.ts +24 -0
  93. package/node_modules/@playdrop/api-client/dist/domains/me.d.ts.map +1 -0
  94. package/node_modules/@playdrop/api-client/dist/domains/me.js +35 -0
  95. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +37 -0
  96. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -0
  97. package/node_modules/@playdrop/api-client/dist/domains/payments.js +148 -0
  98. package/node_modules/@playdrop/api-client/dist/domains/search.d.ts +27 -0
  99. package/node_modules/@playdrop/api-client/dist/domains/search.d.ts.map +1 -0
  100. package/node_modules/@playdrop/api-client/dist/domains/search.js +65 -0
  101. package/node_modules/@playdrop/api-client/dist/index.d.ts +33 -56
  102. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  103. package/node_modules/@playdrop/api-client/dist/index.js +103 -44
  104. package/node_modules/@playdrop/api-client/package.json +3 -2
  105. package/node_modules/@playdrop/boxel-core/package.json +1 -1
  106. package/node_modules/@playdrop/boxel-three/dist/test/glb-skinned.test.js +1 -1
  107. package/node_modules/@playdrop/boxel-three/dist/test/instantiate.test.js +1 -1
  108. package/node_modules/@playdrop/boxel-three/dist/test/skinned-mesh.test.js +1 -1
  109. package/node_modules/@playdrop/boxel-three/package.json +2 -1
  110. package/node_modules/@playdrop/config/client-meta.json +5 -5
  111. package/node_modules/@playdrop/config/dist/src/constants.d.ts +5 -0
  112. package/node_modules/@playdrop/config/dist/src/constants.d.ts.map +1 -1
  113. package/node_modules/@playdrop/config/dist/src/constants.js +5 -1
  114. package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
  115. package/node_modules/@playdrop/config/package.json +1 -1
  116. package/node_modules/@playdrop/types/dist/api.d.ts +178 -17
  117. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  118. package/node_modules/@playdrop/types/dist/api.js +30 -1
  119. package/node_modules/@playdrop/types/dist/app.d.ts +0 -14
  120. package/node_modules/@playdrop/types/dist/app.d.ts.map +1 -1
  121. package/node_modules/@playdrop/types/dist/app.js +0 -10
  122. package/node_modules/@playdrop/types/dist/asset-pack.d.ts +11 -1
  123. package/node_modules/@playdrop/types/dist/asset-pack.d.ts.map +1 -1
  124. package/node_modules/@playdrop/types/dist/asset.d.ts +65 -0
  125. package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
  126. package/node_modules/@playdrop/types/dist/realtime.d.ts +26 -26
  127. package/node_modules/@playdrop/types/dist/realtime.d.ts.map +1 -1
  128. package/node_modules/@playdrop/types/dist/version.d.ts +5 -0
  129. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  130. package/package.json +2 -3
  131. package/bin/playdrop-cli +0 -2
  132. package/dist/commands/asset-packs.d.ts +0 -27
  133. package/dist/commands/asset-packs.js +0 -508
  134. package/dist/commands/assets.d.ts +0 -35
  135. package/dist/commands/assets.js +0 -668
  136. package/dist/commands/list.d.ts +0 -7
  137. package/dist/commands/list.js +0 -347
  138. package/dist/commands/migrateCatalogueV2.d.ts +0 -1
  139. package/dist/commands/migrateCatalogueV2.js +0 -142
  140. package/dist/commands/versions.d.ts +0 -17
  141. package/dist/commands/versions.js +0 -384
package/dist/index.js CHANGED
@@ -1,701 +1,641 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
3
  Object.defineProperty(exports, "__esModule", { value: true });
7
4
  const proxyFetch_1 = require("./proxyFetch");
8
5
  (0, proxyFetch_1.installProxyFetch)();
9
6
  const commander_1 = require("commander");
10
- const node_fs_1 = require("node:fs");
11
- const node_os_1 = __importDefault(require("node:os"));
12
- const node_path_1 = require("node:path");
7
+ const clientInfo_1 = require("./clientInfo");
13
8
  const login_1 = require("./commands/login");
14
9
  const whoami_1 = require("./commands/whoami");
15
10
  const logout_1 = require("./commands/logout");
16
- const create_1 = require("./commands/create");
11
+ const browse_1 = require("./commands/browse");
12
+ const search_1 = require("./commands/search");
17
13
  const detail_1 = require("./commands/detail");
18
- const list_1 = require("./commands/list");
14
+ const comments_1 = require("./commands/comments");
15
+ const versionsBrowse_1 = require("./commands/versionsBrowse");
16
+ const credits_1 = require("./commands/credits");
17
+ const notifications_1 = require("./commands/notifications");
18
+ const creations_1 = require("./commands/creations");
19
19
  const generation_1 = require("./commands/generation");
20
- const assets_1 = require("./commands/assets");
21
- const asset_packs_1 = require("./commands/asset-packs");
20
+ const init_1 = require("./commands/init");
21
+ const create_1 = require("./commands/create");
22
22
  const dev_1 = require("./commands/dev");
23
23
  const capture_1 = require("./commands/capture");
24
- const upload_1 = require("./commands/upload");
24
+ const captureRemote_1 = require("./commands/captureRemote");
25
25
  const validate_1 = require("./commands/validate");
26
26
  const build_1 = require("./commands/build");
27
27
  const format_1 = require("./commands/format");
28
- const migrateCatalogueV2_1 = require("./commands/migrateCatalogueV2");
29
- const clientInfo_1 = require("./clientInfo");
30
- const upgrade_1 = require("./commands/upgrade");
31
- const init_1 = require("./commands/init");
32
- const messages_1 = require("./messages");
28
+ const upload_1 = require("./commands/upload");
33
29
  const documentation_1 = require("./commands/documentation");
34
30
  const feedback_1 = require("./commands/feedback");
35
- const versions_1 = require("./commands/versions");
31
+ const gettingStarted_1 = require("./commands/gettingStarted");
32
+ const upgrade_1 = require("./commands/upgrade");
33
+ const messages_1 = require("./messages");
36
34
  function resolveInitTarget(pathArg) {
37
35
  if (typeof pathArg === 'string' && pathArg.trim().length > 0) {
38
36
  return pathArg.trim();
39
37
  }
40
- const cwdCatalogue = (0, node_path_1.join)(process.cwd(), 'catalogue.json');
41
- if ((0, node_fs_1.existsSync)(cwdCatalogue)) {
42
- return '.';
38
+ return '.';
39
+ }
40
+ function printRemovedCommandHint(command) {
41
+ const replacements = {
42
+ login: 'playdrop auth login',
43
+ whoami: 'playdrop auth whoami',
44
+ logout: 'playdrop auth logout',
45
+ list: 'playdrop browse',
46
+ generate: 'playdrop ai create',
47
+ generations: 'playdrop ai jobs browse',
48
+ init: 'playdrop project init',
49
+ create: 'playdrop project create app',
50
+ dev: 'playdrop project dev',
51
+ capture: 'playdrop project capture',
52
+ validate: 'playdrop project validate',
53
+ build: 'playdrop project build',
54
+ format: 'playdrop project format',
55
+ upload: 'playdrop project publish',
56
+ upgrade: 'playdrop update',
57
+ documentation: 'playdrop documentation browse',
58
+ feedback: 'playdrop feedback send',
59
+ };
60
+ if (replacements[command]) {
61
+ console.error(`"${command}" was removed from the clean-break CLI. Use "${replacements[command]}" instead.`);
62
+ return;
63
+ }
64
+ if (command === 'assets') {
65
+ console.error('"assets" was removed. Use "playdrop detail <creator>/asset/<name>", "playdrop versions browse <creator>/asset/<name>", or "playdrop creations assets ..." instead.');
66
+ return;
67
+ }
68
+ if (command === 'asset-packs') {
69
+ console.error('"asset-packs" was removed. Use "playdrop detail <creator>/asset-pack/<name>", "playdrop versions browse <creator>/asset-pack/<name>", or "playdrop creations asset-packs ..." instead.');
70
+ return;
43
71
  }
44
- const homeDir = node_os_1.default.homedir();
45
- const documentsDir = (0, node_path_1.join)(homeDir, 'Documents');
46
- const baseDir = (0, node_fs_1.existsSync)(documentsDir) ? documentsDir : homeDir;
47
- return (0, node_path_1.join)(baseDir, 'playdrop');
72
+ if (command === 'internal' || command === 'migrate-catalogue-v2' || command === 'download') {
73
+ console.error(`"${command}" is not part of the redesigned CLI surface. Run "playdrop help" to see the supported commands.`);
74
+ return;
75
+ }
76
+ console.error(`Unknown command "${command}". Run "playdrop help" for the current command tree.`);
48
77
  }
49
78
  const program = new commander_1.Command();
50
79
  program
51
- .name('playdrop-cli')
52
- .description('Build, preview, and ship apps to the Playdrop platform.')
80
+ .name('playdrop')
81
+ .description('Playdrop CLI for creators and AI agents.')
53
82
  .usage('<command> [options]')
54
- .version((0, clientInfo_1.getCliVersionLabel)(), '-v, --cli-version', 'Output CLI version information');
83
+ .version((0, clientInfo_1.getCliVersionLabel)(), '-V, --version', 'Print CLI version')
84
+ .addHelpCommand('help [command]', 'Show help for a command');
55
85
  program.showHelpAfterError();
56
86
  program.showSuggestionAfterError();
57
- program.addHelpText('afterAll', '\nFor command-specific flags, run `playdrop-cli <command> --help`.\n');
58
- const defaultHelp = program.createHelp();
59
- program.configureHelp({
60
- formatHelp(cmd, helper) {
61
- if (cmd === program) {
62
- const defaultText = defaultHelp.formatHelp(cmd, helper);
63
- const lines = defaultText.split('\n');
64
- const remaining = lines.length > 4 ? lines.slice(4) : [];
65
- const curated = [
66
- 'Usage: playdrop-cli <command> [options]',
67
- '',
68
- 'Playdrop CLI - build, preview, and ship apps to the Playdrop platform.',
69
- 'Use it to authenticate, scaffold projects, run local previews, and publish updates.',
70
- '',
71
- 'Getting Started',
72
- ' login Sign in and store credentials.',
73
- ' whoami Confirm the account tied to this machine.',
74
- ' init <path> Bootstrap docs and catalogue.json for a project.',
75
- ' create <name> --template <key> Scaffold from a template key.',
76
- ' <name> --remix <key> Remix an existing app key.',
77
- ' dev <name> Serve your app locally with Playdrop headers applied.',
78
- ' capture [name] Run the app headlessly, capture logs, and take screenshots.',
79
- ' validate <pathOrName> Validate catalogue v2 entries without uploading.',
80
- ' build <pathOrName> Build apps and regenerate Boxel and VOX model asset artifacts without uploading.',
81
- ' format <pathOrName> Run formatters for apps, assets, and packs.',
82
- ' migrate-catalogue-v2 [path] Convert legacy catalogue keys into strict schema v2.',
83
- ' upload <pathOrName> Publish a single app or every entry in catalogue.json.',
84
- ' feedback --title <title> [--comment ...|--file ...] Send feedback with CLI metadata.',
85
- '',
86
- 'More Commands',
87
- ' list [--type ...] List apps, assets, and packs.',
88
- ' detail <key> [--format ...] Show detailed metadata for an app, asset, or pack.',
89
- ' generate <type> <prompt> Generate an AI asset directly to your account.',
90
- ' generations [--type ...] List persisted AI generations.',
91
- ' assets download-file <key> Download a single asset file (role-scoped).',
92
- ' assets download-source <key> Download full source bundle ZIP for an asset revision.',
93
- ' assets categories List canonical asset categories and format constraints.',
94
- ' assets versions <subcommand> Manage asset revisions (list, publish, set-current, delete).',
95
- ' asset-packs <subcommand> Manage asset pack metadata and versions.',
96
- ' asset-packs download-source <key> Download full source bundle ZIP for a pack version.',
97
- ' versions <subcommand> Manage app versions (list, set-current, publish, etc.).',
98
- ' documentation [path] List CDN docs or print a specific entry.',
99
- ' logout Clear saved credentials.',
100
- ' upgrade [--dry-run] Update the Playdrop CLI to the latest release.',
101
- '',
102
- 'Next Steps',
103
- ' 1. Run `playdrop-cli login` to authenticate.',
104
- ' 2. Use `playdrop-cli init` (once per repo) and `playdrop-cli create` to spin up an app file.',
105
- ' 3. Preview with `playdrop-cli dev`, iterate, then ship with `playdrop-cli upload`.',
106
- ''
107
- ];
108
- if (remaining.length > 0) {
109
- curated.push(...remaining);
110
- }
111
- return curated.join('\n');
112
- }
113
- return defaultHelp.formatHelp(cmd, helper);
114
- },
115
- visibleOptions(cmd) {
116
- if (cmd === program) {
117
- return [];
118
- }
119
- return defaultHelp.visibleOptions(cmd);
120
- },
121
- visibleCommands(cmd) {
122
- if (cmd === program) {
123
- return [];
124
- }
125
- return defaultHelp.visibleCommands(cmd);
126
- }
127
- });
87
+ program.addHelpText('afterAll', '\nStart here: run `playdrop getting-started`.\n');
128
88
  program.on('command:*', (unknownCommands) => {
129
89
  const [unknownCommand] = unknownCommands;
130
- if (unknownCommand === 'download') {
131
- console.error('`playdrop-cli download` is not available. Use `playdrop-cli assets download-file <asset-key>` or `download-source` instead.');
132
- }
133
- else if (unknownCommand === 'ai') {
134
- console.error('`playdrop-cli ai` is not available. Use `playdrop-cli generate <type> "<prompt>"` or `playdrop-cli generations`.');
135
- }
136
- else if (unknownCommand) {
137
- console.error(`Unknown command "${unknownCommand}". Run "playdrop-cli help" for the full list of commands.`);
90
+ if (unknownCommand) {
91
+ printRemovedCommandHint(unknownCommand);
138
92
  }
139
93
  process.exitCode = 1;
140
94
  });
141
- const envOption = new commander_1.Option('--env <env>', 'Environment (dev|local|prod)');
142
- envOption.default?.('prod');
143
- envOption.hideHelp?.();
144
- const loginCommand = program
95
+ const envOption = new commander_1.Option('--env <env>', 'Environment (dev|local|prod)').default('prod');
96
+ const auth = program.command('auth').description('Authentication commands');
97
+ auth
145
98
  .command('login')
146
- .description('Sign in to Playdrop')
99
+ .description('Log in to Playdrop')
147
100
  .addOption(envOption)
148
101
  .option('--username <username>', 'Username for non-interactive login')
149
- .option('--password <password>', 'Password for non-interactive login');
150
- loginCommand.addHelpText('beforeAll', () => [
151
- '',
152
- 'Authenticate this machine with Playdrop. Accounts are created on https://playdrop.ai, and every CLI command requires an active login before it will run.',
153
- '',
154
- 'Examples:',
155
- ' playdrop-cli login',
156
- ' playdrop-cli login --username bot --password ********',
157
- ' playdrop-cli login --env dev',
158
- ''
159
- ].join('\n'));
160
- loginCommand.addHelpText('afterAll', '\nVerify your session with `playdrop-cli whoami`, then run `playdrop-cli init` in the workspace where you plan to build your first app if you haven\'t bootstrapped it yet.\n');
161
- loginCommand.action(async (opts) => {
102
+ .option('--password <password>', 'Password for non-interactive login')
103
+ .action(async (opts) => {
162
104
  await (0, login_1.login)(opts.env, opts.username, opts.password);
163
105
  });
164
- program
106
+ auth
107
+ .command('logout')
108
+ .description('Log out of Playdrop')
109
+ .action(() => {
110
+ (0, logout_1.logout)();
111
+ });
112
+ auth
165
113
  .command('whoami')
166
- .description('Show current user')
114
+ .description('Show the current account')
167
115
  .action(async () => {
168
116
  await (0, whoami_1.whoami)();
169
117
  });
118
+ const browseCommand = program.command('browse').description('Browse public content');
119
+ browseCommand
120
+ .option('--kind <kind>', 'app, asset, asset-pack, or all')
121
+ .option('--creator <creator>', 'all, me, or a creator slug')
122
+ .option('--app-type <type>', 'game, demo, tool, or template')
123
+ .option('--asset-category <category>', 'Asset category filter')
124
+ .option('--asset-subcategory <subcategory>', 'Asset subcategory filter')
125
+ .option('--limit <number>', 'Maximum number of results to return')
126
+ .option('--offset <number>', 'Result offset')
127
+ .option('--json', 'Output JSON')
128
+ .action(async (opts) => {
129
+ await (0, browse_1.browse)(opts);
130
+ });
131
+ browseCommand
132
+ .command('categories')
133
+ .description('Browse asset category definitions')
134
+ .option('--json', 'Output JSON')
135
+ .action(async (opts) => {
136
+ await (0, browse_1.browseCategories)(opts);
137
+ });
170
138
  program
171
- .command('logout')
172
- .description('Logout of Playdrop')
173
- .action(() => {
174
- (0, logout_1.logout)();
139
+ .command('search <query>')
140
+ .description('Search public content')
141
+ .option('--kind <kind>', 'app, asset, asset-pack, or all')
142
+ .option('--app-type <type>', 'game, demo, tool, or template')
143
+ .option('--asset-category <category>', 'Asset category filter')
144
+ .option('--asset-subcategory <subcategory>', 'Asset subcategory filter')
145
+ .option('--pack-contains-category <category>', 'Asset pack category filter')
146
+ .option('--pack-contains-subcategory <subcategory>', 'Asset pack subcategory filter')
147
+ .option('--limit <number>', 'Maximum number of results to return')
148
+ .option('--offset <number>', 'Result offset')
149
+ .option('--json', 'Output JSON')
150
+ .action(async (query, opts) => {
151
+ await (0, search_1.search)(query, opts);
175
152
  });
176
- const createCommand = program
177
- .command('create <name>')
178
- .description('Register and scaffold a Playdrop app')
179
- .option('--template <key>', 'Template key in {creator}/template/{name} format (short names assume playdrop)')
180
- .option('--remix <key>', 'Remix key in {creator}/{type}/{name} format');
181
- createCommand.addHelpText('beforeAll', () => [
182
- '',
183
- 'Register a new app under your Playdrop account using either a template key or a remix key. The command downloads the scaffold assets, writes the HTML file, and adds the entry to catalogue.json, creating the catalogue if it does not exist yet.',
184
- '',
185
- 'Examples:',
186
- ' playdrop-cli create jumper --template playdrop/template/html_template',
187
- ' playdrop-cli create remix-demo --remix playdrop/game/hangingout',
188
- '',
189
- 'Key formats:',
190
- ' --template expects {creator}/template/{name}; a bare name defaults to playdrop/template/<name>.',
191
- ' --remix expects {creator}/{type}/{name} (e.g. playdrop/game/fly-remix).',
192
- '',
193
- 'What happens:',
194
- ' - Downloads the chosen template or remix and writes <name>.html.',
195
- ' - Ensures catalogue.json exists and records the new app metadata.',
196
- ' - Prints next steps so you can edit, preview, and upload.',
197
- ''
198
- ].join('\n'));
199
- createCommand.addHelpText('afterAll', '\nOnce the file is ready, iterate locally with `playdrop-cli dev <name>` and ship updates with `playdrop-cli upload`.\n');
200
- createCommand.action(async (name, opts) => {
201
- await (0, create_1.create)(name, { template: opts.template, remix: opts.remix });
202
- });
203
- const listCommandInstance = program
204
- .command('list')
205
- .description('List apps, assets, and packs')
206
- .option('--type <type>', 'Filter by type (default: all)')
207
- .option('--creator <creator>', 'Filter by creator (default: me; accepts "me", "all", or a creator slug)')
208
- .option('--max <number>', 'Maximum number of results to display (1-100, default: 10)')
209
- .action(async (opts, cmd) => {
210
- const rawArgs = Array.isArray(cmd.args) ? cmd.args : [];
211
- const unexpectedArgs = rawArgs.filter((arg) => typeof arg === 'string' && arg.trim().length > 0);
212
- if (unexpectedArgs.length > 0) {
213
- const formatted = unexpectedArgs.length === 1
214
- ? `Unexpected argument "${unexpectedArgs[0]}".`
215
- : `Unexpected arguments: ${unexpectedArgs.map(arg => `"${arg}"`).join(', ')}.`;
216
- (0, messages_1.printErrorWithHelp)(formatted, ['Use --type <value> to filter by type.', 'Example: playdrop-cli list --type templates.'], { command: 'list' });
217
- process.exitCode = 1;
218
- return;
219
- }
220
- await (0, list_1.list)({ type: opts.type, creator: opts.creator, max: opts.max });
221
- });
222
- listCommandInstance.addHelpText('beforeAll', () => [
223
- '',
224
- 'List apps, assets, and packs available through your account.',
225
- '',
226
- 'Examples:',
227
- ' playdrop-cli list',
228
- ' playdrop-cli list --type app',
229
- ' playdrop-cli list --creator playdrop --type asset',
230
- ' playdrop-cli list --max 25',
231
- '',
232
- 'Supported type filters:',
233
- ' all, app(s), game(s), template(s), tool(s), demo(s), asset(s), pack(s)',
234
- '',
235
- 'Supported creator filters:',
236
- ' all (default), me, <creator slug>',
237
- '',
238
- ].join('\n'));
239
153
  program
240
- .command('capture [target]')
241
- .description('Launch the Playdrop dev frame in Playwright, stream console logs, and exit on error.')
242
- .option('--timeout <seconds>', 'Seconds to wait before shutting down (default: 5, max: 600)', value => parseFloat(value))
243
- .option('--app <name>', 'Select an app when catalogue.json lists multiple entries')
244
- .option('--log-level <level>', 'Minimum log level to display (debug|info|warn|error, default: info)')
245
- .option('--screenshot <path>', 'Save a PNG screenshot to the provided relative path before exiting')
246
- .option('--surface <target>', 'Surface target to emulate (desktop|mobile-landscape|mobile-portrait)')
247
- .option('--username <username>', 'Override the saved login with a username for this capture run')
248
- .option('--password <password>', 'Override the saved login with a password for this capture run')
249
- .action(async (target, opts) => {
250
- try {
251
- await (0, capture_1.capture)(target, {
252
- timeoutSeconds: opts.timeout,
253
- appName: opts.app,
254
- logLevel: opts.logLevel,
255
- screenshotPath: opts.screenshot,
256
- surfaceTarget: opts.surface,
257
- username: opts.username,
258
- password: opts.password,
259
- });
260
- }
261
- catch (error) {
262
- if (error instanceof Error &&
263
- (error.message === 'Timeout must be a positive number of seconds.' ||
264
- error.message === 'Timeout cannot exceed 600 seconds (10 minutes).')) {
265
- console.error(error.message);
266
- process.exitCode = 1;
267
- return;
268
- }
269
- throw error;
270
- }
154
+ .command('detail <ref>')
155
+ .description('Show one app, asset, or asset pack')
156
+ .option('--json', 'Output JSON')
157
+ .action(async (ref, opts) => {
158
+ await (0, detail_1.detail)(ref, opts);
159
+ });
160
+ const comments = program.command('comments').description('Comments for apps, assets, and asset packs');
161
+ comments
162
+ .command('browse <ref>')
163
+ .description('Browse comments for one item')
164
+ .option('--json', 'Output JSON')
165
+ .action(async (ref, opts) => {
166
+ await (0, comments_1.browseComments)(ref, opts);
167
+ });
168
+ comments
169
+ .command('add <ref>')
170
+ .description('Add a comment to one item')
171
+ .requiredOption('--body <text>', 'Comment text')
172
+ .option('--reply-to <commentId>', 'Parent comment id')
173
+ .option('--json', 'Output JSON')
174
+ .action(async (ref, opts) => {
175
+ await (0, comments_1.addComment)(ref, opts);
176
+ });
177
+ comments
178
+ .command('delete <commentId>')
179
+ .description('Delete a comment')
180
+ .option('--json', 'Output JSON')
181
+ .action(async (commentId, opts) => {
182
+ await (0, comments_1.deleteComment)(commentId, opts);
183
+ });
184
+ const versions = program.command('versions').description('Version history');
185
+ versions
186
+ .command('browse <ref>')
187
+ .description('Browse versions or revisions for one item')
188
+ .option('--limit <number>', 'Maximum number of results to return')
189
+ .option('--offset <number>', 'Result offset')
190
+ .option('--json', 'Output JSON')
191
+ .action(async (ref, opts) => {
192
+ await (0, versionsBrowse_1.browseVersions)(ref, opts);
193
+ });
194
+ const credits = program.command('credits').description('Credit balance and history');
195
+ credits
196
+ .command('balance')
197
+ .description('Show your credit balance')
198
+ .option('--json', 'Output JSON')
199
+ .action(async (opts) => {
200
+ await (0, credits_1.showCreditBalance)(opts);
271
201
  });
272
- const documentationCommandInstance = program
273
- .command('documentation [path]')
274
- .description('List documentation entries or print a specific file from the CDN')
275
- .action(async (targetPath) => {
276
- await (0, documentation_1.documentation)(targetPath);
277
- });
278
- documentationCommandInstance.addHelpText('beforeAll', () => [
279
- '',
280
- 'Read the latest Playdrop documentation directly from the CDN.',
281
- 'Without a path it prints every available entry.',
282
- 'Provide a relative path (such as "sdk/getting-started.md") to output the file contents.',
283
- '',
284
- 'Examples:',
285
- ' playdrop-cli documentation',
286
- ' playdrop-cli documentation getting-started.md',
287
- ''
288
- ].join('\n'));
289
- const detailCommandInstance = program
290
- .command('detail <key>')
291
- .description('Show detailed metadata for an app, asset, or pack')
292
- .option('--format <format>', 'Output format (text|json, default: text)')
293
- .action(async (key, opts) => {
294
- await (0, detail_1.detail)(key, { format: opts.format });
295
- });
296
- detailCommandInstance.addHelpText('beforeAll', () => [
297
- '',
298
- 'Display complete information for a specific app, asset, or pack. Keys follow creator/type/name.',
299
- '',
300
- 'Examples:',
301
- ' playdrop-cli detail playdrop/game/jumper',
302
- ' playdrop-cli detail playdrop/asset/astro --format json',
303
- ' playdrop-cli detail playdrop/pack/starter-kit --format json',
304
- '',
305
- 'Supported type segments include: game, template, tool, demo, asset, pack.',
306
- '',
307
- ].join('\n'));
308
- const generateCommandInstance = program
309
- .command('generate <type> <prompt>')
310
- .description('Generate an AI asset and save it to your account')
311
- .option('--asset-name <name>', 'Optional asset slug override')
312
- .option('--asset-display-name <name>', 'Optional asset display name override')
313
- .option('--asset-description <description>', 'Optional asset description')
314
- .option('--subcategory <subcategory>', 'Asset subcategory slug (example: music)')
315
- .option('--visibility <visibility>', 'Asset visibility (private|public)')
316
- .option('--input-assets <ids>', 'Comma-separated input asset version IDs')
317
- .option('--attach-app-version-id <id>', 'Attach generated asset to an app version ID')
318
- .option('--image1 <input>', 'Reference image 1 as HTTPS URL or local file path (image/video/model_3d)')
319
- .option('--image2 <input>', 'Reference image 2 as HTTPS URL or local file path (image/video/model_3d)')
320
- .option('--duration <value>', 'Duration (music defaults to ms; sfx/video defaults to seconds; supports 20s or 20000ms)')
321
- .option('--instrumental', 'Force instrumental music output')
322
- .option('--loop', 'Loop SFX output')
323
- .option('--cover-prompt <prompt>', 'Cover image prompt for music or sfx')
324
- .option('--ratio <ratio>', 'Aspect ratio (image: 1:1|3:4|9:16|4:3|16:9, video: 16:9|9:16)')
325
- .option('--resolution <resolution>', 'Video resolution (720p)')
326
- .option('--source-mode <mode>', 'Model 3D source mode (TEXT|IMAGE)')
327
- .option('--topology <topology>', 'Model 3D topology (triangle|quad)')
328
- .option('--polycount <count>', 'Model 3D target polycount')
329
- .option('--remesh', 'Enable model 3D remeshing')
330
- .option('--texture', 'Enable model 3D texturing')
331
- .option('--timeout-seconds <seconds>', 'Timeout while waiting for a generation job to finish (default: 900)')
332
- .option('--poll-interval-ms <ms>', 'Polling interval for generation job status (default: 1000)')
333
- .action(async (type, prompt, opts) => {
334
- await (0, generation_1.generate)(type, prompt, opts);
335
- });
336
- generateCommandInstance.addHelpText('beforeAll', () => [
337
- '',
338
- 'Generate assets from text prompts in one command.',
339
- '',
340
- 'Examples:',
341
- ' playdrop-cli generate image "Pixel art hero portrait"',
342
- ' playdrop-cli generate music "Boss battle intro" --duration 20s',
343
- ' playdrop-cli generate sfx "Credit pickup blip" --duration 2s',
344
- ' playdrop-cli generate video "Neon city flyover" --duration 4s --ratio 16:9',
345
- ' playdrop-cli generate image "Neon app icon" --image1 ./style.png --image2 ./gameplay.png',
346
- ' playdrop-cli generate model_3d "Low poly treasure chest" --polycount 20000',
347
- '',
348
- 'Modality values:',
349
- ' image, music, sfx, video, model_3d',
350
- '',
351
- ].join('\n'));
352
- const generationsCommandInstance = program
353
- .command('generations')
354
- .description('List persisted AI generations')
355
- .option('--type <type>', 'Filter by type (text|json|image|music|sfx|video|model_3d)')
356
- .option('--limit <number>', 'Page size')
357
- .option('--offset <number>', 'Offset')
358
- .option('--format <format>', 'Output format (text|json, default: text)')
202
+ credits
203
+ .command('transactions')
204
+ .description('Browse your credit transactions')
205
+ .option('--limit <number>', 'Maximum number of results to return')
206
+ .option('--offset <number>', 'Result offset')
207
+ .option('--json', 'Output JSON')
359
208
  .action(async (opts) => {
360
- await (0, generation_1.generations)(opts);
361
- });
362
- generationsCommandInstance.addHelpText('beforeAll', () => [
363
- '',
364
- 'List your AI generation history and generated asset refs.',
365
- '',
366
- 'Examples:',
367
- ' playdrop-cli generations',
368
- ' playdrop-cli generations --type image --limit 20',
369
- ' playdrop-cli generations --type music --format json',
370
- '',
371
- ].join('\n'));
372
- const assetsCommand = program
373
- .command('assets')
374
- .description('Asset utility commands');
375
- assetsCommand
376
- .command('categories')
377
- .description('List canonical asset categories')
378
- .option('--format <format>', 'Output format (text|json, default: text)')
209
+ await (0, credits_1.browseCreditTransactions)(opts);
210
+ });
211
+ const notifications = program.command('notifications').description('Notification inbox');
212
+ notifications
213
+ .command('browse')
214
+ .description('Browse notifications')
215
+ .option('--status <status>', 'unread, read, or all')
216
+ .option('--limit <number>', 'Maximum number of results to return')
217
+ .option('--offset <number>', 'Result offset')
218
+ .option('--json', 'Output JSON')
219
+ .action(async (opts) => {
220
+ await (0, notifications_1.browseNotifications)(opts);
221
+ });
222
+ notifications
223
+ .command('read <id>')
224
+ .description('Mark one notification as read')
225
+ .option('--json', 'Output JSON')
226
+ .action(async (id, opts) => {
227
+ await (0, notifications_1.readNotification)(id, opts);
228
+ });
229
+ notifications
230
+ .command('read-all')
231
+ .description('Mark all notifications as read')
232
+ .option('--json', 'Output JSON')
233
+ .action(async (opts) => {
234
+ await (0, notifications_1.readAllNotifications)(opts);
235
+ });
236
+ const creations = program.command('creations').description('Manage your creations');
237
+ creations
238
+ .command('browse')
239
+ .description('Browse your apps, assets, and asset packs')
240
+ .option('--kind <kind>', 'app, asset, asset-pack, or all')
241
+ .option('--creator <creator>', 'me or a creator slug')
242
+ .option('--limit <number>', 'Maximum number of results to return')
243
+ .option('--offset <number>', 'Result offset')
244
+ .option('--json', 'Output JSON')
379
245
  .action(async (opts) => {
380
- await (0, assets_1.listCategories)(opts);
381
- });
382
- assetsCommand
383
- .command('download-file <assetRefOrKey>')
384
- .description('Download a single role-scoped asset file')
385
- .option('--revision <revision>', 'Specific revision (rN or N)')
386
- .option('--role <role>', 'Manifest role to download (default: primary)')
387
- .option('--out <path>', 'Output file path or directory (default: ./downloads)')
388
- .action(async (assetRefOrKey, opts) => {
389
- await (0, assets_1.downloadFile)(assetRefOrKey, opts);
390
- });
391
- assetsCommand
392
- .command('download-source <assetRefOrKey>')
393
- .description('Download canonical source ZIP for an asset revision')
394
- .option('--revision <revision>', 'Specific revision (rN or N)')
395
- .option('--out <path>', 'Output file path or directory (default: ./downloads)')
396
- .action(async (assetRefOrKey, opts) => {
397
- await (0, assets_1.downloadSource)(assetRefOrKey, opts);
398
- });
399
- assetsCommand
400
- .command('update <assetRefOrKey>')
246
+ await (0, creations_1.browseCreations)(opts);
247
+ });
248
+ const creationApps = creations.command('apps').description('Manage app creations');
249
+ creationApps
250
+ .command('update <name>')
251
+ .description('Update app metadata')
252
+ .option('--creator <creator>', 'me or a creator slug')
253
+ .option('--display-name <text>', 'Display name')
254
+ .option('--type <type>', 'game, demo, tool, or template')
255
+ .option('--json', 'Output JSON')
256
+ .action(async (name, opts) => {
257
+ await (0, creations_1.updateCreationApp)(name, opts);
258
+ });
259
+ creationApps
260
+ .command('delete <name>')
261
+ .description('Delete an app')
262
+ .option('--creator <creator>', 'me or a creator slug')
263
+ .option('--json', 'Output JSON')
264
+ .action(async (name, opts) => {
265
+ await (0, creations_1.deleteCreationApp)(name, opts);
266
+ });
267
+ const creationAppVersions = creationApps.command('versions').description('Manage app versions');
268
+ creationAppVersions
269
+ .command('set-current <name> <version>')
270
+ .description('Set the current app version')
271
+ .option('--creator <creator>', 'me or a creator slug')
272
+ .option('--json', 'Output JSON')
273
+ .action(async (name, version, opts) => {
274
+ await (0, creations_1.setCurrentCreationAppVersion)(name, version, opts);
275
+ });
276
+ creationAppVersions
277
+ .command('publish <name> <version>')
278
+ .description('Publish an app version')
279
+ .option('--creator <creator>', 'me or a creator slug')
280
+ .option('--json', 'Output JSON')
281
+ .action(async (name, version, opts) => {
282
+ await (0, creations_1.publishCreationAppVersion)(name, version, opts);
283
+ });
284
+ creationAppVersions
285
+ .command('unpublish <name> <version>')
286
+ .description('Unpublish an app version')
287
+ .option('--creator <creator>', 'me or a creator slug')
288
+ .option('--json', 'Output JSON')
289
+ .action(async (name, version, opts) => {
290
+ await (0, creations_1.unpublishCreationAppVersion)(name, version, opts);
291
+ });
292
+ creationAppVersions
293
+ .command('delete <name> <version>')
294
+ .description('Delete an app version')
295
+ .option('--creator <creator>', 'me or a creator slug')
296
+ .option('--json', 'Output JSON')
297
+ .action(async (name, version, opts) => {
298
+ await (0, creations_1.deleteCreationAppVersion)(name, version, opts);
299
+ });
300
+ const creationAssets = creations.command('assets').description('Manage asset creations');
301
+ creationAssets
302
+ .command('update <name>')
401
303
  .description('Update asset metadata')
402
- .option('--creator <creator>', 'Creator username override (supports "me")')
403
- .option('--name <name>', 'Rename asset slug')
404
- .option('--display-name <displayName>', 'Set display name')
405
- .option('--description <description>', 'Set description text')
304
+ .option('--creator <creator>', 'me or a creator slug')
305
+ .option('--name <slug>', 'New asset slug')
306
+ .option('--display-name <text>', 'Display name')
307
+ .option('--description <text>', 'Description')
406
308
  .option('--clear-description', 'Clear description')
407
- .action(async (assetRefOrKey, opts) => {
408
- await (0, assets_1.updateAssetByKey)(assetRefOrKey, opts);
409
- });
410
- const assetVersionsCommand = assetsCommand
411
- .command('versions')
412
- .description('Manage asset revisions');
413
- assetVersionsCommand
414
- .command('list <assetRefOrKey>')
415
- .description('List versions for an asset')
416
- .option('--creator <creator>', 'Creator username override (supports "me")')
417
- .option('--format <format>', 'Output format (text|json, default: text)')
418
- .action(async (assetRefOrKey, opts) => {
419
- await (0, assets_1.listAssetVersionsByKey)(assetRefOrKey, opts);
420
- });
421
- assetVersionsCommand
422
- .command('set-current <assetRefOrKey> <revision>')
423
- .description('Set an asset revision as current')
424
- .option('--creator <creator>', 'Creator username override (supports "me")')
425
- .action(async (assetRefOrKey, revision, opts) => {
426
- await (0, assets_1.setCurrentAssetVersion)(assetRefOrKey, revision, opts);
427
- });
428
- assetVersionsCommand
429
- .command('publish <assetRefOrKey> <revision>')
430
- .description('Set asset version visibility to PUBLIC')
431
- .option('--creator <creator>', 'Creator username override (supports "me")')
432
- .action(async (assetRefOrKey, revision, opts) => {
433
- await (0, assets_1.publishAssetVersion)(assetRefOrKey, revision, opts);
434
- });
435
- assetVersionsCommand
436
- .command('unpublish <assetRefOrKey> <revision>')
437
- .description('Set asset version visibility to PRIVATE')
438
- .option('--creator <creator>', 'Creator username override (supports "me")')
439
- .action(async (assetRefOrKey, revision, opts) => {
440
- await (0, assets_1.unpublishAssetVersion)(assetRefOrKey, revision, opts);
441
- });
442
- assetVersionsCommand
443
- .command('delete <assetRefOrKey> <revision>')
444
- .description('Delete an asset version')
445
- .option('--creator <creator>', 'Creator username override (supports "me")')
446
- .action(async (assetRefOrKey, revision, opts) => {
447
- await (0, assets_1.deleteAssetVersionByKey)(assetRefOrKey, revision, opts);
448
- });
449
- assetsCommand
450
- .command('delete <assetRefOrKey>')
309
+ .option('--json', 'Output JSON')
310
+ .action(async (name, opts) => {
311
+ await (0, creations_1.updateCreationAsset)(name, opts);
312
+ });
313
+ creationAssets
314
+ .command('delete <name>')
451
315
  .description('Delete an asset')
452
- .option('--creator <creator>', 'Creator username override (supports "me")')
453
- .action(async (assetRefOrKey, opts) => {
454
- await (0, assets_1.deleteAssetByKey)(assetRefOrKey, opts);
455
- });
456
- const assetPacksCommand = program
457
- .command('asset-packs')
458
- .description('Manage asset packs');
459
- assetPacksCommand
460
- .command('download-source <packRefOrKey>')
461
- .description('Download canonical source ZIP for an asset pack version')
462
- .option('--creator <creator>', 'Creator username override (supports "me")')
463
- .option('--version <version>', 'Specific semantic version (for example 1.0.0)')
464
- .option('--out <path>', 'Output file path or directory (default: ./downloads)')
465
- .action(async (packRefOrKey, opts) => {
466
- await (0, asset_packs_1.downloadSource)(packRefOrKey, opts);
467
- });
468
- assetPacksCommand
469
- .command('update <packRefOrKey>')
316
+ .option('--creator <creator>', 'me or a creator slug')
317
+ .option('--json', 'Output JSON')
318
+ .action(async (name, opts) => {
319
+ await (0, creations_1.deleteCreationAsset)(name, opts);
320
+ });
321
+ const creationAssetVersions = creationAssets.command('versions').description('Manage asset revisions');
322
+ creationAssetVersions
323
+ .command('set-current <name> <revision>')
324
+ .description('Set the current asset revision')
325
+ .option('--creator <creator>', 'me or a creator slug')
326
+ .option('--json', 'Output JSON')
327
+ .action(async (name, revision, opts) => {
328
+ await (0, creations_1.setCurrentCreationAssetVersion)(name, revision, opts);
329
+ });
330
+ creationAssetVersions
331
+ .command('publish <name> <revision>')
332
+ .description('Publish an asset revision')
333
+ .option('--creator <creator>', 'me or a creator slug')
334
+ .option('--json', 'Output JSON')
335
+ .action(async (name, revision, opts) => {
336
+ await (0, creations_1.publishCreationAssetVersion)(name, revision, opts);
337
+ });
338
+ creationAssetVersions
339
+ .command('unpublish <name> <revision>')
340
+ .description('Unpublish an asset revision')
341
+ .option('--creator <creator>', 'me or a creator slug')
342
+ .option('--json', 'Output JSON')
343
+ .action(async (name, revision, opts) => {
344
+ await (0, creations_1.unpublishCreationAssetVersion)(name, revision, opts);
345
+ });
346
+ creationAssetVersions
347
+ .command('delete <name> <revision>')
348
+ .description('Delete an asset revision')
349
+ .option('--creator <creator>', 'me or a creator slug')
350
+ .option('--json', 'Output JSON')
351
+ .action(async (name, revision, opts) => {
352
+ await (0, creations_1.deleteCreationAssetVersion)(name, revision, opts);
353
+ });
354
+ const creationAssetPacks = creations.command('asset-packs').description('Manage asset pack creations');
355
+ creationAssetPacks
356
+ .command('update <name>')
470
357
  .description('Update asset pack metadata')
471
- .option('--creator <creator>', 'Creator username override (supports "me")')
472
- .option('--name <name>', 'Rename pack slug')
473
- .option('--display-name <displayName>', 'Set display name')
474
- .option('--description <description>', 'Set description text')
358
+ .option('--creator <creator>', 'me or a creator slug')
359
+ .option('--name <slug>', 'New asset pack slug')
360
+ .option('--display-name <text>', 'Display name')
361
+ .option('--description <text>', 'Description')
475
362
  .option('--clear-description', 'Clear description')
476
- .option('--preview-app-version-id <id>', 'Set preview app version id')
363
+ .option('--preview-app-version-id <id>', 'Preview app version id')
477
364
  .option('--clear-preview-app-version-id', 'Clear preview app version id')
478
- .action(async (packRefOrKey, opts) => {
479
- await (0, asset_packs_1.updateAssetPackByKey)(packRefOrKey, opts);
480
- });
481
- const assetPackVersionsCommand = assetPacksCommand
482
- .command('versions')
483
- .description('Manage asset pack versions');
484
- assetPackVersionsCommand
485
- .command('list <packRefOrKey>')
486
- .description('List versions for an asset pack')
487
- .option('--creator <creator>', 'Creator username override (supports "me")')
488
- .option('--format <format>', 'Output format (text|json, default: text)')
489
- .action(async (packRefOrKey, opts) => {
490
- await (0, asset_packs_1.listAssetPackVersionsByKey)(packRefOrKey, opts);
491
- });
492
- assetPackVersionsCommand
493
- .command('set-current <packRefOrKey> <version>')
494
- .description('Set an asset pack version as current')
495
- .option('--creator <creator>', 'Creator username override (supports "me")')
496
- .action(async (packRefOrKey, version, opts) => {
497
- await (0, asset_packs_1.setCurrentAssetPackVersion)(packRefOrKey, version, opts);
498
- });
499
- assetPackVersionsCommand
500
- .command('publish <packRefOrKey> <version>')
501
- .description('Set asset pack version visibility to PUBLIC')
502
- .option('--creator <creator>', 'Creator username override (supports "me")')
503
- .action(async (packRefOrKey, version, opts) => {
504
- await (0, asset_packs_1.publishAssetPackVersion)(packRefOrKey, version, opts);
505
- });
506
- assetPackVersionsCommand
507
- .command('unpublish <packRefOrKey> <version>')
508
- .description('Set asset pack version visibility to PRIVATE')
509
- .option('--creator <creator>', 'Creator username override (supports "me")')
510
- .action(async (packRefOrKey, version, opts) => {
511
- await (0, asset_packs_1.unpublishAssetPackVersion)(packRefOrKey, version, opts);
512
- });
513
- assetPackVersionsCommand
514
- .command('delete <packRefOrKey> <version>')
515
- .description('Delete an asset pack version')
516
- .option('--creator <creator>', 'Creator username override (supports "me")')
517
- .action(async (packRefOrKey, version, opts) => {
518
- await (0, asset_packs_1.deleteAssetPackVersionByKey)(packRefOrKey, version, opts);
365
+ .option('--json', 'Output JSON')
366
+ .action(async (name, opts) => {
367
+ await (0, creations_1.updateCreationAssetPack)(name, opts);
519
368
  });
520
- assetPacksCommand
521
- .command('delete <packRefOrKey>')
369
+ creationAssetPacks
370
+ .command('delete <name>')
522
371
  .description('Delete an asset pack')
523
- .option('--creator <creator>', 'Creator username override (supports "me")')
524
- .action(async (packRefOrKey, opts) => {
525
- await (0, asset_packs_1.deleteAssetPackByKey)(packRefOrKey, opts);
526
- });
527
- program
372
+ .option('--creator <creator>', 'me or a creator slug')
373
+ .option('--json', 'Output JSON')
374
+ .action(async (name, opts) => {
375
+ await (0, creations_1.deleteCreationAssetPack)(name, opts);
376
+ });
377
+ const creationAssetPackVersions = creationAssetPacks.command('versions').description('Manage asset pack versions');
378
+ creationAssetPackVersions
379
+ .command('set-current <name> <version>')
380
+ .description('Set the current asset pack version')
381
+ .option('--creator <creator>', 'me or a creator slug')
382
+ .option('--json', 'Output JSON')
383
+ .action(async (name, version, opts) => {
384
+ await (0, creations_1.setCurrentCreationAssetPackVersion)(name, version, opts);
385
+ });
386
+ creationAssetPackVersions
387
+ .command('publish <name> <version>')
388
+ .description('Publish an asset pack version')
389
+ .option('--creator <creator>', 'me or a creator slug')
390
+ .option('--json', 'Output JSON')
391
+ .action(async (name, version, opts) => {
392
+ await (0, creations_1.publishCreationAssetPackVersion)(name, version, opts);
393
+ });
394
+ creationAssetPackVersions
395
+ .command('unpublish <name> <version>')
396
+ .description('Unpublish an asset pack version')
397
+ .option('--creator <creator>', 'me or a creator slug')
398
+ .option('--json', 'Output JSON')
399
+ .action(async (name, version, opts) => {
400
+ await (0, creations_1.unpublishCreationAssetPackVersion)(name, version, opts);
401
+ });
402
+ creationAssetPackVersions
403
+ .command('delete <name> <version>')
404
+ .description('Delete an asset pack version')
405
+ .option('--creator <creator>', 'me or a creator slug')
406
+ .option('--json', 'Output JSON')
407
+ .action(async (name, version, opts) => {
408
+ await (0, creations_1.deleteCreationAssetPackVersion)(name, version, opts);
409
+ });
410
+ const ai = program.command('ai').description('AI asset creation');
411
+ ai
412
+ .command('create <type> <prompt>')
413
+ .description('Create a new AI-generated asset')
414
+ .option('--asset-name <slug>', 'Generated asset slug')
415
+ .option('--asset-display-name <text>', 'Generated asset display name')
416
+ .option('--asset-description <text>', 'Generated asset description')
417
+ .option('--subcategory <slug>', 'Generated asset subcategory')
418
+ .option('--visibility <visibility>', 'private or public')
419
+ .option('--input-assets <ids>', 'Comma-separated asset version ids')
420
+ .option('--attach-app-version-id <id>', 'Attach generated asset to an app version')
421
+ .option('--image1 <pathOrUrl>', 'Reference image one')
422
+ .option('--image2 <pathOrUrl>', 'Reference image two')
423
+ .option('--duration <value>', 'Duration value')
424
+ .option('--instrumental', 'Force instrumental music generation')
425
+ .option('--loop', 'Create a looping SFX')
426
+ .option('--cover-prompt <text>', 'Cover prompt for music generation')
427
+ .option('--ratio <ratio>', 'Aspect ratio')
428
+ .option('--resolution <resolution>', 'Resolution')
429
+ .option('--source-mode <mode>', '3D source mode')
430
+ .option('--topology <mode>', '3D topology mode')
431
+ .option('--polycount <count>', '3D target polycount')
432
+ .option('--remesh', 'Enable remeshing')
433
+ .option('--texture', 'Enable texturing')
434
+ .option('--timeout <seconds>', 'Wait timeout in seconds')
435
+ .option('--poll <milliseconds>', 'Polling interval in milliseconds')
436
+ .option('--json', 'Output JSON')
437
+ .action(async (type, prompt, opts) => {
438
+ await (0, generation_1.generate)(type, prompt, {
439
+ subcategory: opts.subcategory,
440
+ assetName: opts.assetName,
441
+ assetDisplayName: opts.assetDisplayName,
442
+ assetDescription: opts.assetDescription,
443
+ visibility: opts.visibility,
444
+ inputAssets: opts.inputAssets,
445
+ attachAppVersionId: opts.attachAppVersionId,
446
+ image1: opts.image1,
447
+ image2: opts.image2,
448
+ duration: opts.duration,
449
+ instrumental: opts.instrumental,
450
+ loop: opts.loop,
451
+ coverPrompt: opts.coverPrompt,
452
+ ratio: opts.ratio,
453
+ resolution: opts.resolution,
454
+ sourceMode: opts.sourceMode,
455
+ topology: opts.topology,
456
+ polycount: opts.polycount,
457
+ remesh: opts.remesh,
458
+ texture: opts.texture,
459
+ timeoutSeconds: opts.timeout,
460
+ pollIntervalMs: opts.poll,
461
+ json: opts.json,
462
+ });
463
+ });
464
+ const aiJobs = ai.command('jobs').description('Browse AI generation jobs');
465
+ aiJobs
466
+ .command('browse')
467
+ .description('Browse AI jobs')
468
+ .option('--type <type>', 'image, music, sfx, video, model_3d, or all')
469
+ .option('--limit <number>', 'Maximum number of results to return')
470
+ .option('--offset <number>', 'Result offset')
471
+ .option('--json', 'Output JSON')
472
+ .action(async (opts) => {
473
+ await (0, generation_1.browseGenerationJobs)(opts);
474
+ });
475
+ aiJobs
476
+ .command('detail <id>')
477
+ .description('Show one AI job')
478
+ .option('--json', 'Output JSON')
479
+ .action(async (id, opts) => {
480
+ await (0, generation_1.showGenerationJob)(id, opts);
481
+ });
482
+ const project = program.command('project').description('Local workspace tooling');
483
+ project
484
+ .command('init [path]')
485
+ .description('Initialize a Playdrop workspace')
486
+ .action(async (pathArg) => {
487
+ await (0, init_1.init)(resolveInitTarget(pathArg));
488
+ });
489
+ const projectCreate = project.command('create').description('Create project resources');
490
+ projectCreate
491
+ .command('app <name>')
492
+ .description('Create a new app from a template or remix')
493
+ .option('--template <ref>', 'Template ref')
494
+ .option('--remix <ref>', 'Remix ref')
495
+ .action(async (name, opts) => {
496
+ await (0, create_1.create)(name, opts);
497
+ });
498
+ project
528
499
  .command('dev [target]')
529
- .description('Serve an app locally')
530
- .option('--app <name>', 'Select an app name when multiple entries are present')
531
- .action(async (target, opts) => {
500
+ .description('Run an app locally')
501
+ .option('--app <name>', 'App name when the target is a workspace')
502
+ .action(async (target, opts = {}) => {
532
503
  await (0, dev_1.dev)(target, undefined, opts.app);
533
504
  });
534
- program
535
- .command('validate <pathOrName>')
536
- .description('Validate apps, assets, and packs without building or uploading')
537
- .action(async (pathOrName) => {
538
- await (0, validate_1.validate)(pathOrName);
539
- });
540
- program
541
- .command('build <pathOrName>')
542
- .description('Build apps and regenerate Boxel and VOX model asset artifacts without uploading')
543
- .action(async (pathOrName) => {
544
- await (0, build_1.build)(pathOrName);
505
+ const projectCapture = project.command('capture').description('Run a Playwright capture');
506
+ projectCapture
507
+ .argument('[target]')
508
+ .option('--app <name>', 'App name when the target is a workspace')
509
+ .option('--timeout <seconds>', 'Capture timeout in seconds')
510
+ .option('--log-level <level>', 'debug, info, warn, or error')
511
+ .option('--screenshot <path>', 'Screenshot output path')
512
+ .option('--surface <surface>', 'desktop, mobile-landscape, or mobile-portrait')
513
+ .option('--username <username>', 'Override saved login username for this run')
514
+ .option('--password <password>', 'Override saved login password for this run')
515
+ .action(async (target, opts) => {
516
+ const timeoutSeconds = opts.timeout !== undefined ? Number.parseFloat(String(opts.timeout)) : undefined;
517
+ await (0, capture_1.capture)(target, {
518
+ timeoutSeconds,
519
+ appName: opts.app,
520
+ logLevel: opts.logLevel,
521
+ screenshotPath: opts.screenshot,
522
+ surfaceTarget: opts.surface,
523
+ username: opts.username,
524
+ password: opts.password,
525
+ });
526
+ });
527
+ projectCapture
528
+ .command('remote <url>')
529
+ .description('Run a Playwright capture against a published URL')
530
+ .option('--timeout <seconds>', 'Capture timeout in seconds')
531
+ .option('--screenshot <path>', 'Screenshot output path')
532
+ .option('--log <path>', 'Log output path')
533
+ .option('--username <username>', 'Login username')
534
+ .option('--password <password>', 'Login password')
535
+ .option('--login-url <url>', 'Explicit login URL')
536
+ .action(async (url, opts) => {
537
+ await (0, captureRemote_1.captureRemote)(url, opts);
538
+ });
539
+ project
540
+ .command('validate [target]')
541
+ .description('Validate catalogue content')
542
+ .action(async (target) => {
543
+ await (0, validate_1.validate)(target ?? '.');
545
544
  });
546
- program
547
- .command('format <pathOrName>')
548
- .description('Run formatters for apps, assets, and packs')
549
- .action(async (pathOrName) => {
550
- await (0, format_1.format)(pathOrName);
545
+ project
546
+ .command('build [target]')
547
+ .description('Build local content')
548
+ .action(async (target) => {
549
+ await (0, build_1.build)(target ?? '.');
551
550
  });
552
- program
553
- .command('migrate-catalogue-v2 [target]')
554
- .description('Migrate legacy catalogue keys to strict schemaVersion 2')
551
+ project
552
+ .command('format [target]')
553
+ .description('Run local formatting')
555
554
  .action(async (target) => {
556
- await (0, migrateCatalogueV2_1.migrateCatalogueV2)(target || '.');
555
+ await (0, format_1.format)(target ?? '.');
557
556
  });
558
- program
559
- .command('upload <pathOrName>')
560
- .description('Upload an app HTML file or a directory with a catalogue.json')
561
- .option('--skip-ecs', 'Exclude ecs.json and server.js from the upload even if defined in catalogue.json')
562
- .action(async (pathOrName, opts) => {
563
- await (0, upload_1.upload)(pathOrName, { skipEcs: opts.skipEcs });
564
- });
565
- const feedbackCommand = program
566
- .command('feedback')
567
- .description('Submit feedback to the Playdrop team')
568
- .option('--title <title>', 'Feedback title (max 70 characters)')
569
- .option('--comment <comment>', 'Feedback comment text')
570
- .option('--file <path>', 'Load comment text from a file')
557
+ project
558
+ .command('publish [target]')
559
+ .description('Publish local content to Playdrop')
560
+ .option('--skip-ecs', 'Skip ecs.json and server.js during publish')
561
+ .action(async (target, opts) => {
562
+ await (0, upload_1.upload)(target ?? '.', { skipEcs: opts.skipEcs });
563
+ });
564
+ const documentation = program.command('documentation').description('Public Playdrop documentation');
565
+ documentation
566
+ .command('browse')
567
+ .description('Browse documentation entries')
568
+ .option('--json', 'Output JSON')
571
569
  .action(async (opts) => {
572
- await (0, feedback_1.submitFeedback)(opts);
570
+ await (0, documentation_1.browseDocumentation)(opts);
571
+ });
572
+ documentation
573
+ .command('read <path>')
574
+ .description('Read one documentation entry')
575
+ .action(async (pathArg) => {
576
+ await (0, documentation_1.readDocumentation)(pathArg);
577
+ });
578
+ const feedback = program.command('feedback').description('Send or review feedback');
579
+ feedback
580
+ .command('send')
581
+ .description('Send feedback to the Playdrop team')
582
+ .requiredOption('--title <title>', 'Feedback title')
583
+ .option('--comment <text>', 'Feedback text')
584
+ .option('--file <path>', 'Load feedback text from a file')
585
+ .option('--json', 'Output JSON')
586
+ .action(async (opts) => {
587
+ await (0, feedback_1.sendFeedback)(opts);
573
588
  });
574
- feedbackCommand
575
- .command('list', { hidden: true })
576
- .option('--limit <number>', 'Maximum number of entries to display (1-100)')
589
+ feedback
590
+ .command('browse')
591
+ .description('Browse feedback entries')
592
+ .option('--limit <number>', 'Maximum number of results to return')
577
593
  .option('--offset <number>', 'Result offset')
578
- .option('--source <source>', 'Filter by source label')
579
- .option('--user-id <id>', 'Filter by numeric user id')
580
- .option('--format <format>', 'Output format (text|json, default: text)')
594
+ .option('--source <label>', 'Source filter')
595
+ .option('--user-id <id>', 'User id filter')
596
+ .option('--json', 'Output JSON')
581
597
  .action(async (opts) => {
582
- await (0, feedback_1.listFeedback)(opts);
583
- });
584
- feedbackCommand
585
- .command('delete <id>', { hidden: true })
586
- .action(async (id) => {
587
- await (0, feedback_1.deleteFeedback)(id);
588
- });
589
- const initCommandInstance = program
590
- .command('init <path>')
591
- .description('Prepare a Playdrop project folder');
592
- initCommandInstance.addHelpText('beforeAll', () => [
593
- '',
594
- 'Bootstrap a folder so it is ready to create and publish Playdrop apps. The command adds the required scaffolding: catalogue.json plus starter README and AGENTS files, so you can get moving quickly.',
595
- '',
596
- 'Examples:',
597
- ' playdrop-cli init ~/Documents/playdrop',
598
- ' playdrop-cli init .',
599
- ' playdrop-cli init apps/dev',
600
- ' playdrop-cli init apps/dev',
601
- '',
602
- 'Behavior:',
603
- ' - Ensures the target directory exists (creating it if needed).',
604
- ' - Downloads the latest README and AGENTS templates for your environment.',
605
- ' - Writes an empty catalogue.json that `playdrop-cli create` will populate.',
606
- ''
607
- ].join('\n'));
608
- initCommandInstance.exitOverride((err) => {
609
- if (err && err.code === 'commander.missingArgument') {
610
- (0, messages_1.printErrorWithHelp)('A target directory is required for init.', [
611
- 'Try "playdrop-cli init ~/Documents/playdrop" to create a workspace in Documents.',
612
- 'Or run "playdrop-cli init ." to bootstrap the current folder.',
613
- ], { command: 'init' });
614
- process.exitCode = 1;
615
- return;
616
- }
617
- throw err;
618
- });
619
- initCommandInstance.addHelpText('afterAll', '\nAfter init, run `playdrop-cli create` to register apps, `playdrop-cli dev` to preview, and `playdrop-cli upload` to publish.\n');
620
- initCommandInstance.action(async (pathArg) => {
621
- if (!pathArg || typeof pathArg !== 'string' || pathArg.trim().length === 0) {
622
- (0, messages_1.printErrorWithHelp)('A target directory is required for init.', [
623
- 'Try "playdrop-cli init ~/Documents/playdrop" to create a workspace in Documents.',
624
- 'Or run "playdrop-cli init ." to bootstrap the current folder.',
625
- ], { command: 'init' });
626
- process.exitCode = 1;
627
- return;
628
- }
629
- const target = resolveInitTarget(pathArg);
630
- await (0, init_1.init)(target);
598
+ await (0, feedback_1.browseFeedback)(opts);
599
+ });
600
+ feedback
601
+ .command('delete <id>')
602
+ .description('Delete one feedback entry')
603
+ .option('--json', 'Output JSON')
604
+ .action(async (id, opts) => {
605
+ await (0, feedback_1.deleteFeedbackEntry)(id, opts);
606
+ });
607
+ program
608
+ .command('getting-started')
609
+ .description('Show the recommended first steps')
610
+ .action(() => {
611
+ (0, gettingStarted_1.printGettingStarted)();
612
+ });
613
+ program
614
+ .command('start')
615
+ .description('Alias of getting-started')
616
+ .action(() => {
617
+ (0, gettingStarted_1.printGettingStarted)();
631
618
  });
632
619
  program
633
- .command('upgrade')
634
- .description('Update the Playdrop CLI to the latest version')
635
- .option('--dry-run', 'Show the command without executing it')
620
+ .command('update')
621
+ .description('Update the Playdrop CLI')
622
+ .option('--dry-run', 'Print the update command without running it')
636
623
  .action(async (opts) => {
637
- await (0, upgrade_1.upgrade)(!!opts.dryRun);
638
- });
639
- // ============================================================================
640
- // Versions Command Group
641
- // ============================================================================
642
- const versionsCommand = program
643
- .command('versions')
644
- .description('Manage app versions');
645
- versionsCommand
646
- .command('list <app>')
647
- .description('List all versions for an app')
648
- .option('--creator <creator>', 'Creator username override (supports "me")')
649
- .option('--format <format>', 'Output format (text|json, default: text)')
650
- .action(async (app, opts) => {
651
- await (0, versions_1.listVersions)(app, { format: opts.format, creator: opts.creator });
652
- });
653
- versionsCommand
654
- .command('set-current <app> <version>')
655
- .description('Set a version as the current (live) version')
656
- .option('--creator <creator>', 'Creator username override (supports "me")')
657
- .action(async (app, version, opts) => {
658
- await (0, versions_1.setCurrentVersion)(app, version, { creator: opts.creator });
659
- });
660
- versionsCommand
661
- .command('publish <app> <version>')
662
- .description('Make a version publicly visible')
663
- .option('--creator <creator>', 'Creator username override (supports "me")')
664
- .action(async (app, version, opts) => {
665
- await (0, versions_1.publishVersion)(app, version, { creator: opts.creator });
666
- });
667
- versionsCommand
668
- .command('unpublish <app> <version>')
669
- .description('Make a version private (hidden from public)')
670
- .option('--creator <creator>', 'Creator username override (supports "me")')
671
- .action(async (app, version, opts) => {
672
- await (0, versions_1.unpublishVersion)(app, version, { creator: opts.creator });
673
- });
674
- versionsCommand
675
- .command('delete <app> <version>')
676
- .description('Delete a version permanently')
677
- .option('--creator <creator>', 'Creator username override (supports "me")')
678
- .action(async (app, version, opts) => {
679
- await (0, versions_1.deleteVersion)(app, version, { creator: opts.creator });
680
- });
681
- versionsCommand.addHelpText('beforeAll', () => [
682
- '',
683
- 'Manage versions for your Playdrop apps. Versions allow you to maintain multiple releases',
684
- 'of an app and control which version is live.',
685
- '',
686
- 'Examples:',
687
- ' playdrop-cli versions list myapp',
688
- ' playdrop-cli versions list creator/game/myapp --format json',
689
- ' playdrop-cli versions set-current myapp 1.2.0',
690
- ' playdrop-cli versions publish myapp 1.2.0',
691
- ' playdrop-cli versions unpublish myapp 1.1.0',
692
- ' playdrop-cli versions delete myapp 1.0.0',
693
- '',
694
- 'Workflow:',
695
- ' 1. Add "version" to catalogue.json and run `playdrop-cli upload` to create versions.',
696
- ' 2. Use `versions list` to see all versions for an app.',
697
- ' 3. Use `versions publish` to make a version publicly visible.',
698
- ' 4. Use `versions set-current` to make a public version live.',
699
- '',
700
- ].join('\n'));
701
- program.parseAsync(process.argv);
624
+ await (0, upgrade_1.upgrade)(Boolean(opts.dryRun));
625
+ });
626
+ program
627
+ .command('version')
628
+ .description('Print the Playdrop CLI version')
629
+ .action(() => {
630
+ console.log((0, clientInfo_1.getCliVersionLabel)());
631
+ });
632
+ if (process.argv.length <= 2) {
633
+ program.outputHelp();
634
+ }
635
+ else {
636
+ program.parseAsync(process.argv).catch((error) => {
637
+ const message = error instanceof Error ? error.message : String(error);
638
+ (0, messages_1.printErrorWithHelp)(message, [], {});
639
+ process.exitCode = 1;
640
+ });
641
+ }