@playdrop/playdrop-cli 0.12.26 → 0.12.28
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/apps/build.js +43 -0
- package/dist/apps/index.d.ts +2 -1
- package/dist/apps/index.js +1 -0
- package/dist/apps/upload.d.ts +2 -1
- package/dist/apps/upload.js +1 -0
- package/dist/commands/devRuntimeAssets.js +26 -124
- package/dist/commands/ownedRuntimeImageValidation.d.ts +7 -0
- package/dist/commands/ownedRuntimeImageValidation.js +76 -0
- package/dist/commands/upload.d.ts +7 -1
- package/dist/commands/upload.js +79 -402
- package/dist/commands/worker.d.ts +16 -0
- package/dist/commands/worker.js +164 -35
- package/dist/listingPreflight.d.ts +1 -0
- package/dist/listingPreflight.js +17 -0
- package/node_modules/@playdrop/api-client/dist/client.d.ts +2 -1
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/core/errors.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/core/errors.js +3 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +2 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.js +11 -0
- package/node_modules/@playdrop/api-client/dist/index.d.ts +2 -1
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +5 -0
- package/node_modules/@playdrop/config/client-meta.json +1 -1
- package/node_modules/@playdrop/types/dist/agent-task-playtest.d.ts +23 -0
- package/node_modules/@playdrop/types/dist/agent-task-playtest.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/agent-task-playtest.js +84 -0
- package/node_modules/@playdrop/types/dist/api.d.ts +13 -0
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.d.ts +1 -0
- package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.js +3 -1
- package/node_modules/@playdrop/types/dist/version.d.ts +4 -2
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.js +13 -3
- package/package.json +1 -1
package/config/client-meta.json
CHANGED
package/dist/apps/build.js
CHANGED
|
@@ -226,8 +226,41 @@ function appendIncludedProjectFile(rootDir, includePath, excludedRelativeFiles,
|
|
|
226
226
|
results.push(record);
|
|
227
227
|
fileByRelativePath.set(record.relativePath, record);
|
|
228
228
|
}
|
|
229
|
+
function appendIncludedProjectDirectory(rootDir, includeDirectory, excludedRelativeFiles, fileByRelativePath, results, enforceReservedBundleNames) {
|
|
230
|
+
const normalizedDirectory = (0, node_path_1.normalize)(includeDirectory).split(node_path_1.sep).join('/').replace(/\/+$/, '');
|
|
231
|
+
if (!normalizedDirectory || normalizedDirectory === '.' || normalizedDirectory.startsWith('..')) {
|
|
232
|
+
throw new Error(`[apps][build] Invalid include directory path "${includeDirectory}".`);
|
|
233
|
+
}
|
|
234
|
+
const absoluteDirectory = (0, node_path_1.resolve)(rootDir, normalizedDirectory);
|
|
235
|
+
if (!isPathWithinRoot(rootDir, absoluteDirectory)) {
|
|
236
|
+
throw new Error(`[apps][build] Included directory "${includeDirectory}" escapes the project root.`);
|
|
237
|
+
}
|
|
238
|
+
if (!(0, node_fs_1.existsSync)(absoluteDirectory) || !(0, node_fs_1.statSync)(absoluteDirectory).isDirectory()) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const stack = [absoluteDirectory];
|
|
242
|
+
while (stack.length > 0) {
|
|
243
|
+
const current = stack.pop();
|
|
244
|
+
for (const entry of readProjectDirEntries(current)) {
|
|
245
|
+
const absolutePath = (0, node_path_1.join)(current, entry.name);
|
|
246
|
+
const relativePath = normalizeRelativePath(rootDir, absolutePath);
|
|
247
|
+
if (isExcludedRelativePath(entry.isDirectory() ? `${relativePath}/` : relativePath, excludedRelativeFiles)) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (entry.isDirectory()) {
|
|
251
|
+
stack.push(absolutePath);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (!entry.isFile()) {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
appendIncludedProjectFile(rootDir, relativePath, excludedRelativeFiles, fileByRelativePath, results, enforceReservedBundleNames);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
229
261
|
function collectProjectFiles(rootDir, rules, options) {
|
|
230
262
|
const includeRelativeFiles = options?.includeRelativeFiles ?? [];
|
|
263
|
+
const includeRelativeDirectories = options?.includeRelativeDirectories ?? [];
|
|
231
264
|
const excludeRelativeFiles = new Set(Array.from(options?.excludeRelativeFiles ?? [])
|
|
232
265
|
.map((filePath) => (0, node_path_1.normalize)(filePath).split(node_path_1.sep).join('/'))
|
|
233
266
|
.filter((filePath) => filePath.length > 0 && filePath !== '.'));
|
|
@@ -285,6 +318,9 @@ function collectProjectFiles(rootDir, rules, options) {
|
|
|
285
318
|
for (const includePath of includeRelativeFiles) {
|
|
286
319
|
appendIncludedProjectFile(rootDir, includePath, excludeRelativeFiles, fileByRelativePath, results, enforceReservedBundleNames);
|
|
287
320
|
}
|
|
321
|
+
for (const includeDirectory of includeRelativeDirectories) {
|
|
322
|
+
appendIncludedProjectDirectory(rootDir, includeDirectory, excludeRelativeFiles, fileByRelativePath, results, enforceReservedBundleNames);
|
|
323
|
+
}
|
|
288
324
|
results.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
289
325
|
return results;
|
|
290
326
|
}
|
|
@@ -496,6 +532,11 @@ function createZipArchive(entries) {
|
|
|
496
532
|
function createSourceArchive(task) {
|
|
497
533
|
const rules = buildIgnoreRules(task.projectDir);
|
|
498
534
|
const includeFiles = ['README.md', 'AGENTS.md'];
|
|
535
|
+
for (const heroPath of [task.listing?.heroPortraitPath, task.listing?.heroLandscapePath]) {
|
|
536
|
+
if (heroPath) {
|
|
537
|
+
includeFiles.push(normalizeRelativePath(task.projectDir, heroPath));
|
|
538
|
+
}
|
|
539
|
+
}
|
|
499
540
|
const excludedFiles = task.sourceArchiveExcludeRelativeFiles ?? [];
|
|
500
541
|
if (task.catalogueAbsolutePath && (0, node_fs_1.existsSync)(task.catalogueAbsolutePath)) {
|
|
501
542
|
const relativeCataloguePath = normalizeRelativePath(task.projectDir, task.catalogueAbsolutePath) || 'catalogue.json';
|
|
@@ -509,6 +550,7 @@ function createSourceArchive(task) {
|
|
|
509
550
|
}
|
|
510
551
|
const entries = collectProjectFiles(task.projectDir, rules, {
|
|
511
552
|
includeRelativeFiles: includeFiles,
|
|
553
|
+
includeRelativeDirectories: ['assets/art-direction'],
|
|
512
554
|
excludeRelativeFiles: excludedFiles,
|
|
513
555
|
});
|
|
514
556
|
const buffer = createZipArchive(entries);
|
|
@@ -519,6 +561,7 @@ function createSourceArchive(task) {
|
|
|
519
561
|
}
|
|
520
562
|
const entries = collectProjectFiles(task.projectDir, rules, {
|
|
521
563
|
includeRelativeFiles: includeFiles,
|
|
564
|
+
includeRelativeDirectories: ['assets/art-direction'],
|
|
522
565
|
excludeRelativeFiles: excludedFiles,
|
|
523
566
|
});
|
|
524
567
|
const buffer = createZipArchive(entries);
|
package/dist/apps/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ApiClient } from '@playdrop/api-client';
|
|
2
|
-
import type { UserResponse } from '@playdrop/types';
|
|
2
|
+
import type { AgentTaskPlaytestProof, UserResponse } from '@playdrop/types';
|
|
3
3
|
import type { AppTask } from '../catalogue';
|
|
4
4
|
import type { TaskCaptureSession } from '../appUrls';
|
|
5
5
|
import { collectAppValidationWarnings, validateAppTask, runFormatScript } from './validate';
|
|
@@ -23,6 +23,7 @@ export type AppPipelineOptions = {
|
|
|
23
23
|
loadCheckTimeoutMs?: number;
|
|
24
24
|
ensureRegisteredAppShell?: boolean;
|
|
25
25
|
agentTaskId?: number;
|
|
26
|
+
playtestProof?: AgentTaskPlaytestProof;
|
|
26
27
|
mediaCaptureRequired?: boolean;
|
|
27
28
|
captureSession?: TaskCaptureSession | null;
|
|
28
29
|
};
|
package/dist/apps/index.js
CHANGED
|
@@ -64,6 +64,7 @@ async function runAppPipeline(client, task, options) {
|
|
|
64
64
|
runStagedUploadLoadCheck: options?.runStagedUploadLoadCheck,
|
|
65
65
|
loadCheckTimeoutMs: options?.loadCheckTimeoutMs,
|
|
66
66
|
agentTaskId: options?.agentTaskId,
|
|
67
|
+
playtestProof: options?.playtestProof,
|
|
67
68
|
mediaCaptureRequired: options?.mediaCaptureRequired,
|
|
68
69
|
captureSession: options?.captureSession,
|
|
69
70
|
};
|
package/dist/apps/upload.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ApiClient } from '@playdrop/api-client';
|
|
2
|
-
import type { UserResponse } from '@playdrop/types';
|
|
2
|
+
import type { AgentTaskPlaytestProof, UserResponse } from '@playdrop/types';
|
|
3
3
|
import type { AppTask } from '../catalogue';
|
|
4
4
|
import type { TaskCaptureSession } from '../appUrls';
|
|
5
5
|
import type { AppBuildArtifacts } from './build';
|
|
@@ -21,6 +21,7 @@ export type AppUploadOptions = {
|
|
|
21
21
|
runStagedUploadLoadCheck?: boolean;
|
|
22
22
|
loadCheckTimeoutMs?: number;
|
|
23
23
|
agentTaskId?: number;
|
|
24
|
+
playtestProof?: AgentTaskPlaytestProof;
|
|
24
25
|
mediaCaptureRequired?: boolean;
|
|
25
26
|
captureSession?: TaskCaptureSession | null;
|
|
26
27
|
};
|
package/dist/apps/upload.js
CHANGED
|
@@ -232,6 +232,7 @@ async function uploadAppVersion(client, task, artifacts, options) {
|
|
|
232
232
|
const initializeRequest = {
|
|
233
233
|
version: task.version,
|
|
234
234
|
agentTaskId: options?.agentTaskId,
|
|
235
|
+
playtestProof: options?.playtestProof,
|
|
235
236
|
releaseNotes: task.releaseNotes,
|
|
236
237
|
visibility: task.versionVisibility,
|
|
237
238
|
setAsCurrent: task.versionVisibility === 'PRIVATE' ? false : undefined,
|
|
@@ -37,6 +37,7 @@ const CONTENT_TYPE_BY_EXTENSION = {
|
|
|
37
37
|
'.png': 'image/png',
|
|
38
38
|
'.jpg': 'image/jpeg',
|
|
39
39
|
'.jpeg': 'image/jpeg',
|
|
40
|
+
'.gif': 'image/gif',
|
|
40
41
|
'.svg': 'image/svg+xml',
|
|
41
42
|
'.webp': 'image/webp',
|
|
42
43
|
'.glb': 'model/gltf-binary',
|
|
@@ -52,25 +53,6 @@ function inferContentType(filePath) {
|
|
|
52
53
|
const extension = (0, node_path_1.extname)(filePath).toLowerCase();
|
|
53
54
|
return CONTENT_TYPE_BY_EXTENSION[extension] ?? null;
|
|
54
55
|
}
|
|
55
|
-
function normalizeManifestFiles(fileManifest) {
|
|
56
|
-
const files = fileManifest && typeof fileManifest === 'object' && Array.isArray(fileManifest.files)
|
|
57
|
-
? fileManifest.files
|
|
58
|
-
: [];
|
|
59
|
-
return files
|
|
60
|
-
.filter((entry) => Boolean(entry && typeof entry === 'object'))
|
|
61
|
-
.map((entry) => ({
|
|
62
|
-
role: typeof entry.role === 'string' ? entry.role.trim().toLowerCase() : '',
|
|
63
|
-
key: typeof entry.key === 'string' ? entry.key.trim() : '',
|
|
64
|
-
contentType: typeof entry.contentType === 'string' ? entry.contentType : null,
|
|
65
|
-
sizeBytes: typeof entry.sizeBytes === 'number' && Number.isFinite(entry.sizeBytes) ? entry.sizeBytes : null,
|
|
66
|
-
sha256: typeof entry.sha256 === 'string' ? entry.sha256 : null,
|
|
67
|
-
}))
|
|
68
|
-
.filter((entry) => entry.role.length > 0);
|
|
69
|
-
}
|
|
70
|
-
function buildPublishedAssetFileUrl(apiBase, creatorUsername, assetName, revision, role) {
|
|
71
|
-
const path = `/assets/${encodeURIComponent(creatorUsername)}/${encodeURIComponent(assetName)}/versions/${encodeURIComponent(`r${revision}`)}/file?role=${encodeURIComponent(role)}`;
|
|
72
|
-
return new URL(path, apiBase).toString();
|
|
73
|
-
}
|
|
74
56
|
function buildSyntheticLocalAssetRef(creatorUsername, appName, ownedAssetName) {
|
|
75
57
|
return (0, types_1.formatContentVersionRef)({
|
|
76
58
|
kind: 'asset',
|
|
@@ -87,86 +69,6 @@ function buildLocalDevRuntimeAssetFileUrl(appBaseUrl, ownedAssetName, role) {
|
|
|
87
69
|
const base = appBaseUrl.endsWith('/') ? appBaseUrl.slice(0, -1) : appBaseUrl;
|
|
88
70
|
return `${base}/_playdrop/runtime-assets/files/${encodeURIComponent(ownedAssetName)}?role=${encodeURIComponent(role)}`;
|
|
89
71
|
}
|
|
90
|
-
async function findAssetVersionByRevision(client, creatorUsername, assetName, revision) {
|
|
91
|
-
let offset = 0;
|
|
92
|
-
for (let page = 0; page < 200; page += 1) {
|
|
93
|
-
const response = await client.listAssetVersions(creatorUsername, assetName, {
|
|
94
|
-
limit: 100,
|
|
95
|
-
offset,
|
|
96
|
-
});
|
|
97
|
-
const version = (response.versions ?? []).find((entry) => entry.revision === revision);
|
|
98
|
-
if (version) {
|
|
99
|
-
return version;
|
|
100
|
-
}
|
|
101
|
-
if (!response.pagination?.hasMore) {
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
offset += response.pagination.limit;
|
|
105
|
-
}
|
|
106
|
-
throw new Error(`dev_runtime_asset_version_not_found:${creatorUsername}/${assetName}@r${revision}`);
|
|
107
|
-
}
|
|
108
|
-
async function findPackVersion(client, creatorUsername, packName, versionString) {
|
|
109
|
-
let offset = 0;
|
|
110
|
-
for (let page = 0; page < 200; page += 1) {
|
|
111
|
-
const response = await client.listAssetPackVersions(creatorUsername, packName, {
|
|
112
|
-
limit: 100,
|
|
113
|
-
offset,
|
|
114
|
-
});
|
|
115
|
-
const version = (response.versions ?? []).find((entry) => entry.version === versionString);
|
|
116
|
-
if (version) {
|
|
117
|
-
return version;
|
|
118
|
-
}
|
|
119
|
-
if (!response.pagination?.hasMore) {
|
|
120
|
-
break;
|
|
121
|
-
}
|
|
122
|
-
offset += response.pagination.limit;
|
|
123
|
-
}
|
|
124
|
-
throw new Error(`dev_runtime_pack_version_not_found:${creatorUsername}/${packName}@${versionString}`);
|
|
125
|
-
}
|
|
126
|
-
function createPublishedAssetSummary(input) {
|
|
127
|
-
const files = normalizeManifestFiles(input.version.fileManifest).map((file) => ({
|
|
128
|
-
role: file.role,
|
|
129
|
-
url: buildPublishedAssetFileUrl(input.apiBase, input.creatorUsername, input.assetName, input.revision, file.role),
|
|
130
|
-
contentType: file.contentType ?? null,
|
|
131
|
-
sizeBytes: file.sizeBytes ?? null,
|
|
132
|
-
sha256: file.sha256 ?? null,
|
|
133
|
-
}));
|
|
134
|
-
if (files.length === 0) {
|
|
135
|
-
throw new Error(`dev_runtime_asset_files_missing:${input.creatorUsername}/${input.assetName}@r${input.revision}`);
|
|
136
|
-
}
|
|
137
|
-
return {
|
|
138
|
-
assetRef: (0, types_1.formatContentVersionRef)({
|
|
139
|
-
kind: 'asset',
|
|
140
|
-
creatorUsername: input.creatorUsername,
|
|
141
|
-
name: input.assetName,
|
|
142
|
-
revision: input.revision,
|
|
143
|
-
}),
|
|
144
|
-
runtimeKey: input.runtimeKey?.trim() || null,
|
|
145
|
-
sourceType: input.sourceType,
|
|
146
|
-
...(input.sourcePackRef ? { sourcePackRef: input.sourcePackRef } : {}),
|
|
147
|
-
files,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
async function resolvePublishedAssetSummary(client, apiBase, assetRef, options) {
|
|
151
|
-
const parsed = (0, types_1.parseContentVersionRef)(assetRef);
|
|
152
|
-
if (!parsed || parsed.kind !== 'asset') {
|
|
153
|
-
throw new Error(`dev_runtime_asset_ref_invalid:${assetRef}`);
|
|
154
|
-
}
|
|
155
|
-
const version = await findAssetVersionByRevision(client, parsed.creatorUsername, parsed.name, parsed.revision);
|
|
156
|
-
if (version.visibility !== 'PUBLIC') {
|
|
157
|
-
throw new Error(`dev_runtime_asset_not_public:${assetRef}`);
|
|
158
|
-
}
|
|
159
|
-
return createPublishedAssetSummary({
|
|
160
|
-
apiBase,
|
|
161
|
-
creatorUsername: parsed.creatorUsername,
|
|
162
|
-
assetName: parsed.name,
|
|
163
|
-
revision: parsed.revision,
|
|
164
|
-
version,
|
|
165
|
-
runtimeKey: options.runtimeKey,
|
|
166
|
-
sourceType: options.sourceType,
|
|
167
|
-
sourcePackRef: options.sourcePackRef,
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
72
|
function createLocalOwnedAssetSummary(task, creatorUsername, appBaseUrl, ownedAsset) {
|
|
171
73
|
const files = {};
|
|
172
74
|
const runtimeFiles = [];
|
|
@@ -260,41 +162,41 @@ async function buildDevRuntimeAssetManifest(input) {
|
|
|
260
162
|
assets.push(localAsset.asset);
|
|
261
163
|
ownedAssetFiles[ownedAsset.name] = localAsset.files;
|
|
262
164
|
}
|
|
263
|
-
|
|
165
|
+
const directDependencies = input.task.uses.assets.map((dependency) => {
|
|
264
166
|
assertRuntimeKeyAvailable(seenRuntimeKeys, dependency.runtimeKey, dependency.ref);
|
|
265
167
|
const parsedDependencyRef = (0, types_1.parseContentVersionRef)(dependency.ref);
|
|
266
168
|
if (!parsedDependencyRef || parsedDependencyRef.kind !== 'asset') {
|
|
267
169
|
throw new Error(`dev_runtime_asset_ref_invalid:${dependency.ref}`);
|
|
268
170
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
for (const packRef of input.task.uses.packs) {
|
|
171
|
+
return {
|
|
172
|
+
ref: (0, types_1.formatContentVersionRef)(parsedDependencyRef),
|
|
173
|
+
runtimeKey: dependency.runtimeKey?.trim() || null,
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
const packDependencies = input.task.uses.packs.map((packRef) => {
|
|
277
177
|
const parsedPackRef = (0, types_1.parseContentVersionRef)(packRef);
|
|
278
178
|
if (!parsedPackRef || parsedPackRef.kind !== 'pack') {
|
|
279
179
|
throw new Error(`dev_runtime_pack_ref_invalid:${packRef}`);
|
|
280
180
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
181
|
+
return (0, types_1.formatContentVersionRef)(parsedPackRef);
|
|
182
|
+
});
|
|
183
|
+
if (directDependencies.length > 0 || packDependencies.length > 0) {
|
|
184
|
+
const resolved = await input.client.resolveRuntimeAssets({
|
|
185
|
+
assets: directDependencies,
|
|
186
|
+
packs: packDependencies,
|
|
187
|
+
});
|
|
188
|
+
for (const entry of resolved.assets) {
|
|
189
|
+
if (entry.sourceType !== 'DIRECT' && entry.sourceType !== 'PACK') {
|
|
190
|
+
throw new Error(`dev_runtime_asset_source_type_invalid:${entry.assetRef}:${entry.sourceType}`);
|
|
191
|
+
}
|
|
192
|
+
assertCustomAssetVersionSupported(input.task, entry.assetRef, entry);
|
|
193
|
+
assets.push({
|
|
194
|
+
assetRef: entry.assetRef,
|
|
195
|
+
runtimeKey: entry.runtimeKey,
|
|
196
|
+
sourceType: entry.sourceType,
|
|
197
|
+
...(entry.sourcePackRef ? { sourcePackRef: entry.sourcePackRef } : {}),
|
|
198
|
+
files: entry.files,
|
|
291
199
|
});
|
|
292
|
-
const memberVersion = await findAssetVersionByRevision(input.client, member.creatorUsername, member.name, member.revision);
|
|
293
|
-
assertCustomAssetVersionSupported(input.task, memberRef, memberVersion);
|
|
294
|
-
assets.push(await resolvePublishedAssetSummary(input.client, input.apiBase, memberRef, {
|
|
295
|
-
sourceType: 'PACK',
|
|
296
|
-
sourcePackRef: packRef,
|
|
297
|
-
}));
|
|
298
200
|
}
|
|
299
201
|
}
|
|
300
202
|
return {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AppTask } from '../catalogue';
|
|
2
|
+
export declare function validateOwnedRuntimeImageFile(input: {
|
|
3
|
+
assetName: string;
|
|
4
|
+
filePath: string;
|
|
5
|
+
declaredFormat?: string;
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
export declare function assertOwnedRuntimeImagesAreValid(task: AppTask): Promise<void>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.validateOwnedRuntimeImageFile = validateOwnedRuntimeImageFile;
|
|
7
|
+
exports.assertOwnedRuntimeImagesAreValid = assertOwnedRuntimeImagesAreValid;
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const sharp_1 = __importDefault(require("sharp"));
|
|
11
|
+
const IMAGE_BY_EXTENSION = {
|
|
12
|
+
'.png': { format: 'png', contentType: 'image/png' },
|
|
13
|
+
'.jpg': { format: 'jpeg', contentType: 'image/jpeg' },
|
|
14
|
+
'.jpeg': { format: 'jpeg', contentType: 'image/jpeg' },
|
|
15
|
+
'.webp': { format: 'webp', contentType: 'image/webp' },
|
|
16
|
+
'.gif': { format: 'gif', contentType: 'image/gif' },
|
|
17
|
+
'.svg': { format: 'svg', contentType: 'image/svg+xml' },
|
|
18
|
+
};
|
|
19
|
+
const SHARP_FORMAT_BY_DECLARED_FORMAT = {
|
|
20
|
+
PNG: 'png',
|
|
21
|
+
JPEG: 'jpeg',
|
|
22
|
+
WEBP: 'webp',
|
|
23
|
+
GIF: 'gif',
|
|
24
|
+
};
|
|
25
|
+
async function validateOwnedRuntimeImageFile(input) {
|
|
26
|
+
let stats;
|
|
27
|
+
try {
|
|
28
|
+
stats = (0, node_fs_1.statSync)(input.filePath);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
throw new Error(`agent_task_owned_image_file_missing:${input.assetName}:${input.filePath}`);
|
|
32
|
+
}
|
|
33
|
+
if (!stats.isFile()) {
|
|
34
|
+
throw new Error(`agent_task_owned_image_file_missing:${input.assetName}:${input.filePath}`);
|
|
35
|
+
}
|
|
36
|
+
const extension = (0, node_path_1.extname)(input.filePath).toLowerCase();
|
|
37
|
+
const expected = IMAGE_BY_EXTENSION[extension];
|
|
38
|
+
if (!expected) {
|
|
39
|
+
throw new Error(`agent_task_owned_image_format_unsupported:${input.assetName}:${extension || 'missing_extension'}`);
|
|
40
|
+
}
|
|
41
|
+
let metadata;
|
|
42
|
+
try {
|
|
43
|
+
metadata = await (0, sharp_1.default)(input.filePath, { failOn: 'error' }).metadata();
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
throw new Error(`agent_task_owned_image_decode_failed:${input.assetName}:${input.filePath}`);
|
|
47
|
+
}
|
|
48
|
+
if (!Number.isInteger(metadata.width) || Number(metadata.width) <= 0
|
|
49
|
+
|| !Number.isInteger(metadata.height) || Number(metadata.height) <= 0) {
|
|
50
|
+
throw new Error(`agent_task_owned_image_dimensions_invalid:${input.assetName}:${input.filePath}`);
|
|
51
|
+
}
|
|
52
|
+
if (metadata.format !== expected.format) {
|
|
53
|
+
throw new Error(`agent_task_owned_image_format_mismatch:${input.assetName}:extension=${extension}:contentType=${expected.contentType}:decoded=${metadata.format ?? 'unknown'}`);
|
|
54
|
+
}
|
|
55
|
+
const declaredFormat = input.declaredFormat?.trim().toUpperCase() ?? '';
|
|
56
|
+
const declaredSharpFormat = SHARP_FORMAT_BY_DECLARED_FORMAT[declaredFormat];
|
|
57
|
+
if (declaredSharpFormat && declaredSharpFormat !== metadata.format) {
|
|
58
|
+
throw new Error(`agent_task_owned_image_format_mismatch:${input.assetName}:declared=${declaredFormat}:decoded=${metadata.format}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function assertOwnedRuntimeImagesAreValid(task) {
|
|
62
|
+
for (const ownedAsset of task.ownedAssets) {
|
|
63
|
+
if (!ownedAsset.runtimeKey || ownedAsset.category !== 'IMAGE') {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const primaryPath = ownedAsset.filePaths?.primary;
|
|
67
|
+
if (!primaryPath) {
|
|
68
|
+
throw new Error(`agent_task_owned_image_file_missing:${ownedAsset.name}:primary`);
|
|
69
|
+
}
|
|
70
|
+
await validateOwnedRuntimeImageFile({
|
|
71
|
+
assetName: ownedAsset.name,
|
|
72
|
+
filePath: primaryPath,
|
|
73
|
+
declaredFormat: ownedAsset.format,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { ApiClient } from '@playdrop/api-client';
|
|
2
|
-
import type { AgentExecutionTarget, UserResponse } from '@playdrop/types';
|
|
2
|
+
import type { AgentExecutionTarget, AgentTaskPlaytestProof, UserResponse } from '@playdrop/types';
|
|
3
|
+
import { type AppTask } from '../catalogue';
|
|
3
4
|
export type UploadCommandOptions = {
|
|
4
5
|
env?: string;
|
|
5
6
|
skipReview?: boolean;
|
|
6
7
|
clearTags?: boolean;
|
|
7
8
|
};
|
|
9
|
+
export declare function resolveUploadOwner(actorUsername: string | null | undefined, workspaceOwnerUsername: string | null | undefined): string;
|
|
10
|
+
export declare function resolveDefaultUploadCreator(owner: string | null | undefined, actorUsername: string | null | undefined): string;
|
|
8
11
|
export declare function upload(pathOrName: string, options?: UploadCommandOptions): Promise<void>;
|
|
9
12
|
export type WorkerAppPublishInput = {
|
|
10
13
|
client: ApiClient;
|
|
@@ -33,4 +36,7 @@ export type WorkerAppPublishResult = {
|
|
|
33
36
|
warnings: string[];
|
|
34
37
|
};
|
|
35
38
|
export type WorkerPlaydropAssetRequirement = 'PACK' | 'ASSET_OR_PACK';
|
|
39
|
+
export declare function assertTaskPlaytestEvidenceManifest(task: AppTask, options?: {
|
|
40
|
+
requireOutcomeProof?: boolean;
|
|
41
|
+
}): AgentTaskPlaytestProof | null;
|
|
36
42
|
export declare function publishWorkerAppProject(input: WorkerAppPublishInput): Promise<WorkerAppPublishResult>;
|