@playdrop/playdrop-cli 0.11.9 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/config/client-meta.json +1 -1
  2. package/dist/apps/build.js +2 -3
  3. package/dist/apps/index.d.ts +4 -5
  4. package/dist/apps/index.js +14 -16
  5. package/dist/apps/loadCheck.d.ts +76 -0
  6. package/dist/apps/loadCheck.js +874 -0
  7. package/dist/apps/upload.d.ts +3 -2
  8. package/dist/apps/upload.js +110 -6
  9. package/dist/catalogue-utils.d.ts +1 -0
  10. package/dist/catalogue-utils.js +1 -0
  11. package/dist/catalogue.d.ts +2 -0
  12. package/dist/catalogue.js +17 -0
  13. package/dist/commands/build.js +4 -1
  14. package/dist/commands/captureListing.d.ts +30 -1
  15. package/dist/commands/captureListing.js +305 -120
  16. package/dist/commands/check.d.ts +8 -0
  17. package/dist/commands/check.js +188 -0
  18. package/dist/commands/review.d.ts +20 -0
  19. package/dist/commands/review.js +112 -0
  20. package/dist/commands/upload-content.d.ts +1 -1
  21. package/dist/commands/upload.d.ts +2 -1
  22. package/dist/commands/upload.js +9 -52
  23. package/dist/commands/validate.js +0 -155
  24. package/dist/commands/worker/runtime.d.ts +1 -0
  25. package/dist/commands/worker/runtime.js +43 -3
  26. package/dist/commands/worker.d.ts +1 -1
  27. package/dist/commands/worker.js +22 -44
  28. package/dist/index.js +33 -49
  29. package/dist/playwright.d.ts +5 -0
  30. package/dist/playwright.js +60 -1
  31. package/node_modules/@playdrop/api-client/dist/client.d.ts +4 -2
  32. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  33. package/node_modules/@playdrop/api-client/dist/client.js +0 -10
  34. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +2 -1
  35. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
  36. package/node_modules/@playdrop/api-client/dist/domains/apps.js +11 -0
  37. package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts +3 -1
  38. package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts.map +1 -1
  39. package/node_modules/@playdrop/api-client/dist/domains/auth.js +22 -0
  40. package/node_modules/@playdrop/api-client/dist/index.d.ts +4 -3
  41. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  42. package/node_modules/@playdrop/api-client/dist/index.js +10 -5
  43. package/node_modules/@playdrop/config/client-meta.json +1 -1
  44. package/node_modules/@playdrop/config/dist/src/client-meta.d.ts +8 -0
  45. package/node_modules/@playdrop/config/dist/src/client-meta.d.ts.map +1 -0
  46. package/node_modules/@playdrop/config/dist/src/client-meta.js +51 -0
  47. package/node_modules/@playdrop/config/dist/src/deployment.d.ts.map +1 -1
  48. package/node_modules/@playdrop/config/dist/src/deployment.js +2 -32
  49. package/node_modules/@playdrop/config/dist/src/index.d.ts.map +1 -1
  50. package/node_modules/@playdrop/config/dist/src/index.js +2 -28
  51. package/node_modules/@playdrop/config/dist/src/runtime-service-origins.d.ts +0 -1
  52. package/node_modules/@playdrop/config/dist/src/runtime-service-origins.d.ts.map +1 -1
  53. package/node_modules/@playdrop/config/dist/src/runtime-service-origins.js +21 -5
  54. package/node_modules/@playdrop/config/dist/test/runtime-service-origins.test.js +8 -0
  55. package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
  56. package/node_modules/@playdrop/types/dist/api.d.ts +19 -14
  57. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  58. package/node_modules/@playdrop/types/dist/api.js +5 -10
  59. package/node_modules/@playdrop/types/dist/version.d.ts +14 -0
  60. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  61. package/package.json +1 -1
  62. package/dist/apps/launchCheck.d.ts +0 -39
  63. package/dist/apps/launchCheck.js +0 -414
  64. package/dist/captureRuntime.d.ts +0 -73
  65. package/dist/captureRuntime.js +0 -1021
  66. package/dist/commands/capture.d.ts +0 -14
  67. package/dist/commands/capture.js +0 -548
  68. package/dist/commands/captureRemote.d.ts +0 -17
  69. package/dist/commands/captureRemote.js +0 -394
  70. package/node_modules/@playdrop/types/dist/entity.d.ts +0 -31
  71. package/node_modules/@playdrop/types/dist/entity.d.ts.map +0 -1
  72. package/node_modules/@playdrop/types/dist/entity.js +0 -67
@@ -0,0 +1,8 @@
1
+ type ProjectCheckOptions = {
2
+ app?: string;
3
+ timeout?: string | number;
4
+ screenshot?: string;
5
+ actions?: string;
6
+ };
7
+ export declare function check(targetArg: string | undefined, options?: ProjectCheckOptions): Promise<void>;
8
+ export {};
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.check = check;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const types_1 = require("@playdrop/types");
7
+ const commandContext_1 = require("../commandContext");
8
+ const catalogue_1 = require("../catalogue");
9
+ const catalogue_utils_1 = require("../catalogue-utils");
10
+ const http_1 = require("../http");
11
+ const messages_1 = require("../messages");
12
+ const loadCheck_1 = require("../apps/loadCheck");
13
+ const devShared_1 = require("./devShared");
14
+ function resolveProjectCheckTarget(targetArg, appOption) {
15
+ try {
16
+ return (0, devShared_1.resolveDevTarget)(targetArg, appOption);
17
+ }
18
+ catch (error) {
19
+ const code = error?.code;
20
+ const message = error?.message || 'Unable to resolve project check target.';
21
+ const suggestions = code === 'app_required'
22
+ ? ['Use "--app <name>" to choose which app to check.']
23
+ : code === 'catalogue_missing'
24
+ ? ['Run the command inside a Playdrop workspace with catalogue.json.']
25
+ : ['Provide a valid catalogue-backed app target.'];
26
+ (0, messages_1.printErrorWithHelp)(message, suggestions, { command: 'project check' });
27
+ process.exitCode = 1;
28
+ return null;
29
+ }
30
+ }
31
+ function parseProjectCheckTimeoutMs(value) {
32
+ if (value === undefined || value === null || value === '') {
33
+ return 15000;
34
+ }
35
+ const numeric = typeof value === 'number' ? value : Number(value);
36
+ if (!Number.isFinite(numeric) || numeric <= 0) {
37
+ throw new Error('Timeout must be a positive number of seconds.');
38
+ }
39
+ if (numeric > 600) {
40
+ throw new Error('Timeout cannot exceed 600 seconds.');
41
+ }
42
+ return Math.round(numeric * 1000);
43
+ }
44
+ function readProjectCheckActions(actionsPath) {
45
+ const normalizedPath = actionsPath?.trim() ?? '';
46
+ if (!normalizedPath) {
47
+ return [];
48
+ }
49
+ const raw = (0, node_fs_1.readFileSync)(normalizedPath, 'utf8');
50
+ return (0, loadCheck_1.normalizeProjectCheckActions)(JSON.parse(raw));
51
+ }
52
+ function defaultProjectCheckScreenshotPath(appName) {
53
+ return (0, node_path_1.join)('assets', 'marketing', 'playdrop', 'check', `${appName}.png`);
54
+ }
55
+ async function check(targetArg, options = {}) {
56
+ let timeoutMs;
57
+ let actions;
58
+ try {
59
+ timeoutMs = parseProjectCheckTimeoutMs(options.timeout);
60
+ actions = readProjectCheckActions(options.actions);
61
+ }
62
+ catch (error) {
63
+ (0, messages_1.printErrorWithHelp)(error?.message || 'Invalid project check options.', ['Use --actions with a JSON array of click, press, and wait actions.'], { command: 'project check' });
64
+ process.exitCode = 1;
65
+ return;
66
+ }
67
+ const resolvedTarget = resolveProjectCheckTarget(targetArg, options.app);
68
+ if (!resolvedTarget) {
69
+ return;
70
+ }
71
+ const filePath = resolvedTarget.htmlPath;
72
+ if (!(0, node_fs_1.existsSync)(filePath) || !(0, node_fs_1.statSync)(filePath).isFile()) {
73
+ (0, messages_1.printErrorWithHelp)(`App HTML was not found at ${filePath}.`, ['Run the app build first or fix the catalogue file path.'], { command: 'project check' });
74
+ process.exitCode = 1;
75
+ return;
76
+ }
77
+ const taskLookup = (0, catalogue_1.findAppTaskByFile)(filePath);
78
+ if (taskLookup.errors.length > 0) {
79
+ (0, messages_1.printErrorWithHelp)(taskLookup.errors[0] || 'Failed to resolve the app task from catalogue.json.', taskLookup.errors.slice(1), { command: 'project check' });
80
+ process.exitCode = 1;
81
+ return;
82
+ }
83
+ if (!taskLookup.task) {
84
+ (0, messages_1.printErrorWithHelp)('project check requires a catalogue-backed app target.', ['Add this app to catalogue.json, then rerun "playdrop project check".'], { command: 'project check' });
85
+ process.exitCode = 1;
86
+ return;
87
+ }
88
+ let appName = resolvedTarget.appName;
89
+ try {
90
+ const match = (0, catalogue_utils_1.findAppDefinition)(filePath);
91
+ appName = match.name;
92
+ }
93
+ catch (error) {
94
+ if (resolvedTarget.cataloguePath) {
95
+ (0, messages_1.printErrorWithHelp)(error?.message || 'Catalogue lookup failed.', ['Ensure the HTML file is listed exactly once in catalogue.json with a valid type.'], { command: 'project check' });
96
+ process.exitCode = 1;
97
+ return;
98
+ }
99
+ }
100
+ const screenshotPath = options.screenshot?.trim() || defaultProjectCheckScreenshotPath(appName);
101
+ const workspacePath = resolvedTarget.cataloguePath ?? (0, node_path_1.dirname)(filePath);
102
+ await (0, commandContext_1.withEnvironment)('project check', 'Checking the Playdrop hosted game shell', async ({ client, env, envConfig, account, token }) => {
103
+ let currentUsername = account?.username?.trim() ?? '';
104
+ if (!currentUsername) {
105
+ try {
106
+ currentUsername = await (0, devShared_1.fetchDevUsername)(client);
107
+ }
108
+ catch (error) {
109
+ if (error instanceof http_1.CLIUnsupportedClientError) {
110
+ return;
111
+ }
112
+ if (error instanceof types_1.UnsupportedClientError) {
113
+ (0, http_1.handleUnsupportedError)(error, 'Authentication');
114
+ process.exitCode = 1;
115
+ return;
116
+ }
117
+ if (error instanceof types_1.ApiError) {
118
+ (0, messages_1.printErrorWithHelp)(`Could not fetch your account (status ${error.status}).`, ['Run "playdrop auth login" to refresh your session.'], { command: 'project check' });
119
+ process.exitCode = 1;
120
+ return;
121
+ }
122
+ if ((0, devShared_1.isNetworkError)(error)) {
123
+ (0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to resolve your account.', 'project check');
124
+ process.exitCode = 1;
125
+ return;
126
+ }
127
+ throw error;
128
+ }
129
+ }
130
+ try {
131
+ await (0, devShared_1.assertAppRegistered)(client, currentUsername, appName);
132
+ }
133
+ catch (error) {
134
+ if (error instanceof http_1.CLIUnsupportedClientError) {
135
+ return;
136
+ }
137
+ if (error instanceof types_1.UnsupportedClientError) {
138
+ (0, http_1.handleUnsupportedError)(error, 'Project check');
139
+ process.exitCode = 1;
140
+ return;
141
+ }
142
+ if (error instanceof types_1.ApiError && error.status === 404) {
143
+ (0, messages_1.printErrorWithHelp)(`App ${currentUsername}/${appName} is not registered on ${env}.`, [`Run "playdrop project create app ${appName}" before using project check.`], { command: 'project check' });
144
+ process.exitCode = 1;
145
+ return;
146
+ }
147
+ if ((0, devShared_1.isNetworkError)(error)) {
148
+ (0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to verify the app registration.', 'project check');
149
+ process.exitCode = 1;
150
+ return;
151
+ }
152
+ throw error;
153
+ }
154
+ const projectInfo = (0, devShared_1.findProjectInfo)(filePath);
155
+ const devScriptAvailable = Boolean(projectInfo.projectDir
156
+ && projectInfo.packageJson
157
+ && typeof projectInfo.packageJson.scripts?.dev === 'string');
158
+ const entryLabel = (0, node_path_1.relative)(process.cwd(), filePath) || filePath;
159
+ console.log(`[check] Preparing ${entryLabel}.`);
160
+ if (projectInfo.projectDir && !devScriptAvailable && projectInfo.packageJsonPath) {
161
+ const projectLabel = (0, devShared_1.formatProjectLabel)(projectInfo);
162
+ if (projectLabel) {
163
+ console.log(`[check] package.json detected at ${projectLabel}, but no "dev" script was found. Run your app build manually if needed.`);
164
+ }
165
+ }
166
+ const result = await (0, loadCheck_1.runLocalHostedLoadCheck)({
167
+ client,
168
+ apiBase: envConfig.apiBase,
169
+ webBase: envConfig.webBase,
170
+ creatorUsername: currentUsername,
171
+ task: taskLookup.task,
172
+ timeoutMs,
173
+ token,
174
+ currentUser: null,
175
+ screenshotPath,
176
+ actions,
177
+ });
178
+ if (result.status !== 'PASSED') {
179
+ (0, messages_1.printErrorWithHelp)((0, loadCheck_1.formatHostedLoadCheckFailure)(appName, result, 'local'), result.screenshotPath ? [`Screenshot: ${result.screenshotPath}`] : [], { command: 'project check' });
180
+ process.exitCode = 1;
181
+ return;
182
+ }
183
+ console.log(`[check] Passed ${currentUsername}/${appName}.`);
184
+ if (result.webglRenderer) {
185
+ console.log(`[check] Hardware renderer: ${result.webglRenderer}`);
186
+ }
187
+ }, { workspacePath });
188
+ }
@@ -33,6 +33,26 @@ export type ReviewRatingCardOptions = {
33
33
  punchline?: string;
34
34
  title?: string;
35
35
  };
36
+ export type ReviewListWindowsOptions = {
37
+ pid: string;
38
+ };
39
+ export type ReviewScreenshotOptions = {
40
+ metadata?: string;
41
+ out: string;
42
+ pid: string;
43
+ windowId?: string;
44
+ };
45
+ export declare function buildReviewListWindowsRecorderArgs(options: ReviewListWindowsOptions): string[];
46
+ export declare function buildReviewScreenshotRecorderArgs(options: ReviewScreenshotOptions): {
47
+ args: string[];
48
+ metadataPath: string;
49
+ outputPath: string;
50
+ };
51
+ export declare function listReviewCaptureWindows(options: ReviewListWindowsOptions): Promise<void>;
52
+ export declare function captureReviewScreenshot(options: ReviewScreenshotOptions): Promise<{
53
+ metadataPath: string;
54
+ outputPath: string;
55
+ }>;
36
56
  export declare function validateGameReviewResult(input: ValidateGameReviewResultInput): Promise<ReviewValidationResult>;
37
57
  export declare function validateReviewResultCommand(options: ReviewValidateResultOptions): Promise<void>;
38
58
  export declare function composeReviewEvidence(options: ReviewComposeEvidenceOptions): Promise<{
@@ -4,14 +4,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.REQUIRED_REVIEW_EVIDENCE_FILES = exports.REVIEW_CRITERIA = void 0;
7
+ exports.buildReviewListWindowsRecorderArgs = buildReviewListWindowsRecorderArgs;
8
+ exports.buildReviewScreenshotRecorderArgs = buildReviewScreenshotRecorderArgs;
9
+ exports.listReviewCaptureWindows = listReviewCaptureWindows;
10
+ exports.captureReviewScreenshot = captureReviewScreenshot;
7
11
  exports.validateGameReviewResult = validateGameReviewResult;
8
12
  exports.validateReviewResultCommand = validateReviewResultCommand;
9
13
  exports.composeReviewEvidence = composeReviewEvidence;
10
14
  exports.createReviewRatingCard = createReviewRatingCard;
11
15
  const node_path_1 = __importDefault(require("node:path"));
16
+ const node_child_process_1 = require("node:child_process");
12
17
  const node_fs_1 = require("node:fs");
13
18
  const sharp_1 = __importDefault(require("sharp"));
14
19
  const output_1 = require("../output");
20
+ const captureListing_1 = require("./captureListing");
15
21
  exports.REVIEW_CRITERIA = [
16
22
  'Gameplay / Core Loop',
17
23
  'Depth / Replayability',
@@ -46,6 +52,7 @@ exports.REQUIRED_REVIEW_EVIDENCE_FILES = [
46
52
  'rating-card.png',
47
53
  ];
48
54
  const REVIEW_EVIDENCE_MAX_BYTES = 10 * 1024 * 1024;
55
+ const REVIEW_RECORDER_TIMEOUT_MS = 15000;
49
56
  function resolveFilePath(filePath, code) {
50
57
  const normalized = typeof filePath === 'string' ? filePath.trim() : '';
51
58
  if (!normalized) {
@@ -167,6 +174,111 @@ async function assertPngFile(filePath) {
167
174
  }
168
175
  }
169
176
  }
177
+ async function assertNonBlankPngFile(filePath) {
178
+ await assertPngFile(filePath);
179
+ const image = (0, sharp_1.default)(filePath);
180
+ const metadata = await image.metadata();
181
+ if (!metadata.width || !metadata.height) {
182
+ throw new Error(`review_screenshot_invalid_image:${filePath}`);
183
+ }
184
+ const stats = await image.stats();
185
+ const colorChannels = stats.channels.slice(0, 3);
186
+ const brightest = Math.max(...colorChannels.map((channel) => channel.max));
187
+ if (brightest <= 5) {
188
+ throw new Error(`review_screenshot_blank:${filePath}`);
189
+ }
190
+ }
191
+ function parsePositiveInteger(value, code) {
192
+ const normalized = typeof value === 'string' ? value.trim() : '';
193
+ const parsed = Number.parseInt(normalized, 10);
194
+ if (!Number.isSafeInteger(parsed) || parsed <= 0 || String(parsed) !== normalized) {
195
+ throw new Error(code);
196
+ }
197
+ return parsed;
198
+ }
199
+ function buildReviewListWindowsRecorderArgs(options) {
200
+ const pid = parsePositiveInteger(options.pid, 'invalid_review_window_pid');
201
+ return ['--list-windows', '--pid', String(pid)];
202
+ }
203
+ function buildReviewScreenshotRecorderArgs(options) {
204
+ const pid = parsePositiveInteger(options.pid, 'invalid_review_window_pid');
205
+ const outputPath = resolveFilePath(options.out, 'missing_out');
206
+ const metadataPath = options.metadata
207
+ ? resolveFilePath(options.metadata, 'invalid_review_screenshot_metadata')
208
+ : outputPath.replace(/\.png$/i, '') + '.metadata.json';
209
+ const args = [
210
+ '--pid',
211
+ String(pid),
212
+ '--screenshot',
213
+ outputPath,
214
+ '--metadata',
215
+ metadataPath,
216
+ ];
217
+ if (options.windowId !== undefined && options.windowId !== '') {
218
+ const windowId = parsePositiveInteger(options.windowId, 'invalid_review_window_id');
219
+ args.push('--window-id', String(windowId));
220
+ }
221
+ return { args, metadataPath, outputPath };
222
+ }
223
+ function runRecorder(args) {
224
+ return new Promise((resolve, reject) => {
225
+ const recorderPath = (0, captureListing_1.resolveListingRecorderPath)();
226
+ const child = (0, node_child_process_1.spawn)(recorderPath, args, {
227
+ stdio: ['ignore', 'pipe', 'pipe'],
228
+ });
229
+ let stdout = '';
230
+ let stderr = '';
231
+ let settled = false;
232
+ const timer = setTimeout(() => {
233
+ if (settled) {
234
+ return;
235
+ }
236
+ settled = true;
237
+ child.kill('SIGKILL');
238
+ reject(new Error(`review_recorder_timeout:${REVIEW_RECORDER_TIMEOUT_MS}`));
239
+ }, REVIEW_RECORDER_TIMEOUT_MS);
240
+ child.stdout.on('data', (chunk) => {
241
+ stdout += chunk.toString();
242
+ });
243
+ child.stderr.on('data', (chunk) => {
244
+ stderr += chunk.toString();
245
+ });
246
+ child.on('error', (error) => {
247
+ if (settled) {
248
+ return;
249
+ }
250
+ settled = true;
251
+ clearTimeout(timer);
252
+ reject(new Error(`review_recorder_unavailable:${recorderPath}:${error.message}`));
253
+ });
254
+ child.on('close', (code) => {
255
+ if (settled) {
256
+ return;
257
+ }
258
+ settled = true;
259
+ clearTimeout(timer);
260
+ if (code === 0) {
261
+ resolve(stdout);
262
+ return;
263
+ }
264
+ const detail = (stderr || stdout || `exit ${code}`).trim();
265
+ reject(new Error(`review_recorder_failed:${detail}`));
266
+ });
267
+ });
268
+ }
269
+ async function listReviewCaptureWindows(options) {
270
+ const output = await runRecorder(buildReviewListWindowsRecorderArgs(options));
271
+ process.stdout.write(output);
272
+ }
273
+ async function captureReviewScreenshot(options) {
274
+ const { args, metadataPath, outputPath } = buildReviewScreenshotRecorderArgs(options);
275
+ await node_fs_1.promises.mkdir(node_path_1.default.dirname(outputPath), { recursive: true });
276
+ await node_fs_1.promises.mkdir(node_path_1.default.dirname(metadataPath), { recursive: true });
277
+ await runRecorder(args);
278
+ await assertNonBlankPngFile(outputPath);
279
+ (0, output_1.printSuccess)(`Review screenshot written to ${outputPath}`);
280
+ return { metadataPath, outputPath };
281
+ }
170
282
  async function validateGameReviewResult(input) {
171
283
  const normalizedState = String(input.reviewState || '').trim().toUpperCase();
172
284
  if (!TERMINAL_REVIEW_STATES.has(normalizedState)) {
@@ -2,7 +2,7 @@ import type { ApiClient } from '@playdrop/api-client';
2
2
  import { type AssetPackUploadedLocalAsset } from '@playdrop/types';
3
3
  import type { AssetPackTask, AssetSpecTask, AssetTask, OwnedAssetTask, PackOwnedAssetTask } from '../catalogue';
4
4
  import type { CliTask } from '../taskUtils';
5
- export type CurrentUserRole = 'USER' | 'CREATOR' | 'ADMIN' | null;
5
+ export type CurrentUserRole = 'CREATOR' | 'ADMIN' | null;
6
6
  export type UploadedAssetInfo = {
7
7
  creatorUsername: string;
8
8
  name: string;
@@ -1,5 +1,5 @@
1
1
  import type { ApiClient } from '@playdrop/api-client';
2
- import type { UserResponse } from '@playdrop/types';
2
+ import type { AgentExecutionTarget, UserResponse } from '@playdrop/types';
3
3
  export type UploadCommandOptions = {
4
4
  env?: string;
5
5
  skipReview?: boolean;
@@ -10,6 +10,7 @@ export type WorkerAppPublishInput = {
10
10
  client: ApiClient;
11
11
  taskId: number;
12
12
  kind: 'NEW_GAME' | 'REMIX_GAME' | 'GAME_UPDATE';
13
+ executionTarget?: AgentExecutionTarget;
13
14
  expectedAppName?: string | null;
14
15
  remixSourceRef?: string | null;
15
16
  playdropAssetRequirement?: WorkerPlaydropAssetRequirement | null;
@@ -50,7 +50,6 @@ const taskUtils_1 = require("../taskUtils");
50
50
  const uploadLog_1 = require("../uploadLog");
51
51
  const upload_content_1 = require("./upload-content");
52
52
  const appUrls_1 = require("../appUrls");
53
- const launchCheck_1 = require("../apps/launchCheck");
54
53
  const devRuntimeAssets_1 = require("./devRuntimeAssets");
55
54
  const upload_graph_1 = require("./upload-graph");
56
55
  function normalizeSavedItemKinds(kinds) {
@@ -856,8 +855,6 @@ async function uploadAppTask(state, task, taskCreator, options) {
856
855
  webBase: state.portalBase,
857
856
  token: state.token,
858
857
  user: state.currentUser,
859
- runLocalLaunchCheck: true,
860
- runStagedUploadLaunchCheck: true,
861
858
  ensureRegisteredAppShell: true,
862
859
  });
863
860
  if (!upload.versionCreated || !upload.version) {
@@ -2137,7 +2134,7 @@ function assertNewGameListingScreenshots(task) {
2137
2134
  function readPlaytestEvidenceManifest(task) {
2138
2135
  const manifestPath = (0, node_path_1.join)(task.projectDir, 'playtest-evidence.json');
2139
2136
  if (!(0, node_fs_1.existsSync)(manifestPath)) {
2140
- throw new Error(formatMissingPlaytestEvidenceManifestError('new game tasks must include playtest-evidence.json before upload.'));
2137
+ throw new Error(formatMissingPlaytestEvidenceManifestError('task uploads must include playtest-evidence.json before upload.'));
2141
2138
  }
2142
2139
  try {
2143
2140
  const value = JSON.parse((0, node_fs_1.readFileSync)(manifestPath, 'utf8'));
@@ -2183,7 +2180,7 @@ function assertPlaytestCapturePaths(task, captures) {
2183
2180
  }
2184
2181
  }
2185
2182
  }
2186
- function assertNewGamePlaytestEvidenceManifest(task) {
2183
+ function assertTaskPlaytestEvidenceManifest(task) {
2187
2184
  const manifest = readPlaytestEvidenceManifest(task);
2188
2185
  const entries = Array.isArray(manifest.entries) ? manifest.entries : [];
2189
2186
  if (entries.length === 0) {
@@ -2222,28 +2219,6 @@ function assertNewGamePlaytestEvidenceManifest(task) {
2222
2219
  throw new Error(formatPlaytestEvidenceManifestError(`playtest-evidence.json must include a non-final self-playtest checkedAt after the last source change${sourceLastModifiedAt ? ` (${sourceLastModifiedAt})` : ''}.`));
2223
2220
  }
2224
2221
  }
2225
- function writeFinalPublishedRouteEvidence(input) {
2226
- const manifest = readPlaytestEvidenceManifest(input.task);
2227
- const entries = Array.isArray(manifest.entries) ? manifest.entries : [];
2228
- const finalEntry = {
2229
- environment: 'final',
2230
- url: input.url,
2231
- surface: 'DESKTOP',
2232
- captures: [],
2233
- actions: ['open owner draft play URL'],
2234
- statesObserved: input.status === 'PASSED' ? ['ready', 'nonblank-first-frame'] : [],
2235
- consoleErrors: input.message ? [input.message] : [],
2236
- checkedAt: input.checkedAt,
2237
- sourceLastModifiedAt: readRuntimeSourceLastModifiedAt(input.task.projectDir),
2238
- };
2239
- const nextManifest = {
2240
- ...manifest,
2241
- version: 1,
2242
- entries: [...entries, finalEntry],
2243
- finalPublishedRoute: finalEntry,
2244
- };
2245
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(input.task.projectDir, 'playtest-evidence.json'), `${JSON.stringify(nextManifest, null, 2)}\n`, 'utf8');
2246
- }
2247
2222
  function readRuntimeSourceLastModifiedAt(rootDir) {
2248
2223
  const root = (0, node_path_1.resolve)(rootDir);
2249
2224
  let latest = 0;
@@ -2313,6 +2288,9 @@ async function publishWorkerAppProject(input) {
2313
2288
  throw new Error(`agent_task_remix_ref_mismatch:${catalogueRemixRef}:${expectedRemixRef}`);
2314
2289
  }
2315
2290
  }
2291
+ if (input.kind === 'GAME_UPDATE') {
2292
+ assertTaskPlaytestEvidenceManifest(task);
2293
+ }
2316
2294
  if (input.kind === 'NEW_GAME' || input.kind === 'REMIX_GAME') {
2317
2295
  assertNewGameHeroListingArt(task);
2318
2296
  }
@@ -2355,7 +2333,7 @@ async function publishWorkerAppProject(input) {
2355
2333
  }
2356
2334
  if (input.kind === 'NEW_GAME' || input.kind === 'REMIX_GAME') {
2357
2335
  assertNewGameListingScreenshots(task);
2358
- assertNewGamePlaytestEvidenceManifest(task);
2336
+ assertTaskPlaytestEvidenceManifest(task);
2359
2337
  }
2360
2338
  const { upload: uploadResult, warnings } = await (0, apps_1.runAppPipeline)(input.client, task, {
2361
2339
  creatorUsername,
@@ -2363,10 +2341,11 @@ async function publishWorkerAppProject(input) {
2363
2341
  webBase: normalizePortalBase(input.webBase ?? undefined),
2364
2342
  token: input.token,
2365
2343
  user: input.user,
2366
- runLocalLaunchCheck: true,
2367
- runStagedUploadLaunchCheck: true,
2344
+ runLocalLoadCheck: true,
2345
+ runStagedUploadLoadCheck: true,
2368
2346
  ensureRegisteredAppShell: true,
2369
2347
  agentTaskId: input.taskId,
2348
+ mediaCaptureRequired: input.executionTarget === 'FIRST_PARTY',
2370
2349
  });
2371
2350
  if (!uploadResult.versionCreated || !uploadResult.version) {
2372
2351
  throw new Error(`App "${task.name}" upload did not return a created version.`);
@@ -2377,28 +2356,6 @@ async function publishWorkerAppProject(input) {
2377
2356
  if (typeof uploadResult.versionNodeId !== 'string' || uploadResult.versionNodeId.trim().length === 0) {
2378
2357
  throw new Error(`App "${task.name}" upload did not return versionNodeId.`);
2379
2358
  }
2380
- const finalPlayUrl = (0, appUrls_1.buildPlatformPrivateDraftPlayUrl)(input.webBase ?? null, {
2381
- creatorUsername,
2382
- appName: task.name,
2383
- appType: task.type ?? 'GAME',
2384
- version: uploadResult.version,
2385
- });
2386
- const finalPlayCheck = await (0, launchCheck_1.runPublishedHostedPlayCheck)({
2387
- targetUrl: finalPlayUrl,
2388
- surfaceTargets: task.surfaceTargets,
2389
- token: input.token,
2390
- currentUser: input.user,
2391
- });
2392
- writeFinalPublishedRouteEvidence({
2393
- task,
2394
- url: finalPlayUrl,
2395
- checkedAt: finalPlayCheck.checkedAt,
2396
- status: finalPlayCheck.status,
2397
- message: finalPlayCheck.message,
2398
- });
2399
- if (finalPlayCheck.status !== 'PASSED') {
2400
- throw new Error((0, launchCheck_1.formatHostedLaunchCheckFailure)(task.name, finalPlayCheck, 'final'));
2401
- }
2402
2359
  return {
2403
2360
  appId: app.id,
2404
2361
  appVersionId: uploadResult.versionId,