@playdrop/playdrop-cli 0.5.4 → 0.5.6

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 (99) hide show
  1. package/README.md +2 -1
  2. package/config/client-meta.json +4 -4
  3. package/dist/apps/upload.js +226 -80
  4. package/dist/assetSpecs.d.ts +1 -0
  5. package/dist/assetSpecs.js +22 -1
  6. package/dist/assets/model-artifacts.d.ts +2 -2
  7. package/dist/assets/model-artifacts.js +1 -1
  8. package/dist/captureRuntime.d.ts +1 -0
  9. package/dist/captureRuntime.js +3 -2
  10. package/dist/catalogue.d.ts +33 -8
  11. package/dist/catalogue.js +364 -46
  12. package/dist/commandContext.d.ts +5 -1
  13. package/dist/commandContext.js +90 -29
  14. package/dist/commands/browse.d.ts +3 -0
  15. package/dist/commands/browse.js +90 -17
  16. package/dist/commands/build.js +1 -1
  17. package/dist/commands/capture.d.ts +3 -0
  18. package/dist/commands/capture.js +121 -9
  19. package/dist/commands/captureListing.d.ts +2 -0
  20. package/dist/commands/captureListing.js +157 -61
  21. package/dist/commands/create.js +6 -28
  22. package/dist/commands/createRemixContent.js +4 -26
  23. package/dist/commands/creations.js +2 -3
  24. package/dist/commands/detail.js +24 -2
  25. package/dist/commands/dev.d.ts +8 -1
  26. package/dist/commands/dev.js +180 -2
  27. package/dist/commands/devRuntimeAssets.d.ts +34 -0
  28. package/dist/commands/devRuntimeAssets.js +308 -0
  29. package/dist/commands/devServer.d.ts +11 -0
  30. package/dist/commands/devServer.js +196 -13
  31. package/dist/commands/init.js +6 -24
  32. package/dist/commands/search.d.ts +4 -0
  33. package/dist/commands/search.js +68 -11
  34. package/dist/commands/upload-content.d.ts +3 -3
  35. package/dist/commands/upload-content.js +19 -38
  36. package/dist/commands/upload.js +67 -77
  37. package/dist/commands/validate.js +13 -20
  38. package/dist/commands/whoami.js +23 -26
  39. package/dist/devAuth.d.ts +16 -0
  40. package/dist/devAuth.js +60 -0
  41. package/dist/index.js +22 -4
  42. package/dist/taskSelection.js +4 -3
  43. package/dist/taskUtils.d.ts +2 -2
  44. package/dist/taskUtils.js +1 -1
  45. package/dist/uploadLog.d.ts +1 -1
  46. package/node_modules/@playdrop/ai-client/package.json +1 -1
  47. package/node_modules/@playdrop/api-client/dist/client.d.ts +44 -114
  48. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  49. package/node_modules/@playdrop/api-client/dist/client.js +22 -0
  50. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +2 -1
  51. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
  52. package/node_modules/@playdrop/api-client/dist/domains/admin.js +11 -0
  53. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +11 -19
  54. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
  55. package/node_modules/@playdrop/api-client/dist/domains/apps.js +116 -106
  56. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +2 -1
  57. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
  58. package/node_modules/@playdrop/api-client/dist/domains/assets.js +13 -0
  59. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +5 -5
  60. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
  61. package/node_modules/@playdrop/api-client/dist/domains/payments.js +8 -8
  62. package/node_modules/@playdrop/api-client/dist/domains/search.d.ts.map +1 -1
  63. package/node_modules/@playdrop/api-client/dist/domains/search.js +24 -2
  64. package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts +13 -1
  65. package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts.map +1 -1
  66. package/node_modules/@playdrop/api-client/dist/domains/tags.js +52 -0
  67. package/node_modules/@playdrop/api-client/dist/index.d.ts +28 -29
  68. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  69. package/node_modules/@playdrop/api-client/dist/index.js +26 -8
  70. package/node_modules/@playdrop/api-client/package.json +1 -1
  71. package/node_modules/@playdrop/boxel-core/package.json +1 -1
  72. package/node_modules/@playdrop/boxel-three/package.json +1 -1
  73. package/node_modules/@playdrop/config/client-meta.json +4 -4
  74. package/node_modules/@playdrop/config/package.json +1 -1
  75. package/node_modules/@playdrop/types/dist/api.d.ts +130 -3
  76. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  77. package/node_modules/@playdrop/types/dist/api.js +23 -0
  78. package/node_modules/@playdrop/types/dist/app-capability-filters.d.ts +24 -0
  79. package/node_modules/@playdrop/types/dist/app-capability-filters.d.ts.map +1 -0
  80. package/node_modules/@playdrop/types/dist/app-capability-filters.js +72 -0
  81. package/node_modules/@playdrop/types/dist/asset-pack.d.ts +3 -2
  82. package/node_modules/@playdrop/types/dist/asset-pack.d.ts.map +1 -1
  83. package/node_modules/@playdrop/types/dist/asset.d.ts +2 -3
  84. package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
  85. package/node_modules/@playdrop/types/dist/asset.js +1 -1
  86. package/node_modules/@playdrop/types/dist/index.d.ts +2 -0
  87. package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
  88. package/node_modules/@playdrop/types/dist/index.js +2 -0
  89. package/node_modules/@playdrop/types/dist/owned-assets.d.ts +21 -0
  90. package/node_modules/@playdrop/types/dist/owned-assets.d.ts.map +1 -0
  91. package/node_modules/@playdrop/types/dist/owned-assets.js +35 -0
  92. package/node_modules/@playdrop/types/dist/player-meta.d.ts +28 -0
  93. package/node_modules/@playdrop/types/dist/player-meta.d.ts.map +1 -1
  94. package/node_modules/@playdrop/types/dist/version.d.ts +111 -1
  95. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  96. package/node_modules/@playdrop/types/dist/version.js +4 -0
  97. package/node_modules/@playdrop/types/package.json +1 -1
  98. package/node_modules/@playdrop/vox-three/package.json +1 -1
  99. package/package.json +1 -1
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveAuthenticatedEnvironmentContext = resolveAuthenticatedEnvironmentContext;
4
+ exports.resolveOptionalEnvironmentContext = resolveOptionalEnvironmentContext;
3
5
  exports.withEnvironment = withEnvironment;
4
6
  exports.withPublicEnvironment = withPublicEnvironment;
5
7
  const config_1 = require("./config");
@@ -27,7 +29,7 @@ function resolveConfiguredEnvironment(envName, command, options) {
27
29
  }
28
30
  return envConfig;
29
31
  }
30
- function buildContext(cfg, envConfig, account) {
32
+ function buildContext(cfg, envConfig, account, workspaceAuth = null) {
31
33
  const token = account?.token ?? cfg.token ?? '';
32
34
  const client = (0, apiClient_1.createCliApiClient)({ baseUrl: envConfig.apiBase, token });
33
35
  const aiClient = (0, apiClient_1.createCliAiClient)({ baseUrl: envConfig.aiBase, token });
@@ -39,6 +41,7 @@ function buildContext(cfg, envConfig, account) {
39
41
  token,
40
42
  config: cfg,
41
43
  account,
44
+ workspaceAuth,
42
45
  };
43
46
  }
44
47
  function buildLegacyAccountSession(cfg) {
@@ -77,32 +80,7 @@ async function migrateLegacyConfigIfNeeded(cfg, envConfig, command) {
77
80
  return null;
78
81
  }
79
82
  }
80
- function resolveWorkspaceSelectedAccount(cfg, currentAccount, workspaceAuth) {
81
- const matchingSessions = (0, config_1.listAccountSessionsForUsername)(workspaceAuth.config.ownerUsername, cfg);
82
- if (workspaceAuth.config.env) {
83
- return {
84
- account: (0, config_1.findAccountSession)(workspaceAuth.config.ownerUsername, workspaceAuth.config.env, cfg),
85
- matchingSessions,
86
- };
87
- }
88
- if (matchingSessions.length === 1) {
89
- return {
90
- account: matchingSessions[0],
91
- matchingSessions,
92
- };
93
- }
94
- if (currentAccount && currentAccount.username === workspaceAuth.config.ownerUsername) {
95
- return {
96
- account: (0, config_1.findAccountSession)(workspaceAuth.config.ownerUsername, currentAccount.env, cfg),
97
- matchingSessions,
98
- };
99
- }
100
- return {
101
- account: null,
102
- matchingSessions,
103
- };
104
- }
105
- async function resolveAuthenticatedEnvironment(command, actionLabel, options = {}) {
83
+ async function loadWorkspaceAwareConfig(command, options) {
106
84
  let cfg = (0, config_1.loadConfig)();
107
85
  let workspaceAuth = null;
108
86
  try {
@@ -133,6 +111,39 @@ async function resolveAuthenticatedEnvironment(command, actionLabel, options = {
133
111
  }
134
112
  cfg = migrated;
135
113
  }
114
+ return { cfg, workspaceAuth };
115
+ }
116
+ function resolveWorkspaceSelectedAccount(cfg, currentAccount, workspaceAuth) {
117
+ const matchingSessions = (0, config_1.listAccountSessionsForUsername)(workspaceAuth.config.ownerUsername, cfg);
118
+ if (workspaceAuth.config.env) {
119
+ return {
120
+ account: (0, config_1.findAccountSession)(workspaceAuth.config.ownerUsername, workspaceAuth.config.env, cfg),
121
+ matchingSessions,
122
+ };
123
+ }
124
+ if (matchingSessions.length === 1) {
125
+ return {
126
+ account: matchingSessions[0],
127
+ matchingSessions,
128
+ };
129
+ }
130
+ if (currentAccount && currentAccount.username === workspaceAuth.config.ownerUsername) {
131
+ return {
132
+ account: (0, config_1.findAccountSession)(workspaceAuth.config.ownerUsername, currentAccount.env, cfg),
133
+ matchingSessions,
134
+ };
135
+ }
136
+ return {
137
+ account: null,
138
+ matchingSessions,
139
+ };
140
+ }
141
+ async function resolveAuthenticatedEnvironment(command, actionLabel, options = {}) {
142
+ const loaded = await loadWorkspaceAwareConfig(command, options);
143
+ if (!loaded) {
144
+ return null;
145
+ }
146
+ const { cfg, workspaceAuth } = loaded;
136
147
  const currentAccount = (0, config_1.getCurrentAccountSession)(cfg) ?? buildLegacyAccountSession(cfg);
137
148
  const selectedAccount = workspaceAuth
138
149
  ? resolveWorkspaceSelectedAccount(cfg, currentAccount, workspaceAuth).account
@@ -171,14 +182,64 @@ async function resolveAuthenticatedEnvironment(command, actionLabel, options = {
171
182
  cfg: (0, config_1.loadConfig)(),
172
183
  account: selectedAccount,
173
184
  envConfig,
185
+ workspaceAuth,
174
186
  };
175
187
  }
176
- async function withEnvironment(command, actionLabel, callback, options = {}) {
188
+ async function resolveAuthenticatedEnvironmentContext(command, actionLabel, options = {}) {
177
189
  const resolved = await resolveAuthenticatedEnvironment(command, actionLabel, options);
178
190
  if (!resolved) {
191
+ return null;
192
+ }
193
+ return buildContext(resolved.cfg, resolved.envConfig, resolved.account, resolved.workspaceAuth);
194
+ }
195
+ async function resolveOptionalEnvironmentContext(command, options = {}) {
196
+ const loaded = await loadWorkspaceAwareConfig(command, options);
197
+ if (!loaded) {
198
+ return null;
199
+ }
200
+ const { cfg, workspaceAuth } = loaded;
201
+ const currentAccount = (0, config_1.getCurrentAccountSession)(cfg) ?? buildLegacyAccountSession(cfg);
202
+ if (!currentAccount && !workspaceAuth) {
203
+ return null;
204
+ }
205
+ const selectedAccount = workspaceAuth
206
+ ? resolveWorkspaceSelectedAccount(cfg, currentAccount, workspaceAuth).account
207
+ : currentAccount;
208
+ if (!selectedAccount) {
209
+ if (workspaceAuth) {
210
+ const matchingSessions = (0, config_1.listAccountSessionsForUsername)(workspaceAuth.config.ownerUsername, cfg);
211
+ if (!workspaceAuth.config.env && matchingSessions.length > 1) {
212
+ (0, messages_1.printErrorWithHelp)(`This workspace is pinned to ${workspaceAuth.config.ownerUsername}, but that account is logged in on multiple environments.`, [
213
+ `Run "playdrop auth use ${workspaceAuth.config.ownerUsername} --env <env>" to select the matching environment.`,
214
+ 'Or add an "env" field to .playdrop.json.',
215
+ ], { command });
216
+ }
217
+ else {
218
+ const envSuffix = workspaceAuth.config.env ? ` on ${workspaceAuth.config.env}` : '';
219
+ (0, messages_1.printErrorWithHelp)(`This workspace is pinned to ${workspaceAuth.config.ownerUsername}${envSuffix}, but that account is not logged in.`, [
220
+ `Run "playdrop auth login${workspaceAuth.config.env ? ` --env ${workspaceAuth.config.env}` : ''}" while authenticated as ${workspaceAuth.config.ownerUsername}.`,
221
+ 'Or remove .playdrop.json if this workspace should use your current default account.',
222
+ ], { command });
223
+ }
224
+ process.exitCode = 1;
225
+ }
226
+ return null;
227
+ }
228
+ const envConfig = resolveConfiguredEnvironment(selectedAccount.env, command, {
229
+ requireAuth: true,
230
+ allowDefaultPublicEnv: false,
231
+ });
232
+ if (!envConfig) {
233
+ return null;
234
+ }
235
+ return buildContext((0, config_1.loadConfig)(), envConfig, selectedAccount, workspaceAuth);
236
+ }
237
+ async function withEnvironment(command, actionLabel, callback, options = {}) {
238
+ const ctx = await resolveAuthenticatedEnvironmentContext(command, actionLabel, options);
239
+ if (!ctx) {
179
240
  return;
180
241
  }
181
- await callback(buildContext(resolved.cfg, resolved.envConfig, resolved.account));
242
+ await callback(ctx);
182
243
  }
183
244
  async function withPublicEnvironment(command, callback) {
184
245
  const cfg = (0, config_1.loadConfig)();
@@ -4,6 +4,9 @@ export type BrowseOptions = {
4
4
  kind?: string;
5
5
  creator?: string;
6
6
  appType?: string;
7
+ auth?: string;
8
+ controller?: string;
9
+ surface?: string;
7
10
  assetCategory?: string;
8
11
  assetSubcategory?: string;
9
12
  assetSpec?: string;
@@ -71,6 +71,9 @@ function parseBrowseSort(raw) {
71
71
  return undefined;
72
72
  }
73
73
  const normalized = raw.trim().toLowerCase();
74
+ if (types_1.APP_LIST_SORT_VALUES.includes(normalized)) {
75
+ return normalized;
76
+ }
74
77
  if (types_1.ASSET_LIST_SORT_VALUES.includes(normalized)) {
75
78
  return normalized;
76
79
  }
@@ -117,12 +120,6 @@ function buildCountsSuffix(item) {
117
120
  if (typeof item.item.viewCount === 'number') {
118
121
  parts.push(`view ${formatCountValue(item.item.viewCount)}`);
119
122
  }
120
- if (typeof item.item.downloadCount === 'number') {
121
- parts.push(`download ${formatCountValue(item.item.downloadCount)}`);
122
- }
123
- if (typeof item.item.playCount === 'number') {
124
- parts.push(`play ${formatCountValue(item.item.playCount)}`);
125
- }
126
123
  }
127
124
  if (item.kind === 'asset-pack' && typeof item.item.downloadCount === 'number') {
128
125
  parts.push(`download ${formatCountValue(item.item.downloadCount)}`);
@@ -249,12 +246,17 @@ function buildTagSuffix(tags) {
249
246
  function parseBrowseSortOption(rawSort, kind) {
250
247
  const sort = parseBrowseSort(rawSort);
251
248
  if (sort === null) {
252
- (0, messages_1.printErrorWithHelp)(`Sort "${rawSort ?? ''}" is not supported.`, ['Use one of: recent, likes, downloads, remixes, comments, assets, apps, alpha.'], { command: 'browse' });
249
+ (0, messages_1.printErrorWithHelp)(`Sort "${rawSort ?? ''}" is not supported.`, ['Use one of: newest, updated, views, likes, used, recent, remixes, comments, assets, apps, alpha.'], { command: 'browse' });
253
250
  process.exitCode = 1;
254
251
  return null;
255
252
  }
256
253
  if (sort && kind === 'all') {
257
- (0, messages_1.printErrorWithHelp)('The --sort option is not supported with --kind all.', ['Choose --kind asset or --kind asset-spec when sorting browse results.'], { command: 'browse' });
254
+ (0, messages_1.printErrorWithHelp)('The --sort option is not supported with --kind all.', ['Choose --kind app, --kind asset, or --kind asset-spec when sorting browse results.'], { command: 'browse' });
255
+ process.exitCode = 1;
256
+ return null;
257
+ }
258
+ if (sort && kind === 'app' && !types_1.APP_LIST_SORT_VALUES.includes(sort)) {
259
+ (0, messages_1.printErrorWithHelp)(`Sort "${sort}" is not supported for apps.`, ['Use one of: newest, updated, views, likes, used.'], { command: 'browse' });
258
260
  process.exitCode = 1;
259
261
  return null;
260
262
  }
@@ -264,7 +266,7 @@ function parseBrowseSortOption(rawSort, kind) {
264
266
  return null;
265
267
  }
266
268
  if (sort && kind === 'asset' && !types_1.ASSET_LIST_SORT_VALUES.includes(sort)) {
267
- (0, messages_1.printErrorWithHelp)(`Sort "${sort}" is not supported for assets.`, ['Use one of: recent, likes, downloads, remixes, comments.'], { command: 'browse' });
269
+ (0, messages_1.printErrorWithHelp)(`Sort "${sort}" is not supported for assets.`, ['Use one of: recent, likes, remixes, comments.'], { command: 'browse' });
268
270
  process.exitCode = 1;
269
271
  return null;
270
272
  }
@@ -321,6 +323,30 @@ function parseBrowseCommandOptions(options) {
321
323
  process.exitCode = 1;
322
324
  return { ok: false };
323
325
  }
326
+ const auth = options.auth === undefined ? undefined : (0, types_1.parseAppAuthFilter)(options.auth);
327
+ if (options.auth !== undefined && !auth) {
328
+ (0, messages_1.printErrorWithHelp)(`Auth filter "${options.auth}" is not supported.`, ['Use one of: required, optional, none.'], { command: 'browse' });
329
+ process.exitCode = 1;
330
+ return { ok: false };
331
+ }
332
+ const controller = options.controller === undefined ? undefined : (0, types_1.parseAppControllerFilter)(options.controller);
333
+ if (options.controller !== undefined && !controller) {
334
+ (0, messages_1.printErrorWithHelp)(`Controller filter "${options.controller}" is not supported.`, ['Use one of: required, optional, none.'], { command: 'browse' });
335
+ process.exitCode = 1;
336
+ return { ok: false };
337
+ }
338
+ const surface = options.surface === undefined ? undefined : (0, types_1.parseAppSurfaceFilter)(options.surface);
339
+ if (options.surface !== undefined && !surface) {
340
+ (0, messages_1.printErrorWithHelp)(`Surface filter "${options.surface}" is not supported.`, ['Use one of: desktop, mobile-portrait, mobile-landscape.'], { command: 'browse' });
341
+ process.exitCode = 1;
342
+ return { ok: false };
343
+ }
344
+ const hasCapabilityFilters = Boolean(auth || controller || surface);
345
+ if (hasCapabilityFilters && kind !== 'app') {
346
+ (0, messages_1.printErrorWithHelp)('Capability filters only support app browse.', ['Use --kind app together with --auth, --surface, or --controller.'], { command: 'browse' });
347
+ process.exitCode = 1;
348
+ return { ok: false };
349
+ }
324
350
  const browseFilters = parseBrowseLimitsAndFilters(options);
325
351
  if (!browseFilters) {
326
352
  return { ok: false };
@@ -341,6 +367,9 @@ function parseBrowseCommandOptions(options) {
341
367
  kind,
342
368
  creator,
343
369
  appType: appType ?? undefined,
370
+ auth: auth ?? undefined,
371
+ controller: controller ?? undefined,
372
+ surface: surface ?? undefined,
344
373
  ...browseFilters,
345
374
  assetSpec: normalizeOptionalOption(options.assetSpec),
346
375
  assetSpecOwner: normalizeOptionalOption(options.assetSpecOwner),
@@ -353,17 +382,33 @@ function parseBrowseCommandOptions(options) {
353
382
  async function loadMergedBrowseResults(input) {
354
383
  const { client, creator, creatorUsername, appType, assetCategory, assetSubcategory, assetSpec, assetSpecOwner, assetSpecName, limit, offset, tags, } = input;
355
384
  const appResultsPromise = creatorUsername
356
- ? collectPaginatedItems((pageLimit, pageOffset) => client.fetchAppsByCreator(creatorUsername, { limit: pageLimit, offset: pageOffset, type: appType, ...(tags.length > 0 ? { tags } : {}) })
385
+ ? collectPaginatedItems((pageLimit, pageOffset) => client.fetchAppsByCreator(creatorUsername, {
386
+ limit: pageLimit,
387
+ offset: pageOffset,
388
+ type: appType,
389
+ ...(tags.length > 0 ? { tags } : {}),
390
+ ...(input.sort ? { sort: input.sort } : {}),
391
+ })
357
392
  .then((response) => ({
358
393
  items: response.apps ?? [],
359
394
  pagination: requirePagination(response.pagination, 'browse_all_creator_apps'),
360
395
  })))
361
396
  : (appType
362
- ? collectPaginatedItems((pageLimit, pageOffset) => client.fetchAppsByType(appType, { limit: pageLimit, offset: pageOffset, ...(tags.length > 0 ? { tags } : {}) }).then((response) => ({
397
+ ? collectPaginatedItems((pageLimit, pageOffset) => client.fetchAppsByType(appType, {
398
+ limit: pageLimit,
399
+ offset: pageOffset,
400
+ ...(tags.length > 0 ? { tags } : {}),
401
+ ...(input.sort ? { sort: input.sort } : {}),
402
+ }).then((response) => ({
363
403
  items: response.apps ?? [],
364
404
  pagination: requirePagination(response.pagination, 'browse_all_type_apps'),
365
405
  })))
366
- : collectPaginatedItems((pageLimit, pageOffset) => client.fetchApps({ limit: pageLimit, offset: pageOffset, ...(tags.length > 0 ? { tags } : {}) }).then((response) => ({
406
+ : collectPaginatedItems((pageLimit, pageOffset) => client.fetchApps({
407
+ limit: pageLimit,
408
+ offset: pageOffset,
409
+ ...(tags.length > 0 ? { tags } : {}),
410
+ ...(input.sort ? { sort: input.sort } : {}),
411
+ }).then((response) => ({
367
412
  items: response.apps ?? [],
368
413
  pagination: requirePagination(response.pagination, 'browse_all_apps'),
369
414
  }))));
@@ -501,23 +546,48 @@ async function runAssetPackBrowseCommand(input) {
501
546
  };
502
547
  }
503
548
  async function runBrowseCommand(input) {
504
- const { client, kind, creator, creatorUsername, appType, limit, offset, tags, } = input;
549
+ const { client, kind, creator, creatorUsername, appType, auth, controller, surface, sort, limit, offset, tags, } = input;
505
550
  if (kind === 'app') {
506
551
  if (creatorUsername) {
507
- const response = await client.fetchAppsByCreator(creatorUsername, { limit, offset, type: appType, ...(tags.length > 0 ? { tags } : {}) });
552
+ const response = await client.fetchAppsByCreator(creatorUsername, {
553
+ limit,
554
+ offset,
555
+ type: appType,
556
+ ...(auth ? { auth } : {}),
557
+ ...(controller ? { controller } : {}),
558
+ ...(surface ? { surface } : {}),
559
+ ...(tags.length > 0 ? { tags } : {}),
560
+ ...(sort ? { sort: sort } : {}),
561
+ });
508
562
  return {
509
563
  items: (response.apps ?? []).map((item) => toBrowseItem('app', item)),
510
564
  pagination: requirePagination(response.pagination, 'browse_app_creator'),
511
565
  };
512
566
  }
513
567
  if (appType) {
514
- const response = await client.fetchAppsByType(appType, { limit, offset, ...(tags.length > 0 ? { tags } : {}) });
568
+ const response = await client.fetchAppsByType(appType, {
569
+ limit,
570
+ offset,
571
+ ...(auth ? { auth } : {}),
572
+ ...(controller ? { controller } : {}),
573
+ ...(surface ? { surface } : {}),
574
+ ...(tags.length > 0 ? { tags } : {}),
575
+ ...(sort ? { sort: sort } : {}),
576
+ });
515
577
  return {
516
578
  items: (response.apps ?? []).map((item) => toBrowseItem('app', item)),
517
579
  pagination: requirePagination(response.pagination, 'browse_app_type'),
518
580
  };
519
581
  }
520
- const response = await client.fetchApps({ limit, offset, ...(tags.length > 0 ? { tags } : {}) });
582
+ const response = await client.fetchApps({
583
+ limit,
584
+ offset,
585
+ ...(auth ? { auth } : {}),
586
+ ...(controller ? { controller } : {}),
587
+ ...(surface ? { surface } : {}),
588
+ ...(tags.length > 0 ? { tags } : {}),
589
+ ...(sort ? { sort: sort } : {}),
590
+ });
521
591
  return {
522
592
  items: (response.apps ?? []).map((item) => toBrowseItem('app', item)),
523
593
  pagination: requirePagination(response.pagination, 'browse_app_all'),
@@ -539,7 +609,7 @@ async function browse(options = {}) {
539
609
  if (!parsed.ok) {
540
610
  return;
541
611
  }
542
- const { kind, creator, appType, assetCategory, assetSubcategory, assetSpec, assetSpecOwner, assetSpecName, sort, limit, offset, tags } = parsed.input;
612
+ const { kind, creator, appType, auth, controller, surface, assetCategory, assetSubcategory, assetSpec, assetSpecOwner, assetSpecName, sort, limit, offset, tags, } = parsed.input;
543
613
  await (0, commandContext_1.withPublicEnvironment)('browse', async ({ client }) => {
544
614
  try {
545
615
  const creatorUsername = await resolveCreatorUsername(creator, 'browse', async () => {
@@ -559,6 +629,9 @@ async function browse(options = {}) {
559
629
  creator,
560
630
  creatorUsername,
561
631
  appType,
632
+ auth,
633
+ controller,
634
+ surface,
562
635
  assetCategory,
563
636
  assetSubcategory,
564
637
  assetSpec,
@@ -27,7 +27,7 @@ async function build(pathOrName) {
27
27
  await (0, apps_1.validateAppTask)(task);
28
28
  await (0, apps_1.buildApp)(task);
29
29
  }
30
- else if (task.kind === 'asset' || task.kind === 'embedded-asset') {
30
+ else if (task.kind === 'asset' || task.kind === 'owned-asset') {
31
31
  const generated = await (0, model_artifacts_1.prepareModel3DAssetArtifacts)(task);
32
32
  if (!generated) {
33
33
  throw new Error(`project build does not support ${task.kind} "${entityId}" because it has no local build step.`);
@@ -4,6 +4,9 @@ type CaptureOptions = {
4
4
  logLevel?: string;
5
5
  screenshotPath?: string;
6
6
  surfaceTarget?: string;
7
+ devAuth?: string;
8
+ player?: string | number;
9
+ resetDevPlayer?: string;
7
10
  username?: string;
8
11
  password?: string;
9
12
  };
@@ -7,11 +7,15 @@ const types_1 = require("@playdrop/types");
7
7
  const http_1 = require("../http");
8
8
  const messages_1 = require("../messages");
9
9
  const catalogue_utils_1 = require("../catalogue-utils");
10
+ const catalogue_1 = require("../catalogue");
10
11
  const devShared_1 = require("./devShared");
11
12
  const devServer_1 = require("./devServer");
12
13
  const commandContext_1 = require("../commandContext");
13
14
  const appUrls_1 = require("../appUrls");
14
15
  const captureRuntime_1 = require("../captureRuntime");
16
+ const devAuth_1 = require("../devAuth");
17
+ const devRuntimeAssets_1 = require("./devRuntimeAssets");
18
+ const dev_1 = require("./dev");
15
19
  const MOBILE_USER_AGENT = 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1';
16
20
  const SURFACE_PRESETS = {
17
21
  desktop: {
@@ -96,22 +100,55 @@ function resolveCaptureLoginOverride(options) {
96
100
  }
97
101
  return { username, password };
98
102
  }
103
+ function resolveCaptureDevOptions(options) {
104
+ const selection = (0, devAuth_1.parseHostedDevAuthSelection)({
105
+ devAuth: options.devAuth,
106
+ player: options.player,
107
+ defaultMode: 'anonymous',
108
+ allowPrompt: false,
109
+ });
110
+ const resetMode = (0, devAuth_1.parseResetDevPlayerMode)(options.resetDevPlayer);
111
+ if (selection.devAuth !== 'player' && resetMode !== 'never') {
112
+ throw new Error('reset_dev_player_requires_player_auth');
113
+ }
114
+ return {
115
+ selection,
116
+ resetMode,
117
+ };
118
+ }
99
119
  // eslint-disable-next-line max-lines-per-function
100
120
  async function capture(targetArg, options = {}) {
101
121
  const timeoutSeconds = (0, captureRuntime_1.validateCaptureTimeout)(options.timeoutSeconds);
102
122
  const timeoutMs = Math.round(timeoutSeconds * 1000);
103
123
  let minimumLogLevel;
104
124
  let loginOverride;
125
+ let devOptions;
105
126
  try {
106
127
  minimumLogLevel = (0, captureRuntime_1.resolveCaptureLogLevel)(options.logLevel);
107
128
  loginOverride = resolveCaptureLoginOverride(options);
129
+ devOptions = resolveCaptureDevOptions(options);
108
130
  }
109
131
  catch (error) {
110
132
  if (error instanceof Error && error.message === 'invalid_credentials_pair') {
111
133
  (0, messages_1.printErrorWithHelp)('Use --username and --password together.', [], { command: 'project capture' });
112
134
  }
135
+ else if (error instanceof Error && error.message === 'dev_auth_prompt_not_supported') {
136
+ (0, messages_1.printErrorWithHelp)('Use --dev-auth anonymous, viewer, or player for deterministic capture.', [], { command: 'project capture' });
137
+ }
138
+ else if (error instanceof Error && error.message === 'dev_auth_player_slot_required') {
139
+ (0, messages_1.printErrorWithHelp)('Use --player 1, 2, 3, or 4 when --dev-auth player is selected.', [], { command: 'project capture' });
140
+ }
141
+ else if (error instanceof Error && error.message === 'reset_dev_player_requires_player_auth') {
142
+ (0, messages_1.printErrorWithHelp)('Use --reset-dev-player only with --dev-auth player.', [], { command: 'project capture' });
143
+ }
144
+ else if (error instanceof Error && error.message === 'reset_dev_player_invalid') {
145
+ (0, messages_1.printErrorWithHelp)('Use --reset-dev-player before, after, before-and-after, or never.', [], { command: 'project capture' });
146
+ }
113
147
  else {
114
- (0, messages_1.printErrorWithHelp)(error?.message || 'Invalid log level.', ['Valid values: debug, info, warn, error'], { command: 'project capture' });
148
+ (0, messages_1.printErrorWithHelp)(error?.message || 'Invalid capture options.', [
149
+ 'Valid log levels: debug, info, warn, error',
150
+ 'Valid --dev-auth values: anonymous, viewer, player',
151
+ ], { command: 'project capture' });
115
152
  }
116
153
  process.exitCode = 1;
117
154
  return;
@@ -203,11 +240,10 @@ async function capture(targetArg, options = {}) {
203
240
  }
204
241
  const projectInfo = (0, devShared_1.findProjectInfo)(filePath);
205
242
  const devScriptAvailable = Boolean(projectInfo.projectDir && projectInfo.packageJson && typeof projectInfo.packageJson.scripts?.dev === 'string');
206
- await (0, commandContext_1.withEnvironment)('project capture', 'Capturing app logs', async ({ client, env, envConfig, token }) => {
207
- let currentUser = null;
243
+ await (0, commandContext_1.withEnvironment)('project capture', 'Capturing app logs', async ({ client, env, envConfig }) => {
208
244
  let currentUsername = '';
209
245
  try {
210
- currentUser = await (0, devShared_1.fetchDevUser)(client);
246
+ const currentUser = await (0, devShared_1.fetchDevUser)(client);
211
247
  currentUsername = currentUser.username.trim();
212
248
  }
213
249
  catch (error) {
@@ -242,8 +278,14 @@ async function capture(targetArg, options = {}) {
242
278
  }
243
279
  throw error;
244
280
  }
281
+ if ((devOptions.selection.devAuth === 'viewer' || devOptions.selection.devAuth === 'player') && !loginOverride) {
282
+ (0, messages_1.printErrorWithHelp)(`Use --username and --password to establish a browser session before running --dev-auth ${devOptions.selection.devAuth}.`, [], { command: 'project capture' });
283
+ process.exitCode = 1;
284
+ return;
285
+ }
286
+ let registeredApp = null;
245
287
  try {
246
- await (0, devShared_1.assertAppRegistered)(client, currentUsername, appName);
288
+ registeredApp = await (0, devShared_1.assertAppRegistered)(client, currentUsername, appName);
247
289
  }
248
290
  catch (error) {
249
291
  if (error instanceof http_1.CLIUnsupportedClientError) {
@@ -302,6 +344,35 @@ async function capture(targetArg, options = {}) {
302
344
  }
303
345
  }
304
346
  };
347
+ let runtimeAssetManifest = (0, devRuntimeAssets_1.createEmptyDevRuntimeAssetManifest)();
348
+ const taskLookup = (0, catalogue_1.findAppTaskByFile)(filePath);
349
+ if (taskLookup.errors.length > 0) {
350
+ (0, messages_1.printErrorWithHelp)(taskLookup.errors[0] || 'Failed to resolve the app task from catalogue.json.', taskLookup.errors.slice(1), { command: 'project capture' });
351
+ process.exitCode = 1;
352
+ return;
353
+ }
354
+ if (taskLookup.task) {
355
+ try {
356
+ runtimeAssetManifest = await (0, devRuntimeAssets_1.buildDevRuntimeAssetManifest)({
357
+ client,
358
+ apiBase: envConfig.apiBase,
359
+ task: taskLookup.task,
360
+ creatorUsername: currentUsername,
361
+ appBaseUrl: new URL('.', (0, devServer_1.buildLocalDevAppUrl)({
362
+ creatorUsername: currentUsername,
363
+ appType: appTypeSlug,
364
+ appName,
365
+ port: devServer_1.DEV_ROUTER_PORT,
366
+ })).toString(),
367
+ });
368
+ }
369
+ catch (error) {
370
+ const formatted = (0, dev_1.formatDevRuntimeAssetManifestFailure)(error);
371
+ (0, messages_1.printErrorWithHelp)(formatted.message, formatted.suggestions, { command: 'project capture' });
372
+ process.exitCode = 1;
373
+ return;
374
+ }
375
+ }
305
376
  if (serverAlreadyRunning) {
306
377
  console.log(`[capture] Reusing dev server at ${(0, devServer_1.buildLocalDevAppUrl)({
307
378
  creatorUsername: currentUsername,
@@ -309,6 +380,21 @@ async function capture(targetArg, options = {}) {
309
380
  appName,
310
381
  port: devServer_1.DEV_ROUTER_PORT,
311
382
  })}`);
383
+ try {
384
+ await (0, devServer_1.updateMountedDevRuntimeAssetManifest)({
385
+ creatorUsername: currentUsername,
386
+ appName,
387
+ runtimeAssetManifest,
388
+ port: devServer_1.DEV_ROUTER_PORT,
389
+ });
390
+ }
391
+ catch (error) {
392
+ (0, messages_1.printErrorWithHelp)(error?.message || 'Failed to refresh the local runtime asset manifest on the shared dev router.', [
393
+ 'Restart "playdrop project dev" for this app and try capture again.',
394
+ ], { command: 'project capture' });
395
+ process.exitCode = 1;
396
+ return;
397
+ }
312
398
  }
313
399
  else {
314
400
  try {
@@ -319,6 +405,7 @@ async function capture(targetArg, options = {}) {
319
405
  htmlPath: filePath,
320
406
  port: devServer_1.DEV_ROUTER_PORT,
321
407
  projectInfo,
408
+ runtimeAssetManifest,
322
409
  });
323
410
  signalHandler = () => {
324
411
  void cleanup().finally(() => process.exit(130));
@@ -355,8 +442,22 @@ async function capture(targetArg, options = {}) {
355
442
  await new Promise(resolve => setTimeout(resolve, 1000));
356
443
  }
357
444
  const webBase = envConfig.webBase ?? 'https://www.playdrop.ai';
358
- const frameUrl = `${webBase}/creators/${encodeURIComponent(currentUsername)}/apps/${appTypeSlug}/${encodeURIComponent(appName)}/dev`;
445
+ const frameUrl = (0, devAuth_1.applyHostedDevAuthSelectionToUrl)(`${webBase}/creators/${encodeURIComponent(currentUsername)}/apps/${appTypeSlug}/${encodeURIComponent(appName)}/dev`, devOptions.selection);
359
446
  console.log(`[capture] Launching Playwright against ${frameUrl}`);
447
+ const shouldResetBefore = devOptions.selection.devAuth === 'player'
448
+ && (devOptions.resetMode === 'before' || devOptions.resetMode === 'before-and-after');
449
+ const shouldResetAfter = devOptions.selection.devAuth === 'player'
450
+ && (devOptions.resetMode === 'after' || devOptions.resetMode === 'before-and-after');
451
+ const selectedPlayerSlot = devOptions.selection.player;
452
+ const registeredAppId = typeof registeredApp.id === 'number' ? registeredApp.id : null;
453
+ if ((shouldResetBefore || shouldResetAfter) && registeredAppId === null) {
454
+ (0, messages_1.printErrorWithHelp)('The registered app is missing a numeric id, so test-player reset is unavailable.', [], { command: 'project capture' });
455
+ process.exitCode = 1;
456
+ return;
457
+ }
458
+ if (shouldResetBefore && selectedPlayerSlot !== null) {
459
+ await client.resetDevPlayer(registeredAppId, selectedPlayerSlot);
460
+ }
360
461
  try {
361
462
  const result = await (0, captureRuntime_1.runCapture)({
362
463
  targetUrl: frameUrl,
@@ -365,9 +466,11 @@ async function capture(targetArg, options = {}) {
365
466
  minimumLogLevel,
366
467
  screenshotPath,
367
468
  contextOptions: surfaceContextOptions,
368
- token: loginOverride ? undefined : token,
369
- user: loginOverride ? undefined : currentUser,
370
- login: loginOverride,
469
+ login: loginOverride ? {
470
+ username: loginOverride.username,
471
+ password: loginOverride.password,
472
+ } : undefined,
473
+ savedSessionBootstrap: false,
371
474
  enableCaptureBridge: true,
372
475
  });
373
476
  if (result.errorCount > 0) {
@@ -396,6 +499,15 @@ async function capture(targetArg, options = {}) {
396
499
  process.exitCode = 1;
397
500
  }
398
501
  finally {
502
+ if (shouldResetAfter && selectedPlayerSlot !== null) {
503
+ try {
504
+ await client.resetDevPlayer(registeredAppId, selectedPlayerSlot);
505
+ }
506
+ catch (error) {
507
+ console.error(error instanceof Error ? error.message : String(error));
508
+ process.exitCode = 1;
509
+ }
510
+ }
399
511
  if (signalHandler) {
400
512
  process.off('SIGINT', signalHandler);
401
513
  process.off('SIGTERM', signalHandler);
@@ -45,6 +45,8 @@ type CropRect = {
45
45
  width: number;
46
46
  height: number;
47
47
  };
48
+ export declare function resolveListingCaptureRouteSegment(previewable: boolean): 'dev' | 'dev-preview';
49
+ export declare function resolveListingCaptureSceneId(width: number, height: number): 'listing-landscape' | 'listing-portrait';
48
50
  export declare function parseCaptureListingOptions(targetArg: string | undefined, options?: CaptureListingOptions): ParsedCaptureListingOptions;
49
51
  export declare function assertSupportedListingEnvironment(platform?: NodeJS.Platform, macosVersion?: string): void;
50
52
  export declare function resolveListingRecorderPath(baseDir?: string): string;