@playdrop/playdrop-cli 0.12.27 → 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/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 +2 -0
- package/dist/commands/upload.js +19 -399
- package/dist/commands/worker.d.ts +16 -0
- package/dist/commands/worker.js +164 -35
- 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/api.d.ts +13 -0
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/commands/upload.js
CHANGED
|
@@ -1,38 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveUploadOwner = resolveUploadOwner;
|
|
4
|
+
exports.resolveDefaultUploadCreator = resolveDefaultUploadCreator;
|
|
36
5
|
exports.upload = upload;
|
|
37
6
|
exports.assertTaskPlaytestEvidenceManifest = assertTaskPlaytestEvidenceManifest;
|
|
38
7
|
exports.publishWorkerAppProject = publishWorkerAppProject;
|
|
@@ -53,6 +22,7 @@ const uploadLog_1 = require("../uploadLog");
|
|
|
53
22
|
const upload_content_1 = require("./upload-content");
|
|
54
23
|
const appUrls_1 = require("../appUrls");
|
|
55
24
|
const devRuntimeAssets_1 = require("./devRuntimeAssets");
|
|
25
|
+
const ownedRuntimeImageValidation_1 = require("./ownedRuntimeImageValidation");
|
|
56
26
|
const listingPreflight_1 = require("../listingPreflight");
|
|
57
27
|
const upload_graph_1 = require("./upload-graph");
|
|
58
28
|
function normalizeSavedItemKinds(kinds) {
|
|
@@ -222,6 +192,19 @@ function normalizeCreatorUsername(username) {
|
|
|
222
192
|
}
|
|
223
193
|
return trimmed;
|
|
224
194
|
}
|
|
195
|
+
function resolveUploadOwner(actorUsername, workspaceOwnerUsername) {
|
|
196
|
+
const workspaceOwner = workspaceOwnerUsername?.trim() ?? '';
|
|
197
|
+
if (workspaceOwner) {
|
|
198
|
+
return workspaceOwner;
|
|
199
|
+
}
|
|
200
|
+
const actor = actorUsername?.trim() ?? '';
|
|
201
|
+
return actor || 'unknown';
|
|
202
|
+
}
|
|
203
|
+
function resolveDefaultUploadCreator(owner, actorUsername) {
|
|
204
|
+
return normalizeCreatorUsername(owner ?? null)
|
|
205
|
+
?? normalizeCreatorUsername(actorUsername ?? null)
|
|
206
|
+
?? 'unknown';
|
|
207
|
+
}
|
|
225
208
|
function isNotFoundApiError(error) {
|
|
226
209
|
return error instanceof types_1.ApiError && error.status === 404;
|
|
227
210
|
}
|
|
@@ -1059,7 +1042,7 @@ async function flushGraphState(client, graphState, results) {
|
|
|
1059
1042
|
}
|
|
1060
1043
|
async function processUploadTasks(client, tasks, owner, ownerUsername, currentUserRole, localTagGroups, currentUser, token, warnings, apiBase, webBase, options) {
|
|
1061
1044
|
const portalBase = normalizePortalBase(webBase);
|
|
1062
|
-
const defaultCreator =
|
|
1045
|
+
const defaultCreator = resolveDefaultUploadCreator(owner, ownerUsername);
|
|
1063
1046
|
const results = [];
|
|
1064
1047
|
const preflight = await buildUploadPreflight(client, tasks, defaultCreator, currentUserRole, localTagGroups);
|
|
1065
1048
|
const sortedTasks = preflight.nodes.map((node) => node.task);
|
|
@@ -1177,9 +1160,7 @@ async function upload(pathOrName, options) {
|
|
|
1177
1160
|
}
|
|
1178
1161
|
throw error;
|
|
1179
1162
|
}
|
|
1180
|
-
const owner =
|
|
1181
|
-
? userInfo.username.trim()
|
|
1182
|
-
: 'unknown';
|
|
1163
|
+
const owner = resolveUploadOwner(userInfo.username, ctx.workspaceAuth?.config.ownerUsername);
|
|
1183
1164
|
const tagGroupLoad = (0, catalogue_1.resolveCatalogueTagGroups)(workspaceRoot);
|
|
1184
1165
|
if (tagGroupLoad.errors.length > 0) {
|
|
1185
1166
|
(0, taskSelection_1.reportTaskErrors)(tagGroupLoad.errors.map((message) => ({
|
|
@@ -1234,8 +1215,6 @@ const WORKER_SUPERVISOR_BUNDLE_EXCLUDES = [
|
|
|
1234
1215
|
'assets/marketing/',
|
|
1235
1216
|
'listing/',
|
|
1236
1217
|
];
|
|
1237
|
-
const OWNED_RUNTIME_IMAGE_CHECK_SIZE = 96;
|
|
1238
|
-
const OWNED_RUNTIME_IMAGE_GRID_CHECK_SIZE = 512;
|
|
1239
1218
|
function appTaskSatisfiesPlaydropAssetRequirement(task, requirement) {
|
|
1240
1219
|
if (!requirement) {
|
|
1241
1220
|
return true;
|
|
@@ -1253,361 +1232,6 @@ function appTaskSatisfiesPlaydropAssetRequirement(task, requirement) {
|
|
|
1253
1232
|
});
|
|
1254
1233
|
return hasFirstPartyPack || hasFirstPartyAsset || task.ownedAssets.length > 0;
|
|
1255
1234
|
}
|
|
1256
|
-
function isUnsafeDirectPlaydropAiImageAsset(detail, parsedRevision) {
|
|
1257
|
-
const asset = detail?.asset;
|
|
1258
|
-
if (!asset || asset.category !== 'IMAGE') {
|
|
1259
|
-
return false;
|
|
1260
|
-
}
|
|
1261
|
-
const currentVersion = asset.currentVersion;
|
|
1262
|
-
if (!currentVersion || Number(currentVersion.revision) !== parsedRevision) {
|
|
1263
|
-
return false;
|
|
1264
|
-
}
|
|
1265
|
-
const sourceKind = typeof currentVersion.sourceKind === 'string' ? currentVersion.sourceKind.toUpperCase() : '';
|
|
1266
|
-
if (sourceKind !== 'AI') {
|
|
1267
|
-
return false;
|
|
1268
|
-
}
|
|
1269
|
-
const format = typeof currentVersion.format === 'string' ? currentVersion.format.toUpperCase() : '';
|
|
1270
|
-
const files = Array.isArray(currentVersion.fileManifest?.files) ? currentVersion.fileManifest.files : [];
|
|
1271
|
-
const primary = files.find((file) => file?.role === 'primary');
|
|
1272
|
-
const primaryContentType = typeof primary?.contentType === 'string' ? primary.contentType.toLowerCase() : '';
|
|
1273
|
-
return format === 'JPEG' || primaryContentType === 'image/jpeg' || primaryContentType === 'image/jpg';
|
|
1274
|
-
}
|
|
1275
|
-
async function assertNewGameDirectPlaydropAssetsAreRuntimeSafe(input) {
|
|
1276
|
-
const unsafeRefs = [];
|
|
1277
|
-
for (const dependency of input.task.uses.assets) {
|
|
1278
|
-
const parsed = (0, types_1.parseContentVersionRef)(dependency.ref);
|
|
1279
|
-
if (!parsed || parsed.kind !== 'asset' || parsed.creatorUsername.toLowerCase() !== 'playdrop') {
|
|
1280
|
-
continue;
|
|
1281
|
-
}
|
|
1282
|
-
const detail = await input.client.fetchAssetBySlug(parsed.creatorUsername, parsed.name);
|
|
1283
|
-
if (isUnsafeDirectPlaydropAiImageAsset(detail, parsed.revision)) {
|
|
1284
|
-
unsafeRefs.push((0, types_1.formatContentVersionRef)(parsed));
|
|
1285
|
-
}
|
|
1286
|
-
}
|
|
1287
|
-
if (unsafeRefs.length > 0) {
|
|
1288
|
-
throw new Error(`agent_task_playdrop_direct_ai_image_asset_unsafe: direct PlayDrop AI JPEG image assets cannot be used as new-game runtime sprites because their backgrounds are baked into the image. Use a PlayDrop asset pack or create clean owned PNG assets through the PlayDrop make-assets workflow instead. Unsafe refs: ${unsafeRefs.join(', ')}`);
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
function isOwnedRuntimeImageAsset(task, ownedAsset) {
|
|
1292
|
-
if (!ownedAsset.runtimeKey || ownedAsset.category !== 'IMAGE') {
|
|
1293
|
-
return false;
|
|
1294
|
-
}
|
|
1295
|
-
const primaryPath = ownedAsset.filePaths?.primary;
|
|
1296
|
-
if (!primaryPath) {
|
|
1297
|
-
return false;
|
|
1298
|
-
}
|
|
1299
|
-
const relativePath = primaryPath.slice((0, node_path_1.resolve)(task.projectDir).length + 1).replace(/\\/g, '/');
|
|
1300
|
-
return !relativePath.startsWith('assets/marketing/') && !relativePath.startsWith('listing/');
|
|
1301
|
-
}
|
|
1302
|
-
function isBackgroundLikeOwnedRuntimeImageAsset(ownedAsset) {
|
|
1303
|
-
const value = `${ownedAsset.name ?? ''} ${ownedAsset.runtimeKey ?? ''}`.toLowerCase();
|
|
1304
|
-
return /\b(background|backdrop|scene|sky|floor|ground|wall|terrain|landscape|level|arena)\b/i.test(value);
|
|
1305
|
-
}
|
|
1306
|
-
function looksLikeBakedCheckerboardPreview(input) {
|
|
1307
|
-
const { data, width, height, channels } = input;
|
|
1308
|
-
if (width < 24 || height < 24 || channels < 4) {
|
|
1309
|
-
return false;
|
|
1310
|
-
}
|
|
1311
|
-
const border = Math.max(8, Math.floor(Math.min(width, height) * 0.16));
|
|
1312
|
-
let sampled = 0;
|
|
1313
|
-
let transparent = 0;
|
|
1314
|
-
let opaqueGray = 0;
|
|
1315
|
-
let lowGray = 0;
|
|
1316
|
-
let highGray = 0;
|
|
1317
|
-
for (let y = 0; y < height; y += 1) {
|
|
1318
|
-
for (let x = 0; x < width; x += 1) {
|
|
1319
|
-
const inBorder = x < border || y < border || x >= width - border || y >= height - border;
|
|
1320
|
-
if (!inBorder) {
|
|
1321
|
-
continue;
|
|
1322
|
-
}
|
|
1323
|
-
sampled += 1;
|
|
1324
|
-
const offset = (y * width + x) * channels;
|
|
1325
|
-
const red = data[offset] ?? 0;
|
|
1326
|
-
const green = data[offset + 1] ?? 0;
|
|
1327
|
-
const blue = data[offset + 2] ?? 0;
|
|
1328
|
-
const alpha = data[offset + 3] ?? 255;
|
|
1329
|
-
if (alpha < 32) {
|
|
1330
|
-
transparent += 1;
|
|
1331
|
-
continue;
|
|
1332
|
-
}
|
|
1333
|
-
const brightness = (red + green + blue) / 3;
|
|
1334
|
-
const grayish = Math.max(Math.abs(red - green), Math.abs(red - blue), Math.abs(green - blue)) <= 10;
|
|
1335
|
-
if (alpha > 240 && grayish && brightness >= 70 && brightness <= 210) {
|
|
1336
|
-
opaqueGray += 1;
|
|
1337
|
-
if (brightness < 125) {
|
|
1338
|
-
lowGray += 1;
|
|
1339
|
-
}
|
|
1340
|
-
else if (brightness > 145) {
|
|
1341
|
-
highGray += 1;
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
if (sampled === 0 || transparent / sampled > 0.18) {
|
|
1347
|
-
return false;
|
|
1348
|
-
}
|
|
1349
|
-
return opaqueGray / sampled > 0.58
|
|
1350
|
-
&& lowGray / sampled > 0.08
|
|
1351
|
-
&& highGray / sampled > 0.18;
|
|
1352
|
-
}
|
|
1353
|
-
function looksLikeBakedSolidMattePreview(input) {
|
|
1354
|
-
const { data, width, height, channels } = input;
|
|
1355
|
-
if (width < 24 || height < 24 || channels < 4) {
|
|
1356
|
-
return false;
|
|
1357
|
-
}
|
|
1358
|
-
const border = Math.max(8, Math.floor(Math.min(width, height) * 0.18));
|
|
1359
|
-
let sampled = 0;
|
|
1360
|
-
let transparent = 0;
|
|
1361
|
-
let matteLike = 0;
|
|
1362
|
-
for (let y = 0; y < height; y += 1) {
|
|
1363
|
-
for (let x = 0; x < width; x += 1) {
|
|
1364
|
-
const inBorder = x < border || y < border || x >= width - border || y >= height - border;
|
|
1365
|
-
if (!inBorder) {
|
|
1366
|
-
continue;
|
|
1367
|
-
}
|
|
1368
|
-
sampled += 1;
|
|
1369
|
-
const offset = (y * width + x) * channels;
|
|
1370
|
-
const red = data[offset] ?? 0;
|
|
1371
|
-
const green = data[offset + 1] ?? 0;
|
|
1372
|
-
const blue = data[offset + 2] ?? 0;
|
|
1373
|
-
const alpha = data[offset + 3] ?? 255;
|
|
1374
|
-
if (alpha < 32) {
|
|
1375
|
-
transparent += 1;
|
|
1376
|
-
continue;
|
|
1377
|
-
}
|
|
1378
|
-
const dominant = Math.max(red, green, blue);
|
|
1379
|
-
const weakest = Math.min(red, green, blue);
|
|
1380
|
-
const chroma = dominant - weakest;
|
|
1381
|
-
const brightGreenMatte = green >= 220 && green - Math.max(red, blue) >= 65 && Math.max(red, blue) >= 70;
|
|
1382
|
-
const brightRedMatte = red >= 220 && red - Math.max(green, blue) >= 65;
|
|
1383
|
-
const brightBlueMatte = blue >= 220 && blue - Math.max(red, green) >= 65;
|
|
1384
|
-
const brightTwoChannelMatte = dominant >= 220
|
|
1385
|
-
&& chroma >= 65
|
|
1386
|
-
&& ((red >= 220 && green >= 220) || (red >= 220 && blue >= 220) || (green >= 220 && blue >= 220));
|
|
1387
|
-
if (brightGreenMatte || brightRedMatte || brightBlueMatte || brightTwoChannelMatte) {
|
|
1388
|
-
matteLike += 1;
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
if (sampled === 0 || transparent / sampled > 0.18) {
|
|
1393
|
-
return false;
|
|
1394
|
-
}
|
|
1395
|
-
return matteLike / sampled > 0.58;
|
|
1396
|
-
}
|
|
1397
|
-
function looksLikeBakedNeutralSolidMattePreview(input) {
|
|
1398
|
-
const { data, width, height, channels } = input;
|
|
1399
|
-
if (width < 24 || height < 24 || channels < 4) {
|
|
1400
|
-
return false;
|
|
1401
|
-
}
|
|
1402
|
-
const border = Math.max(8, Math.floor(Math.min(width, height) * 0.18));
|
|
1403
|
-
let sampled = 0;
|
|
1404
|
-
let transparent = 0;
|
|
1405
|
-
let neutralMatteLike = 0;
|
|
1406
|
-
for (let y = 0; y < height; y += 1) {
|
|
1407
|
-
for (let x = 0; x < width; x += 1) {
|
|
1408
|
-
const inBorder = x < border || y < border || x >= width - border || y >= height - border;
|
|
1409
|
-
if (!inBorder) {
|
|
1410
|
-
continue;
|
|
1411
|
-
}
|
|
1412
|
-
sampled += 1;
|
|
1413
|
-
const offset = (y * width + x) * channels;
|
|
1414
|
-
const red = data[offset] ?? 0;
|
|
1415
|
-
const green = data[offset + 1] ?? 0;
|
|
1416
|
-
const blue = data[offset + 2] ?? 0;
|
|
1417
|
-
const alpha = data[offset + 3] ?? 255;
|
|
1418
|
-
if (alpha < 32) {
|
|
1419
|
-
transparent += 1;
|
|
1420
|
-
continue;
|
|
1421
|
-
}
|
|
1422
|
-
const brightness = (red + green + blue) / 3;
|
|
1423
|
-
const grayish = Math.max(Math.abs(red - green), Math.abs(red - blue), Math.abs(green - blue)) <= 12;
|
|
1424
|
-
if (alpha > 240 && grayish && (brightness >= 235 || brightness <= 20)) {
|
|
1425
|
-
neutralMatteLike += 1;
|
|
1426
|
-
}
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
if (sampled === 0 || transparent / sampled > 0.18) {
|
|
1430
|
-
return false;
|
|
1431
|
-
}
|
|
1432
|
-
return neutralMatteLike / sampled > 0.58;
|
|
1433
|
-
}
|
|
1434
|
-
function looksLikeResidualMatteGridArtifact(input) {
|
|
1435
|
-
const { data, width, height, channels } = input;
|
|
1436
|
-
if (width < 48 || height < 48 || channels < 4) {
|
|
1437
|
-
return false;
|
|
1438
|
-
}
|
|
1439
|
-
const rowCounts = new Uint32Array(height);
|
|
1440
|
-
const columnCounts = new Uint32Array(width);
|
|
1441
|
-
let matteLike = 0;
|
|
1442
|
-
for (let y = 0; y < height; y += 1) {
|
|
1443
|
-
for (let x = 0; x < width; x += 1) {
|
|
1444
|
-
const offset = (y * width + x) * channels;
|
|
1445
|
-
const red = data[offset] ?? 0;
|
|
1446
|
-
const green = data[offset + 1] ?? 0;
|
|
1447
|
-
const blue = data[offset + 2] ?? 0;
|
|
1448
|
-
const alpha = data[offset + 3] ?? 255;
|
|
1449
|
-
if (alpha < 24 || alpha > 190) {
|
|
1450
|
-
continue;
|
|
1451
|
-
}
|
|
1452
|
-
const greenMatte = green >= 35 && green - Math.max(red, blue) >= 25 && red <= 110 && blue <= 110;
|
|
1453
|
-
const redMatte = red >= 35 && red - Math.max(green, blue) >= 25 && green <= 120 && blue <= 120;
|
|
1454
|
-
const blueMatte = blue >= 35 && blue - Math.max(red, green) >= 25 && red <= 120 && green <= 120;
|
|
1455
|
-
const purpleMatte = red >= 35 && blue >= 35 && Math.min(red, blue) - green >= 25 && green <= 120;
|
|
1456
|
-
if (!greenMatte && !redMatte && !blueMatte && !purpleMatte) {
|
|
1457
|
-
continue;
|
|
1458
|
-
}
|
|
1459
|
-
matteLike += 1;
|
|
1460
|
-
rowCounts[y] += 1;
|
|
1461
|
-
columnCounts[x] += 1;
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
const matteRatio = matteLike / (width * height);
|
|
1465
|
-
if (matteRatio < 0.0015) {
|
|
1466
|
-
return false;
|
|
1467
|
-
}
|
|
1468
|
-
let rowsWithLines = 0;
|
|
1469
|
-
for (const count of rowCounts) {
|
|
1470
|
-
if (count / width >= 0.12) {
|
|
1471
|
-
rowsWithLines += 1;
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
let columnsWithLines = 0;
|
|
1475
|
-
for (const count of columnCounts) {
|
|
1476
|
-
if (count / height >= 0.12) {
|
|
1477
|
-
columnsWithLines += 1;
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
return rowsWithLines + columnsWithLines >= 3
|
|
1481
|
-
&& (rowsWithLines >= 2 || columnsWithLines >= 2);
|
|
1482
|
-
}
|
|
1483
|
-
function looksLikeResidualTransparentGridLineArtifact(input) {
|
|
1484
|
-
const { data, width, height, channels } = input;
|
|
1485
|
-
if (width < 48 || height < 48 || channels < 4) {
|
|
1486
|
-
return false;
|
|
1487
|
-
}
|
|
1488
|
-
const rowVisibleCounts = new Uint32Array(height);
|
|
1489
|
-
const rowSemiCounts = new Uint32Array(height);
|
|
1490
|
-
const columnVisibleCounts = new Uint32Array(width);
|
|
1491
|
-
const columnSemiCounts = new Uint32Array(width);
|
|
1492
|
-
for (let y = 0; y < height; y += 1) {
|
|
1493
|
-
for (let x = 0; x < width; x += 1) {
|
|
1494
|
-
const offset = (y * width + x) * channels;
|
|
1495
|
-
const alpha = data[offset + 3] ?? 255;
|
|
1496
|
-
if (alpha <= 0) {
|
|
1497
|
-
continue;
|
|
1498
|
-
}
|
|
1499
|
-
rowVisibleCounts[y] += 1;
|
|
1500
|
-
columnVisibleCounts[x] += 1;
|
|
1501
|
-
if (alpha < 255) {
|
|
1502
|
-
rowSemiCounts[y] += 1;
|
|
1503
|
-
columnSemiCounts[x] += 1;
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
let rowsWithLines = 0;
|
|
1508
|
-
for (let row = 0; row < height; row += 1) {
|
|
1509
|
-
const visibleRatio = rowVisibleCounts[row] / width;
|
|
1510
|
-
const semiRatio = rowSemiCounts[row] / width;
|
|
1511
|
-
if (visibleRatio >= 0.94 && semiRatio >= 0.72) {
|
|
1512
|
-
rowsWithLines += 1;
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
let columnsWithLines = 0;
|
|
1516
|
-
for (let column = 0; column < width; column += 1) {
|
|
1517
|
-
const visibleRatio = columnVisibleCounts[column] / height;
|
|
1518
|
-
const semiRatio = columnSemiCounts[column] / height;
|
|
1519
|
-
if (visibleRatio >= 0.94 && semiRatio >= 0.72) {
|
|
1520
|
-
columnsWithLines += 1;
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
return rowsWithLines + columnsWithLines >= 3
|
|
1524
|
-
&& (rowsWithLines >= 2 || columnsWithLines >= 2);
|
|
1525
|
-
}
|
|
1526
|
-
async function imageHasBakedPreviewBackground(filePath, options = {}) {
|
|
1527
|
-
let sharp;
|
|
1528
|
-
try {
|
|
1529
|
-
const sharpModule = await Promise.resolve().then(() => __importStar(require('sharp')));
|
|
1530
|
-
sharp = sharpModule.default ?? sharpModule;
|
|
1531
|
-
}
|
|
1532
|
-
catch {
|
|
1533
|
-
return false;
|
|
1534
|
-
}
|
|
1535
|
-
try {
|
|
1536
|
-
const { data, info } = await sharp(filePath, { failOn: 'error' })
|
|
1537
|
-
.rotate()
|
|
1538
|
-
.resize(OWNED_RUNTIME_IMAGE_CHECK_SIZE, OWNED_RUNTIME_IMAGE_CHECK_SIZE, { fit: 'fill' })
|
|
1539
|
-
.ensureAlpha()
|
|
1540
|
-
.raw()
|
|
1541
|
-
.toBuffer({ resolveWithObject: true });
|
|
1542
|
-
const hasPreviewBackground = looksLikeBakedCheckerboardPreview({
|
|
1543
|
-
data,
|
|
1544
|
-
width: info.width,
|
|
1545
|
-
height: info.height,
|
|
1546
|
-
channels: info.channels,
|
|
1547
|
-
}) || looksLikeBakedSolidMattePreview({
|
|
1548
|
-
data,
|
|
1549
|
-
width: info.width,
|
|
1550
|
-
height: info.height,
|
|
1551
|
-
channels: info.channels,
|
|
1552
|
-
}) || (!options.allowNeutralSolidMatte && looksLikeBakedNeutralSolidMattePreview({
|
|
1553
|
-
data,
|
|
1554
|
-
width: info.width,
|
|
1555
|
-
height: info.height,
|
|
1556
|
-
channels: info.channels,
|
|
1557
|
-
}));
|
|
1558
|
-
if (hasPreviewBackground) {
|
|
1559
|
-
return true;
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
catch {
|
|
1563
|
-
return false;
|
|
1564
|
-
}
|
|
1565
|
-
try {
|
|
1566
|
-
const { data, info } = await sharp(filePath, { failOn: 'error' })
|
|
1567
|
-
.rotate()
|
|
1568
|
-
.resize(OWNED_RUNTIME_IMAGE_GRID_CHECK_SIZE, OWNED_RUNTIME_IMAGE_GRID_CHECK_SIZE, { fit: 'inside', withoutEnlargement: true })
|
|
1569
|
-
.ensureAlpha()
|
|
1570
|
-
.raw()
|
|
1571
|
-
.toBuffer({ resolveWithObject: true });
|
|
1572
|
-
if (looksLikeResidualMatteGridArtifact({
|
|
1573
|
-
data,
|
|
1574
|
-
width: info.width,
|
|
1575
|
-
height: info.height,
|
|
1576
|
-
channels: info.channels,
|
|
1577
|
-
}) || looksLikeResidualTransparentGridLineArtifact({
|
|
1578
|
-
data,
|
|
1579
|
-
width: info.width,
|
|
1580
|
-
height: info.height,
|
|
1581
|
-
channels: info.channels,
|
|
1582
|
-
})) {
|
|
1583
|
-
return true;
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
1586
|
-
catch {
|
|
1587
|
-
return false;
|
|
1588
|
-
}
|
|
1589
|
-
return false;
|
|
1590
|
-
}
|
|
1591
|
-
async function assertNewGameOwnedRuntimeImageAssetsAreClean(task) {
|
|
1592
|
-
const unsafeAssets = [];
|
|
1593
|
-
for (const ownedAsset of task.ownedAssets) {
|
|
1594
|
-
if (!isOwnedRuntimeImageAsset(task, ownedAsset)) {
|
|
1595
|
-
continue;
|
|
1596
|
-
}
|
|
1597
|
-
const primaryPath = ownedAsset.filePaths?.primary;
|
|
1598
|
-
if (!primaryPath) {
|
|
1599
|
-
continue;
|
|
1600
|
-
}
|
|
1601
|
-
if (await imageHasBakedPreviewBackground(primaryPath, {
|
|
1602
|
-
allowNeutralSolidMatte: isBackgroundLikeOwnedRuntimeImageAsset(ownedAsset),
|
|
1603
|
-
})) {
|
|
1604
|
-
unsafeAssets.push(`${ownedAsset.name}:${ownedAsset.runtimeKey}`);
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
if (unsafeAssets.length > 0) {
|
|
1608
|
-
throw new Error(`agent_task_owned_image_asset_checkerboard_background: owned runtime image assets must not contain baked transparency checkerboard, matte-color, or preview backgrounds. Recreate the asset through the PlayDrop make-assets workflow, use a cleaner PlayDrop asset, or use the image as non-runtime listing art only. Unsafe owned assets: ${unsafeAssets.join(', ')}`);
|
|
1609
|
-
}
|
|
1610
|
-
}
|
|
1611
1235
|
const RUNTIME_SOURCE_EXTENSIONS = new Set([
|
|
1612
1236
|
'.html',
|
|
1613
1237
|
'.js',
|
|
@@ -2242,11 +1866,7 @@ async function publishWorkerAppProject(input) {
|
|
|
2242
1866
|
throw new Error('agent_task_playdrop_asset_dependency_missing: the creator asked for PlayDrop assets, but catalogue.json does not declare a matching uses.assets, uses.packs, or ownedAssets dependency.');
|
|
2243
1867
|
}
|
|
2244
1868
|
if (input.kind === 'NEW_GAME' || input.kind === 'REMIX_GAME') {
|
|
2245
|
-
await
|
|
2246
|
-
client: input.client,
|
|
2247
|
-
task,
|
|
2248
|
-
});
|
|
2249
|
-
await assertNewGameOwnedRuntimeImageAssetsAreClean(task);
|
|
1869
|
+
await (0, ownedRuntimeImageValidation_1.assertOwnedRuntimeImagesAreValid)(task);
|
|
2250
1870
|
}
|
|
2251
1871
|
assertRequestedPlaydropAssetsAreUsedAtRuntime({
|
|
2252
1872
|
task,
|
|
@@ -310,6 +310,22 @@ export declare function acquireWorkerSupervisorLock(input: {
|
|
|
310
310
|
file?: string;
|
|
311
311
|
}): WorkerSupervisorLock;
|
|
312
312
|
export declare function releaseWorkerSupervisorLock(lock: WorkerSupervisorLock): void;
|
|
313
|
+
export type TaskUploadLockOwner = {
|
|
314
|
+
schemaVersion: 1;
|
|
315
|
+
command: 'task upload';
|
|
316
|
+
pid: number;
|
|
317
|
+
processStartIdentity: string;
|
|
318
|
+
createdAt: string;
|
|
319
|
+
};
|
|
320
|
+
export type TaskUploadLock = {
|
|
321
|
+
file: string;
|
|
322
|
+
owner: TaskUploadLockOwner;
|
|
323
|
+
};
|
|
324
|
+
export declare function resolveTaskUploadLockFile(workspaceDir: string): string;
|
|
325
|
+
export declare function removeStaleTaskUploadLockIfUnchanged(file: string, observedHolder: TaskUploadLockOwner): boolean;
|
|
326
|
+
export declare function acquireTaskUploadLock(workspaceDir: string): TaskUploadLock;
|
|
327
|
+
export declare function releaseTaskUploadLock(lock: TaskUploadLock): void;
|
|
328
|
+
export declare function withTaskUploadLock<T>(workspaceDir: string, work: () => Promise<T>): Promise<T>;
|
|
313
329
|
export declare function readActivePersonalWorkerRuntimeState(): WorkerRuntimeState | null;
|
|
314
330
|
export declare function parseLaunchAgentProgramArguments(plist: string): string[];
|
|
315
331
|
export declare function assertLaunchAgentPlistCompatible(input: {
|