@playdrop/playdrop-cli 0.9.6 → 0.10.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 (73) hide show
  1. package/config/client-meta.json +2 -2
  2. package/dist/apiClient.d.ts +10 -0
  3. package/dist/apiClient.js +55 -2
  4. package/dist/appUrls.d.ts +1 -0
  5. package/dist/appUrls.js +9 -0
  6. package/dist/apps/build.js +39 -28
  7. package/dist/apps/index.d.ts +1 -0
  8. package/dist/apps/index.js +2 -0
  9. package/dist/apps/launchCheck.d.ts +2 -0
  10. package/dist/apps/launchCheck.js +31 -6
  11. package/dist/apps/registration.d.ts +1 -0
  12. package/dist/apps/registration.js +1 -0
  13. package/dist/apps/upload.d.ts +1 -0
  14. package/dist/apps/upload.js +4 -17
  15. package/dist/captureRuntime.d.ts +14 -0
  16. package/dist/captureRuntime.js +329 -0
  17. package/dist/catalogue.d.ts +4 -2
  18. package/dist/catalogue.js +50 -7
  19. package/dist/commandContext.js +61 -4
  20. package/dist/commands/capture.d.ts +1 -0
  21. package/dist/commands/capture.js +30 -13
  22. package/dist/commands/captureRemote.d.ts +2 -0
  23. package/dist/commands/captureRemote.js +90 -0
  24. package/dist/commands/create.d.ts +0 -1
  25. package/dist/commands/create.js +2 -151
  26. package/dist/commands/creations.d.ts +0 -13
  27. package/dist/commands/creations.js +0 -141
  28. package/dist/commands/dev.d.ts +2 -1
  29. package/dist/commands/dev.js +23 -6
  30. package/dist/commands/devServer.js +3 -1
  31. package/dist/commands/generation.d.ts +1 -0
  32. package/dist/commands/generation.js +274 -0
  33. package/dist/commands/review.d.ts +46 -0
  34. package/dist/commands/review.js +353 -0
  35. package/dist/commands/upload.d.ts +27 -1
  36. package/dist/commands/upload.js +962 -21
  37. package/dist/commands/validate.js +5 -0
  38. package/dist/commands/worker/runtime.d.ts +81 -0
  39. package/dist/commands/worker/runtime.js +458 -0
  40. package/dist/commands/worker.d.ts +158 -0
  41. package/dist/commands/worker.js +2626 -0
  42. package/dist/config.d.ts +2 -0
  43. package/dist/config.js +23 -0
  44. package/dist/index.js +116 -30
  45. package/dist/shellProbe.d.ts +1 -1
  46. package/dist/shellProbe.js +3 -3
  47. package/dist/workspaceAuth.d.ts +3 -0
  48. package/dist/workspaceAuth.js +14 -0
  49. package/node_modules/@playdrop/api-client/dist/client.d.ts +36 -15
  50. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  51. package/node_modules/@playdrop/api-client/dist/client.js +2 -2
  52. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +5 -2
  53. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
  54. package/node_modules/@playdrop/api-client/dist/domains/admin.js +51 -3
  55. package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.d.ts +75 -0
  56. package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.d.ts.map +1 -0
  57. package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.js +478 -0
  58. package/node_modules/@playdrop/api-client/dist/index.d.ts +36 -15
  59. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  60. package/node_modules/@playdrop/api-client/dist/index.js +153 -42
  61. package/node_modules/@playdrop/config/client-meta.json +2 -2
  62. package/node_modules/@playdrop/types/dist/api.d.ts +662 -75
  63. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  64. package/node_modules/@playdrop/types/dist/api.js +100 -9
  65. package/node_modules/@playdrop/types/dist/app.d.ts +2 -0
  66. package/node_modules/@playdrop/types/dist/app.d.ts.map +1 -1
  67. package/node_modules/@playdrop/types/dist/app.js +3 -0
  68. package/node_modules/@playdrop/types/dist/version.d.ts +1 -0
  69. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  70. package/package.json +2 -1
  71. package/node_modules/@playdrop/api-client/dist/domains/game-ideas.d.ts +0 -46
  72. package/node_modules/@playdrop/api-client/dist/domains/game-ideas.d.ts.map +0 -1
  73. package/node_modules/@playdrop/api-client/dist/domains/game-ideas.js +0 -177
@@ -1,7 +1,7 @@
1
1
  {
2
- "version": "0.9.6",
3
- "runtimeSdkVersion": "0.7.21",
2
+ "version": "0.10.1",
4
3
  "build": 1,
4
+ "runtimeSdkVersion": "0.10.0",
5
5
  "clients": {
6
6
  "all": {
7
7
  "minimumVersion": "0.7.4"
@@ -3,7 +3,17 @@ import { type AiClient } from '@playdrop/ai-client';
3
3
  type ClientOptions = {
4
4
  baseUrl: string;
5
5
  token?: string | null;
6
+ onBehalfCreatorUsername?: string | null;
7
+ agentTaskToken?: string | null;
8
+ agentTaskId?: number | null;
9
+ agentTaskAttempt?: number | null;
6
10
  };
11
+ export declare function createCliClientHeaders(input: {
12
+ onBehalfCreatorUsername?: string | null;
13
+ agentTaskToken?: string | null;
14
+ agentTaskId?: number | null;
15
+ agentTaskAttempt?: number | null;
16
+ }): Record<string, string>;
7
17
  export declare function createCliApiClient(options: ClientOptions): ApiClient;
8
18
  export declare function createCliAiClient(options: ClientOptions): AiClient;
9
19
  export {};
package/dist/apiClient.js CHANGED
@@ -1,21 +1,74 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCliClientHeaders = createCliClientHeaders;
3
4
  exports.createCliApiClient = createCliApiClient;
4
5
  exports.createCliAiClient = createCliAiClient;
5
6
  const api_client_1 = require("@playdrop/api-client");
6
7
  const ai_client_1 = require("@playdrop/ai-client");
7
8
  const clientInfo_1 = require("./clientInfo");
9
+ function readPositiveIntEnv(name) {
10
+ const raw = process.env[name]?.trim();
11
+ if (!raw) {
12
+ return null;
13
+ }
14
+ const parsed = Number(raw);
15
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
16
+ }
17
+ function createCliClientHeaders(input) {
18
+ const onBehalfCreatorUsername = input.onBehalfCreatorUsername;
19
+ const creatorUsername = typeof onBehalfCreatorUsername === 'string'
20
+ ? onBehalfCreatorUsername.trim()
21
+ : '';
22
+ const agentTaskToken = typeof input.agentTaskToken === 'string'
23
+ ? input.agentTaskToken.trim()
24
+ : '';
25
+ const agentTaskId = Number.isInteger(input.agentTaskId) && Number(input.agentTaskId) > 0
26
+ ? Number(input.agentTaskId)
27
+ : readPositiveIntEnv('PLAYDROP_WORKER_TASK_ID');
28
+ const agentTaskAttempt = Number.isInteger(input.agentTaskAttempt) && Number(input.agentTaskAttempt) > 0
29
+ ? Number(input.agentTaskAttempt)
30
+ : readPositiveIntEnv('PLAYDROP_WORKER_TASK_ATTEMPT');
31
+ if (agentTaskToken && process.env.PLAYDROP_WORKER_CONTEXT === '1' && (!agentTaskId || !agentTaskAttempt)) {
32
+ throw new Error('worker_ai_generation_task_context_missing');
33
+ }
34
+ return {
35
+ ...(0, clientInfo_1.createClientHeaders)(),
36
+ ...(creatorUsername
37
+ ? {
38
+ 'x-playdrop-creator-username': creatorUsername,
39
+ 'x-playdrop-ai-paid-by': 'creator',
40
+ }
41
+ : {}),
42
+ ...(agentTaskToken
43
+ ? {
44
+ 'x-playdrop-agent-task-token': agentTaskToken,
45
+ ...(agentTaskId ? { 'x-playdrop-agent-task-id': String(agentTaskId) } : {}),
46
+ ...(agentTaskAttempt ? { 'x-playdrop-agent-task-attempt': String(agentTaskAttempt) } : {}),
47
+ }
48
+ : {}),
49
+ };
50
+ }
8
51
  function createCliApiClient(options) {
9
52
  return (0, api_client_1.createApiClient)({
10
53
  baseUrl: options.baseUrl,
11
- clientHeaders: () => (0, clientInfo_1.createClientHeaders)(),
54
+ clientHeaders: () => createCliClientHeaders({
55
+ onBehalfCreatorUsername: options.onBehalfCreatorUsername,
56
+ agentTaskToken: options.agentTaskToken,
57
+ agentTaskId: options.agentTaskId,
58
+ agentTaskAttempt: options.agentTaskAttempt,
59
+ }),
12
60
  tokenProvider: () => options.token ?? null,
13
61
  });
14
62
  }
15
63
  function createCliAiClient(options) {
16
64
  return (0, ai_client_1.createAiClient)({
17
65
  baseUrl: options.baseUrl,
18
- clientHeaders: () => (0, clientInfo_1.createClientHeaders)(),
66
+ clientHeaders: () => createCliClientHeaders({
67
+ onBehalfCreatorUsername: options.onBehalfCreatorUsername,
68
+ agentTaskToken: options.agentTaskToken,
69
+ agentTaskId: options.agentTaskId,
70
+ agentTaskAttempt: options.agentTaskAttempt,
71
+ }),
19
72
  tokenProvider: () => options.token ?? null,
20
73
  });
21
74
  }
package/dist/appUrls.d.ts CHANGED
@@ -11,6 +11,7 @@ export declare function buildPlatformDevUrl(webBase: string | null | undefined,
11
11
  devAuth?: 'prompt' | 'anonymous' | 'viewer' | 'player';
12
12
  player?: string | null;
13
13
  launchCheck?: boolean;
14
+ localDevPort?: number | string | null;
14
15
  }): string;
15
16
  export declare function buildUploadLaunchCheckUrl(webBase: string | null | undefined, input: AppUrlInput & {
16
17
  sessionId: string;
package/dist/appUrls.js CHANGED
@@ -48,6 +48,15 @@ function buildPlatformDevUrl(webBase, input) {
48
48
  if (input.launchCheck) {
49
49
  url.searchParams.set('launchCheck', '1');
50
50
  }
51
+ if (input.localDevPort !== null && input.localDevPort !== undefined) {
52
+ const port = typeof input.localDevPort === 'number'
53
+ ? input.localDevPort
54
+ : Number.parseInt(String(input.localDevPort), 10);
55
+ if (!Number.isInteger(port) || port <= 0 || port > 65535) {
56
+ throw new Error('invalid_local_dev_port');
57
+ }
58
+ url.searchParams.set('localDevPort', String(port));
59
+ }
51
60
  return url.toString();
52
61
  }
53
62
  function buildUploadLaunchCheckUrl(webBase, input) {
@@ -249,6 +249,9 @@ function collectProjectFiles(rootDir, rules, options) {
249
249
  if (!relativePath)
250
250
  continue;
251
251
  const isDir = entry.isDirectory();
252
+ const exclusionTarget = isDir ? `${relativePath}/` : relativePath;
253
+ if (isExcludedRelativePath(exclusionTarget, excludeRelativeFiles))
254
+ continue;
252
255
  if (!isDir && enforceReservedBundleNames && isReservedBundlePath(relativePath)) {
253
256
  throw new Error(`[apps][build] Reserved bundle filename "${relativePath}" is not allowed. Use dedicated source/ecs/server upload fields.`);
254
257
  }
@@ -270,7 +273,7 @@ function collectProjectFiles(rootDir, rules, options) {
270
273
  }
271
274
  if (!stats.isFile())
272
275
  continue;
273
- if (excludeRelativeFiles.has(relativePath))
276
+ if (isExcludedRelativePath(relativePath, excludeRelativeFiles))
274
277
  continue;
275
278
  const record = {
276
279
  absolutePath,
@@ -287,6 +290,17 @@ function collectProjectFiles(rootDir, rules, options) {
287
290
  results.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
288
291
  return results;
289
292
  }
293
+ function isExcludedRelativePath(relativePath, excludedRelativeFiles) {
294
+ if (excludedRelativeFiles.has(relativePath)) {
295
+ return true;
296
+ }
297
+ for (const excludedPath of excludedRelativeFiles) {
298
+ if (excludedPath.endsWith('/') && relativePath.startsWith(excludedPath)) {
299
+ return true;
300
+ }
301
+ }
302
+ return false;
303
+ }
290
304
  // Files that are uploaded separately and should not be included in the bundle zip.
291
305
  const RESERVED_BUNDLE_FILES = new Set(['source.zip', 'ecs.json', 'server.js']);
292
306
  function isReservedBundlePath(relativePath) {
@@ -297,32 +311,6 @@ function isReservedBundlePath(relativePath) {
297
311
  function resolveRuntimeRoot(task) {
298
312
  return (0, node_path_1.resolve)((0, node_path_1.dirname)(task.filePath));
299
313
  }
300
- function collectSeparatelyUploadedSourceFiles(task) {
301
- const excludedFiles = new Set();
302
- const addFile = (filePath) => {
303
- if (!filePath) {
304
- return;
305
- }
306
- const absolutePath = (0, node_path_1.resolve)(filePath);
307
- if (!isPathWithinRoot(task.projectDir, absolutePath)) {
308
- return;
309
- }
310
- const relativePath = normalizeRelativePath(task.projectDir, absolutePath);
311
- if (!relativePath || relativePath.startsWith('..')) {
312
- return;
313
- }
314
- excludedFiles.add(relativePath);
315
- };
316
- addFile(task.listing?.iconPath);
317
- addFile(task.listing?.heroPortraitPath);
318
- addFile(task.listing?.heroLandscapePath);
319
- task.listing?.screenshotPortraitPaths?.forEach(addFile);
320
- task.listing?.screenshotLandscapePaths?.forEach(addFile);
321
- task.listing?.videoPortraitPaths?.forEach(addFile);
322
- task.listing?.videoLandscapePaths?.forEach(addFile);
323
- task.achievements?.forEach((definition) => addFile(definition.iconPath));
324
- return Array.from(excludedFiles).sort((left, right) => left.localeCompare(right));
325
- }
326
314
  function resolveBundleEntryPoint(task, runtimeRoot) {
327
315
  const entryPoint = normalizeRelativePath(runtimeRoot, task.filePath);
328
316
  if (!entryPoint || entryPoint.startsWith('..')) {
@@ -330,6 +318,28 @@ function resolveBundleEntryPoint(task, runtimeRoot) {
330
318
  }
331
319
  return entryPoint;
332
320
  }
321
+ function normalizeBundleArchiveExclusions(task, runtimeRoot) {
322
+ const exclusions = task.bundleArchiveExcludeRelativeFiles ?? [];
323
+ const runtimeRootAbsolute = (0, node_path_1.resolve)(runtimeRoot);
324
+ const normalized = [];
325
+ for (const exclusion of exclusions) {
326
+ const trimmed = exclusion.trim();
327
+ if (!trimmed) {
328
+ continue;
329
+ }
330
+ const isDirectory = trimmed.replace(/\\/g, '/').endsWith('/');
331
+ const absolutePath = (0, node_path_1.resolve)(task.projectDir, trimmed);
332
+ if (!isPathWithinRoot(runtimeRootAbsolute, absolutePath)) {
333
+ continue;
334
+ }
335
+ const runtimeRelativePath = normalizeRelativePath(runtimeRootAbsolute, absolutePath);
336
+ if (!runtimeRelativePath || runtimeRelativePath.startsWith('..')) {
337
+ continue;
338
+ }
339
+ normalized.push(isDirectory ? `${runtimeRelativePath}/` : runtimeRelativePath);
340
+ }
341
+ return normalized;
342
+ }
333
343
  /**
334
344
  * Collect files for the bundle zip.
335
345
  * Collect files for the bundle zip from the runtime root (dirname(entry file)).
@@ -340,6 +350,7 @@ function collectBundleFiles(task) {
340
350
  const rules = buildIgnoreRules(task.projectDir);
341
351
  const entries = collectProjectFiles(runtimeRoot, rules, {
342
352
  includeRelativeFiles: [entryPoint],
353
+ excludeRelativeFiles: normalizeBundleArchiveExclusions(task, runtimeRoot),
343
354
  enforceReservedBundleNames: true,
344
355
  });
345
356
  return { entries, entryPoint };
@@ -486,7 +497,7 @@ function createZipArchive(entries) {
486
497
  function createSourceArchive(task) {
487
498
  const rules = buildIgnoreRules(task.projectDir);
488
499
  const includeFiles = ['README.md', 'AGENTS.md'];
489
- const excludedFiles = collectSeparatelyUploadedSourceFiles(task);
500
+ const excludedFiles = task.sourceArchiveExcludeRelativeFiles ?? [];
490
501
  if (task.catalogueAbsolutePath && (0, node_fs_1.existsSync)(task.catalogueAbsolutePath)) {
491
502
  const relativeCataloguePath = normalizeRelativePath(task.projectDir, task.catalogueAbsolutePath) || 'catalogue.json';
492
503
  includeFiles.push(relativeCataloguePath);
@@ -23,6 +23,7 @@ export type AppPipelineOptions = {
23
23
  runStagedUploadLaunchCheck?: boolean;
24
24
  launchCheckTimeoutMs?: number;
25
25
  ensureRegisteredAppShell?: boolean;
26
+ agentTaskId?: number;
26
27
  };
27
28
  export declare function runAppPipeline(client: ApiClient, task: AppTask, options?: AppPipelineOptions): Promise<AppPipelineResult>;
28
29
  export { collectAppValidationWarnings, validateAppTask, buildApp, uploadApp, runFormatScript };
@@ -29,6 +29,7 @@ async function runAppPipeline(client, task, options) {
29
29
  client,
30
30
  creatorUsername,
31
31
  task,
32
+ agentTaskId: options.agentTaskId,
32
33
  });
33
34
  }
34
35
  if (!isExternal && options?.runLocalLaunchCheck) {
@@ -65,6 +66,7 @@ async function runAppPipeline(client, task, options) {
65
66
  user: options?.user ?? null,
66
67
  runStagedUploadLaunchCheck: options?.runStagedUploadLaunchCheck,
67
68
  launchCheckTimeoutMs: options?.launchCheckTimeoutMs,
69
+ agentTaskId: options?.agentTaskId,
68
70
  };
69
71
  const upload = await (0, upload_1.uploadApp)(client, task, artifacts, uploadOptions);
70
72
  return { artifacts, upload, warnings };
@@ -15,9 +15,11 @@ export declare function runLocalHostedLaunchCheck(input: {
15
15
  webBase: string | null | undefined;
16
16
  creatorUsername: string;
17
17
  task: AppTask;
18
+ devRouterPort?: number;
18
19
  timeoutMs?: number;
19
20
  token?: string | null;
20
21
  currentUser?: UserResponse | null;
22
+ allowUnregisteredViewerLaunch?: boolean;
21
23
  }): Promise<HostedLaunchCheckResult>;
22
24
  export declare function runUploadedHostedLaunchCheck(input: {
23
25
  client: ApiClient;
@@ -49,6 +49,20 @@ function buildFailedLaunchCheckResult(errorCode, message, artifactFingerprint) {
49
49
  artifactFingerprint,
50
50
  };
51
51
  }
52
+ function formatCaptureErrors(errors) {
53
+ if (!Array.isArray(errors) || errors.length === 0) {
54
+ return '';
55
+ }
56
+ const normalized = errors
57
+ .map((entry) => entry.replace(/\s+/g, ' ').trim())
58
+ .filter(Boolean)
59
+ .slice(0, 3)
60
+ .map((entry) => entry.length > 240 ? `${entry.slice(0, 237)}...` : entry);
61
+ if (normalized.length === 0) {
62
+ return '';
63
+ }
64
+ return ` First error${normalized.length === 1 ? '' : 's'}: ${normalized.join(' | ')}`;
65
+ }
52
66
  function formatHostedLaunchCheckFailure(taskName, result, scope = 'local') {
53
67
  const scopeLabel = scope === 'staged' ? 'staged upload preview' : 'local hosted preview';
54
68
  const errorCode = result.errorCode?.trim() || 'startup_failure';
@@ -113,7 +127,7 @@ async function runHostedLaunchCheckWithCapture(options) {
113
127
  savedSessionBootstrap: options.savedSessionBootstrap ?? false,
114
128
  });
115
129
  if (result.errorCount > 0) {
116
- return buildFailedLaunchCheckResult('startup_failure', `Hosted app emitted ${result.errorCount} console, page, or network error(s) during startup.`, options.artifactFingerprint ?? null);
130
+ return buildFailedLaunchCheckResult('startup_failure', `Hosted app emitted ${result.errorCount} console, page, or network error(s) during startup.${formatCaptureErrors(result.errors)}`, options.artifactFingerprint ?? null);
117
131
  }
118
132
  if (result.hostedLaunchState?.state !== expectedHostedLaunchState) {
119
133
  return buildFailedLaunchCheckResult('startup_failure', expectedHostedLaunchState === 'login_required'
@@ -135,6 +149,7 @@ async function runHostedLaunchCheckWithCapture(options) {
135
149
  }
136
150
  async function startOrReuseMountedDevRuntime(input) {
137
151
  const appTypeSlug = (0, appUrls_1.getAppTypeSlug)(input.task.type ?? 'GAME');
152
+ const devRouterPort = input.devRouterPort ?? devServer_1.DEV_ROUTER_PORT;
138
153
  const runtimeAssetManifest = input.task.hostingMode === 'EXTERNAL' || input.task.externalUrl
139
154
  ? (0, devRuntimeAssets_1.createEmptyDevRuntimeAssetManifest)()
140
155
  : await (0, devRuntimeAssets_1.buildDevRuntimeAssetManifest)({
@@ -142,20 +157,20 @@ async function startOrReuseMountedDevRuntime(input) {
142
157
  apiBase: input.apiBase,
143
158
  task: input.task,
144
159
  creatorUsername: input.creatorUsername,
145
- appBaseUrl: `http://127.0.0.1:${devServer_1.DEV_ROUTER_PORT}/apps/dev/${encodeURIComponent(input.creatorUsername)}/${encodeURIComponent(appTypeSlug)}/${encodeURIComponent(input.task.name)}/`,
160
+ appBaseUrl: `http://127.0.0.1:${devRouterPort}/apps/dev/${encodeURIComponent(input.creatorUsername)}/${encodeURIComponent(appTypeSlug)}/${encodeURIComponent(input.task.name)}/`,
146
161
  });
147
162
  const serverAlreadyRunning = await (0, devServer_1.isDevServerAvailable)({
148
163
  creatorUsername: input.creatorUsername,
149
164
  appType: appTypeSlug,
150
165
  appName: input.task.name,
151
- port: devServer_1.DEV_ROUTER_PORT,
166
+ port: devRouterPort,
152
167
  }, 750);
153
168
  if (serverAlreadyRunning) {
154
169
  await (0, devServer_1.updateMountedDevRuntimeAssetManifest)({
155
170
  creatorUsername: input.creatorUsername,
156
171
  appName: input.task.name,
157
172
  runtimeAssetManifest,
158
- port: devServer_1.DEV_ROUTER_PORT,
173
+ port: devRouterPort,
159
174
  });
160
175
  return { handle: null, runtimeAssetManifest };
161
176
  }
@@ -165,7 +180,7 @@ async function startOrReuseMountedDevRuntime(input) {
165
180
  appType: appTypeSlug,
166
181
  creatorUsername: input.creatorUsername,
167
182
  htmlPath: input.task.filePath,
168
- port: devServer_1.DEV_ROUTER_PORT,
183
+ port: devRouterPort,
169
184
  projectInfo: (0, devShared_1.findProjectInfo)(input.task.filePath),
170
185
  runtimeAssetManifest,
171
186
  });
@@ -189,12 +204,19 @@ async function runLocalHostedLaunchCheck(input) {
189
204
  apiBase: input.apiBase,
190
205
  creatorUsername: input.creatorUsername,
191
206
  task: input.task,
207
+ devRouterPort: input.devRouterPort,
192
208
  });
209
+ const localDevPort = input.devRouterPort === undefined || input.devRouterPort === devServer_1.DEV_ROUTER_PORT
210
+ ? null
211
+ : input.devRouterPort;
193
212
  const localAppMetadata = mountedRuntime.runtimeAssetManifest.response.localAppMetadata;
194
213
  const anonymousExpectedState = resolveAnonymousHostedLaunchState(localAppMetadata);
195
214
  if (anonymousExpectedState === 'login_required') {
196
215
  const registeredApp = await (0, registration_1.fetchRegisteredAppShell)(input.client, input.creatorUsername, input.task.name);
197
- if (!registeredApp?.id) {
216
+ const canLaunchUnregisteredViewer = input.allowUnregisteredViewerLaunch === true
217
+ && Boolean(input.token)
218
+ && Boolean(input.currentUser);
219
+ if (!registeredApp?.id && !canLaunchUnregisteredViewer) {
198
220
  return buildRegistrationRequiredLaunchCheckFailure(input.task.name);
199
221
  }
200
222
  const anonymousGateResult = await runHostedLaunchCheckWithCapture({
@@ -204,6 +226,7 @@ async function runLocalHostedLaunchCheck(input) {
204
226
  appType: input.task.type ?? 'GAME',
205
227
  devAuth: 'anonymous',
206
228
  launchCheck: true,
229
+ localDevPort,
207
230
  }),
208
231
  timeoutMs: input.timeoutMs,
209
232
  expectedHostedLaunchState: anonymousExpectedState,
@@ -218,6 +241,7 @@ async function runLocalHostedLaunchCheck(input) {
218
241
  appType: input.task.type ?? 'GAME',
219
242
  devAuth: 'viewer',
220
243
  launchCheck: true,
244
+ localDevPort,
221
245
  }),
222
246
  timeoutMs: input.timeoutMs,
223
247
  expectedHostedLaunchState: resolvePostAuthHostedLaunchState(localAppMetadata.controllerMode),
@@ -233,6 +257,7 @@ async function runLocalHostedLaunchCheck(input) {
233
257
  appType: input.task.type ?? 'GAME',
234
258
  devAuth: 'anonymous',
235
259
  launchCheck: true,
260
+ localDevPort,
236
261
  }),
237
262
  timeoutMs: input.timeoutMs,
238
263
  expectedHostedLaunchState: anonymousExpectedState,
@@ -6,6 +6,7 @@ export declare function ensureRegisteredHostedAppShell(input: {
6
6
  client: ApiClient;
7
7
  creatorUsername: string;
8
8
  task: AppTask;
9
+ agentTaskId?: number;
9
10
  }): Promise<{
10
11
  app: AppResponse;
11
12
  created: boolean;
@@ -27,6 +27,7 @@ async function ensureRegisteredHostedAppShell(input) {
27
27
  const createdResponse = await input.client.createApp(input.creatorUsername, {
28
28
  name: input.task.name,
29
29
  displayName,
30
+ ...(input.agentTaskId !== undefined ? { agentTaskId: input.agentTaskId } : {}),
30
31
  });
31
32
  app = createdResponse.app;
32
33
  created = true;
@@ -22,5 +22,6 @@ export type AppUploadOptions = {
22
22
  user?: UserResponse | null;
23
23
  runStagedUploadLaunchCheck?: boolean;
24
24
  launchCheckTimeoutMs?: number;
25
+ agentTaskId?: number;
25
26
  };
26
27
  export declare function uploadApp(client: ApiClient, task: AppTask, artifacts: AppBuildArtifacts | null, options?: AppUploadOptions): Promise<AppUploadResult>;
@@ -125,26 +125,11 @@ async function prepareOwnedAssetUpload(task) {
125
125
  files,
126
126
  };
127
127
  }
128
- // eslint-disable-next-line complexity -- Source archive failures need distinct guidance when publish-only media is already excluded.
129
- function ensurePreparedArtifactSize(file, maxBytes, code, label, task) {
128
+ function ensurePreparedArtifactSize(file, maxBytes, code, label) {
130
129
  if (!file) {
131
130
  return 0;
132
131
  }
133
132
  if (file.size > maxBytes) {
134
- if (code === 'source_too_large' && task) {
135
- const hasSeparatePublishMedia = Boolean(task.listing?.iconPath
136
- || task.listing?.heroPortraitPath
137
- || task.listing?.heroLandscapePath
138
- || task.listing?.screenshotPortraitPaths?.length
139
- || task.listing?.screenshotLandscapePaths?.length
140
- || task.listing?.videoPortraitPaths?.length
141
- || task.listing?.videoLandscapePaths?.length
142
- || task.achievements?.length);
143
- const suffix = hasSeparatePublishMedia
144
- ? ' Listing and achievement media are uploaded separately and excluded from source.zip. The remaining source archive is still too large.'
145
- : '';
146
- throw createUploadValidationError(code, `${label} must be under ${formatLimitBytes(maxBytes)}. Received ${file.size} bytes.${suffix}`);
147
- }
148
133
  throw createUploadValidationError(code, `${label} must be under ${formatLimitBytes(maxBytes)}. Received ${file.size} bytes.`);
149
134
  }
150
135
  return file.size;
@@ -160,7 +145,7 @@ function pushPreparedFile(taskName, currentTotal, prepared) {
160
145
  return accumulateUploadBytes(taskName, currentTotal, prepared.size);
161
146
  }
162
147
  function pushPreparedArtifact(task, currentTotal, file, maxBytes, code, label) {
163
- const bytes = ensurePreparedArtifactSize(file, maxBytes, code, label, task);
148
+ const bytes = ensurePreparedArtifactSize(file, maxBytes, code, label);
164
149
  return accumulateUploadBytes(task.name, currentTotal, bytes);
165
150
  }
166
151
  async function uploadApp(client, task, artifacts, options) {
@@ -256,8 +241,10 @@ async function uploadAppVersion(client, task, artifacts, options) {
256
241
  }
257
242
  const initializeRequest = {
258
243
  version: task.version,
244
+ agentTaskId: options?.agentTaskId,
259
245
  releaseNotes: task.releaseNotes,
260
246
  visibility: task.versionVisibility,
247
+ setAsCurrent: task.versionVisibility === 'PRIVATE' ? false : undefined,
261
248
  license: task.license,
262
249
  remixRef: task.remix,
263
250
  usesAssets: task.uses.assets.map((entry) => ({
@@ -24,8 +24,21 @@ export type RunCaptureOptions = {
24
24
  enableCaptureBridge?: boolean;
25
25
  requireHostedLaunchReady?: boolean;
26
26
  expectedHostedLaunchState?: HostedLaunchExpectedState;
27
+ actions?: CaptureAction[];
27
28
  };
28
29
  export type HostedLaunchExpectedState = 'ready' | 'login_required' | 'controller_required' | 'surface_unsupported';
30
+ export type CaptureAction = {
31
+ type: 'click';
32
+ x: number;
33
+ y: number;
34
+ button?: 'left' | 'right' | 'middle';
35
+ } | {
36
+ type: 'press';
37
+ key: string;
38
+ } | {
39
+ type: 'wait';
40
+ ms: number;
41
+ };
29
42
  export type CaptureHostedLaunchState = {
30
43
  state: 'ready';
31
44
  } | {
@@ -41,6 +54,7 @@ export type CaptureHostedLaunchState = {
41
54
  };
42
55
  export type CaptureRunResult = {
43
56
  errorCount: number;
57
+ errors: string[];
44
58
  warningCount: number;
45
59
  warnings: string[];
46
60
  finalUrl: string;