@playdrop/playdrop-cli 0.5.2 → 0.5.4

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 (118) hide show
  1. package/config/client-meta.json +4 -4
  2. package/dist/apps/build.js +49 -6
  3. package/dist/apps/index.d.ts +2 -0
  4. package/dist/apps/index.js +2 -0
  5. package/dist/apps/upload.d.ts +2 -0
  6. package/dist/apps/upload.js +126 -28
  7. package/dist/assetSpecs.d.ts +16 -0
  8. package/dist/assetSpecs.js +263 -0
  9. package/dist/assets/model-artifacts.js +3 -0
  10. package/dist/catalogue.d.ts +57 -3
  11. package/dist/catalogue.js +342 -16
  12. package/dist/commandContext.d.ts +6 -2
  13. package/dist/commandContext.js +144 -20
  14. package/dist/commands/accounts.d.ts +2 -0
  15. package/dist/commands/accounts.js +48 -0
  16. package/dist/commands/ads.d.ts +8 -0
  17. package/dist/commands/ads.js +124 -0
  18. package/dist/commands/boosts.d.ts +25 -0
  19. package/dist/commands/boosts.js +209 -0
  20. package/dist/commands/browse.d.ts +6 -1
  21. package/dist/commands/browse.js +365 -124
  22. package/dist/commands/capture.js +30 -9
  23. package/dist/commands/captureListing.d.ts +53 -0
  24. package/dist/commands/captureListing.js +815 -0
  25. package/dist/commands/create.d.ts +1 -0
  26. package/dist/commands/create.js +183 -3
  27. package/dist/commands/credits.d.ts +6 -0
  28. package/dist/commands/credits.js +47 -1
  29. package/dist/commands/detail.js +38 -4
  30. package/dist/commands/dev.js +169 -192
  31. package/dist/commands/devServer.d.ts +26 -3
  32. package/dist/commands/devServer.js +415 -72
  33. package/dist/commands/login.js +10 -2
  34. package/dist/commands/logout.d.ts +6 -1
  35. package/dist/commands/logout.js +25 -3
  36. package/dist/commands/search.d.ts +5 -0
  37. package/dist/commands/search.js +139 -17
  38. package/dist/commands/tags.d.ts +7 -0
  39. package/dist/commands/tags.js +63 -0
  40. package/dist/commands/upload-content.d.ts +13 -3
  41. package/dist/commands/upload-content.js +86 -20
  42. package/dist/commands/upload.d.ts +2 -0
  43. package/dist/commands/upload.js +187 -11
  44. package/dist/commands/validate.js +163 -2
  45. package/dist/commands/versionsBrowse.js +128 -91
  46. package/dist/commands/whoami.js +10 -2
  47. package/dist/config.d.ts +37 -0
  48. package/dist/config.js +205 -3
  49. package/dist/index.js +177 -5
  50. package/dist/refs.d.ts +2 -2
  51. package/dist/refs.js +13 -1
  52. package/dist/taskSelection.js +6 -3
  53. package/dist/taskUtils.d.ts +2 -2
  54. package/dist/taskUtils.js +1 -0
  55. package/dist/uploadLog.d.ts +1 -1
  56. package/dist/uploadLog.js +2 -2
  57. package/dist/workspaceAuth.d.ts +14 -0
  58. package/dist/workspaceAuth.js +75 -0
  59. package/node_modules/@playdrop/ai-client/package.json +1 -1
  60. package/node_modules/@playdrop/api-client/dist/client.d.ts +139 -10
  61. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  62. package/node_modules/@playdrop/api-client/dist/client.js +6 -0
  63. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +9 -1
  64. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
  65. package/node_modules/@playdrop/api-client/dist/domains/admin.js +45 -0
  66. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +7 -1
  67. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
  68. package/node_modules/@playdrop/api-client/dist/domains/apps.js +58 -0
  69. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts +2 -0
  70. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts.map +1 -1
  71. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.js +16 -0
  72. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +44 -2
  73. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
  74. package/node_modules/@playdrop/api-client/dist/domains/assets.js +260 -3
  75. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +22 -1
  76. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
  77. package/node_modules/@playdrop/api-client/dist/domains/payments.js +228 -0
  78. package/node_modules/@playdrop/api-client/dist/domains/search.d.ts.map +1 -1
  79. package/node_modules/@playdrop/api-client/dist/domains/search.js +39 -11
  80. package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts +34 -0
  81. package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts.map +1 -0
  82. package/node_modules/@playdrop/api-client/dist/domains/tags.js +111 -0
  83. package/node_modules/@playdrop/api-client/dist/index.d.ts +69 -1
  84. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  85. package/node_modules/@playdrop/api-client/dist/index.js +74 -0
  86. package/node_modules/@playdrop/api-client/package.json +1 -1
  87. package/node_modules/@playdrop/boxel-core/package.json +1 -1
  88. package/node_modules/@playdrop/boxel-three/package.json +1 -1
  89. package/node_modules/@playdrop/config/client-meta.json +4 -4
  90. package/node_modules/@playdrop/config/dist/src/constants.d.ts +11 -0
  91. package/node_modules/@playdrop/config/dist/src/constants.d.ts.map +1 -1
  92. package/node_modules/@playdrop/config/dist/src/constants.js +12 -1
  93. package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
  94. package/node_modules/@playdrop/config/package.json +1 -1
  95. package/node_modules/@playdrop/types/dist/api.d.ts +366 -6
  96. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  97. package/node_modules/@playdrop/types/dist/api.js +52 -1
  98. package/node_modules/@playdrop/types/dist/asset-pack.d.ts +7 -1
  99. package/node_modules/@playdrop/types/dist/asset-pack.d.ts.map +1 -1
  100. package/node_modules/@playdrop/types/dist/asset-spec-contract-meta-schema.json +86 -0
  101. package/node_modules/@playdrop/types/dist/asset-spec.d.ts +163 -0
  102. package/node_modules/@playdrop/types/dist/asset-spec.d.ts.map +1 -0
  103. package/node_modules/@playdrop/types/dist/asset-spec.js +101 -0
  104. package/node_modules/@playdrop/types/dist/asset.d.ts +23 -6
  105. package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
  106. package/node_modules/@playdrop/types/dist/asset.js +4 -1
  107. package/node_modules/@playdrop/types/dist/graph.d.ts +4 -2
  108. package/node_modules/@playdrop/types/dist/graph.d.ts.map +1 -1
  109. package/node_modules/@playdrop/types/dist/graph.js +9 -2
  110. package/node_modules/@playdrop/types/dist/index.d.ts +1 -0
  111. package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
  112. package/node_modules/@playdrop/types/dist/index.js +1 -0
  113. package/node_modules/@playdrop/types/dist/version.d.ts +13 -0
  114. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  115. package/node_modules/@playdrop/types/dist/version.js +21 -0
  116. package/node_modules/@playdrop/types/package.json +6 -1
  117. package/node_modules/@playdrop/vox-three/package.json +1 -1
  118. package/package.json +3 -1
package/dist/index.js CHANGED
@@ -8,13 +8,17 @@ const clientInfo_1 = require("./clientInfo");
8
8
  const login_1 = require("./commands/login");
9
9
  const whoami_1 = require("./commands/whoami");
10
10
  const logout_1 = require("./commands/logout");
11
+ const accounts_1 = require("./commands/accounts");
11
12
  const browse_1 = require("./commands/browse");
12
13
  const search_1 = require("./commands/search");
14
+ const tags_1 = require("./commands/tags");
13
15
  const detail_1 = require("./commands/detail");
14
16
  const play_1 = require("./commands/play");
17
+ const ads_1 = require("./commands/ads");
15
18
  const comments_1 = require("./commands/comments");
16
19
  const versionsBrowse_1 = require("./commands/versionsBrowse");
17
20
  const credits_1 = require("./commands/credits");
21
+ const boosts_1 = require("./commands/boosts");
18
22
  const notifications_1 = require("./commands/notifications");
19
23
  const creations_1 = require("./commands/creations");
20
24
  const generation_1 = require("./commands/generation");
@@ -23,6 +27,8 @@ const create_1 = require("./commands/create");
23
27
  const createRemixContent_1 = require("./commands/createRemixContent");
24
28
  const dev_1 = require("./commands/dev");
25
29
  const capture_1 = require("./commands/capture");
30
+ const devServer_1 = require("./commands/devServer");
31
+ const captureListing_1 = require("./commands/captureListing");
26
32
  const captureRemote_1 = require("./commands/captureRemote");
27
33
  const validate_1 = require("./commands/validate");
28
34
  const build_1 = require("./commands/build");
@@ -95,6 +101,9 @@ program.on('command:*', (unknownCommands) => {
95
101
  function createEnvOption() {
96
102
  return new commander_1.Option('--env <env>', 'Environment (dev|local|prod)').default('prod');
97
103
  }
104
+ function collectRepeatedOption(value, previous = []) {
105
+ return [...previous, value];
106
+ }
98
107
  function registerLoginCommand(parent) {
99
108
  parent
100
109
  .command('login')
@@ -115,8 +124,10 @@ function registerLogoutCommand(parent) {
115
124
  parent
116
125
  .command('logout')
117
126
  .description('Log out of Playdrop')
118
- .action(() => {
119
- (0, logout_1.logout)();
127
+ .argument('[username]', 'Username to log out')
128
+ .option('--env <env>', 'Environment for targeted logout')
129
+ .action((username, opts) => {
130
+ (0, logout_1.logout)({ username, env: opts.env });
120
131
  });
121
132
  }
122
133
  function registerWhoamiCommand(parent) {
@@ -127,6 +138,23 @@ function registerWhoamiCommand(parent) {
127
138
  await (0, whoami_1.whoami)();
128
139
  });
129
140
  }
141
+ function registerAccountsCommand(parent) {
142
+ parent
143
+ .command('accounts')
144
+ .description('List stored Playdrop accounts')
145
+ .action(() => {
146
+ (0, accounts_1.listAccounts)();
147
+ });
148
+ }
149
+ function registerUseAccountCommand(parent) {
150
+ parent
151
+ .command('use <username>')
152
+ .description('Switch the default Playdrop account')
153
+ .option('--env <env>', 'Environment for the selected account')
154
+ .action((username, opts) => {
155
+ (0, accounts_1.useAccount)(username, opts.env);
156
+ });
157
+ }
130
158
  registerLoginCommand(program);
131
159
  registerLogoutCommand(program);
132
160
  registerWhoamiCommand(program);
@@ -134,15 +162,22 @@ const auth = program.command('auth').description('Authentication commands');
134
162
  registerLoginCommand(auth);
135
163
  registerLogoutCommand(auth);
136
164
  registerWhoamiCommand(auth);
165
+ registerAccountsCommand(auth);
166
+ registerUseAccountCommand(auth);
137
167
  const browseCommand = program.command('browse').description('Browse public content');
138
168
  browseCommand
139
- .option('--kind <kind>', 'app, asset, asset-pack, or all')
169
+ .option('--kind <kind>', 'app, asset, asset-pack, asset-spec, or all')
140
170
  .option('--creator <creator>', 'all, me, or a creator slug')
141
171
  .option('--app-type <type>', 'game, demo, tool, or template')
142
172
  .option('--asset-category <category>', 'Asset category filter')
143
173
  .option('--asset-subcategory <subcategory>', 'Asset subcategory filter')
174
+ .option('--asset-spec <ref>', 'Asset spec family or version filter for assets')
175
+ .option('--asset-spec-owner <creator>', 'Asset spec owner filter for assets')
176
+ .option('--asset-spec-name <name>', 'Asset spec name filter for assets')
177
+ .option('--sort <sort>', 'Sort asset or asset-spec browse results')
144
178
  .option('--limit <number>', 'Maximum number of results to return')
145
179
  .option('--offset <number>', 'Result offset')
180
+ .option('--tag <ref>', 'Filter by canonical tag ref', collectRepeatedOption, [])
146
181
  .option('--json', 'Output JSON')
147
182
  .action(async (opts) => {
148
183
  await (0, browse_1.browse)(opts);
@@ -157,18 +192,33 @@ browseCommand
157
192
  program
158
193
  .command('search <query>')
159
194
  .description('Search public content')
160
- .option('--kind <kind>', 'app, asset, asset-pack, or all')
195
+ .option('--kind <kind>', 'app, asset, asset-pack, asset-spec, or all')
161
196
  .option('--app-type <type>', 'game, demo, tool, or template')
162
197
  .option('--asset-category <category>', 'Asset category filter')
163
198
  .option('--asset-subcategory <subcategory>', 'Asset subcategory filter')
199
+ .option('--asset-spec <ref>', 'Asset spec family or version filter for assets')
200
+ .option('--asset-spec-owner <creator>', 'Asset spec owner filter for assets')
201
+ .option('--asset-spec-name <name>', 'Asset spec name filter for assets')
202
+ .option('--sort <sort>', 'Sort search results')
164
203
  .option('--pack-contains-category <category>', 'Asset pack category filter')
165
204
  .option('--pack-contains-subcategory <subcategory>', 'Asset pack subcategory filter')
166
205
  .option('--limit <number>', 'Maximum number of results to return')
167
206
  .option('--offset <number>', 'Result offset')
207
+ .option('--tag <ref>', 'Filter by canonical tag ref', collectRepeatedOption, [])
168
208
  .option('--json', 'Output JSON')
169
209
  .action(async (query, opts) => {
170
210
  await (0, search_1.search)(query, opts);
171
211
  });
212
+ const tags = program.command('tags').description('Browse and inspect tags');
213
+ tags
214
+ .command('browse')
215
+ .description('Browse public tags')
216
+ .option('--group <slug>', 'Filter to one tag group')
217
+ .option('--kind <kind>', 'Filter to one target kind')
218
+ .option('--json', 'Output JSON')
219
+ .action(async (opts) => {
220
+ await (0, tags_1.browseTags)(opts);
221
+ });
172
222
  program
173
223
  .command('detail <ref>')
174
224
  .description('Show one app, asset, or asset pack')
@@ -234,6 +284,88 @@ credits
234
284
  .action(async (opts) => {
235
285
  await (0, credits_1.browseCreditTransactions)(opts);
236
286
  });
287
+ credits
288
+ .command('earnings')
289
+ .description('Show creator ad earnings and boost spend summary')
290
+ .option('--days <number>', 'Number of days to include')
291
+ .option('--app-id <number>', 'Filter to one app id')
292
+ .option('--json', 'Output JSON')
293
+ .action(async (opts) => {
294
+ await (0, credits_1.showCreatorEarnings)(opts);
295
+ });
296
+ const ads = program.command('ads').description('Creator ad earnings and reporting');
297
+ ads
298
+ .command('earnings')
299
+ .description('Show ad earnings grouped by app and format')
300
+ .option('--app <ref>', 'Filter to one app ref')
301
+ .option('--days <number>', 'Number of days to include')
302
+ .option('--json', 'Output JSON')
303
+ .action(async (opts) => {
304
+ await (0, ads_1.showAdsEarnings)(opts);
305
+ });
306
+ ads
307
+ .command('report')
308
+ .description('Show daily ad reporting buckets')
309
+ .option('--app <ref>', 'Filter to one app ref')
310
+ .option('--days <number>', 'Number of days to include')
311
+ .option('--json', 'Output JSON')
312
+ .action(async (opts) => {
313
+ await (0, ads_1.showAdsReport)(opts);
314
+ });
315
+ const boosts = program.command('boosts').description('Buy, activate, and inspect game boosts');
316
+ boosts
317
+ .command('balance')
318
+ .description('Show your available boost inventory')
319
+ .option('--json', 'Output JSON')
320
+ .action(async (opts) => {
321
+ await (0, boosts_1.showBoostBalance)(opts);
322
+ });
323
+ boosts
324
+ .command('offers')
325
+ .description('List available boost packs')
326
+ .option('--json', 'Output JSON')
327
+ .action(async (opts) => {
328
+ await (0, boosts_1.browseBoostOffers)(opts);
329
+ });
330
+ boosts
331
+ .command('buy <sku>')
332
+ .description('Buy one boost pack')
333
+ .option('--quantity <number>', 'Number of packs to buy')
334
+ .option('--json', 'Output JSON')
335
+ .action(async (sku, opts) => {
336
+ await (0, boosts_1.buyBoostPack)(sku, opts);
337
+ });
338
+ boosts
339
+ .command('activate <ref>')
340
+ .description('Activate one or more boosts for an app')
341
+ .option('--units <number>', 'Number of boosts to consume')
342
+ .option('--json', 'Output JSON')
343
+ .action(async (ref, opts) => {
344
+ await (0, boosts_1.activateBoost)(ref, opts);
345
+ });
346
+ boosts
347
+ .command('status <ref>')
348
+ .description('Show the current boost status for an app')
349
+ .option('--json', 'Output JSON')
350
+ .action(async (ref, opts) => {
351
+ await (0, boosts_1.showBoostStatus)(ref, opts);
352
+ });
353
+ boosts
354
+ .command('history <ref>')
355
+ .description('Show past boost runs for an app')
356
+ .option('--limit <number>', 'Maximum number of boost runs to include')
357
+ .option('--json', 'Output JSON')
358
+ .action(async (ref, opts) => {
359
+ await (0, boosts_1.showBoostHistory)(ref, opts);
360
+ });
361
+ boosts
362
+ .command('report <ref>')
363
+ .description('Show daily boost performance for an app')
364
+ .option('--days <number>', 'Number of days to include')
365
+ .option('--json', 'Output JSON')
366
+ .action(async (ref, opts) => {
367
+ await (0, boosts_1.showBoostReport)(ref, opts);
368
+ });
237
369
  const notifications = program.command('notifications').description('Notification inbox');
238
370
  notifications
239
371
  .command('browse')
@@ -504,6 +636,13 @@ aiJobs
504
636
  await (0, generation_1.showGenerationJob)(id, opts);
505
637
  });
506
638
  const project = program.command('project').description('Local workspace tooling');
639
+ const internalDevRouter = project.command('_dev-router').description('Internal shared dev router');
640
+ internalDevRouter
641
+ .command('serve')
642
+ .description('Run the internal shared dev router')
643
+ .action(async () => {
644
+ await (0, devServer_1.runDevRouterServer)();
645
+ });
507
646
  project
508
647
  .command('init [path]')
509
648
  .description('Initialize a Playdrop workspace')
@@ -519,6 +658,12 @@ projectCreate
519
658
  .action(async (name, opts) => {
520
659
  await (0, create_1.create)(name, opts);
521
660
  });
661
+ projectCreate
662
+ .command('asset-spec <name>')
663
+ .description('Create a new custom asset spec scaffold')
664
+ .action(async (name) => {
665
+ await (0, create_1.createAssetSpecProject)(name);
666
+ });
522
667
  projectCreate
523
668
  .command('asset <name>')
524
669
  .description('Create a new asset remix from an exact asset version')
@@ -562,6 +707,31 @@ projectCapture
562
707
  password: opts.password,
563
708
  });
564
709
  });
710
+ projectCapture
711
+ .command('listing [target]', { hidden: true })
712
+ .description('Record private listing video for a hosted app')
713
+ .option('--app <name>', 'App name when the target is a workspace')
714
+ .option('--duration <seconds>', 'Recording duration in seconds')
715
+ .option('--width <pixels>', 'Final gameplay width in pixels')
716
+ .option('--height <pixels>', 'Final gameplay height in pixels')
717
+ .option('--fps <number>', 'Final output frame rate')
718
+ .option('--poster-at <seconds>', 'Poster frame offset in seconds')
719
+ .option('--audio', 'Capture gameplay audio')
720
+ .option('--output-dir <path>', 'Output directory for the capture artifacts')
721
+ .option('--keep-raw', 'Keep the raw intermediate recording')
722
+ .action(async (target, opts) => {
723
+ await (0, captureListing_1.captureListing)(target, {
724
+ app: opts.app,
725
+ duration: opts.duration,
726
+ width: opts.width,
727
+ height: opts.height,
728
+ fps: opts.fps,
729
+ posterAt: opts.posterAt,
730
+ audio: opts.audio,
731
+ outputDir: opts.outputDir,
732
+ keepRaw: opts.keepRaw,
733
+ });
734
+ });
565
735
  projectCapture
566
736
  .command('remote <url>')
567
737
  .description('Run a Playwright capture against a published URL')
@@ -600,8 +770,10 @@ project
600
770
  .command('publish [target]')
601
771
  .description('Publish local content to Playdrop')
602
772
  .option('--skip-ecs', 'Skip ecs.json and server.js during publish')
773
+ .option('--clear-tags', 'Confirm clearing existing live tags when this publish removes them')
774
+ .addOption(new commander_1.Option('--skip-review', 'Mark uploaded app versions as NOT_REQUIRED review. Admin-only.').hideHelp())
603
775
  .action(async (target, opts) => {
604
- await (0, upload_1.upload)(target ?? '.', { skipEcs: opts.skipEcs });
776
+ await (0, upload_1.upload)(target ?? '.', { skipEcs: opts.skipEcs, skipReview: opts.skipReview, clearTags: opts.clearTags });
605
777
  });
606
778
  const documentation = program.command('documentation').description('Public Playdrop documentation');
607
779
  documentation
package/dist/refs.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { ApiClient } from '@playdrop/api-client';
2
2
  import type { SavedItemKind } from '@playdrop/types';
3
- export type ContentKind = 'app' | 'asset' | 'asset-pack';
3
+ export type ContentKind = 'app' | 'asset' | 'asset-pack' | 'asset-spec';
4
4
  export type ContentRef = {
5
5
  creator: string;
6
6
  kind: ContentKind;
@@ -13,6 +13,6 @@ export declare function parseCreatorOption(raw: string | undefined): string | 'm
13
13
  export declare function resolveCreatorOption(client: ApiClient, raw: string | undefined): Promise<string>;
14
14
  export declare function buildScopedRef(creator: string, kind: ContentKind, name: string): ContentRef;
15
15
  export declare function contentKindToSavedItemKind(kind: ContentKind): SavedItemKind;
16
- export declare function contentKindToSearchKind(kind: ContentKind | 'all'): 'all' | 'app' | 'asset' | 'pack';
16
+ export declare function contentKindToSearchKind(kind: ContentKind | 'all'): 'all' | 'app' | 'asset' | 'asset-spec' | 'pack';
17
17
  export declare function contentKindLabel(kind: ContentKind): string;
18
18
  export declare function contentKindPluralLabel(kind: ContentKind): string;
package/dist/refs.js CHANGED
@@ -31,7 +31,7 @@ function parseContentRef(raw) {
31
31
  const creator = sanitizeSegment(segments[0], 'creator');
32
32
  const kind = sanitizeSegment(segments[1], 'kind');
33
33
  const name = sanitizeSegment(segments[2], 'name');
34
- if (kind !== 'app' && kind !== 'asset' && kind !== 'asset-pack') {
34
+ if (kind !== 'app' && kind !== 'asset' && kind !== 'asset-pack' && kind !== 'asset-spec') {
35
35
  throw new Error('invalid_kind');
36
36
  }
37
37
  return {
@@ -83,9 +83,15 @@ function contentKindToSavedItemKind(kind) {
83
83
  if (kind === 'asset') {
84
84
  return 'ASSET';
85
85
  }
86
+ if (kind === 'asset-spec') {
87
+ throw new Error('asset_specs_not_saveable');
88
+ }
86
89
  return 'PACK';
87
90
  }
88
91
  function contentKindToSearchKind(kind) {
92
+ if (kind === 'asset-spec') {
93
+ return 'asset-spec';
94
+ }
89
95
  if (kind === 'asset-pack') {
90
96
  return 'pack';
91
97
  }
@@ -95,11 +101,17 @@ function contentKindLabel(kind) {
95
101
  if (kind === 'asset-pack') {
96
102
  return 'asset pack';
97
103
  }
104
+ if (kind === 'asset-spec') {
105
+ return 'asset spec';
106
+ }
98
107
  return kind;
99
108
  }
100
109
  function contentKindPluralLabel(kind) {
101
110
  if (kind === 'asset-pack') {
102
111
  return 'asset packs';
103
112
  }
113
+ if (kind === 'asset-spec') {
114
+ return 'asset specs';
115
+ }
104
116
  return `${kind}s`;
105
117
  }
@@ -29,6 +29,8 @@ function createDirectAppTask(htmlPath) {
29
29
  emoji: undefined,
30
30
  color: undefined,
31
31
  surfaceTargets: [],
32
+ assetSpecSupport: [],
33
+ tags: [],
32
34
  missingMetadata: [],
33
35
  achievements: [],
34
36
  leaderboards: [],
@@ -53,7 +55,7 @@ function selectTasks(pathOrName) {
53
55
  });
54
56
  return { tasks: [], warnings, errors, resolution: 'directory' };
55
57
  }
56
- const { apps, assets, embeddedAssets, assetPacks, warnings: rawWarnings, errors: catalogueErrors } = (0, catalogue_1.resolveCatalogueEntries)(absolute);
58
+ const { apps, assetSpecs, assets, embeddedAssets, assetPacks, warnings: rawWarnings, errors: catalogueErrors } = (0, catalogue_1.resolveCatalogueEntries)(absolute);
57
59
  if (catalogueErrors.length > 0) {
58
60
  catalogueErrors.forEach(message => {
59
61
  errors.push({
@@ -64,12 +66,12 @@ function selectTasks(pathOrName) {
64
66
  });
65
67
  return { tasks: [], warnings, errors, resolution: 'directory' };
66
68
  }
67
- const taskList = [...assets, ...apps, ...embeddedAssets, ...assetPacks];
69
+ const taskList = [...assetSpecs, ...assets, ...apps, ...embeddedAssets, ...assetPacks];
68
70
  if (taskList.length === 0) {
69
71
  errors.push({
70
72
  type: 'no-tasks',
71
73
  message: `No publishable entries were found under ${absolute}.`,
72
- help: ['Add child catalogue.json files or add apps, assets, or assetPacks to the root catalogue.json, then rerun the command.'],
74
+ help: ['Add child catalogue.json files or add apps, assetSpecs, assets, or assetPacks to the root catalogue.json, then rerun the command.'],
73
75
  directory: absolute,
74
76
  });
75
77
  return { tasks: [], warnings, errors, resolution: 'directory' };
@@ -126,6 +128,7 @@ function selectTasks(pathOrName) {
126
128
  }
127
129
  const candidates = [
128
130
  ...lookup.apps.filter(task => task.name === pathOrName),
131
+ ...lookup.assetSpecs.filter(task => task.name === pathOrName),
129
132
  ...lookup.assets.filter(task => task.name === pathOrName),
130
133
  ...lookup.assetPacks.filter(task => task.name === pathOrName),
131
134
  ];
@@ -1,4 +1,4 @@
1
- import type { AppTask, AssetTask, EmbeddedAssetTask, AssetPackTask } from './catalogue';
2
- export type CliTask = AppTask | AssetTask | EmbeddedAssetTask | AssetPackTask;
1
+ import type { AppTask, AssetSpecTask, AssetTask, EmbeddedAssetTask, AssetPackTask } from './catalogue';
2
+ export type CliTask = AppTask | AssetSpecTask | AssetTask | EmbeddedAssetTask | AssetPackTask;
3
3
  export declare function sortTasks(tasks: CliTask[]): CliTask[];
4
4
  export declare function addTaskWarnings(warnings: Set<string>, tasks: CliTask[]): void;
package/dist/taskUtils.js CHANGED
@@ -5,6 +5,7 @@ exports.addTaskWarnings = addTaskWarnings;
5
5
  const catalogue_1 = require("./catalogue");
6
6
  function sortTasks(tasks) {
7
7
  const order = {
8
+ 'asset-spec': -1,
8
9
  asset: 0,
9
10
  app: 1,
10
11
  'embedded-asset': 2,
@@ -3,7 +3,7 @@ export type TaskStatus = 'success' | 'skipped' | 'error';
3
3
  export type TaskLogEntry = {
4
4
  action: TaskAction;
5
5
  status: TaskStatus;
6
- entityType: 'app' | 'asset' | 'embedded-asset' | 'asset-pack';
6
+ entityType: 'app' | 'asset-spec' | 'asset' | 'embedded-asset' | 'asset-pack' | 'tag-group' | 'tag';
7
7
  entityId: string;
8
8
  catalogue: string;
9
9
  detail?: string;
package/dist/uploadLog.js CHANGED
@@ -39,7 +39,7 @@ function formatLabel(entry) {
39
39
  function formatTaskLogLine(entry) {
40
40
  const prefix = `${STATUS_EMOJI[entry.status]} ${formatLabel(entry)}`;
41
41
  if (entry.detail) {
42
- return `${prefix} ${entry.detail}`;
42
+ return `${prefix} - ${entry.detail}`;
43
43
  }
44
44
  return prefix;
45
45
  }
@@ -108,7 +108,7 @@ function printTaskSummary(entries, warnings, options) {
108
108
  summaryText = labels.complete;
109
109
  }
110
110
  const parts = [
111
- `${summaryEmoji} ${summaryText} ${success} ${labels.unit}`,
111
+ `${summaryEmoji} ${summaryText} - ${success} ${labels.unit}`,
112
112
  `${skipped} skipped`,
113
113
  `${failed} failed`,
114
114
  ];
@@ -0,0 +1,14 @@
1
+ export interface WorkspaceAuthConfig {
2
+ ownerUsername: string;
3
+ env?: string;
4
+ }
5
+ export interface ResolvedWorkspaceAuthConfig {
6
+ path: string;
7
+ directory: string;
8
+ config: WorkspaceAuthConfig;
9
+ }
10
+ export declare class WorkspaceAuthConfigError extends Error {
11
+ readonly filePath: string;
12
+ constructor(filePath: string, reason: 'invalid_json' | 'invalid_shape' | 'missing_owner_username');
13
+ }
14
+ export declare function findWorkspaceAuthConfig(startPath: string): ResolvedWorkspaceAuthConfig | null;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WorkspaceAuthConfigError = void 0;
4
+ exports.findWorkspaceAuthConfig = findWorkspaceAuthConfig;
5
+ const node_fs_1 = require("node:fs");
6
+ const node_path_1 = require("node:path");
7
+ class WorkspaceAuthConfigError extends Error {
8
+ constructor(filePath, reason) {
9
+ super(reason);
10
+ this.name = 'WorkspaceAuthConfigError';
11
+ this.filePath = filePath;
12
+ }
13
+ }
14
+ exports.WorkspaceAuthConfigError = WorkspaceAuthConfigError;
15
+ const WORKSPACE_AUTH_FILENAME = '.playdrop.json';
16
+ function normalizeOwnerUsername(value) {
17
+ if (typeof value !== 'string') {
18
+ return null;
19
+ }
20
+ const normalized = value.trim();
21
+ return normalized.length > 0 ? normalized : null;
22
+ }
23
+ function normalizeEnv(value) {
24
+ if (typeof value !== 'string') {
25
+ return undefined;
26
+ }
27
+ const normalized = value.trim();
28
+ return normalized.length > 0 ? normalized : undefined;
29
+ }
30
+ function readWorkspaceAuthConfig(filePath) {
31
+ let parsed;
32
+ try {
33
+ const raw = (0, node_fs_1.readFileSync)(filePath, 'utf8');
34
+ parsed = JSON.parse(raw);
35
+ }
36
+ catch {
37
+ throw new WorkspaceAuthConfigError(filePath, 'invalid_json');
38
+ }
39
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
40
+ throw new WorkspaceAuthConfigError(filePath, 'invalid_shape');
41
+ }
42
+ const ownerUsername = normalizeOwnerUsername(parsed.ownerUsername);
43
+ if (!ownerUsername) {
44
+ throw new WorkspaceAuthConfigError(filePath, 'missing_owner_username');
45
+ }
46
+ return {
47
+ ownerUsername,
48
+ env: normalizeEnv(parsed.env),
49
+ };
50
+ }
51
+ function findWorkspaceAuthConfig(startPath) {
52
+ let current = (0, node_path_1.resolve)(startPath);
53
+ if ((0, node_fs_1.existsSync)(current) && (0, node_fs_1.statSync)(current).isFile()) {
54
+ current = (0, node_path_1.dirname)(current);
55
+ }
56
+ while (true) {
57
+ const candidate = (0, node_path_1.join)(current, WORKSPACE_AUTH_FILENAME);
58
+ if ((0, node_fs_1.existsSync)(candidate) && (0, node_fs_1.statSync)(candidate).isFile()) {
59
+ const config = readWorkspaceAuthConfig(candidate);
60
+ if (config) {
61
+ return {
62
+ path: candidate,
63
+ directory: current,
64
+ config,
65
+ };
66
+ }
67
+ return null;
68
+ }
69
+ const parent = (0, node_path_1.dirname)(current);
70
+ if (parent === current) {
71
+ return null;
72
+ }
73
+ current = parent;
74
+ }
75
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playdrop/ai-client",
3
- "version": "0.5.1",
3
+ "version": "0.5.4",
4
4
  "description": "Client SDK for the Playdrop AI server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",