@playdrop/playdrop-cli 0.5.5 → 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 (96) 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 +43 -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/apps.d.ts +11 -19
  51. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
  52. package/node_modules/@playdrop/api-client/dist/domains/apps.js +116 -106
  53. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +2 -1
  54. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
  55. package/node_modules/@playdrop/api-client/dist/domains/assets.js +13 -0
  56. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +5 -5
  57. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
  58. package/node_modules/@playdrop/api-client/dist/domains/payments.js +8 -8
  59. package/node_modules/@playdrop/api-client/dist/domains/search.d.ts.map +1 -1
  60. package/node_modules/@playdrop/api-client/dist/domains/search.js +24 -2
  61. package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts +13 -1
  62. package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts.map +1 -1
  63. package/node_modules/@playdrop/api-client/dist/domains/tags.js +52 -0
  64. package/node_modules/@playdrop/api-client/dist/index.d.ts +25 -29
  65. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  66. package/node_modules/@playdrop/api-client/dist/index.js +23 -8
  67. package/node_modules/@playdrop/api-client/package.json +1 -1
  68. package/node_modules/@playdrop/boxel-core/package.json +1 -1
  69. package/node_modules/@playdrop/boxel-three/package.json +1 -1
  70. package/node_modules/@playdrop/config/client-meta.json +4 -4
  71. package/node_modules/@playdrop/config/package.json +1 -1
  72. package/node_modules/@playdrop/types/dist/api.d.ts +124 -3
  73. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  74. package/node_modules/@playdrop/types/dist/api.js +23 -0
  75. package/node_modules/@playdrop/types/dist/app-capability-filters.d.ts +24 -0
  76. package/node_modules/@playdrop/types/dist/app-capability-filters.d.ts.map +1 -0
  77. package/node_modules/@playdrop/types/dist/app-capability-filters.js +72 -0
  78. package/node_modules/@playdrop/types/dist/asset-pack.d.ts +3 -2
  79. package/node_modules/@playdrop/types/dist/asset-pack.d.ts.map +1 -1
  80. package/node_modules/@playdrop/types/dist/asset.d.ts +2 -3
  81. package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
  82. package/node_modules/@playdrop/types/dist/asset.js +1 -1
  83. package/node_modules/@playdrop/types/dist/index.d.ts +2 -0
  84. package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
  85. package/node_modules/@playdrop/types/dist/index.js +2 -0
  86. package/node_modules/@playdrop/types/dist/owned-assets.d.ts +21 -0
  87. package/node_modules/@playdrop/types/dist/owned-assets.d.ts.map +1 -0
  88. package/node_modules/@playdrop/types/dist/owned-assets.js +35 -0
  89. package/node_modules/@playdrop/types/dist/player-meta.d.ts +28 -0
  90. package/node_modules/@playdrop/types/dist/player-meta.d.ts.map +1 -1
  91. package/node_modules/@playdrop/types/dist/version.d.ts +111 -1
  92. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  93. package/node_modules/@playdrop/types/dist/version.js +3 -0
  94. package/node_modules/@playdrop/types/package.json +1 -1
  95. package/node_modules/@playdrop/vox-three/package.json +1 -1
  96. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ /* eslint-disable max-lines */
2
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
4
  if (k2 === undefined) k2 = k;
4
5
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -33,6 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
33
34
  };
34
35
  })();
35
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.resolveListingCaptureRouteSegment = resolveListingCaptureRouteSegment;
38
+ exports.resolveListingCaptureSceneId = resolveListingCaptureSceneId;
36
39
  exports.parseCaptureListingOptions = parseCaptureListingOptions;
37
40
  exports.assertSupportedListingEnvironment = assertSupportedListingEnvironment;
38
41
  exports.resolveListingRecorderPath = resolveListingRecorderPath;
@@ -47,7 +50,6 @@ const commandContext_1 = require("../commandContext");
47
50
  const http_1 = require("../http");
48
51
  const messages_1 = require("../messages");
49
52
  const playwright_1 = require("../playwright");
50
- const sessionCookie_1 = require("../sessionCookie");
51
53
  const devShared_1 = require("./devShared");
52
54
  const devServer_1 = require("./devServer");
53
55
  const appUrls_1 = require("../appUrls");
@@ -63,6 +65,12 @@ const MIN_DIMENSION = 16;
63
65
  const MAX_DIMENSION = 8192;
64
66
  const MAX_FPS = 120;
65
67
  const SUPPORTED_MACOS_PREFIX = '26.4';
68
+ function resolveListingCaptureRouteSegment(previewable) {
69
+ return previewable ? 'dev-preview' : 'dev';
70
+ }
71
+ function resolveListingCaptureSceneId(width, height) {
72
+ return height > width ? 'listing-portrait' : 'listing-landscape';
73
+ }
66
74
  function parsePositiveNumber(raw, fallback, fieldName, { minimum = 0, maximum = Number.POSITIVE_INFINITY, integer = false, } = {}) {
67
75
  if (raw === undefined) {
68
76
  return fallback;
@@ -223,30 +231,6 @@ async function launchListingBrowser(initialViewport) {
223
231
  throw (0, playwright_1.createPlaywrightLaunchError)('playdrop', error);
224
232
  }
225
233
  }
226
- async function bootstrapAuthState(context, page, targetUrl, token, user) {
227
- const cookies = (0, sessionCookie_1.buildCaptureAccessTokenCookies)(targetUrl, token);
228
- await context.addCookies(cookies);
229
- await page.addInitScript(({ bootstrapToken, bootstrapUser }) => {
230
- try {
231
- window.localStorage.setItem('playdrop.accessToken', bootstrapToken);
232
- }
233
- catch {
234
- // ignore storage failures
235
- }
236
- try {
237
- window.localStorage.setItem('playdrop.user', JSON.stringify(bootstrapUser));
238
- }
239
- catch {
240
- // ignore storage failures
241
- }
242
- try {
243
- window.sessionStorage.removeItem('playdrop.logoutReason');
244
- }
245
- catch {
246
- // ignore storage failures
247
- }
248
- }, { bootstrapToken: token, bootstrapUser: user });
249
- }
250
234
  async function waitForHostedGameMeasurement(page, timeoutMs) {
251
235
  const startedAt = Date.now();
252
236
  let lastSignature = '';
@@ -310,6 +294,74 @@ async function waitForHostedGameMeasurement(page, timeoutMs) {
310
294
  }
311
295
  throw new Error('hosted_game_measurement_timeout');
312
296
  }
297
+ async function waitForHostedGameFrame(page, timeoutMs) {
298
+ const frameHandle = await page.waitForSelector(CAPTURE_FRAME_SELECTOR, {
299
+ state: 'attached',
300
+ timeout: timeoutMs,
301
+ });
302
+ const startedAt = Date.now();
303
+ while (Date.now() - startedAt < timeoutMs) {
304
+ const frame = await frameHandle.contentFrame();
305
+ if (frame) {
306
+ return frame;
307
+ }
308
+ await page.waitForTimeout(200);
309
+ }
310
+ throw new Error('hosted_game_frame_missing');
311
+ }
312
+ async function prepareHostedListingScene(page, sceneId) {
313
+ const frame = await waitForHostedGameFrame(page, 20000);
314
+ await frame.waitForFunction(() => {
315
+ const captureWindow = window;
316
+ return typeof captureWindow.__listingCapture?.prepare === 'function';
317
+ }, undefined, { timeout: 20000 });
318
+ await frame.evaluate(async (requestedSceneId) => {
319
+ const captureWindow = window;
320
+ const hook = captureWindow.__listingCapture;
321
+ if (!hook || typeof hook.prepare !== 'function') {
322
+ throw new Error('listing_capture_hook_missing');
323
+ }
324
+ await hook.prepare(requestedSceneId);
325
+ }, sceneId);
326
+ }
327
+ async function startHostedListingAudioCapture(page) {
328
+ const frame = await waitForHostedGameFrame(page, 20000);
329
+ await frame.waitForFunction(() => {
330
+ const captureWindow = window;
331
+ return typeof captureWindow.__listingCapture?.startAudioCapture === 'function';
332
+ }, undefined, { timeout: 20000 });
333
+ await frame.evaluate(async () => {
334
+ const captureWindow = window;
335
+ const hook = captureWindow.__listingCapture;
336
+ if (!hook || typeof hook.startAudioCapture !== 'function') {
337
+ throw new Error('listing_audio_capture_hook_missing');
338
+ }
339
+ await hook.startAudioCapture();
340
+ });
341
+ }
342
+ function resolveExportedAudioFileName(mimeType) {
343
+ if (mimeType.includes('webm')) {
344
+ return 'listing-audio.webm';
345
+ }
346
+ if (mimeType.includes('mp4') || mimeType.includes('aac') || mimeType.includes('m4a')) {
347
+ return 'listing-audio.m4a';
348
+ }
349
+ throw new Error(`unsupported_listing_audio_mime_type:${mimeType}`);
350
+ }
351
+ async function stopHostedListingAudioCapture(page, outputDir) {
352
+ const frame = await waitForHostedGameFrame(page, 20000);
353
+ const exportedAudio = await frame.evaluate(async () => {
354
+ const captureWindow = window;
355
+ const hook = captureWindow.__listingCapture;
356
+ if (!hook || typeof hook.stopAudioCapture !== 'function') {
357
+ throw new Error('listing_audio_capture_hook_missing');
358
+ }
359
+ return hook.stopAudioCapture();
360
+ });
361
+ const filePath = (0, node_path_1.join)(outputDir, resolveExportedAudioFileName(exportedAudio.mimeType));
362
+ await (0, promises_1.writeFile)(filePath, Buffer.from(exportedAudio.base64, 'base64'));
363
+ return filePath;
364
+ }
313
365
  async function fitWindowToRequestedGameplay(page, requestedWidth, requestedHeight) {
314
366
  const cdpSession = await page.context().newCDPSession(page);
315
367
  let measurement = await waitForHostedGameMeasurement(page, 15000);
@@ -531,6 +583,23 @@ async function encodeListingVideo(rawVideoPath, finalVideoPath, posterPath, crop
531
583
  posterPath,
532
584
  ], 'ffmpeg_failed');
533
585
  }
586
+ async function muxListingAudio(finalVideoPath, audioPath) {
587
+ const muxedVideoPath = `${finalVideoPath}.muxed.mp4`;
588
+ runTool('ffmpeg', [
589
+ '-y',
590
+ '-i',
591
+ finalVideoPath,
592
+ '-i',
593
+ audioPath,
594
+ '-c:v',
595
+ 'copy',
596
+ '-c:a',
597
+ 'aac',
598
+ '-shortest',
599
+ muxedVideoPath,
600
+ ], 'ffmpeg_failed');
601
+ await (0, promises_1.rename)(muxedVideoPath, finalVideoPath);
602
+ }
534
603
  function ensureTolerance(actual, expected, tolerance, failureCode) {
535
604
  if (Math.abs(actual - expected) > tolerance) {
536
605
  throw new Error(`${failureCode}:${actual}:${expected}`);
@@ -544,44 +613,30 @@ function createWarnings(finalProbe, requestedAudio) {
544
613
  }
545
614
  return warnings;
546
615
  }
547
- async function captureListing(targetArg, options = {}) {
548
- let parsedOptions;
549
- try {
550
- parsedOptions = parseCaptureListingOptions(targetArg, options);
551
- assertSupportedListingEnvironment();
552
- }
553
- catch (error) {
554
- const detail = formatCommandError(error instanceof Error ? error : new Error(String(error)));
555
- (0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: 'project capture listing' });
556
- process.exitCode = 1;
557
- return;
558
- }
559
- if (!commandExists('ffmpeg')) {
560
- (0, messages_1.printErrorWithHelp)('ffmpeg is required for listing capture.', ['Install ffmpeg and make sure it is available on PATH.'], {
561
- command: 'project capture listing',
562
- });
563
- process.exitCode = 1;
564
- return;
616
+ function ensureCaptureListingToolAvailable(command) {
617
+ if (commandExists(command)) {
618
+ return true;
565
619
  }
566
- if (!commandExists('ffprobe')) {
567
- (0, messages_1.printErrorWithHelp)('ffprobe is required for listing capture.', ['Install ffprobe and make sure it is available on PATH.'], {
568
- command: 'project capture listing',
569
- });
570
- process.exitCode = 1;
571
- return;
572
- }
573
- const recorderPath = resolveListingRecorderPath();
620
+ (0, messages_1.printErrorWithHelp)(`${command} is required for listing capture.`, [`Install ${command} and make sure it is available on PATH.`], {
621
+ command: 'project capture listing',
622
+ });
623
+ process.exitCode = 1;
624
+ return false;
625
+ }
626
+ async function ensureListingRecorderAvailable(recorderPath) {
574
627
  try {
575
628
  await ensureExecutableFile(recorderPath);
629
+ return true;
576
630
  }
577
631
  catch {
578
632
  (0, messages_1.printErrorWithHelp)(`Native recorder binary was not found at ${recorderPath}.`, ['Build it with: (cd clients/apple/playdrop-listing-recorder && swift build -c release)'], { command: 'project capture listing' });
579
633
  process.exitCode = 1;
580
- return;
634
+ return false;
581
635
  }
582
- let resolvedTarget;
636
+ }
637
+ function resolveCaptureListingTarget(parsedOptions) {
583
638
  try {
584
- resolvedTarget = (0, devShared_1.resolveDevTarget)(parsedOptions.targetArg, parsedOptions.appName);
639
+ return (0, devShared_1.resolveDevTarget)(parsedOptions.targetArg, parsedOptions.appName);
585
640
  }
586
641
  catch (error) {
587
642
  const code = error?.code;
@@ -593,6 +648,30 @@ async function captureListing(targetArg, options = {}) {
593
648
  : ['Provide a valid app target or HTML file path.'];
594
649
  (0, messages_1.printErrorWithHelp)(message, suggestions, { command: 'project capture listing' });
595
650
  process.exitCode = 1;
651
+ return null;
652
+ }
653
+ }
654
+ async function captureListing(targetArg, options = {}) {
655
+ let parsedOptions;
656
+ try {
657
+ parsedOptions = parseCaptureListingOptions(targetArg, options);
658
+ assertSupportedListingEnvironment();
659
+ }
660
+ catch (error) {
661
+ const detail = formatCommandError(error instanceof Error ? error : new Error(String(error)));
662
+ (0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: 'project capture listing' });
663
+ process.exitCode = 1;
664
+ return;
665
+ }
666
+ if (!ensureCaptureListingToolAvailable('ffmpeg') || !ensureCaptureListingToolAvailable('ffprobe')) {
667
+ return;
668
+ }
669
+ const recorderPath = resolveListingRecorderPath();
670
+ if (!await ensureListingRecorderAvailable(recorderPath)) {
671
+ return;
672
+ }
673
+ const resolvedTarget = resolveCaptureListingTarget(parsedOptions);
674
+ if (!resolvedTarget) {
596
675
  return;
597
676
  }
598
677
  let appName = resolvedTarget.appName;
@@ -609,11 +688,11 @@ async function captureListing(targetArg, options = {}) {
609
688
  const projectInfo = (0, devShared_1.findProjectInfo)(resolvedTarget.htmlPath);
610
689
  const devScriptAvailable = Boolean(projectInfo.projectDir && projectInfo.packageJson && typeof projectInfo.packageJson.scripts?.dev === 'string');
611
690
  // eslint-disable-next-line complexity
612
- await (0, commandContext_1.withEnvironment)('project capture listing', 'Capturing listing media', async ({ client, env, envConfig, token }) => {
613
- let currentUser = null;
691
+ await (0, commandContext_1.withEnvironment)('project capture listing', 'Capturing listing media', async ({ client, env, envConfig }) => {
614
692
  let currentUsername = '';
693
+ let registeredApp = null;
615
694
  try {
616
- currentUser = await (0, devShared_1.fetchDevUser)(client);
695
+ const currentUser = await (0, devShared_1.fetchDevUser)(client);
617
696
  currentUsername = currentUser.username.trim();
618
697
  }
619
698
  catch (error) {
@@ -641,7 +720,7 @@ async function captureListing(targetArg, options = {}) {
641
720
  throw error;
642
721
  }
643
722
  try {
644
- await (0, devShared_1.assertAppRegistered)(client, currentUsername, appName);
723
+ registeredApp = await (0, devShared_1.assertAppRegistered)(client, currentUsername, appName);
645
724
  }
646
725
  catch (error) {
647
726
  if (error instanceof http_1.CLIUnsupportedClientError) {
@@ -717,18 +796,32 @@ async function captureListing(targetArg, options = {}) {
717
796
  await new Promise(resolve => setTimeout(resolve, 1000));
718
797
  }
719
798
  const webBase = envConfig.webBase ?? 'https://www.playdrop.ai';
720
- const frameUrl = `${webBase}/creators/${encodeURIComponent(currentUsername)}/apps/${appTypeSlug}/${encodeURIComponent(appName)}/dev`;
799
+ const routeSegment = resolveListingCaptureRouteSegment(Boolean(registeredApp?.previewable));
800
+ const frameUrl = `${webBase}/creators/${encodeURIComponent(currentUsername)}/apps/${appTypeSlug}/${encodeURIComponent(appName)}/${routeSegment}`;
801
+ const captureSceneId = resolveListingCaptureSceneId(parsedOptions.width, parsedOptions.height);
802
+ const shouldExportPreviewAudio = parsedOptions.audio && routeSegment === 'dev-preview';
721
803
  browserHandle = await launchListingBrowser({
722
804
  width: parsedOptions.width,
723
805
  height: parsedOptions.height,
724
806
  });
725
- await bootstrapAuthState(browserHandle.context, browserHandle.page, frameUrl, token, currentUser);
726
807
  await browserHandle.page.goto(frameUrl, { waitUntil: 'domcontentloaded' });
727
808
  await browserHandle.page.waitForTimeout(1000);
809
+ if (routeSegment === 'dev-preview') {
810
+ console.log(`[listing] Preparing preview scene ${captureSceneId}.`);
811
+ await prepareHostedListingScene(browserHandle.page, captureSceneId);
812
+ await browserHandle.page.waitForTimeout(1000);
813
+ }
814
+ if (shouldExportPreviewAudio) {
815
+ console.log('[listing] Starting in-app preview audio capture.');
816
+ await startHostedListingAudioCapture(browserHandle.page);
817
+ }
728
818
  const measurement = await fitWindowToRequestedGameplay(browserHandle.page, parsedOptions.width, parsedOptions.height);
729
819
  console.log(`[listing] Gameplay frame ${Math.round(measurement.iframeRect.width)}x${Math.round(measurement.iframeRect.height)} in window ${Math.round(measurement.outerWidth)}x${Math.round(measurement.outerHeight)}.`);
730
820
  await browserHandle.page.waitForTimeout(750);
731
- const recorderMetadata = await runListingRecorder(recorderPath, browserHandle.processId, parsedOptions.durationSeconds, outputPaths.rawVideoPath, outputPaths.metadataPath, parsedOptions.audio);
821
+ const recorderMetadata = await runListingRecorder(recorderPath, browserHandle.processId, parsedOptions.durationSeconds, outputPaths.rawVideoPath, outputPaths.metadataPath, shouldExportPreviewAudio ? false : parsedOptions.audio);
822
+ const exportedAudioPath = shouldExportPreviewAudio
823
+ ? await stopHostedListingAudioCapture(browserHandle.page, outputPaths.outputDir)
824
+ : null;
732
825
  const rawProbe = await probeMediaFile(outputPaths.rawVideoPath);
733
826
  const rawVideoStream = extractVideoStream(rawProbe);
734
827
  const rawDurationSeconds = readDurationSeconds(rawProbe);
@@ -736,6 +829,9 @@ async function captureListing(targetArg, options = {}) {
736
829
  const crop = computeRecordedCrop(measurement, rawVideoStream.width, rawVideoStream.height);
737
830
  console.log(`[listing] Cropping raw capture to gameplay at ${crop.width}x${crop.height}+${crop.x}+${crop.y}.`);
738
831
  await encodeListingVideo(outputPaths.rawVideoPath, outputPaths.finalVideoPath, outputPaths.posterPath, crop, parsedOptions.width, parsedOptions.height, parsedOptions.fps, parsedOptions.posterAtSeconds);
832
+ if (exportedAudioPath) {
833
+ await muxListingAudio(outputPaths.finalVideoPath, exportedAudioPath);
834
+ }
739
835
  const finalProbe = await probeMediaFile(outputPaths.finalVideoPath);
740
836
  const finalVideoStream = extractVideoStream(finalProbe);
741
837
  const finalDurationSeconds = readDurationSeconds(finalProbe);
@@ -12,10 +12,8 @@ const node_path_1 = require("node:path");
12
12
  const node_child_process_1 = require("node:child_process");
13
13
  const fflate_1 = require("fflate");
14
14
  const types_1 = require("@playdrop/types");
15
- const config_1 = require("../config");
16
- const apiClient_1 = require("../apiClient");
15
+ const commandContext_1 = require("../commandContext");
17
16
  const http_1 = require("../http");
18
- const environment_1 = require("../environment");
19
17
  const messages_1 = require("../messages");
20
18
  const init_1 = require("./init");
21
19
  const app_1 = require("@playdrop/types/app");
@@ -635,7 +633,7 @@ function buildPendingExternalRemixTask(name, metadata, sourceInfo, cataloguePath
635
633
  remix: typeof entry.remix === 'string' ? entry.remix : undefined,
636
634
  achievements: [],
637
635
  leaderboards: [],
638
- embeddedAssets: [],
636
+ ownedAssets: [],
639
637
  uses: { assets: [], packs: [] },
640
638
  graph: { relations: [] },
641
639
  };
@@ -1047,31 +1045,11 @@ async function create(name, options = {}) {
1047
1045
  }
1048
1046
  const templateOption = templateOptionRaw || undefined;
1049
1047
  const remixOption = remixOptionRaw || undefined;
1050
- const cfg = (0, config_1.loadConfig)();
1051
- const envName = cfg.env;
1052
- if (!envName) {
1053
- (0, messages_1.printLoginRequired)('Creating an app', 'project create app');
1054
- process.exitCode = 1;
1055
- return;
1056
- }
1057
- const envConfig = (0, environment_1.resolveEnvironmentConfig)(envName);
1058
- if (!envConfig) {
1059
- const choices = (0, environment_1.formatEnvironmentList)();
1060
- (0, messages_1.printErrorWithHelp)(`Environment "${envName}" from your Playdrop config is not supported.`, [
1061
- `Available environments: ${choices}.`,
1062
- 'Run "playdrop auth login --env <env>" to save a supported environment before creating apps.'
1063
- ], { command: 'project create app', includeGeneralHelp: false });
1064
- process.exitCode = 1;
1048
+ const ctx = await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)('project create app', 'Creating an app', { workspacePath: process.cwd() });
1049
+ if (!ctx) {
1065
1050
  return;
1066
1051
  }
1067
- if (!cfg.token) {
1068
- (0, messages_1.printLoginRequired)('Creating an app', 'project create app');
1069
- process.exitCode = 1;
1070
- return;
1071
- }
1072
- if (envConfig.allowInsecureRequests) {
1073
- process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
1074
- }
1052
+ const envName = ctx.env;
1075
1053
  const currentDir = process.cwd();
1076
1054
  const localCataloguePath = (0, node_path_1.resolve)(currentDir, CATALOGUE_FILENAME);
1077
1055
  let initSummary = null;
@@ -1080,7 +1058,7 @@ async function create(name, options = {}) {
1080
1058
  let projectCatalogueResult = null;
1081
1059
  let cataloguePathToUse = null;
1082
1060
  let projectCataloguePath = null;
1083
- const client = (0, apiClient_1.createCliApiClient)({ baseUrl: envConfig.apiBase, token: cfg.token });
1061
+ const client = ctx.client;
1084
1062
  const sourceInfo = {};
1085
1063
  let remixSourceTarget = null;
1086
1064
  let scaffold = null;
@@ -6,9 +6,7 @@ exports.createRemixContent = createRemixContent;
6
6
  const node_fs_1 = require("node:fs");
7
7
  const node_path_1 = require("node:path");
8
8
  const types_1 = require("@playdrop/types");
9
- const apiClient_1 = require("../apiClient");
10
- const config_1 = require("../config");
11
- const environment_1 = require("../environment");
9
+ const commandContext_1 = require("../commandContext");
12
10
  const http_1 = require("../http");
13
11
  const messages_1 = require("../messages");
14
12
  const init_1 = require("./init");
@@ -104,31 +102,11 @@ function assertWorkspaceRootCanOwnEntries(cataloguePath) {
104
102
  }
105
103
  }
106
104
  async function createAuthenticatedClient(commandLabel) {
107
- const cfg = (0, config_1.loadConfig)();
108
- const envName = cfg.env;
109
- if (!envName) {
110
- (0, messages_1.printLoginRequired)(commandLabel, commandLabel);
111
- process.exitCode = 1;
112
- throw new Error('missing_env');
113
- }
114
- const envConfig = (0, environment_1.resolveEnvironmentConfig)(envName);
115
- if (!envConfig) {
116
- (0, messages_1.printErrorWithHelp)(`Environment "${envName}" from your Playdrop config is not supported.`, [
117
- `Available environments: ${(0, environment_1.formatEnvironmentList)()}.`,
118
- 'Run "playdrop auth login --env <env>" to save a supported environment before retrying.',
119
- ], { command: commandLabel, includeGeneralHelp: false });
120
- process.exitCode = 1;
121
- throw new Error('unsupported_env');
122
- }
123
- if (!cfg.token) {
124
- (0, messages_1.printLoginRequired)(commandLabel, commandLabel);
125
- process.exitCode = 1;
105
+ const ctx = await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)(commandLabel, commandLabel, { workspacePath: process.cwd() });
106
+ if (!ctx) {
126
107
  throw new Error('missing_token');
127
108
  }
128
- if (envConfig.allowInsecureRequests) {
129
- process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
130
- }
131
- return (0, apiClient_1.createCliApiClient)({ baseUrl: envConfig.apiBase, token: cfg.token });
109
+ return ctx.client;
132
110
  }
133
111
  function ensureUniqueEntryName(catalogue, kind, name) {
134
112
  const entries = kind === 'asset' ? catalogue.assets : catalogue.assetPacks;
@@ -105,11 +105,10 @@ function buildCountsSuffix(item) {
105
105
  }
106
106
  parts.push(`launch ${formatCountValue(item.item.playCount)}`);
107
107
  }
108
- if (item.kind === 'asset' && 'playCount' in item.item && typeof item.item.playCount === 'number') {
109
- if (typeof item.item.viewCount === 'number') {
108
+ if (item.kind === 'asset') {
109
+ if ('viewCount' in item.item && typeof item.item.viewCount === 'number') {
110
110
  parts.push(`view ${formatCountValue(item.item.viewCount)}`);
111
111
  }
112
- parts.push(`play ${formatCountValue(item.item.playCount)}`);
113
112
  }
114
113
  if (item.kind === 'asset-pack' && 'downloadCount' in item.item && typeof item.item.downloadCount === 'number') {
115
114
  parts.push(`download ${formatCountValue(item.item.downloadCount)}`);
@@ -42,9 +42,10 @@ function extractAssetFileUrls(asset) {
42
42
  .filter((entry) => typeof entry?.role === 'string' && typeof entry?.url === 'string' && entry.url.trim().length > 0)
43
43
  .map((entry) => ({ role: entry.role, url: entry.url.trim() }));
44
44
  }
45
- function decorateApp(app, webBase) {
45
+ function decorateApp(app, webBase, dependencies) {
46
46
  return {
47
47
  ...app,
48
+ dependencies,
48
49
  urls: {
49
50
  play: (0, appUrls_1.buildPlatformPlayUrl)(webBase, {
50
51
  creatorUsername: app.creatorUsername,
@@ -80,6 +81,16 @@ function formatTagSummary(tags) {
80
81
  }
81
82
  return normalizedTags.map((tag) => `${tag.ref} (${tag.displayName})`).join(', ');
82
83
  }
84
+ function printAppDependencyLines(label, items) {
85
+ if (items.length === 0) {
86
+ console.log(`${label}: none`);
87
+ return;
88
+ }
89
+ console.log(`${label}:`);
90
+ for (const item of items) {
91
+ console.log(`- ${item}`);
92
+ }
93
+ }
83
94
  function printAppText(ref, app) {
84
95
  console.log(`[app] ${ref}`);
85
96
  console.log(`Display name: ${(0, output_1.formatOptionalValue)(app.displayName)}`);
@@ -90,6 +101,17 @@ function printAppText(ref, app) {
90
101
  console.log(`Updated: ${(0, output_1.formatTimestamp)(app.updatedAt)}`);
91
102
  console.log(`Current version: ${(0, output_1.formatOptionalValue)(app.currentVersion)}`);
92
103
  console.log(`Tags: ${formatTagSummary(app.tags)}`);
104
+ console.log(`Owned assets: ${app.dependencies?.ownedAssets.length ?? 0}`);
105
+ console.log(`Direct assets: ${app.dependencies?.directAssets.length ?? 0}`);
106
+ console.log(`Packs: ${app.dependencies?.packs.length ?? 0}`);
107
+ printAppDependencyLines('Registered assets', (app.dependencies?.ownedAssets ?? []).map((item) => {
108
+ const runtimeKey = typeof item.runtimeKey === 'string' && item.runtimeKey.trim().length > 0
109
+ ? `runtimeKey=${item.runtimeKey} `
110
+ : '';
111
+ return `${runtimeKey}${item.assetRef}`;
112
+ }));
113
+ printAppDependencyLines('Linked assets', (app.dependencies?.directAssets ?? []).map((item) => item.assetRef));
114
+ printAppDependencyLines('Linked packs', (app.dependencies?.packs ?? []).map((item) => item.packRef));
93
115
  console.log(`Play URL: ${(0, output_1.formatOptionalValue)(app.urls.play)}`);
94
116
  console.log(`Source URL: ${(0, output_1.formatOptionalValue)(app.urls.source)}`);
95
117
  console.log('\nNext: run "playdrop versions browse ' + ref + '" to inspect version history.');
@@ -156,7 +178,7 @@ async function detail(rawRef, options = {}) {
156
178
  try {
157
179
  if (ref.kind === 'app') {
158
180
  const response = await client.fetchAppBySlug(ref.creator, ref.name);
159
- const item = decorateApp(response.app, envConfig.webBase);
181
+ const item = decorateApp(response.app, envConfig.webBase, response.dependencies);
160
182
  if (options.json) {
161
183
  (0, output_1.printJson)({ kind: ref.kind, ref: ref.ref, item });
162
184
  return;
@@ -1 +1,8 @@
1
- export declare function dev(targetArg: string | undefined, _port?: number, appOption?: string): Promise<void>;
1
+ export declare function formatDevRuntimeAssetManifestFailure(error: unknown): {
2
+ message: string;
3
+ suggestions: string[];
4
+ };
5
+ export declare function dev(targetArg: string | undefined, _port?: number, appOption?: string, devOptions?: {
6
+ devAuth?: string;
7
+ player?: string | number;
8
+ }): Promise<void>;