@playdrop/playdrop-cli 0.11.8 → 0.12.0

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 (116) hide show
  1. package/config/client-meta.json +2 -2
  2. package/dist/apps/build.d.ts +1 -1
  3. package/dist/apps/build.js +4 -6
  4. package/dist/apps/index.d.ts +4 -5
  5. package/dist/apps/index.js +14 -16
  6. package/dist/apps/loadCheck.d.ts +76 -0
  7. package/dist/apps/loadCheck.js +874 -0
  8. package/dist/apps/upload.d.ts +3 -2
  9. package/dist/apps/upload.js +112 -9
  10. package/dist/assetSpecs.js +2 -5
  11. package/dist/catalogue-utils.d.ts +1 -0
  12. package/dist/catalogue-utils.js +1 -0
  13. package/dist/catalogue.d.ts +3 -1
  14. package/dist/catalogue.js +19 -3
  15. package/dist/commands/browse.js +2 -2
  16. package/dist/commands/build.js +4 -1
  17. package/dist/commands/captureListing.d.ts +30 -1
  18. package/dist/commands/captureListing.js +308 -125
  19. package/dist/commands/check.d.ts +8 -0
  20. package/dist/commands/check.js +188 -0
  21. package/dist/commands/create.js +17 -15
  22. package/dist/commands/createRemixContent.js +2 -2
  23. package/dist/commands/detail.js +2 -2
  24. package/dist/commands/dev.js +9 -12
  25. package/dist/commands/devServer.d.ts +2 -2
  26. package/dist/commands/devServer.js +3 -4
  27. package/dist/commands/marketing.js +10 -11
  28. package/dist/commands/play.js +2 -2
  29. package/dist/commands/review.d.ts +20 -0
  30. package/dist/commands/review.js +112 -0
  31. package/dist/commands/upload-content.d.ts +1 -1
  32. package/dist/commands/upload-content.js +1 -1
  33. package/dist/commands/upload.d.ts +2 -1
  34. package/dist/commands/upload.js +9 -52
  35. package/dist/commands/validate.js +4 -161
  36. package/dist/commands/worker/runtime.d.ts +1 -0
  37. package/dist/commands/worker/runtime.js +48 -5
  38. package/dist/commands/worker.d.ts +5 -6
  39. package/dist/commands/worker.js +42 -92
  40. package/dist/config.js +1 -2
  41. package/dist/index.js +33 -49
  42. package/dist/playwright.d.ts +5 -0
  43. package/dist/playwright.js +60 -1
  44. package/node_modules/@playdrop/api-client/dist/client.d.ts +5 -3
  45. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  46. package/node_modules/@playdrop/api-client/dist/client.js +10 -27
  47. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +1 -1
  48. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
  49. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +2 -1
  50. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
  51. package/node_modules/@playdrop/api-client/dist/domains/apps.js +11 -0
  52. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +1 -1
  53. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
  54. package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts +3 -1
  55. package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts.map +1 -1
  56. package/node_modules/@playdrop/api-client/dist/domains/auth.js +22 -0
  57. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +1 -1
  58. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
  59. package/node_modules/@playdrop/api-client/dist/index.d.ts +7 -6
  60. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  61. package/node_modules/@playdrop/api-client/dist/index.js +11 -6
  62. package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js +0 -7
  63. package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js.map +1 -1
  64. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js +2 -46
  65. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js.map +1 -1
  66. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js +2 -22
  67. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js.map +1 -1
  68. package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.js.map +1 -1
  69. package/node_modules/@playdrop/boxel-core/dist/src/serialization.js +1 -1
  70. package/node_modules/@playdrop/boxel-core/dist/src/serialization.js.map +1 -1
  71. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/infer-face.js.map +1 -1
  72. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.js +2 -2
  73. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.js.map +1 -1
  74. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.js +0 -11
  75. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.js.map +1 -1
  76. package/node_modules/@playdrop/boxel-core/dist/src/transforms/upscale.js.map +1 -1
  77. package/node_modules/@playdrop/boxel-core/dist/src/validation.d.ts +1 -1
  78. package/node_modules/@playdrop/boxel-core/dist/src/validation.js +0 -20
  79. package/node_modules/@playdrop/boxel-core/dist/src/validation.js.map +1 -1
  80. package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.d.ts +2 -2
  81. package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.js +2 -12
  82. package/node_modules/@playdrop/boxel-three/dist/src/voxels/faces.js +1 -125
  83. package/node_modules/@playdrop/config/client-meta.json +2 -2
  84. package/node_modules/@playdrop/config/dist/src/client-meta.d.ts +8 -0
  85. package/node_modules/@playdrop/config/dist/src/client-meta.d.ts.map +1 -0
  86. package/node_modules/@playdrop/config/dist/src/client-meta.js +51 -0
  87. package/node_modules/@playdrop/config/dist/src/deployment.d.ts.map +1 -1
  88. package/node_modules/@playdrop/config/dist/src/deployment.js +2 -32
  89. package/node_modules/@playdrop/config/dist/src/index.d.ts +2 -2
  90. package/node_modules/@playdrop/config/dist/src/index.d.ts.map +1 -1
  91. package/node_modules/@playdrop/config/dist/src/index.js +3 -29
  92. package/node_modules/@playdrop/config/dist/src/runtime-service-origins.d.ts +0 -1
  93. package/node_modules/@playdrop/config/dist/src/runtime-service-origins.d.ts.map +1 -1
  94. package/node_modules/@playdrop/config/dist/src/runtime-service-origins.js +21 -5
  95. package/node_modules/@playdrop/config/dist/src/server/fastify.d.ts +2 -2
  96. package/node_modules/@playdrop/config/dist/src/server/fastify.d.ts.map +1 -1
  97. package/node_modules/@playdrop/config/dist/src/server/fastify.js +0 -1
  98. package/node_modules/@playdrop/config/dist/test/runtime-service-origins.test.js +8 -0
  99. package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
  100. package/node_modules/@playdrop/types/dist/api.d.ts +25 -24
  101. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  102. package/node_modules/@playdrop/types/dist/api.js +5 -10
  103. package/node_modules/@playdrop/types/dist/version.d.ts +14 -0
  104. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  105. package/package.json +1 -1
  106. package/dist/apps/launchCheck.d.ts +0 -39
  107. package/dist/apps/launchCheck.js +0 -414
  108. package/dist/captureRuntime.d.ts +0 -73
  109. package/dist/captureRuntime.js +0 -1021
  110. package/dist/commands/capture.d.ts +0 -14
  111. package/dist/commands/capture.js +0 -550
  112. package/dist/commands/captureRemote.d.ts +0 -17
  113. package/dist/commands/captureRemote.js +0 -394
  114. package/node_modules/@playdrop/types/dist/entity.d.ts +0 -31
  115. package/node_modules/@playdrop/types/dist/entity.d.ts.map +0 -1
  116. package/node_modules/@playdrop/types/dist/entity.js +0 -67
@@ -17,8 +17,9 @@ export type AppUploadOptions = {
17
17
  creatorUsername?: string;
18
18
  token?: string | null;
19
19
  user?: UserResponse | null;
20
- runStagedUploadLaunchCheck?: boolean;
21
- launchCheckTimeoutMs?: number;
20
+ runStagedUploadLoadCheck?: boolean;
21
+ loadCheckTimeoutMs?: number;
22
22
  agentTaskId?: number;
23
+ mediaCaptureRequired?: boolean;
23
24
  };
24
25
  export declare function uploadApp(client: ApiClient, task: AppTask, artifacts: AppBuildArtifacts | null, options?: AppUploadOptions): Promise<AppUploadResult>;
@@ -1,13 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.uploadApp = uploadApp;
4
- const node_crypto_1 = require("node:crypto");
5
- const types_1 = require("@playdrop/types");
6
4
  const config_1 = require("@playdrop/config");
5
+ const types_1 = require("@playdrop/types");
6
+ const node_crypto_1 = require("node:crypto");
7
7
  const node_fs_1 = require("node:fs");
8
8
  const node_path_1 = require("node:path");
9
9
  const http_1 = require("../http");
10
- const launchCheck_1 = require("./launchCheck");
10
+ const loadCheck_1 = require("./loadCheck");
11
11
  const EXTENSION_TO_MIME = {
12
12
  '.png': 'image/png',
13
13
  '.jpg': 'image/jpeg',
@@ -74,6 +74,97 @@ async function bufferFromFile(file) {
74
74
  function computeSha256Hex(buffer) {
75
75
  return (0, node_crypto_1.createHash)('sha256').update(buffer).digest('hex');
76
76
  }
77
+ function isSha256Hex(value) {
78
+ return typeof value === 'string' && /^[a-f0-9]{64}$/i.test(value.trim());
79
+ }
80
+ function isCaptureMediaFileKey(fileKey) {
81
+ return fileKey.startsWith('screenshotsPortrait:')
82
+ || fileKey.startsWith('screenshotsLandscape:')
83
+ || fileKey.startsWith('videosPortrait:')
84
+ || fileKey.startsWith('videosLandscape:');
85
+ }
86
+ function readMediaCaptureProof(input) {
87
+ const reportPath = input.task.listing?.captureReportPath;
88
+ if (!reportPath) {
89
+ throw new Error(`agent_task_media_capture_report_missing: ${input.task.name} must include listing.captureReport from "playdrop project capture".`);
90
+ }
91
+ let report;
92
+ try {
93
+ report = JSON.parse((0, node_fs_1.readFileSync)(reportPath, 'utf8'));
94
+ }
95
+ catch (error) {
96
+ const reason = error instanceof Error ? error.message : String(error);
97
+ throw new Error(`agent_task_media_capture_report_invalid: ${reportPath} must be valid JSON. ${reason}`);
98
+ }
99
+ const binding = report?.binding;
100
+ if (!binding || typeof binding !== 'object' || Array.isArray(binding)) {
101
+ throw new Error(`agent_task_media_capture_report_invalid: ${reportPath} is missing binding.`);
102
+ }
103
+ const contentHash = typeof binding.contentHash === 'string' ? binding.contentHash.trim().toLowerCase() : '';
104
+ if (!isSha256Hex(contentHash)) {
105
+ throw new Error(`agent_task_media_capture_report_invalid: ${reportPath} binding.contentHash must be a SHA-256 hex string.`);
106
+ }
107
+ if (contentHash !== input.artifacts.bundleHash) {
108
+ throw new Error(`agent_task_media_capture_build_mismatch: capture report contentHash does not match the bundle being uploaded for ${input.task.name}. Rerun "playdrop project capture" after the final build changes.`);
109
+ }
110
+ const rawArtifactHashes = binding.artifactHashes;
111
+ if (!rawArtifactHashes || typeof rawArtifactHashes !== 'object' || Array.isArray(rawArtifactHashes)) {
112
+ throw new Error(`agent_task_media_capture_report_invalid: ${reportPath} binding.artifactHashes must be an object.`);
113
+ }
114
+ const captureArtifactHashEntries = Object.values(rawArtifactHashes)
115
+ .map((value) => typeof value === 'string' ? value.trim().toLowerCase() : '')
116
+ .filter((value) => isSha256Hex(value));
117
+ const captureArtifactHashes = new Set(captureArtifactHashEntries);
118
+ if (captureArtifactHashEntries.length === 0) {
119
+ throw new Error(`agent_task_media_capture_report_invalid: ${reportPath} binding.artifactHashes is empty.`);
120
+ }
121
+ const captureArtifactHashCounts = new Map();
122
+ for (const hash of captureArtifactHashEntries) {
123
+ captureArtifactHashCounts.set(hash, (captureArtifactHashCounts.get(hash) ?? 0) + 1);
124
+ }
125
+ const mediaFiles = input.preparedSessionFiles.filter((file) => isCaptureMediaFileKey(file.fileKey));
126
+ if (mediaFiles.length === 0) {
127
+ throw new Error(`agent_task_media_capture_artifacts_missing: ${input.task.name} must upload recorder stills and video in listing.screenshots* and listing.videos*.`);
128
+ }
129
+ const uploadedMediaHashCounts = new Map();
130
+ for (const file of mediaFiles) {
131
+ uploadedMediaHashCounts.set(file.sha256, (uploadedMediaHashCounts.get(file.sha256) ?? 0) + 1);
132
+ }
133
+ const missingUploadedCaptureArtifacts = [...captureArtifactHashCounts.entries()]
134
+ .filter(([hash, expectedCount]) => (uploadedMediaHashCounts.get(hash) ?? 0) < expectedCount);
135
+ if (missingUploadedCaptureArtifacts.length > 0) {
136
+ throw new Error(`agent_task_media_capture_artifact_missing_from_listing: ${input.task.name} must list every recorder poster and video from listing.captureReport in catalogue.json.`);
137
+ }
138
+ let hasStill = false;
139
+ let hasVideo = false;
140
+ const artifactHashes = {};
141
+ for (const file of mediaFiles) {
142
+ if (!captureArtifactHashes.has(file.sha256)) {
143
+ throw new Error(`agent_task_media_capture_artifact_mismatch: ${file.fileKey} was not produced by listing.captureReport for ${input.task.name}.`);
144
+ }
145
+ hasStill = hasStill || file.fileKey.startsWith('screenshots');
146
+ hasVideo = hasVideo || file.fileKey.startsWith('videos');
147
+ artifactHashes[file.fileKey] = file.sha256;
148
+ }
149
+ if (!hasStill || !hasVideo) {
150
+ throw new Error(`agent_task_media_capture_artifacts_missing: ${input.task.name} must upload at least one recorder still and one recorder video.`);
151
+ }
152
+ const capturedSurfaces = Array.isArray(binding.capturedSurfaces)
153
+ ? Array.from(new Set(binding.capturedSurfaces
154
+ .map((surface) => typeof surface === 'string' ? surface.trim().toUpperCase() : '')
155
+ .filter((surface) => input.task.surfaceTargets.includes(surface))))
156
+ : [];
157
+ const capturedSurfaceSet = new Set(capturedSurfaces);
158
+ const missingSurfaces = input.task.surfaceTargets.filter((surface) => !capturedSurfaceSet.has(surface));
159
+ if (missingSurfaces.length > 0) {
160
+ throw new Error(`agent_task_media_capture_surface_missing: listing.captureReport did not capture ${missingSurfaces.join(', ')} for ${input.task.name}.`);
161
+ }
162
+ return {
163
+ contentHash,
164
+ artifactHashes,
165
+ capturedSurfaces,
166
+ };
167
+ }
77
168
  function resolveMimeType(filename, fallback = 'application/octet-stream') {
78
169
  const type = EXTENSION_TO_MIME[(0, node_path_1.extname)(filename).toLowerCase()];
79
170
  return type ?? fallback;
@@ -159,7 +250,6 @@ async function uploadApp(client, task, artifacts, options) {
159
250
  /**
160
251
  * Upload an app version using the versioned API.
161
252
  */
162
- // eslint-disable-next-line complexity -- Upload assembly mirrors the versioned multipart contract in one place.
163
253
  async function uploadAppVersion(client, task, artifacts, options) {
164
254
  const creatorUsername = typeof options?.creatorUsername === 'string' ? options.creatorUsername.trim() : '';
165
255
  if (!creatorUsername) {
@@ -340,20 +430,33 @@ async function uploadAppVersion(client, task, artifacts, options) {
340
430
  })),
341
431
  });
342
432
  }
433
+ if (options?.mediaCaptureRequired === true
434
+ && task.hostingMode !== 'EXTERNAL'
435
+ && !task.externalUrl) {
436
+ if (!artifacts) {
437
+ throw new Error(`agent_task_media_capture_build_missing: ${task.name} has no hosted build artifacts to bind capture media to.`);
438
+ }
439
+ const mediaCaptureProof = readMediaCaptureProof({
440
+ task,
441
+ artifacts,
442
+ preparedSessionFiles,
443
+ });
444
+ await client.recordAppUploadMediaCapture(creatorUsername, task.name, sessionId, mediaCaptureProof);
445
+ }
343
446
  if (task.hostingMode !== 'EXTERNAL'
344
447
  && !task.externalUrl
345
- && options?.runStagedUploadLaunchCheck) {
346
- const launchCheck = await (0, launchCheck_1.runUploadedHostedLaunchCheck)({
448
+ && options?.runStagedUploadLoadCheck) {
449
+ const loadCheck = await (0, loadCheck_1.runUploadedHostedLoadCheck)({
347
450
  client,
348
451
  creatorUsername,
349
452
  task,
350
453
  sessionId,
351
- timeoutMs: options.launchCheckTimeoutMs,
454
+ timeoutMs: options.loadCheckTimeoutMs,
352
455
  token: options?.token ?? null,
353
456
  currentUser: options?.user ?? null,
354
457
  });
355
- if (launchCheck.status !== 'PASSED') {
356
- throw new Error((0, launchCheck_1.formatHostedLaunchCheckFailure)(task.name, launchCheck, 'staged'));
458
+ if (loadCheck.status !== 'PASSED') {
459
+ throw new Error((0, loadCheck_1.formatHostedLoadCheckFailure)(task.name, loadCheck, 'staged'));
357
460
  }
358
461
  }
359
462
  }
@@ -9,11 +9,11 @@ exports.loadAndValidateAssetSpecContract = loadAndValidateAssetSpecContract;
9
9
  exports.validateCustomAssetFilesAgainstContract = validateCustomAssetFilesAgainstContract;
10
10
  exports.normalizeAssetSpecVersionRef = normalizeAssetSpecVersionRef;
11
11
  exports.validateAssetSpecName = validateAssetSpecName;
12
+ const types_1 = require("@playdrop/types");
13
+ const ajv_1 = __importDefault(require("ajv"));
12
14
  const node_fs_1 = require("node:fs");
13
15
  const node_path_1 = require("node:path");
14
- const ajv_1 = __importDefault(require("ajv"));
15
16
  const semver_1 = require("semver");
16
- const types_1 = require("@playdrop/types");
17
17
  const LOCAL_EXTENSION_TO_MIME = {
18
18
  '.png': 'image/png',
19
19
  '.jpg': 'image/jpeg',
@@ -75,7 +75,6 @@ function resolveCustomAssetRoleContentType(filePath, roleContract) {
75
75
  }
76
76
  return resolvedContentType;
77
77
  }
78
- // eslint-disable-next-line complexity
79
78
  function normalizeAssetSpecSupportEntries(raw, errors, context) {
80
79
  if (raw === undefined || raw === null) {
81
80
  return [];
@@ -130,7 +129,6 @@ function normalizeAssetSpecSupportEntries(raw, errors, context) {
130
129
  }
131
130
  return rows;
132
131
  }
133
- // eslint-disable-next-line complexity
134
132
  function loadAndValidateAssetSpecContract(catalogueDir, relativeContractPath, errors, context) {
135
133
  const contractPath = (0, node_path_1.resolve)(catalogueDir, relativeContractPath);
136
134
  let rawText;
@@ -213,7 +211,6 @@ function loadAndValidateAssetSpecContract(catalogueDir, relativeContractPath, er
213
211
  contractPath,
214
212
  };
215
213
  }
216
- // eslint-disable-next-line complexity
217
214
  function validateCustomAssetFilesAgainstContract(contract, filePaths, errors, context) {
218
215
  const contractRoles = new Map(contract.roles.map((role) => [role.name.trim().toLowerCase(), role]));
219
216
  const providedRoles = new Set();
@@ -12,6 +12,7 @@ export declare function parseSurfaceTargets(input: unknown): ParsedSurfaceTarget
12
12
  export type AppCatalogueInfo = {
13
13
  name: string;
14
14
  type: string;
15
+ previewable: boolean;
15
16
  surfaceTargets: ParsedSurfaceTargets;
16
17
  cataloguePath: string;
17
18
  };
@@ -108,6 +108,7 @@ function findAppCatalogueInfo(htmlPath) {
108
108
  return {
109
109
  name,
110
110
  type: parsedType,
111
+ previewable: match.previewable === true,
111
112
  surfaceTargets: cloneSurfaceTargets(surfaceTargets),
112
113
  cataloguePath,
113
114
  };
@@ -1,4 +1,4 @@
1
- import { type AppSurface, type AppType, type AppHostingMode, type AppAuthMode, type AppControllerMode, type AppVersionVisibility, type AppVersionDesign, type AppAchievementCatalogueDefinition, type AppLeaderboardCatalogueDefinition, type PlayerMetaDefinitionStatus, type CatalogueTagGroupDefinition, type AppMetadataAssetSpecSupport, type AssetSpecContract, type AssetSpecStatus, type AssetSpecValidationKind, type ContentLicense } from '@playdrop/types';
1
+ import { type AppAchievementCatalogueDefinition, type AppAuthMode, type AppControllerMode, type AppHostingMode, type AppLeaderboardCatalogueDefinition, type AppMetadataAssetSpecSupport, type AppSurface, type AppType, type AppVersionDesign, type AppVersionVisibility, type AssetSpecContract, type AssetSpecStatus, type AssetSpecValidationKind, type CatalogueTagGroupDefinition, type ContentLicense, type PlayerMetaDefinitionStatus } from '@playdrop/types';
2
2
  import { type AssetSpecSupportDeclaration } from './assetSpecs';
3
3
  export type CatalogueJson = {
4
4
  apps?: Array<Record<string, unknown>>;
@@ -19,6 +19,7 @@ export type AppListingConfig = {
19
19
  screenshotsLandscape?: string[];
20
20
  videosPortrait?: string[];
21
21
  videosLandscape?: string[];
22
+ captureReport?: string;
22
23
  };
23
24
  export type ResolvedAppAchievementDefinition = Omit<AppAchievementCatalogueDefinition, 'status'> & {
24
25
  iconPath: string;
@@ -152,6 +153,7 @@ export type ResolvedListingAssets = {
152
153
  screenshotLandscapePaths: string[];
153
154
  videoPortraitPaths: string[];
154
155
  videoLandscapePaths: string[];
156
+ captureReportPath?: string;
155
157
  };
156
158
  export type AppDependencyAssetRef = {
157
159
  ref: string;
package/dist/catalogue.js CHANGED
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- /* eslint-disable complexity, max-lines, max-lines-per-function */
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.validateAppMetadata = validateAppMetadata;
5
4
  exports.resolveCatalogueTagGroups = resolveCatalogueTagGroups;
@@ -8,11 +7,11 @@ exports.resolveWorkspaceApp = resolveWorkspaceApp;
8
7
  exports.formatMetadataWarnings = formatMetadataWarnings;
9
8
  exports.formatMissingMetadataWarnings = formatMissingMetadataWarnings;
10
9
  exports.findAppTaskByFile = findAppTaskByFile;
10
+ const types_1 = require("@playdrop/types");
11
11
  const node_fs_1 = require("node:fs");
12
12
  const node_path_1 = require("node:path");
13
- const types_1 = require("@playdrop/types");
14
- const catalogue_utils_1 = require("./catalogue-utils");
15
13
  const assetSpecs_1 = require("./assetSpecs");
14
+ const catalogue_utils_1 = require("./catalogue-utils");
16
15
  const HEX_COLOR_REGEX = /^#?(?:[0-9a-fA-F]{6})$/;
17
16
  const SEMVER_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
18
17
  const LEGACY_CATALOGUE_VERSION_KEY = ['schema', 'Version'].join('');
@@ -1250,6 +1249,23 @@ function buildAppTasks(rootDir, catalogues, options) {
1250
1249
  }
1251
1250
  }
1252
1251
  }
1252
+ if (!listingValidationFailed && typeof rawListing.captureReport === 'string' && rawListing.captureReport.trim()) {
1253
+ const captureReportPath = rawListing.captureReport.trim();
1254
+ if ((0, node_path_1.extname)(captureReportPath).toLowerCase() !== '.json') {
1255
+ errors.push(`[${label}] Skipping ${rawName}: listing.captureReport must point to a JSON file.`);
1256
+ listingValidationFailed = true;
1257
+ }
1258
+ else {
1259
+ const validationError = validateListingAssetPath(file.directory, captureReportPath, 'listing.captureReport');
1260
+ if (validationError) {
1261
+ errors.push(`[${label}] Skipping ${rawName}: ${validationError}`);
1262
+ listingValidationFailed = true;
1263
+ }
1264
+ else {
1265
+ resolvedListing.captureReportPath = resolveListingAssetPath(file.directory, captureReportPath);
1266
+ }
1267
+ }
1268
+ }
1253
1269
  if (listingValidationFailed) {
1254
1270
  continue;
1255
1271
  }
@@ -380,7 +380,7 @@ function parseBrowseCommandOptions(options) {
380
380
  };
381
381
  }
382
382
  async function loadMergedBrowseResults(input) {
383
- const { client, creator, creatorUsername, appType, assetCategory, assetSubcategory, assetSpec, assetSpecOwner, assetSpecName, limit, offset, tags, } = input;
383
+ const { client, creatorUsername, appType, assetCategory, assetSubcategory, assetSpec, assetSpecOwner, assetSpecName, limit, offset, tags, } = input;
384
384
  const appResultsPromise = creatorUsername
385
385
  ? collectPaginatedItems((pageLimit, pageOffset) => client.fetchAppsByCreator(creatorUsername, {
386
386
  limit: pageLimit,
@@ -546,7 +546,7 @@ async function runAssetPackBrowseCommand(input) {
546
546
  };
547
547
  }
548
548
  async function runBrowseCommand(input) {
549
- const { client, kind, creator, creatorUsername, appType, auth, controller, surface, sort, limit, offset, tags, } = input;
549
+ const { client, kind, creatorUsername, appType, auth, controller, surface, sort, limit, offset, tags, } = input;
550
550
  if (kind === 'app') {
551
551
  if (creatorUsername) {
552
552
  const response = await client.fetchAppsByCreator(creatorUsername, {
@@ -23,9 +23,11 @@ async function build(pathOrName) {
23
23
  for (const task of (0, taskUtils_1.sortTasks)(tasks)) {
24
24
  const entityId = task.name;
25
25
  try {
26
+ let detail;
26
27
  if (task.kind === 'app') {
27
28
  await (0, apps_1.validateAppTask)(task);
28
- await (0, apps_1.buildApp)(task);
29
+ const artifacts = await (0, apps_1.buildApp)(task);
30
+ detail = `bundleHash=${artifacts.bundleHash}`;
29
31
  }
30
32
  else if (task.kind === 'asset' || task.kind === 'owned-asset') {
31
33
  const generated = await (0, model_artifacts_1.prepareModel3DAssetArtifacts)(task);
@@ -42,6 +44,7 @@ async function build(pathOrName) {
42
44
  entityType: task.kind,
43
45
  entityId,
44
46
  catalogue: task.cataloguePath,
47
+ detail,
45
48
  };
46
49
  results.push(entry);
47
50
  console.log((0, uploadLog_1.formatTaskLogLine)(entry));
@@ -1,3 +1,5 @@
1
+ import { type AppSurface } from '@playdrop/types';
2
+ import type { BrowserContextOptions } from 'playwright-core';
1
3
  type CaptureListingOptions = {
2
4
  app?: string;
3
5
  duration?: string | number;
@@ -8,6 +10,7 @@ type CaptureListingOptions = {
8
10
  audio?: boolean;
9
11
  outputDir?: string;
10
12
  keepRaw?: boolean;
13
+ contentHash?: string;
11
14
  };
12
15
  type ParsedCaptureListingOptions = {
13
16
  targetArg?: string;
@@ -20,6 +23,8 @@ type ParsedCaptureListingOptions = {
20
23
  audio: boolean;
21
24
  outputDir: string | null;
22
25
  keepRaw: boolean;
26
+ contentHash: string | null;
27
+ explicitDimensions: boolean;
23
28
  };
24
29
  type CaptureRect = {
25
30
  x: number;
@@ -45,11 +50,35 @@ type CropRect = {
45
50
  width: number;
46
51
  height: number;
47
52
  };
48
- export declare function resolveListingCaptureRouteSegment(previewable: boolean): 'dev' | 'dev-preview';
53
+ export declare function resolveListingCaptureRouteSegment(previewable: boolean): 'dev-preview';
54
+ export declare function resolveCaptureListingDevRouterPort(env?: NodeJS.ProcessEnv): number;
55
+ export declare function resolveListingCaptureLocalDevPort(devRouterPort: number): number | null;
56
+ export declare function buildListingCaptureFrameUrl({ webBase, currentUsername, appTypeSlug, appName, routeSegment, localDevPort, }: {
57
+ webBase: string;
58
+ currentUsername: string;
59
+ appTypeSlug: string;
60
+ appName: string;
61
+ routeSegment: 'dev-preview';
62
+ localDevPort: number | null;
63
+ }): string;
49
64
  export declare function resolveListingCaptureSceneId(width: number, height: number): 'listing-landscape' | 'listing-portrait';
50
65
  export declare function parseCaptureListingOptions(targetArg: string | undefined, options?: CaptureListingOptions): ParsedCaptureListingOptions;
51
66
  export declare function assertSupportedListingEnvironment(platform?: NodeJS.Platform, macosVersion?: string): void;
52
67
  export declare function resolveListingRecorderPath(baseDir?: string): string;
68
+ export declare function buildSurfaceOutputPaths(outputDir: string, surface: AppSurface, useSurfacePrefix: boolean): {
69
+ rawVideoPath: string;
70
+ metadataPath: string;
71
+ posterPath: string;
72
+ finalVideoPath: string;
73
+ };
74
+ export declare function resolveCaptureDimensions(surface: AppSurface, parsedOptions: ParsedCaptureListingOptions): {
75
+ width: number;
76
+ height: number;
77
+ };
78
+ export declare function resolveListingCaptureBrowserContextOptions(surface: AppSurface, dimensions: {
79
+ width: number;
80
+ height: number;
81
+ }): BrowserContextOptions;
53
82
  export declare function computeRecordedCrop(measurement: HostedGameMeasurement, rawWidth: number, rawHeight: number): CropRect;
54
83
  export declare function captureListing(targetArg: string | undefined, options?: CaptureListingOptions): Promise<void>;
55
84
  export {};