@playdrop/playdrop-cli 0.14.0 → 0.14.2
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 +2 -2
- package/dist/apps/loadCheck.d.ts +2 -21
- package/dist/apps/loadCheck.js +22 -106
- package/dist/apps/surfaceProfiles.d.ts +1 -0
- package/dist/apps/surfaceProfiles.js +5 -4
- package/dist/apps/validate.d.ts +1 -1
- package/dist/apps/validate.js +2 -420
- package/dist/catalogue.js +32 -1
- package/dist/commands/captureListing.d.ts +13 -0
- package/dist/commands/captureListing.js +187 -0
- package/dist/commands/check.d.ts +11 -1
- package/dist/commands/check.js +19 -39
- package/dist/commands/detail.js +35 -2
- package/dist/commands/review.d.ts +11 -1
- package/dist/commands/review.js +20 -13
- package/dist/commands/upload.d.ts +1 -7
- package/dist/commands/upload.js +20 -462
- package/dist/commands/validate.js +0 -3
- package/dist/commands/worker.d.ts +1 -3
- package/dist/commands/worker.js +39 -37
- package/dist/index.js +2 -4
- package/dist/listingPreflight.d.ts +3 -0
- package/dist/listingPreflight.js +37 -10
- package/node_modules/@playdrop/config/client-meta.json +2 -2
- package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/app-playtest-tape.js +23 -15
- package/package.json +1 -1
package/dist/commands/worker.js
CHANGED
|
@@ -35,7 +35,6 @@ exports.classifyWorkerEventDrainError = classifyWorkerEventDrainError;
|
|
|
35
35
|
exports.isWorkerTransientNetworkError = isWorkerTransientNetworkError;
|
|
36
36
|
exports.classifyWorkerHeartbeatError = classifyWorkerHeartbeatError;
|
|
37
37
|
exports.assertWorkerClaimErrorRetryable = assertWorkerClaimErrorRetryable;
|
|
38
|
-
exports.resolvePlaydropAssetRequirementFromText = resolvePlaydropAssetRequirementFromText;
|
|
39
38
|
exports.resolvePlaywrightBrowsersPathFromExecutable = resolvePlaywrightBrowsersPathFromExecutable;
|
|
40
39
|
exports.loadWorkerEnvFile = loadWorkerEnvFile;
|
|
41
40
|
exports.buildWorkerCapabilities = buildWorkerCapabilities;
|
|
@@ -686,6 +685,18 @@ function normalizeWorkerInstrumentSurface(value) {
|
|
|
686
685
|
? value
|
|
687
686
|
: null;
|
|
688
687
|
}
|
|
688
|
+
const LEGACY_REVIEW_SURFACE_PRIORITY = [
|
|
689
|
+
'MOBILE_PORTRAIT',
|
|
690
|
+
'MOBILE_LANDSCAPE',
|
|
691
|
+
'DESKTOP',
|
|
692
|
+
];
|
|
693
|
+
function normalizeWorkerInstrumentSurfaceTargets(value) {
|
|
694
|
+
if (!Array.isArray(value))
|
|
695
|
+
return [];
|
|
696
|
+
return Array.from(new Set(value
|
|
697
|
+
.map(normalizeWorkerInstrumentSurface)
|
|
698
|
+
.filter((surface) => Boolean(surface))));
|
|
699
|
+
}
|
|
689
700
|
function resolveWorkerInstrumentSurface(taskContext) {
|
|
690
701
|
const playdrop = taskContext.metadata.playdrop;
|
|
691
702
|
if (!playdrop || typeof playdrop !== 'object' || Array.isArray(playdrop)) {
|
|
@@ -714,7 +725,12 @@ function resolveWorkerInstrumentSurface(taskContext) {
|
|
|
714
725
|
if (!reviewTarget || typeof reviewTarget !== 'object' || Array.isArray(reviewTarget)) {
|
|
715
726
|
return null;
|
|
716
727
|
}
|
|
717
|
-
|
|
728
|
+
const target = reviewTarget;
|
|
729
|
+
const primarySurface = normalizeWorkerInstrumentSurface(target.primarySurface);
|
|
730
|
+
if (primarySurface)
|
|
731
|
+
return primarySurface;
|
|
732
|
+
const surfaceTargets = normalizeWorkerInstrumentSurfaceTargets(target.surfaceTargets);
|
|
733
|
+
return LEGACY_REVIEW_SURFACE_PRIORITY.find((surface) => surfaceTargets.includes(surface)) ?? null;
|
|
718
734
|
}
|
|
719
735
|
if (taskContext.kind === 'GAME_EVAL') {
|
|
720
736
|
const evalTarget = metadata.evalTarget;
|
|
@@ -958,12 +974,15 @@ function normalizeTaskClaimSlugPrimarySurface(value) {
|
|
|
958
974
|
}
|
|
959
975
|
return primarySurface;
|
|
960
976
|
}
|
|
961
|
-
function
|
|
977
|
+
function resolveTaskWorkspaceInputPath(filePath, workspaceDir) {
|
|
978
|
+
return node_path_1.default.isAbsolute(filePath) ? node_path_1.default.normalize(filePath) : node_path_1.default.resolve(workspaceDir, filePath);
|
|
979
|
+
}
|
|
980
|
+
function readTaskNextStepsFile(filePath, workspaceDir = node_process_1.default.cwd()) {
|
|
962
981
|
const normalizedPath = typeof filePath === 'string' ? filePath.trim() : '';
|
|
963
982
|
if (!normalizedPath) {
|
|
964
983
|
return undefined;
|
|
965
984
|
}
|
|
966
|
-
const resolved =
|
|
985
|
+
const resolved = resolveTaskWorkspaceInputPath(normalizedPath, workspaceDir);
|
|
967
986
|
if (!(0, node_fs_1.existsSync)(resolved)) {
|
|
968
987
|
throw new Error('invalid_agent_task_next_steps_file');
|
|
969
988
|
}
|
|
@@ -1038,7 +1057,7 @@ async function recordRejectedNextStepsFile(input) {
|
|
|
1038
1057
|
}
|
|
1039
1058
|
async function readTaskNextStepsFileForCommand(input) {
|
|
1040
1059
|
try {
|
|
1041
|
-
return readTaskNextStepsFile(input.filePath);
|
|
1060
|
+
return readTaskNextStepsFile(input.filePath, input.workspaceDir);
|
|
1042
1061
|
}
|
|
1043
1062
|
catch (error) {
|
|
1044
1063
|
const code = error instanceof Error ? error.message : String(error);
|
|
@@ -1609,9 +1628,6 @@ function assertWorkerClaimErrorRetryable(error) {
|
|
|
1609
1628
|
throw new Error(`worker_claim_forbidden: the server rejected this worker session (${error.message}).`);
|
|
1610
1629
|
}
|
|
1611
1630
|
}
|
|
1612
|
-
function resolvePlaydropAssetRequirementFromText(value) {
|
|
1613
|
-
return (0, upload_1.resolveWorkerPlaydropAssetRequirement)(value);
|
|
1614
|
-
}
|
|
1615
1631
|
function readCodexAuthenticated(homeDir = node_os_1.default.homedir()) {
|
|
1616
1632
|
try {
|
|
1617
1633
|
const parsed = JSON.parse((0, node_fs_1.readFileSync)(node_path_1.default.join(homeDir, '.codex', 'auth.json'), 'utf8'));
|
|
@@ -2592,6 +2608,8 @@ function buildAgentFailureCode(agent, result, request) {
|
|
|
2592
2608
|
|| providerStatus === 502
|
|
2593
2609
|
|| providerStatus === 503
|
|
2594
2610
|
|| providerStatus === 504
|
|
2611
|
+
|| (agent === 'CLAUDE_CODE'
|
|
2612
|
+
&& /\bResponse stalled mid-stream\.?(?:\s+The response above may be incomplete\.?)?/i.test(combinedOutput))
|
|
2595
2613
|
|| /\b(?:service|provider|api)\s+(?:is\s+)?(?:temporarily\s+)?unavailable\b/i.test(combinedOutput)
|
|
2596
2614
|
|| /\btoo many requests\b/i.test(combinedOutput)) {
|
|
2597
2615
|
return `agent_provider_temporarily_unavailable:${provider}${providerStatus ? `:${providerStatus}` : ''}`;
|
|
@@ -5518,8 +5536,6 @@ async function uploadTask(options = {}) {
|
|
|
5518
5536
|
expectedSubtitle: taskContext.outputSubtitle,
|
|
5519
5537
|
expectedPrimarySurface: taskContext.outputPrimarySurface,
|
|
5520
5538
|
remixSourceRef: taskContext.remixSourceRef ?? null,
|
|
5521
|
-
playdropAssetRequirement: (0, upload_1.resolveWorkerPlaydropAssetRequirement)(taskContext.creatorRequest),
|
|
5522
|
-
creatorRequest: taskContext.creatorRequest,
|
|
5523
5539
|
projectDir,
|
|
5524
5540
|
creatorUsername: taskContext.creatorUsername,
|
|
5525
5541
|
apiBase: ctx.envConfig.apiBase,
|
|
@@ -5559,8 +5575,6 @@ async function uploadTask(options = {}) {
|
|
|
5559
5575
|
expectedSubtitle: taskContext.outputSubtitle,
|
|
5560
5576
|
expectedPrimarySurface: taskContext.outputPrimarySurface,
|
|
5561
5577
|
remixSourceRef: taskContext.remixSourceRef ?? null,
|
|
5562
|
-
playdropAssetRequirement: (0, upload_1.resolveWorkerPlaydropAssetRequirement)(taskContext.creatorRequest),
|
|
5563
|
-
creatorRequest: taskContext.creatorRequest,
|
|
5564
5578
|
projectDir,
|
|
5565
5579
|
creatorUsername: taskContext.creatorUsername,
|
|
5566
5580
|
apiBase: ctx.envConfig.apiBase,
|
|
@@ -5643,6 +5657,7 @@ async function claimSlugTask(options) {
|
|
|
5643
5657
|
}
|
|
5644
5658
|
async function completeTask(options) {
|
|
5645
5659
|
const taskContext = readTaskContextFile();
|
|
5660
|
+
const workspaceDir = resolveTaskWorkspaceDir();
|
|
5646
5661
|
if (taskContext.kind === 'GAME_REVIEW' || taskContext.kind === 'GAME_EVAL') {
|
|
5647
5662
|
throw new Error('task_done_not_allowed_for_game_review');
|
|
5648
5663
|
}
|
|
@@ -5668,6 +5683,7 @@ async function completeTask(options) {
|
|
|
5668
5683
|
}
|
|
5669
5684
|
const nextSteps = await readTaskNextStepsFileForCommand({
|
|
5670
5685
|
filePath: options.nextSteps,
|
|
5686
|
+
workspaceDir,
|
|
5671
5687
|
command: 'task done',
|
|
5672
5688
|
ctx,
|
|
5673
5689
|
taskContext,
|
|
@@ -5675,7 +5691,7 @@ async function completeTask(options) {
|
|
|
5675
5691
|
if (nextSteps === NEXT_STEPS_READ_FAILED) {
|
|
5676
5692
|
return;
|
|
5677
5693
|
}
|
|
5678
|
-
const platformFeedback = readPlatformFeedbackFile(options.platformFeedbackFile);
|
|
5694
|
+
const platformFeedback = readPlatformFeedbackFile(options.platformFeedbackFile, workspaceDir);
|
|
5679
5695
|
try {
|
|
5680
5696
|
await ctx.client.workerCompleteAgentTask(taskContext.taskId, {
|
|
5681
5697
|
taskToken: taskContext.taskToken,
|
|
@@ -5714,18 +5730,6 @@ async function completeTask(options) {
|
|
|
5714
5730
|
}
|
|
5715
5731
|
(0, output_1.printSuccess)('Task marked done.');
|
|
5716
5732
|
}
|
|
5717
|
-
function normalizeReviewSubmitState(value) {
|
|
5718
|
-
const normalized = typeof value === 'string' ? value.trim().toUpperCase() : '';
|
|
5719
|
-
if (normalized !== 'PASSED'
|
|
5720
|
-
&& normalized !== 'GOOD'
|
|
5721
|
-
&& normalized !== 'EXCELLENT'
|
|
5722
|
-
&& normalized !== 'LOW_QUALITY'
|
|
5723
|
-
&& normalized !== 'FAILED'
|
|
5724
|
-
&& normalized !== 'ERROR') {
|
|
5725
|
-
throw new Error('invalid_review_state');
|
|
5726
|
-
}
|
|
5727
|
-
return normalized;
|
|
5728
|
-
}
|
|
5729
5733
|
function readRequiredTextFile(filePath, code) {
|
|
5730
5734
|
const normalizedPath = typeof filePath === 'string' ? filePath.trim() : '';
|
|
5731
5735
|
if (!normalizedPath) {
|
|
@@ -5753,13 +5757,13 @@ function readOptionalTextFile(filePath) {
|
|
|
5753
5757
|
const content = (0, node_fs_1.readFileSync)(resolved, 'utf8').trim();
|
|
5754
5758
|
return content || null;
|
|
5755
5759
|
}
|
|
5756
|
-
function readPlatformFeedbackFile(filePath) {
|
|
5760
|
+
function readPlatformFeedbackFile(filePath, workspaceDir) {
|
|
5757
5761
|
const normalizedPath = typeof filePath === 'string' ? filePath.trim() : '';
|
|
5758
5762
|
if (!normalizedPath) {
|
|
5759
5763
|
return null;
|
|
5760
5764
|
}
|
|
5761
5765
|
try {
|
|
5762
|
-
const resolved =
|
|
5766
|
+
const resolved = resolveTaskWorkspaceInputPath(normalizedPath, workspaceDir);
|
|
5763
5767
|
if (!(0, node_fs_1.existsSync)(resolved)) {
|
|
5764
5768
|
throw new Error('file_missing');
|
|
5765
5769
|
}
|
|
@@ -5839,27 +5843,23 @@ function resolveReviewEvidenceDir(evidenceDir) {
|
|
|
5839
5843
|
}
|
|
5840
5844
|
async function submitReviewTask(options) {
|
|
5841
5845
|
const taskContext = readTaskContextFile();
|
|
5846
|
+
const workspaceDir = resolveTaskWorkspaceDir();
|
|
5842
5847
|
if (taskContext.kind !== 'GAME_REVIEW') {
|
|
5843
5848
|
throw new Error('task_submit_review_requires_game_review');
|
|
5844
5849
|
}
|
|
5845
|
-
const state =
|
|
5850
|
+
const state = (0, review_1.normalizeTerminalReviewState)(options.state);
|
|
5846
5851
|
const reviewMessageFile = resolveReviewFilePath(options.messageFile, 'invalid_review_message_file');
|
|
5847
|
-
const creatorFeedbackFile =
|
|
5848
|
-
? null
|
|
5849
|
-
: resolveReviewFilePath(options.creatorFeedbackFile, 'invalid_creator_feedback_file');
|
|
5852
|
+
const creatorFeedbackFile = resolveReviewFilePath(options.creatorFeedbackFile, 'invalid_creator_feedback_file');
|
|
5850
5853
|
const evidenceDir = resolveReviewEvidenceDir(options.evidenceDir);
|
|
5851
5854
|
const reviewMessage = readRequiredTextFile(reviewMessageFile, 'invalid_review_message_file');
|
|
5852
|
-
const creatorFeedback =
|
|
5853
|
-
|
|
5854
|
-
: readOptionalTextFile(creatorFeedbackFile ?? undefined);
|
|
5855
|
-
const platformFeedback = readPlatformFeedbackFile(options.platformFeedbackFile);
|
|
5855
|
+
const creatorFeedback = readOptionalTextFile(creatorFeedbackFile);
|
|
5856
|
+
const platformFeedback = readPlatformFeedbackFile(options.platformFeedbackFile, workspaceDir);
|
|
5856
5857
|
await (0, review_1.validateGameReviewResult)({
|
|
5857
5858
|
reviewState: state,
|
|
5858
5859
|
reviewMessage,
|
|
5859
5860
|
creatorFeedback: creatorFeedback ?? '',
|
|
5860
5861
|
evidenceDir,
|
|
5861
5862
|
});
|
|
5862
|
-
const workspaceDir = resolveTaskWorkspaceDir();
|
|
5863
5863
|
const instrumentEvidence = (0, instrument_evidence_1.readAndValidateInstrumentEvidenceManifest)({ workspaceDir, evidenceDir });
|
|
5864
5864
|
const evidenceFiles = readReviewEvidenceFiles(evidenceDir, workspaceDir);
|
|
5865
5865
|
const ctx = await resolveTaskCommandContext('task submit-review', options.env, taskContext);
|
|
@@ -5868,6 +5868,7 @@ async function submitReviewTask(options) {
|
|
|
5868
5868
|
}
|
|
5869
5869
|
const nextSteps = await readTaskNextStepsFileForCommand({
|
|
5870
5870
|
filePath: options.nextSteps,
|
|
5871
|
+
workspaceDir,
|
|
5871
5872
|
command: 'task submit-review',
|
|
5872
5873
|
ctx,
|
|
5873
5874
|
taskContext,
|
|
@@ -6005,11 +6006,12 @@ async function failTask(options) {
|
|
|
6005
6006
|
return;
|
|
6006
6007
|
}
|
|
6007
6008
|
const taskContext = readTaskContextFile();
|
|
6009
|
+
const workspaceDir = resolveTaskWorkspaceDir();
|
|
6008
6010
|
const ctx = await resolveTaskCommandContext('task fail', options.env, taskContext);
|
|
6009
6011
|
if (!ctx) {
|
|
6010
6012
|
return;
|
|
6011
6013
|
}
|
|
6012
|
-
const platformFeedback = readPlatformFeedbackFile(options.platformFeedbackFile);
|
|
6014
|
+
const platformFeedback = readPlatformFeedbackFile(options.platformFeedbackFile, workspaceDir);
|
|
6013
6015
|
await ctx.client.workerFailAgentTask(taskContext.taskId, {
|
|
6014
6016
|
taskToken: taskContext.taskToken,
|
|
6015
6017
|
error: message,
|
package/dist/index.js
CHANGED
|
@@ -289,7 +289,7 @@ task
|
|
|
289
289
|
task
|
|
290
290
|
.command('submit-review')
|
|
291
291
|
.description('Submit the active game review task result')
|
|
292
|
-
.requiredOption('--state <state>', '
|
|
292
|
+
.requiredOption('--state <state>', 'Review outcome: Blocked, Limited, Passed, Good, or Excellent')
|
|
293
293
|
.requiredOption('--message-file <path>', 'Internal assessment file')
|
|
294
294
|
.option('--creator-feedback-file <path>', 'Creator feedback file')
|
|
295
295
|
.option('--platform-feedback-file <path>', 'Markdown feedback about the PlayDrop platform')
|
|
@@ -339,7 +339,7 @@ const review = program.command('review').description('Review helper commands for
|
|
|
339
339
|
review
|
|
340
340
|
.command('validate-result')
|
|
341
341
|
.description('Validate a canonical game review result')
|
|
342
|
-
.requiredOption('--state <state>', '
|
|
342
|
+
.requiredOption('--state <state>', 'Review outcome: Blocked, Limited, Passed, Good, or Excellent')
|
|
343
343
|
.requiredOption('--message-file <path>', 'Internal assessment file')
|
|
344
344
|
.option('--creator-feedback-file <path>', 'Creator feedback file')
|
|
345
345
|
.requiredOption('--evidence-dir <path>', 'Review evidence directory')
|
|
@@ -1038,14 +1038,12 @@ project
|
|
|
1038
1038
|
.option('--app <name>', 'App name when the target is a workspace')
|
|
1039
1039
|
.option('--timeout <seconds>', 'Load-check timeout in seconds')
|
|
1040
1040
|
.option('--screenshot <path>', 'Screenshot output path')
|
|
1041
|
-
.option('--actions <path>', 'JSON array of click {x,y}, press {key}, and wait {ms <= 60000} actions')
|
|
1042
1041
|
.option('--tape <surface>', 'Run matched idle and catalogue playtest-tape captures on a declared surface')
|
|
1043
1042
|
.action(async (target, opts = {}) => {
|
|
1044
1043
|
await (0, check_1.check)(target, {
|
|
1045
1044
|
app: opts.app,
|
|
1046
1045
|
timeout: opts.timeout,
|
|
1047
1046
|
screenshot: opts.screenshot,
|
|
1048
|
-
actions: opts.actions,
|
|
1049
1047
|
tape: opts.tape,
|
|
1050
1048
|
});
|
|
1051
1049
|
});
|
|
@@ -17,6 +17,9 @@ export declare function readMediaCaptureProof(input: {
|
|
|
17
17
|
}): RecordAppUploadMediaCaptureRequest;
|
|
18
18
|
export declare function readWorkerUploadPreflightContext(projectDir: string): WorkerUploadPreflightContext | null;
|
|
19
19
|
export declare function assertAgentGameHeroListingPreflight(task: AppTask): void;
|
|
20
|
+
export declare function assertAgentGameIconListingPreflight(task: AppTask): void;
|
|
21
|
+
export declare function assertAgentGameVideoListingPreflight(task: AppTask): void;
|
|
22
|
+
export declare function assertAgentGamePreviewListingPreflight(task: AppTask): void;
|
|
20
23
|
export declare function assertAgentGameScreenshotListingPreflight(task: AppTask): void;
|
|
21
24
|
export declare function assertAgentGameCaptureReportPreflight(task: AppTask, options: {
|
|
22
25
|
requireCaptureReport: boolean;
|
package/dist/listingPreflight.js
CHANGED
|
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.readMediaCaptureProof = readMediaCaptureProof;
|
|
4
4
|
exports.readWorkerUploadPreflightContext = readWorkerUploadPreflightContext;
|
|
5
5
|
exports.assertAgentGameHeroListingPreflight = assertAgentGameHeroListingPreflight;
|
|
6
|
+
exports.assertAgentGameIconListingPreflight = assertAgentGameIconListingPreflight;
|
|
7
|
+
exports.assertAgentGameVideoListingPreflight = assertAgentGameVideoListingPreflight;
|
|
8
|
+
exports.assertAgentGamePreviewListingPreflight = assertAgentGamePreviewListingPreflight;
|
|
6
9
|
exports.assertAgentGameScreenshotListingPreflight = assertAgentGameScreenshotListingPreflight;
|
|
7
10
|
exports.assertAgentGameCaptureReportPreflight = assertAgentGameCaptureReportPreflight;
|
|
8
11
|
exports.assertAgentGameListingPreflight = assertAgentGameListingPreflight;
|
|
@@ -37,9 +40,12 @@ function readPngDimensions(filePath, errorCode = 'agent_task_listing_hero_art_in
|
|
|
37
40
|
}
|
|
38
41
|
return { width, height };
|
|
39
42
|
}
|
|
40
|
-
function assertListingIcon(task) {
|
|
43
|
+
function assertListingIcon(task, required = false) {
|
|
41
44
|
const iconPath = task.listing?.iconPath;
|
|
42
45
|
if (!iconPath) {
|
|
46
|
+
if (required) {
|
|
47
|
+
throw new Error('agent_task_listing_icon_missing: agent game listings must include a dedicated square PNG through listing.icon.');
|
|
48
|
+
}
|
|
43
49
|
return;
|
|
44
50
|
}
|
|
45
51
|
const icon = readPngDimensions(iconPath, 'listing_icon_invalid');
|
|
@@ -116,15 +122,6 @@ function assertScreenshotPath(task, filePath, field) {
|
|
|
116
122
|
function assertListingScreenshots(task) {
|
|
117
123
|
const portraitPaths = task.listing?.screenshotPortraitPaths ?? [];
|
|
118
124
|
const landscapePaths = task.listing?.screenshotLandscapePaths ?? [];
|
|
119
|
-
if (portraitPaths.length === 0 && landscapePaths.length === 0) {
|
|
120
|
-
throw new Error('agent_task_listing_screenshots_missing: new game tasks must include listing.screenshotsPortrait and/or listing.screenshotsLandscape PNG marketing images.');
|
|
121
|
-
}
|
|
122
|
-
if (task.surfaceTargets.includes('MOBILE_PORTRAIT') && portraitPaths.length === 0) {
|
|
123
|
-
throw new Error('agent_task_listing_screenshots_missing: mobile portrait games must include at least one listing.screenshotsPortrait PNG marketing image.');
|
|
124
|
-
}
|
|
125
|
-
if ((task.surfaceTargets.includes('DESKTOP') || task.surfaceTargets.includes('MOBILE_LANDSCAPE')) && landscapePaths.length === 0) {
|
|
126
|
-
throw new Error('agent_task_listing_screenshots_missing: desktop or landscape games must include at least one listing.screenshotsLandscape PNG marketing image.');
|
|
127
|
-
}
|
|
128
125
|
for (const filePath of portraitPaths) {
|
|
129
126
|
assertScreenshotPath(task, filePath, 'listing.screenshotsPortrait');
|
|
130
127
|
readPngDimensions(filePath);
|
|
@@ -134,6 +131,18 @@ function assertListingScreenshots(task) {
|
|
|
134
131
|
readPngDimensions(filePath);
|
|
135
132
|
}
|
|
136
133
|
}
|
|
134
|
+
function assertListingGameplayVideo(task) {
|
|
135
|
+
const videoCount = (task.listing?.videoPortraitPaths?.length ?? 0)
|
|
136
|
+
+ (task.listing?.videoLandscapePaths?.length ?? 0);
|
|
137
|
+
if (videoCount === 0) {
|
|
138
|
+
throw new Error('agent_task_listing_video_missing: agent game listings must include at least one real gameplay video through listing.videosPortrait or listing.videosLandscape.');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function assertListingPreviewMode(task) {
|
|
142
|
+
if (task.previewable !== true) {
|
|
143
|
+
throw new Error('agent_task_listing_preview_missing: agent game listings must set previewable: true and implement the real gameplay preview contract before capture.');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
137
146
|
function isSha256Hex(value) {
|
|
138
147
|
return typeof value === 'string' && /^[a-f0-9]{64}$/i.test(value.trim());
|
|
139
148
|
}
|
|
@@ -158,6 +167,12 @@ function readMediaCaptureProof(input) {
|
|
|
158
167
|
if (!binding || typeof binding !== 'object' || Array.isArray(binding)) {
|
|
159
168
|
throw new Error(`agent_task_media_capture_report_invalid: ${reportPath} is missing binding.`);
|
|
160
169
|
}
|
|
170
|
+
if (report.appName !== input.task.name) {
|
|
171
|
+
throw new Error(`agent_task_media_capture_wrong_app: listing.captureReport is for ${String(report.appName || 'unknown')}, not ${input.task.name}; rerun "playdrop project capture".`);
|
|
172
|
+
}
|
|
173
|
+
if (binding.pixelValidation !== 'passed') {
|
|
174
|
+
throw new Error(`agent_task_media_capture_pixels_unvalidated: ${reportPath} must come from a capture that passed wrong-game, black-frame, and motion checks.`);
|
|
175
|
+
}
|
|
161
176
|
if (input.expectedRuntimeBundleHash) {
|
|
162
177
|
const capturedRuntimeBundleHash = typeof binding.runtimeBundleHash === 'string'
|
|
163
178
|
? binding.runtimeBundleHash.trim().toLowerCase()
|
|
@@ -309,6 +324,15 @@ function readWorkerUploadPreflightContext(projectDir) {
|
|
|
309
324
|
function assertAgentGameHeroListingPreflight(task) {
|
|
310
325
|
assertHeroListingArt(task);
|
|
311
326
|
}
|
|
327
|
+
function assertAgentGameIconListingPreflight(task) {
|
|
328
|
+
assertListingIcon(task, true);
|
|
329
|
+
}
|
|
330
|
+
function assertAgentGameVideoListingPreflight(task) {
|
|
331
|
+
assertListingGameplayVideo(task);
|
|
332
|
+
}
|
|
333
|
+
function assertAgentGamePreviewListingPreflight(task) {
|
|
334
|
+
assertListingPreviewMode(task);
|
|
335
|
+
}
|
|
312
336
|
function assertAgentGameScreenshotListingPreflight(task) {
|
|
313
337
|
assertListingScreenshots(task);
|
|
314
338
|
}
|
|
@@ -322,7 +346,10 @@ function assertAgentGameCaptureReportPreflight(task, options) {
|
|
|
322
346
|
}
|
|
323
347
|
}
|
|
324
348
|
function assertAgentGameListingPreflight(task, options) {
|
|
349
|
+
assertAgentGameIconListingPreflight(task);
|
|
325
350
|
assertAgentGameHeroListingPreflight(task);
|
|
351
|
+
assertAgentGamePreviewListingPreflight(task);
|
|
352
|
+
assertAgentGameVideoListingPreflight(task);
|
|
326
353
|
assertAgentGameScreenshotListingPreflight(task);
|
|
327
354
|
assertAgentGameCaptureReportPreflight(task, options);
|
|
328
355
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-playtest-tape.d.ts","sourceRoot":"","sources":["../src/app-playtest-tape.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EAEhB,MAAM,UAAU,CAAC;AAElB,eAAO,MAAM,yBAAyB,EAAG,CAAU,CAAC;AACpD,eAAO,MAAM,oCAAoC,KAAK,CAAC;AAEvD,eAAO,MAAM,gCAAgC,0CAA2C,CAAC;AACzF,MAAM,MAAM,sBAAsB,GAAG,OAAO,gCAAgC,CAAC,MAAM,CAAC,CAAC;AASrF,wBAAgB,kCAAkC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAEtG;AAED,eAAO,MAAM,uCAAuC,sFAK1C,CAAC;AAEX,MAAM,MAAM,4BAA4B,GAAG,OAAO,uCAAuC,CAAC,MAAM,CAAC,CAAC;AAElG,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,4BAA4B,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,qBAAqB,GAAG;IAC5D,IAAI,EAAE,aAAa,GAAG,aAAa,GAAG,WAAW,CAAC;IAClD,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,GAAG;IACxD,IAAI,EAAE,KAAK,CAAC;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,qBAAqB,GAAG;IAC7D,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAC5B,uBAAuB,GACvB,mBAAmB,GACnB,wBAAwB,CAAC;AAE7B,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,yBAAyB,CAAC;IAC1C,WAAW,EAAE,sBAAsB,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,wBAAwB,EAAE,CAAC;IAC3C,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC;AAE5E,MAAM,MAAM,+BAA+B,GACvC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE,GACrC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"app-playtest-tape.d.ts","sourceRoot":"","sources":["../src/app-playtest-tape.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EAEhB,MAAM,UAAU,CAAC;AAElB,eAAO,MAAM,yBAAyB,EAAG,CAAU,CAAC;AACpD,eAAO,MAAM,oCAAoC,KAAK,CAAC;AAEvD,eAAO,MAAM,gCAAgC,0CAA2C,CAAC;AACzF,MAAM,MAAM,sBAAsB,GAAG,OAAO,gCAAgC,CAAC,MAAM,CAAC,CAAC;AASrF,wBAAgB,kCAAkC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAEtG;AAED,eAAO,MAAM,uCAAuC,sFAK1C,CAAC;AAEX,MAAM,MAAM,4BAA4B,GAAG,OAAO,uCAAuC,CAAC,MAAM,CAAC,CAAC;AAElG,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,4BAA4B,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,qBAAqB,GAAG;IAC5D,IAAI,EAAE,aAAa,GAAG,aAAa,GAAG,WAAW,CAAC;IAClD,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,GAAG;IACxD,IAAI,EAAE,KAAK,CAAC;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,qBAAqB,GAAG;IAC7D,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAC5B,uBAAuB,GACvB,mBAAmB,GACnB,wBAAwB,CAAC;AAE7B,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,yBAAyB,CAAC;IAC1C,WAAW,EAAE,sBAAsB,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,wBAAwB,EAAE,CAAC;IAC3C,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC;AAE5E,MAAM,MAAM,+BAA+B,GACvC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE,GACrC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAgQjC,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,OAAO,EACd,OAAO,GAAE;IACP,cAAc,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IACvC,eAAe,CAAC,EAAE,OAAO,CAAC;CACtB,GACL,+BAA+B,CAoCjC"}
|
|
@@ -106,7 +106,8 @@ function normalizeEvent(value, surface, durationMs, index) {
|
|
|
106
106
|
if (surface !== "DESKTOP") {
|
|
107
107
|
throw new Error(`keyboard_input_not_allowed_on_mobile_tape:${surface}:${index}`);
|
|
108
108
|
}
|
|
109
|
-
const
|
|
109
|
+
const rawKey = typeof value["key"] === "string" ? value["key"] : "";
|
|
110
|
+
const key = rawKey === " " ? rawKey : rawKey.trim();
|
|
110
111
|
if (!key || key.length > 64) {
|
|
111
112
|
throw new Error(`invalid_playtest_tape_key:${surface}:${index}`);
|
|
112
113
|
}
|
|
@@ -242,30 +243,37 @@ function normalizeTape(value, surface) {
|
|
|
242
243
|
if (primaryVerb === "tap")
|
|
243
244
|
return event.type === "tap";
|
|
244
245
|
if (primaryVerb === "key")
|
|
245
|
-
return event.type === "keyDown"
|
|
246
|
+
return event.type === "keyDown";
|
|
246
247
|
return event.type === "pointerDown" || event.type === "pointerMove" || event.type === "pointerUp";
|
|
247
248
|
};
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
throw new Error(`playtest_tape_primary_verb_event_mismatch:${surface}:${primaryVerb}:${startOnlyEventCount + mismatchedEventIndex}`);
|
|
249
|
+
if (!postStartEvents.some(postStartEventMatchesVerb)) {
|
|
250
|
+
throw new Error(`playtest_tape_primary_verb_events_required:${surface}:${primaryVerb}`);
|
|
251
251
|
}
|
|
252
252
|
if (primaryVerb === "swipe" || primaryVerb === "drag") {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
253
|
+
const openGestures = /* @__PURE__ */ new Set();
|
|
254
|
+
const movedGestures = /* @__PURE__ */ new Set();
|
|
255
|
+
let completePrimaryGesture = false;
|
|
256
|
+
for (const event of postStartEvents) {
|
|
257
|
+
if (event.type !== "pointerDown" && event.type !== "pointerMove" && event.type !== "pointerUp")
|
|
258
|
+
continue;
|
|
259
|
+
const pointerId = event.pointerId ?? 0;
|
|
257
260
|
if (event.type === "pointerDown") {
|
|
258
|
-
|
|
259
|
-
|
|
261
|
+
openGestures.add(pointerId);
|
|
262
|
+
movedGestures.delete(pointerId);
|
|
260
263
|
} else if (event.type === "pointerMove") {
|
|
261
|
-
|
|
264
|
+
if (openGestures.has(pointerId))
|
|
265
|
+
movedGestures.add(pointerId);
|
|
262
266
|
} else if (event.type === "pointerUp") {
|
|
263
|
-
if (
|
|
264
|
-
|
|
267
|
+
if (openGestures.has(pointerId) && movedGestures.has(pointerId)) {
|
|
268
|
+
completePrimaryGesture = true;
|
|
265
269
|
}
|
|
266
|
-
|
|
270
|
+
openGestures.delete(pointerId);
|
|
271
|
+
movedGestures.delete(pointerId);
|
|
267
272
|
}
|
|
268
273
|
}
|
|
274
|
+
if (!completePrimaryGesture) {
|
|
275
|
+
throw new Error(`playtest_tape_primary_verb_gesture_required:${surface}:${primaryVerb}`);
|
|
276
|
+
}
|
|
269
277
|
}
|
|
270
278
|
return {
|
|
271
279
|
version: APP_PLAYTEST_TAPE_VERSION,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playdrop/playdrop-cli",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"description": "Official Playdrop CLI for publishing browser games, creator apps, and AI-generated game assets on playdrop.ai",
|
|
5
5
|
"homepage": "https://www.playdrop.ai",
|
|
6
6
|
"repository": {
|