@playdrop/playdrop-cli 0.12.3 → 0.12.4
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.
- package/config/client-meta.json +1 -1
- package/dist/appUrls.d.ts +9 -0
- package/dist/appUrls.js +29 -0
- package/dist/apps/index.d.ts +2 -0
- package/dist/apps/index.js +2 -0
- package/dist/apps/loadCheck.d.ts +4 -0
- package/dist/apps/loadCheck.js +48 -13
- package/dist/apps/upload.d.ts +2 -0
- package/dist/apps/upload.js +1 -0
- package/dist/commands/captureListing.d.ts +3 -1
- package/dist/commands/captureListing.js +24 -8
- package/dist/commands/check.js +40 -26
- package/dist/commands/create.js +47 -2
- package/dist/commands/devShared.d.ts +5 -0
- package/dist/commands/devShared.js +9 -0
- package/dist/commands/taskCaptureSession.d.ts +4 -0
- package/dist/commands/taskCaptureSession.js +21 -0
- package/dist/commands/upload.d.ts +1 -0
- package/dist/commands/upload.js +16 -1
- package/dist/commands/worker.d.ts +5 -1
- package/dist/commands/worker.js +187 -119
- package/node_modules/@playdrop/config/client-meta.json +1 -1
- package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +9 -10
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +3 -60
- package/package.json +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/game-ideas.d.ts +0 -46
- package/node_modules/@playdrop/api-client/dist/domains/game-ideas.d.ts.map +0 -1
- package/node_modules/@playdrop/api-client/dist/domains/game-ideas.js +0 -177
- package/node_modules/@playdrop/api-client/dist/domains/music.d.ts +0 -27
- package/node_modules/@playdrop/api-client/dist/domains/music.d.ts.map +0 -1
- package/node_modules/@playdrop/api-client/dist/domains/music.js +0 -96
package/config/client-meta.json
CHANGED
package/dist/appUrls.d.ts
CHANGED
|
@@ -4,6 +4,14 @@ export type AppUrlInput = {
|
|
|
4
4
|
appName: string;
|
|
5
5
|
appType?: AppType | string | null;
|
|
6
6
|
};
|
|
7
|
+
export type TaskCaptureSession = {
|
|
8
|
+
appId: number;
|
|
9
|
+
taskId: number;
|
|
10
|
+
taskToken: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function appendTaskCaptureSession(url: URL, session: TaskCaptureSession | null | undefined): void;
|
|
13
|
+
export declare function redactTaskCaptureSession(url: string): string;
|
|
14
|
+
export declare function redactTaskCaptureToken(text: string, sourceUrl: string): string;
|
|
7
15
|
export declare function normalizePlaydropWebBase(webBase?: string | null): string;
|
|
8
16
|
export declare function getAppTypeSlug(type: AppType | string | null | undefined): string;
|
|
9
17
|
export declare function buildPlatformPlayUrl(webBase: string | null | undefined, input: AppUrlInput): string;
|
|
@@ -15,6 +23,7 @@ export declare function buildPlatformDevUrl(webBase: string | null | undefined,
|
|
|
15
23
|
player?: string | null;
|
|
16
24
|
launchCheck?: boolean;
|
|
17
25
|
localDevPort?: number | string | null;
|
|
26
|
+
captureSession?: TaskCaptureSession | null;
|
|
18
27
|
}): string;
|
|
19
28
|
export declare function buildUploadLaunchCheckUrl(webBase: string | null | undefined, input: AppUrlInput & {
|
|
20
29
|
sessionId: string;
|
package/dist/appUrls.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.appendTaskCaptureSession = appendTaskCaptureSession;
|
|
4
|
+
exports.redactTaskCaptureSession = redactTaskCaptureSession;
|
|
5
|
+
exports.redactTaskCaptureToken = redactTaskCaptureToken;
|
|
3
6
|
exports.normalizePlaydropWebBase = normalizePlaydropWebBase;
|
|
4
7
|
exports.getAppTypeSlug = getAppTypeSlug;
|
|
5
8
|
exports.buildPlatformPlayUrl = buildPlatformPlayUrl;
|
|
@@ -7,6 +10,31 @@ exports.buildPlatformPrivateDraftPlayUrl = buildPlatformPrivateDraftPlayUrl;
|
|
|
7
10
|
exports.buildPlatformDevUrl = buildPlatformDevUrl;
|
|
8
11
|
exports.buildUploadLaunchCheckUrl = buildUploadLaunchCheckUrl;
|
|
9
12
|
const DEFAULT_WEB_BASE = 'https://www.playdrop.ai';
|
|
13
|
+
function appendTaskCaptureSession(url, session) {
|
|
14
|
+
if (!session) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (!Number.isInteger(session.appId) || session.appId <= 0) {
|
|
18
|
+
throw new Error('invalid_capture_app_id');
|
|
19
|
+
}
|
|
20
|
+
if (!Number.isInteger(session.taskId) || session.taskId <= 0 || !session.taskToken.trim()) {
|
|
21
|
+
throw new Error('invalid_capture_task_session');
|
|
22
|
+
}
|
|
23
|
+
url.searchParams.set('captureAppId', String(session.appId));
|
|
24
|
+
url.searchParams.set('captureTaskId', String(session.taskId));
|
|
25
|
+
url.searchParams.set('captureTaskToken', session.taskToken.trim());
|
|
26
|
+
}
|
|
27
|
+
function redactTaskCaptureSession(url) {
|
|
28
|
+
const redacted = new URL(url);
|
|
29
|
+
if (redacted.searchParams.has('captureTaskToken')) {
|
|
30
|
+
redacted.searchParams.set('captureTaskToken', '<redacted>');
|
|
31
|
+
}
|
|
32
|
+
return redacted.toString();
|
|
33
|
+
}
|
|
34
|
+
function redactTaskCaptureToken(text, sourceUrl) {
|
|
35
|
+
const taskToken = new URL(sourceUrl).searchParams.get('captureTaskToken')?.trim() ?? '';
|
|
36
|
+
return taskToken ? text.split(taskToken).join('<redacted>') : text;
|
|
37
|
+
}
|
|
10
38
|
function normalizePlaydropWebBase(webBase) {
|
|
11
39
|
const base = typeof webBase === 'string' && webBase.trim().length > 0 ? webBase.trim() : DEFAULT_WEB_BASE;
|
|
12
40
|
return base.replace(/\/$/, '');
|
|
@@ -69,6 +97,7 @@ function buildPlatformDevUrl(webBase, input) {
|
|
|
69
97
|
}
|
|
70
98
|
url.searchParams.set('localDevPort', String(port));
|
|
71
99
|
}
|
|
100
|
+
appendTaskCaptureSession(url, input.captureSession);
|
|
72
101
|
return url.toString();
|
|
73
102
|
}
|
|
74
103
|
function buildUploadLaunchCheckUrl(webBase, input) {
|
package/dist/apps/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ApiClient } from '@playdrop/api-client';
|
|
2
2
|
import type { UserResponse } from '@playdrop/types';
|
|
3
3
|
import type { AppTask } from '../catalogue';
|
|
4
|
+
import type { TaskCaptureSession } from '../appUrls';
|
|
4
5
|
import { collectAppValidationWarnings, validateAppTask, runFormatScript } from './validate';
|
|
5
6
|
import { buildApp, type AppBuildArtifacts } from './build';
|
|
6
7
|
import { uploadApp, type AppUploadResult } from './upload';
|
|
@@ -23,6 +24,7 @@ export type AppPipelineOptions = {
|
|
|
23
24
|
ensureRegisteredAppShell?: boolean;
|
|
24
25
|
agentTaskId?: number;
|
|
25
26
|
mediaCaptureRequired?: boolean;
|
|
27
|
+
captureSession?: TaskCaptureSession | null;
|
|
26
28
|
};
|
|
27
29
|
export declare function runAppPipeline(client: ApiClient, task: AppTask, options?: AppPipelineOptions): Promise<AppPipelineResult>;
|
|
28
30
|
export { collectAppValidationWarnings, validateAppTask, buildApp, uploadApp, runFormatScript };
|
package/dist/apps/index.js
CHANGED
|
@@ -49,6 +49,7 @@ async function runAppPipeline(client, task, options) {
|
|
|
49
49
|
timeoutMs: options.loadCheckTimeoutMs,
|
|
50
50
|
token: options.token ?? null,
|
|
51
51
|
currentUser: options.user ?? null,
|
|
52
|
+
captureSession: options.captureSession,
|
|
52
53
|
});
|
|
53
54
|
if (loadCheck.status !== 'PASSED') {
|
|
54
55
|
throw new Error((0, loadCheck_1.formatHostedLoadCheckFailure)(task.name, loadCheck, 'local'));
|
|
@@ -64,6 +65,7 @@ async function runAppPipeline(client, task, options) {
|
|
|
64
65
|
loadCheckTimeoutMs: options?.loadCheckTimeoutMs,
|
|
65
66
|
agentTaskId: options?.agentTaskId,
|
|
66
67
|
mediaCaptureRequired: options?.mediaCaptureRequired,
|
|
68
|
+
captureSession: options?.captureSession,
|
|
67
69
|
};
|
|
68
70
|
const upload = await (0, upload_1.uploadApp)(client, task, artifacts, uploadOptions);
|
|
69
71
|
return { artifacts, upload, warnings };
|
package/dist/apps/loadCheck.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { ApiClient } from '@playdrop/api-client';
|
|
|
2
2
|
import type { UserResponse } from '@playdrop/types';
|
|
3
3
|
import type { Frame, Page } from 'playwright-core';
|
|
4
4
|
import type { AppTask } from '../catalogue';
|
|
5
|
+
import { type TaskCaptureSession } from '../appUrls';
|
|
5
6
|
export type HostedLoadCheckResult = {
|
|
6
7
|
status: 'PASSED' | 'FAILED';
|
|
7
8
|
errorCode: string | null;
|
|
@@ -50,6 +51,7 @@ export declare function focusProjectCheckGameFrame(page: Pick<Page, 'mouse'>, fr
|
|
|
50
51
|
}>;
|
|
51
52
|
export declare function dispatchProjectCheckActionsToFrame(page: Page, frame: Frame, actions: readonly ProjectCheckAction[]): Promise<void>;
|
|
52
53
|
export declare function formatHostedLoadCheckFailure(taskName: string, result: HostedLoadCheckResult, scope?: 'local' | 'staged' | 'final'): string;
|
|
54
|
+
export declare function redactHostedLoadCheckSecrets(text: string, sourceUrl: string): string;
|
|
53
55
|
export declare function runLocalHostedLoadCheck(input: {
|
|
54
56
|
client: ApiClient;
|
|
55
57
|
apiBase: string;
|
|
@@ -60,6 +62,7 @@ export declare function runLocalHostedLoadCheck(input: {
|
|
|
60
62
|
timeoutMs?: number;
|
|
61
63
|
token?: string | null;
|
|
62
64
|
currentUser?: UserResponse | null;
|
|
65
|
+
captureSession?: TaskCaptureSession | null;
|
|
63
66
|
allowUnregisteredViewerLaunch?: boolean;
|
|
64
67
|
screenshotPath?: string | null;
|
|
65
68
|
actions?: ProjectCheckAction[];
|
|
@@ -72,5 +75,6 @@ export declare function runUploadedHostedLoadCheck(input: {
|
|
|
72
75
|
timeoutMs?: number;
|
|
73
76
|
token?: string | null;
|
|
74
77
|
currentUser?: UserResponse | null;
|
|
78
|
+
captureSession?: TaskCaptureSession | null;
|
|
75
79
|
screenshotPath?: string | null;
|
|
76
80
|
}): Promise<HostedLoadCheckResult>;
|
package/dist/apps/loadCheck.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.normalizeProjectCheckActions = normalizeProjectCheckActions;
|
|
|
5
5
|
exports.focusProjectCheckGameFrame = focusProjectCheckGameFrame;
|
|
6
6
|
exports.dispatchProjectCheckActionsToFrame = dispatchProjectCheckActionsToFrame;
|
|
7
7
|
exports.formatHostedLoadCheckFailure = formatHostedLoadCheckFailure;
|
|
8
|
+
exports.redactHostedLoadCheckSecrets = redactHostedLoadCheckSecrets;
|
|
8
9
|
exports.runLocalHostedLoadCheck = runLocalHostedLoadCheck;
|
|
9
10
|
exports.runUploadedHostedLoadCheck = runUploadedHostedLoadCheck;
|
|
10
11
|
const promises_1 = require("node:fs/promises");
|
|
@@ -427,13 +428,18 @@ function formatHostedLoadCheckFailure(taskName, result, scope = 'local') {
|
|
|
427
428
|
const message = normalizeLoadCheckMessage(result.message);
|
|
428
429
|
return `hosted_load_check_failed: ${taskName} failed the ${scopeLabel} check (${errorCode}). ${message}`;
|
|
429
430
|
}
|
|
431
|
+
function redactHostedLoadCheckSecrets(text, sourceUrl) {
|
|
432
|
+
return (0, appUrls_1.redactTaskCaptureToken)(text, sourceUrl).replace(/("(?:authToken|appAccessToken|captureTaskToken|reviewToken)"\s*:\s*")[^"]+(")/g, '$1<redacted>$2');
|
|
433
|
+
}
|
|
430
434
|
async function runHostedLoadCheck(options) {
|
|
431
435
|
const expectedHostedLaunchState = options.expectedHostedLaunchState ?? 'ready';
|
|
432
436
|
const artifactFingerprint = options.artifactFingerprint ?? null;
|
|
433
437
|
const timeoutMs = options.timeoutMs ?? DEFAULT_HOSTED_LOAD_TIMEOUT_MS;
|
|
434
438
|
const errors = [];
|
|
435
439
|
const warnings = [];
|
|
436
|
-
let finalUrl = options.targetUrl;
|
|
440
|
+
let finalUrl = (0, appUrls_1.redactTaskCaptureSession)(options.targetUrl);
|
|
441
|
+
const reportTargetUrl = (0, appUrls_1.redactTaskCaptureSession)(options.targetUrl);
|
|
442
|
+
const redactReportText = (text) => redactHostedLoadCheckSecrets(text, options.targetUrl);
|
|
437
443
|
let screenshotPath = null;
|
|
438
444
|
let webglRenderer = null;
|
|
439
445
|
try {
|
|
@@ -494,7 +500,7 @@ async function runHostedLoadCheck(options) {
|
|
|
494
500
|
}
|
|
495
501
|
}
|
|
496
502
|
const serialized = serializePayload(payload);
|
|
497
|
-
const line = ['[check][custom]', typeof type === 'string' ? type.toLowerCase() : 'info', serialized].filter(Boolean).join(' ');
|
|
503
|
+
const line = redactReportText(['[check][custom]', typeof type === 'string' ? type.toLowerCase() : 'info', serialized].filter(Boolean).join(' '));
|
|
498
504
|
console.log(line);
|
|
499
505
|
});
|
|
500
506
|
await page.addInitScript(() => {
|
|
@@ -533,7 +539,7 @@ async function runHostedLoadCheck(options) {
|
|
|
533
539
|
const type = message.type();
|
|
534
540
|
const args = await Promise.all(message.args().map((arg) => arg.jsonValue().catch(() => arg.toString())));
|
|
535
541
|
const rendered = args.length > 0 ? args.map(formatConsoleValue).join(' ') : message.text();
|
|
536
|
-
const line = `[check][console:${type}] ${rendered}
|
|
542
|
+
const line = redactReportText(`[check][console:${type}] ${rendered}`);
|
|
537
543
|
if (type === 'error' || type === 'assert' || type === 'trace') {
|
|
538
544
|
errors.push(line);
|
|
539
545
|
console.error(line);
|
|
@@ -543,19 +549,19 @@ async function runHostedLoadCheck(options) {
|
|
|
543
549
|
};
|
|
544
550
|
page.on('console', (message) => {
|
|
545
551
|
void handleConsoleMessage(message).catch((error) => {
|
|
546
|
-
const text = error instanceof Error ? error.message : String(error);
|
|
552
|
+
const text = redactReportText(error instanceof Error ? error.message : String(error));
|
|
547
553
|
errors.push(`[check][console-handler] ${text}`);
|
|
548
554
|
});
|
|
549
555
|
});
|
|
550
556
|
page.on('pageerror', (error) => {
|
|
551
|
-
const text = error?.message ?? String(error);
|
|
557
|
+
const text = redactReportText(error?.message ?? String(error));
|
|
552
558
|
errors.push(`[check][pageerror] ${text}`);
|
|
553
559
|
console.error(`[check][pageerror] ${text}`);
|
|
554
560
|
});
|
|
555
561
|
page.on('requestfailed', (request) => {
|
|
556
562
|
const failure = request.failure();
|
|
557
563
|
const errorText = failure?.errorText ?? '';
|
|
558
|
-
const text = `${request.method()} ${request.url()} - ${errorText || 'failed'}
|
|
564
|
+
const text = redactReportText(`${request.method()} ${request.url()} - ${errorText || 'failed'}`);
|
|
559
565
|
if (isKnownGoogleTelemetryUrl(request.url())) {
|
|
560
566
|
warnings.push(`[check][requestfailed] ${text}`);
|
|
561
567
|
return;
|
|
@@ -571,7 +577,7 @@ async function runHostedLoadCheck(options) {
|
|
|
571
577
|
if (status < 400) {
|
|
572
578
|
return;
|
|
573
579
|
}
|
|
574
|
-
const text = `${status} ${response.statusText()} ${response.url()}
|
|
580
|
+
const text = redactReportText(`${status} ${response.statusText()} ${response.url()}`);
|
|
575
581
|
if (isKnownGoogleTelemetryUrl(response.url())) {
|
|
576
582
|
warnings.push(`[check][response] ${text}`);
|
|
577
583
|
return;
|
|
@@ -579,10 +585,10 @@ async function runHostedLoadCheck(options) {
|
|
|
579
585
|
errors.push(`[check][response] ${text}`);
|
|
580
586
|
console.error(`[check][response] ${text}`);
|
|
581
587
|
});
|
|
582
|
-
console.log(`[check] Opening ${
|
|
588
|
+
console.log(`[check] Opening ${reportTargetUrl}`);
|
|
583
589
|
const response = await page.goto(options.targetUrl, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
584
590
|
if (response && !response.ok()) {
|
|
585
|
-
errors.push(`[check][navigation] ${response.status()} ${response.statusText()} ${
|
|
591
|
+
errors.push(`[check][navigation] ${response.status()} ${response.statusText()} ${reportTargetUrl}`);
|
|
586
592
|
}
|
|
587
593
|
const readinessTimeoutMs = Math.min(Math.max(timeoutMs, 1000), 30000);
|
|
588
594
|
await page.waitForFunction(({ frameSelector }) => {
|
|
@@ -610,7 +616,7 @@ async function runHostedLoadCheck(options) {
|
|
|
610
616
|
if (launchState.state !== expectedHostedLaunchState) {
|
|
611
617
|
throw new Error(formatHostedLaunchStateMismatch(launchState.state, expectedHostedLaunchState));
|
|
612
618
|
}
|
|
613
|
-
finalUrl = page.url();
|
|
619
|
+
finalUrl = (0, appUrls_1.redactTaskCaptureSession)(page.url());
|
|
614
620
|
if (expectedHostedLaunchState !== 'ready') {
|
|
615
621
|
screenshotPath = await saveScreenshot(page, options.screenshotPath);
|
|
616
622
|
return;
|
|
@@ -632,11 +638,11 @@ async function runHostedLoadCheck(options) {
|
|
|
632
638
|
});
|
|
633
639
|
}
|
|
634
640
|
catch (error) {
|
|
635
|
-
const launchFailureMessage = error instanceof Error ? error.message : String(error);
|
|
641
|
+
const launchFailureMessage = redactReportText(error instanceof Error ? error.message : String(error));
|
|
636
642
|
if (/Failed to launch installed Google Chrome/i.test(launchFailureMessage)) {
|
|
637
643
|
throw error;
|
|
638
644
|
}
|
|
639
|
-
const normalized = normalizeLoadCheckFailure(
|
|
645
|
+
const normalized = normalizeLoadCheckFailure(new Error(launchFailureMessage), artifactFingerprint);
|
|
640
646
|
return {
|
|
641
647
|
...normalized,
|
|
642
648
|
screenshotPath,
|
|
@@ -733,6 +739,23 @@ async function runLocalHostedLoadCheck(input) {
|
|
|
733
739
|
...localAppMetadata,
|
|
734
740
|
captureSurface,
|
|
735
741
|
});
|
|
742
|
+
if (input.captureSession) {
|
|
743
|
+
return await runHostedLoadCheck({
|
|
744
|
+
targetUrl: (0, appUrls_1.buildPlatformDevUrl)(input.webBase, {
|
|
745
|
+
creatorUsername: input.creatorUsername,
|
|
746
|
+
appName: input.task.name,
|
|
747
|
+
appType: input.task.type ?? 'GAME',
|
|
748
|
+
launchCheck: true,
|
|
749
|
+
localDevPort,
|
|
750
|
+
captureSession: input.captureSession,
|
|
751
|
+
}),
|
|
752
|
+
timeoutMs: input.timeoutMs,
|
|
753
|
+
expectedHostedLaunchState: resolvePostAuthHostedLaunchState(localAppMetadata.controllerMode),
|
|
754
|
+
contextOptions,
|
|
755
|
+
screenshotPath: input.screenshotPath,
|
|
756
|
+
actions: input.actions,
|
|
757
|
+
});
|
|
758
|
+
}
|
|
736
759
|
if (anonymousExpectedState === 'login_required') {
|
|
737
760
|
const registeredApp = await (0, registration_1.fetchRegisteredAppShell)(input.client, input.creatorUsername, input.task.name);
|
|
738
761
|
const canLaunchUnregisteredViewer = input.allowUnregisteredViewerLaunch === true
|
|
@@ -813,7 +836,19 @@ async function runUploadedHostedLoadCheck(input) {
|
|
|
813
836
|
captureSurface,
|
|
814
837
|
});
|
|
815
838
|
let result;
|
|
816
|
-
if (
|
|
839
|
+
if (input.captureSession) {
|
|
840
|
+
const targetUrl = new URL(preview.launchUrl);
|
|
841
|
+
(0, appUrls_1.appendTaskCaptureSession)(targetUrl, input.captureSession);
|
|
842
|
+
result = await runHostedLoadCheck({
|
|
843
|
+
targetUrl: targetUrl.toString(),
|
|
844
|
+
artifactFingerprint: preview.session.manifestHash,
|
|
845
|
+
timeoutMs: input.timeoutMs,
|
|
846
|
+
expectedHostedLaunchState: resolvePostAuthHostedLaunchState(preview.localAppMetadata.controllerMode),
|
|
847
|
+
contextOptions,
|
|
848
|
+
screenshotPath: input.screenshotPath,
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
else if (anonymousExpectedState === 'login_required') {
|
|
817
852
|
if (!preview.app?.id) {
|
|
818
853
|
result = buildFailedLoadCheckResult('app_registration_required_for_auth_validation', `Auth-required hosted app "${input.task.name}" must be registered on PlayDrop before viewer load-check can run.`, preview.session.manifestHash);
|
|
819
854
|
}
|
package/dist/apps/upload.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ApiClient } from '@playdrop/api-client';
|
|
2
2
|
import type { UserResponse } from '@playdrop/types';
|
|
3
3
|
import type { AppTask } from '../catalogue';
|
|
4
|
+
import type { TaskCaptureSession } from '../appUrls';
|
|
4
5
|
import type { AppBuildArtifacts } from './build';
|
|
5
6
|
export type AppUploadResult = {
|
|
6
7
|
fileUnchanged?: boolean;
|
|
@@ -21,5 +22,6 @@ export type AppUploadOptions = {
|
|
|
21
22
|
loadCheckTimeoutMs?: number;
|
|
22
23
|
agentTaskId?: number;
|
|
23
24
|
mediaCaptureRequired?: boolean;
|
|
25
|
+
captureSession?: TaskCaptureSession | null;
|
|
24
26
|
};
|
|
25
27
|
export declare function uploadApp(client: ApiClient, task: AppTask, artifacts: AppBuildArtifacts | null, options?: AppUploadOptions): Promise<AppUploadResult>;
|
package/dist/apps/upload.js
CHANGED
|
@@ -454,6 +454,7 @@ async function uploadAppVersion(client, task, artifacts, options) {
|
|
|
454
454
|
timeoutMs: options.loadCheckTimeoutMs,
|
|
455
455
|
token: options?.token ?? null,
|
|
456
456
|
currentUser: options?.user ?? null,
|
|
457
|
+
captureSession: options?.captureSession,
|
|
457
458
|
});
|
|
458
459
|
if (loadCheck.status !== 'PASSED') {
|
|
459
460
|
throw new Error((0, loadCheck_1.formatHostedLoadCheckFailure)(task.name, loadCheck, 'staged'));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type AppSurface } from '@playdrop/types';
|
|
2
2
|
import type { BrowserContextOptions } from 'playwright-core';
|
|
3
|
+
import { type TaskCaptureSession } from '../appUrls';
|
|
3
4
|
type CaptureListingOptions = {
|
|
4
5
|
app?: string;
|
|
5
6
|
duration?: string | number;
|
|
@@ -53,13 +54,14 @@ type CropRect = {
|
|
|
53
54
|
export declare function resolveListingCaptureRouteSegment(previewable: boolean): 'dev-preview';
|
|
54
55
|
export declare function resolveCaptureListingDevRouterPort(env?: NodeJS.ProcessEnv): number;
|
|
55
56
|
export declare function resolveListingCaptureLocalDevPort(devRouterPort: number): number | null;
|
|
56
|
-
export declare function buildListingCaptureFrameUrl({ webBase, currentUsername, appTypeSlug, appName, routeSegment, localDevPort, }: {
|
|
57
|
+
export declare function buildListingCaptureFrameUrl({ webBase, currentUsername, appTypeSlug, appName, routeSegment, localDevPort, captureSession, }: {
|
|
57
58
|
webBase: string;
|
|
58
59
|
currentUsername: string;
|
|
59
60
|
appTypeSlug: string;
|
|
60
61
|
appName: string;
|
|
61
62
|
routeSegment: 'dev-preview';
|
|
62
63
|
localDevPort: number | null;
|
|
64
|
+
captureSession?: TaskCaptureSession | null;
|
|
63
65
|
}): string;
|
|
64
66
|
export declare function resolveListingCaptureSceneId(width: number, height: number): 'listing-landscape' | 'listing-portrait';
|
|
65
67
|
export declare function parseCaptureListingOptions(targetArg: string | undefined, options?: CaptureListingOptions): ParsedCaptureListingOptions;
|
|
@@ -62,6 +62,7 @@ const dev_1 = require("./dev");
|
|
|
62
62
|
const devRuntimeAssets_1 = require("./devRuntimeAssets");
|
|
63
63
|
const devServer_1 = require("./devServer");
|
|
64
64
|
const devShared_1 = require("./devShared");
|
|
65
|
+
const taskCaptureSession_1 = require("./taskCaptureSession");
|
|
65
66
|
const CAPTURE_FRAME_SELECTOR = 'iframe[title="Game"]';
|
|
66
67
|
const DEFAULT_DURATION_SECONDS = 8;
|
|
67
68
|
const DEFAULT_WIDTH = 1280;
|
|
@@ -100,15 +101,15 @@ function resolveCaptureListingDevRouterPort(env = process.env) {
|
|
|
100
101
|
function resolveListingCaptureLocalDevPort(devRouterPort) {
|
|
101
102
|
return devRouterPort === devServer_1.DEV_ROUTER_PORT ? null : devRouterPort;
|
|
102
103
|
}
|
|
103
|
-
function buildListingCaptureFrameUrl({ webBase, currentUsername, appTypeSlug, appName, routeSegment, localDevPort, }) {
|
|
104
|
+
function buildListingCaptureFrameUrl({ webBase, currentUsername, appTypeSlug, appName, routeSegment, localDevPort, captureSession, }) {
|
|
104
105
|
const frameUrlObject = new URL(`${(0, appUrls_1.normalizePlaydropWebBase)(webBase)}/creators/${encodeURIComponent(currentUsername)}/apps/${appTypeSlug}/${encodeURIComponent(appName)}/${routeSegment}`);
|
|
105
|
-
frameUrlObject.searchParams.set('devAuth', 'anonymous');
|
|
106
106
|
frameUrlObject.searchParams.set('launchCheck', '1');
|
|
107
107
|
frameUrlObject.searchParams.set('captureShell', 'listing');
|
|
108
108
|
frameUrlObject.searchParams.set('mobileFrameHost', 'admin');
|
|
109
109
|
if (localDevPort !== null) {
|
|
110
110
|
frameUrlObject.searchParams.set('localDevPort', String(localDevPort));
|
|
111
111
|
}
|
|
112
|
+
(0, appUrls_1.appendTaskCaptureSession)(frameUrlObject, captureSession);
|
|
112
113
|
return frameUrlObject.toString();
|
|
113
114
|
}
|
|
114
115
|
function resolveListingCaptureSceneId(width, height) {
|
|
@@ -812,11 +813,14 @@ async function captureListing(targetArg, options = {}) {
|
|
|
812
813
|
const outputPaths = await ensureOutputPaths(appName, parsedOptions.outputDir);
|
|
813
814
|
const projectInfo = (0, devShared_1.findProjectInfo)(resolvedTarget.htmlPath);
|
|
814
815
|
const devScriptAvailable = Boolean(projectInfo.projectDir && projectInfo.packageJson && typeof projectInfo.packageJson.scripts?.dev === 'string');
|
|
815
|
-
await (0, commandContext_1.withEnvironment)('project capture', 'Capturing game media', async ({ client, env, envConfig }) => {
|
|
816
|
+
await (0, commandContext_1.withEnvironment)('project capture', 'Capturing game media', async ({ client, env, envConfig, account, workspaceAuth }) => {
|
|
816
817
|
let currentUsername = '';
|
|
817
818
|
try {
|
|
818
|
-
|
|
819
|
-
|
|
819
|
+
currentUsername = await (0, devShared_1.resolveDevCreatorUsername)({
|
|
820
|
+
client,
|
|
821
|
+
workspaceOwnerUsername: workspaceAuth?.config.ownerUsername,
|
|
822
|
+
accountUsername: account?.username,
|
|
823
|
+
});
|
|
820
824
|
}
|
|
821
825
|
catch (error) {
|
|
822
826
|
if (error instanceof http_1.CLIUnsupportedClientError) {
|
|
@@ -842,8 +846,9 @@ async function captureListing(targetArg, options = {}) {
|
|
|
842
846
|
}
|
|
843
847
|
throw error;
|
|
844
848
|
}
|
|
849
|
+
let registeredApp;
|
|
845
850
|
try {
|
|
846
|
-
await (0, devShared_1.assertAppRegistered)(client, currentUsername, appName);
|
|
851
|
+
registeredApp = await (0, devShared_1.assertAppRegistered)(client, currentUsername, appName);
|
|
847
852
|
}
|
|
848
853
|
catch (error) {
|
|
849
854
|
if (error instanceof http_1.CLIUnsupportedClientError) {
|
|
@@ -866,6 +871,15 @@ async function captureListing(targetArg, options = {}) {
|
|
|
866
871
|
}
|
|
867
872
|
throw error;
|
|
868
873
|
}
|
|
874
|
+
let captureSession;
|
|
875
|
+
try {
|
|
876
|
+
captureSession = (0, taskCaptureSession_1.resolveTaskCaptureSession)(workspaceAuth, registeredApp);
|
|
877
|
+
}
|
|
878
|
+
catch (error) {
|
|
879
|
+
(0, messages_1.printErrorWithHelp)(error?.message || 'Worker capture session is invalid.', ['Run this command from the active worker task workspace.'], { command: 'project capture' });
|
|
880
|
+
process.exitCode = 1;
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
869
883
|
const entryLabel = (0, node_path_1.relative)(process.cwd(), resolvedTarget.htmlPath) || resolvedTarget.htmlPath;
|
|
870
884
|
console.log(`[listing] Preparing ${entryLabel} for ${env} (${appTypeSlug}).`);
|
|
871
885
|
const taskLookup = (0, catalogue_1.findAppTaskByFile)(resolvedTarget.htmlPath);
|
|
@@ -960,9 +974,11 @@ async function captureListing(targetArg, options = {}) {
|
|
|
960
974
|
appName,
|
|
961
975
|
routeSegment,
|
|
962
976
|
localDevPort,
|
|
977
|
+
captureSession,
|
|
963
978
|
});
|
|
964
979
|
const frameUrlObject = new URL(frameUrl);
|
|
965
|
-
|
|
980
|
+
const reportFrameUrl = (0, appUrls_1.redactTaskCaptureSession)(frameUrl);
|
|
981
|
+
console.log(`[listing] Opening ${reportFrameUrl}`);
|
|
966
982
|
const shouldExportPreviewAudio = parsedOptions.audio && routeSegment === 'dev-preview';
|
|
967
983
|
const surfaces = resolvedTarget.surfaceTargets.list;
|
|
968
984
|
const useSurfacePrefix = surfaces.length > 1;
|
|
@@ -1026,7 +1042,7 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1026
1042
|
capturedSurfaces.push(surface);
|
|
1027
1043
|
captures.push({
|
|
1028
1044
|
surface,
|
|
1029
|
-
targetUrl:
|
|
1045
|
+
targetUrl: reportFrameUrl,
|
|
1030
1046
|
command: {
|
|
1031
1047
|
durationSeconds: parsedOptions.durationSeconds,
|
|
1032
1048
|
width: dimensions.width,
|
package/dist/commands/check.js
CHANGED
|
@@ -11,6 +11,7 @@ const http_1 = require("../http");
|
|
|
11
11
|
const messages_1 = require("../messages");
|
|
12
12
|
const loadCheck_1 = require("../apps/loadCheck");
|
|
13
13
|
const devShared_1 = require("./devShared");
|
|
14
|
+
const taskCaptureSession_1 = require("./taskCaptureSession");
|
|
14
15
|
function resolveProjectCheckTarget(targetArg, appOption) {
|
|
15
16
|
try {
|
|
16
17
|
return (0, devShared_1.resolveDevTarget)(targetArg, appOption);
|
|
@@ -99,36 +100,39 @@ async function check(targetArg, options = {}) {
|
|
|
99
100
|
}
|
|
100
101
|
const screenshotPath = options.screenshot?.trim() || defaultProjectCheckScreenshotPath(appName);
|
|
101
102
|
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 =
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
await (0, commandContext_1.withEnvironment)('project check', 'Checking the Playdrop hosted game shell', async ({ client, env, envConfig, account, token, workspaceAuth }) => {
|
|
104
|
+
let currentUsername = '';
|
|
105
|
+
try {
|
|
106
|
+
currentUsername = await (0, devShared_1.resolveDevCreatorUsername)({
|
|
107
|
+
client,
|
|
108
|
+
workspaceOwnerUsername: workspaceAuth?.config.ownerUsername,
|
|
109
|
+
accountUsername: account?.username,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
if (error instanceof http_1.CLIUnsupportedClientError) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (error instanceof types_1.UnsupportedClientError) {
|
|
117
|
+
(0, http_1.handleUnsupportedError)(error, 'Authentication');
|
|
118
|
+
process.exitCode = 1;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (error instanceof types_1.ApiError) {
|
|
122
|
+
(0, messages_1.printErrorWithHelp)(`Could not fetch your account (status ${error.status}).`, ['Run "playdrop auth login" to refresh your session.'], { command: 'project check' });
|
|
123
|
+
process.exitCode = 1;
|
|
124
|
+
return;
|
|
107
125
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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;
|
|
126
|
+
if ((0, devShared_1.isNetworkError)(error)) {
|
|
127
|
+
(0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to resolve your account.', 'project check');
|
|
128
|
+
process.exitCode = 1;
|
|
129
|
+
return;
|
|
128
130
|
}
|
|
131
|
+
throw error;
|
|
129
132
|
}
|
|
133
|
+
let registeredApp;
|
|
130
134
|
try {
|
|
131
|
-
await (0, devShared_1.assertAppRegistered)(client, currentUsername, appName);
|
|
135
|
+
registeredApp = await (0, devShared_1.assertAppRegistered)(client, currentUsername, appName);
|
|
132
136
|
}
|
|
133
137
|
catch (error) {
|
|
134
138
|
if (error instanceof http_1.CLIUnsupportedClientError) {
|
|
@@ -151,6 +155,15 @@ async function check(targetArg, options = {}) {
|
|
|
151
155
|
}
|
|
152
156
|
throw error;
|
|
153
157
|
}
|
|
158
|
+
let captureSession;
|
|
159
|
+
try {
|
|
160
|
+
captureSession = (0, taskCaptureSession_1.resolveTaskCaptureSession)(workspaceAuth, registeredApp);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
(0, messages_1.printErrorWithHelp)(error?.message || 'Worker capture session is invalid.', ['Run this command from the active worker task workspace.'], { command: 'project check' });
|
|
164
|
+
process.exitCode = 1;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
154
167
|
const projectInfo = (0, devShared_1.findProjectInfo)(filePath);
|
|
155
168
|
const devScriptAvailable = Boolean(projectInfo.projectDir
|
|
156
169
|
&& projectInfo.packageJson
|
|
@@ -172,6 +185,7 @@ async function check(targetArg, options = {}) {
|
|
|
172
185
|
timeoutMs,
|
|
173
186
|
token,
|
|
174
187
|
currentUser: null,
|
|
188
|
+
captureSession,
|
|
175
189
|
screenshotPath,
|
|
176
190
|
actions,
|
|
177
191
|
});
|
package/dist/commands/create.js
CHANGED
|
@@ -19,7 +19,10 @@ const clientInfo_1 = require("../clientInfo");
|
|
|
19
19
|
const commandContext_1 = require("../commandContext");
|
|
20
20
|
const http_1 = require("../http");
|
|
21
21
|
const messages_1 = require("../messages");
|
|
22
|
+
const loadCheck_1 = require("../apps/loadCheck");
|
|
23
|
+
const devShared_1 = require("./devShared");
|
|
22
24
|
const init_1 = require("./init");
|
|
25
|
+
const taskCaptureSession_1 = require("./taskCaptureSession");
|
|
23
26
|
const CATALOGUE_FILENAME = 'catalogue.json';
|
|
24
27
|
const LEGACY_CATALOGUE_VERSION_KEY = ['schema', 'Version'].join('');
|
|
25
28
|
const ALLOWED_CATALOGUE_TOP_LEVEL_KEYS = new Set(['apps', 'assetSpecs', 'assets', 'assetPacks']);
|
|
@@ -1420,6 +1423,19 @@ async function create(name, options = {}) {
|
|
|
1420
1423
|
}
|
|
1421
1424
|
registrationTask = selectedTask;
|
|
1422
1425
|
}
|
|
1426
|
+
let dependenciesInstalled = false;
|
|
1427
|
+
if (!reuseExistingApp
|
|
1428
|
+
&& projectDir
|
|
1429
|
+
&& packageJsonPath
|
|
1430
|
+
&& (ctx.workspaceAuth?.config.taskId || ctx.workspaceAuth?.config.taskToken)) {
|
|
1431
|
+
console.log(`Installing dependencies for worker shell preflight in ${(0, node_path_1.relative)(process.cwd(), projectDir) || projectDir}.`);
|
|
1432
|
+
dependenciesInstalled = runNpmInstall(projectDir);
|
|
1433
|
+
if (!dependenciesInstalled) {
|
|
1434
|
+
(0, messages_1.printErrorWithHelp)('Worker project dependency installation failed.', ['Fix the template dependencies before retrying the task.'], { command: 'project create app' });
|
|
1435
|
+
process.exitCode = 1;
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1423
1439
|
if (registrationTask) {
|
|
1424
1440
|
const metadataWarning = (0, catalogue_1.formatMissingMetadataWarnings)(registrationTask);
|
|
1425
1441
|
if (metadataWarning) {
|
|
@@ -1542,12 +1558,41 @@ async function create(name, options = {}) {
|
|
|
1542
1558
|
throw error;
|
|
1543
1559
|
}
|
|
1544
1560
|
}
|
|
1561
|
+
if (ctx.workspaceAuth?.config.taskId || ctx.workspaceAuth?.config.taskToken) {
|
|
1562
|
+
try {
|
|
1563
|
+
const registeredApp = await (0, devShared_1.assertAppRegistered)(client, creatorUsername, name);
|
|
1564
|
+
const captureSession = (0, taskCaptureSession_1.resolveTaskCaptureSession)(ctx.workspaceAuth, registeredApp);
|
|
1565
|
+
if (!captureSession) {
|
|
1566
|
+
throw new Error('worker_capture_session_missing');
|
|
1567
|
+
}
|
|
1568
|
+
console.log(`Running worker shell preflight for ${creatorUsername}/${name}.`);
|
|
1569
|
+
const preflight = await (0, loadCheck_1.runLocalHostedLoadCheck)({
|
|
1570
|
+
client,
|
|
1571
|
+
apiBase: ctx.envConfig.apiBase,
|
|
1572
|
+
webBase: ctx.envConfig.webBase,
|
|
1573
|
+
creatorUsername,
|
|
1574
|
+
task: registrationTask,
|
|
1575
|
+
captureSession,
|
|
1576
|
+
timeoutMs: 15000,
|
|
1577
|
+
});
|
|
1578
|
+
if (preflight.status !== 'PASSED') {
|
|
1579
|
+
(0, messages_1.printErrorWithHelp)((0, loadCheck_1.formatHostedLoadCheckFailure)(name, preflight, 'local'), ['Fix the worker shell or capture authorization before implementing the game.'], { command: 'project create app' });
|
|
1580
|
+
process.exitCode = 1;
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
console.log(`Worker shell preflight passed for ${creatorUsername}/${name}.`);
|
|
1584
|
+
}
|
|
1585
|
+
catch (error) {
|
|
1586
|
+
(0, messages_1.printErrorWithHelp)(`Worker shell preflight failed: ${error?.message || 'unknown_error'}`, ['Fix the worker shell or capture authorization before implementing the game.'], { command: 'project create app' });
|
|
1587
|
+
process.exitCode = 1;
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1545
1591
|
}
|
|
1546
1592
|
const projectDirLabel = projectDir ? ((0, node_path_1.relative)(process.cwd(), projectDir) || projectDir) : null;
|
|
1547
1593
|
const entryPointLabel = (0, node_path_1.relative)(process.cwd(), htmlFilePath) || (0, node_path_1.basename)(htmlFilePath);
|
|
1548
1594
|
const hasPackageJson = Boolean(packageJsonPath);
|
|
1549
|
-
|
|
1550
|
-
if (!reuseExistingApp && projectDir && packageJsonPath && node_process_1.stdin.isTTY && node_process_1.stdout.isTTY) {
|
|
1595
|
+
if (!dependenciesInstalled && !reuseExistingApp && projectDir && packageJsonPath && node_process_1.stdin.isTTY && node_process_1.stdout.isTTY) {
|
|
1551
1596
|
const accepted = await promptInstallDependencies(projectDir);
|
|
1552
1597
|
if (accepted) {
|
|
1553
1598
|
const success = runNpmInstall(projectDir);
|
|
@@ -16,6 +16,11 @@ export declare function createDevError(code: string, message: string): Error;
|
|
|
16
16
|
export declare function isNetworkError(error: unknown): boolean;
|
|
17
17
|
export declare function fetchDevUser(client: ApiClient): Promise<UserResponse>;
|
|
18
18
|
export declare function fetchDevUsername(client: ApiClient): Promise<string>;
|
|
19
|
+
export declare function resolveDevCreatorUsername(input: {
|
|
20
|
+
client: ApiClient;
|
|
21
|
+
workspaceOwnerUsername?: string | null;
|
|
22
|
+
accountUsername?: string | null;
|
|
23
|
+
}): Promise<string>;
|
|
19
24
|
export declare function assertAppRegistered(client: ApiClient, creatorUsername: string, appName: string): Promise<AppResponse>;
|
|
20
25
|
export declare function resolveDevTarget(targetArg: string | undefined, appOption: string | undefined): ResolvedDevTarget;
|
|
21
26
|
export declare function findProjectInfo(htmlPath: string): ProjectInfo;
|
|
@@ -4,6 +4,7 @@ exports.createDevError = createDevError;
|
|
|
4
4
|
exports.isNetworkError = isNetworkError;
|
|
5
5
|
exports.fetchDevUser = fetchDevUser;
|
|
6
6
|
exports.fetchDevUsername = fetchDevUsername;
|
|
7
|
+
exports.resolveDevCreatorUsername = resolveDevCreatorUsername;
|
|
7
8
|
exports.assertAppRegistered = assertAppRegistered;
|
|
8
9
|
exports.resolveDevTarget = resolveDevTarget;
|
|
9
10
|
exports.findProjectInfo = findProjectInfo;
|
|
@@ -54,6 +55,14 @@ async function fetchDevUsername(client) {
|
|
|
54
55
|
const user = await fetchDevUser(client);
|
|
55
56
|
return user.username;
|
|
56
57
|
}
|
|
58
|
+
async function resolveDevCreatorUsername(input) {
|
|
59
|
+
const workspaceOwnerUsername = input.workspaceOwnerUsername?.trim() ?? '';
|
|
60
|
+
if (workspaceOwnerUsername) {
|
|
61
|
+
return workspaceOwnerUsername;
|
|
62
|
+
}
|
|
63
|
+
const accountUsername = input.accountUsername?.trim() ?? '';
|
|
64
|
+
return accountUsername || await fetchDevUsername(input.client);
|
|
65
|
+
}
|
|
57
66
|
async function assertAppRegistered(client, creatorUsername, appName) {
|
|
58
67
|
const response = await client.fetchAppBySlug(creatorUsername, appName);
|
|
59
68
|
if (!response?.app) {
|