@aura3d/cli 2.0.3 → 3.0.0
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/LICENSE +21 -0
- package/README.md +20 -0
- package/dist/asset-core-types.d.ts +19 -0
- package/dist/asset-core-types.d.ts.map +1 -1
- package/dist/asset-manifest.d.ts.map +1 -1
- package/dist/asset-manifest.js +2 -1
- package/dist/asset-manifest.js.map +1 -1
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +2 -0
- package/dist/cli-help.js.map +1 -1
- package/dist/cli.js +24 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/meshy/admission.d.ts +40 -0
- package/dist/meshy/admission.d.ts.map +1 -0
- package/dist/meshy/admission.js +126 -0
- package/dist/meshy/admission.js.map +1 -0
- package/dist/meshy/evidence.d.ts +12 -0
- package/dist/meshy/evidence.d.ts.map +1 -0
- package/dist/meshy/evidence.js +37 -0
- package/dist/meshy/evidence.js.map +1 -0
- package/dist/meshy/import.d.ts +25 -0
- package/dist/meshy/import.d.ts.map +1 -0
- package/dist/meshy/import.js +104 -0
- package/dist/meshy/import.js.map +1 -0
- package/dist/meshy/index.d.ts +10 -0
- package/dist/meshy/index.d.ts.map +1 -0
- package/dist/meshy/index.js +6 -0
- package/dist/meshy/index.js.map +1 -0
- package/dist/meshy/metadata.d.ts +22 -0
- package/dist/meshy/metadata.d.ts.map +1 -0
- package/dist/meshy/metadata.js +176 -0
- package/dist/meshy/metadata.js.map +1 -0
- package/dist/meshy/provenance.d.ts +4 -0
- package/dist/meshy/provenance.d.ts.map +1 -0
- package/dist/meshy/provenance.js +21 -0
- package/dist/meshy/provenance.js.map +1 -0
- package/dist/meshy/thumbnail.d.ts +19 -0
- package/dist/meshy/thumbnail.d.ts.map +1 -0
- package/dist/meshy/thumbnail.js +59 -0
- package/dist/meshy/thumbnail.js.map +1 -0
- package/dist/meshy/validation.d.ts +5 -0
- package/dist/meshy/validation.d.ts.map +1 -0
- package/dist/meshy/validation.js +64 -0
- package/dist/meshy/validation.js.map +1 -0
- package/dist/pull-bridge/profiles.d.ts +12 -2
- package/dist/pull-bridge/profiles.d.ts.map +1 -1
- package/dist/pull-bridge/profiles.js +35 -4
- package/dist/pull-bridge/profiles.js.map +1 -1
- package/dist/pull-bridge/provenance.d.ts +15 -1
- package/dist/pull-bridge/provenance.d.ts.map +1 -1
- package/dist/pull-bridge/provenance.js +60 -1
- package/dist/pull-bridge/provenance.js.map +1 -1
- package/dist/pull-bridge/scoring.d.ts +10 -1
- package/dist/pull-bridge/scoring.d.ts.map +1 -1
- package/dist/pull-bridge/scoring.js +9 -1
- package/dist/pull-bridge/scoring.js.map +1 -1
- package/dist/pull-bridge/search.js +2 -2
- package/dist/pull-bridge/search.js.map +1 -1
- package/dist/pull-bridge.d.ts +15 -3
- package/dist/pull-bridge.d.ts.map +1 -1
- package/dist/pull-bridge.js +68 -5
- package/dist/pull-bridge.js.map +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync, statSync } from "node:fs";
|
|
3
|
+
export const MAX_MESHY_METADATA_BYTES = 1024 * 1024;
|
|
4
|
+
const SECRET_KEY = /(?:^|_)(?:api_?key|access_?token|refresh_?token|authorization|bearer|client_?secret|password|credentials?|cookie|private_?key|environment|env)(?:$|_)/i;
|
|
5
|
+
const SIGNED_QUERY_KEY = /^(?:x-amz-|x-goog-|signature$|sig$|token$|expires$|credential$|policy$|key-pair-id$)/i;
|
|
6
|
+
export function readMeshyMetadata(path) {
|
|
7
|
+
const size = statSync(path).size;
|
|
8
|
+
if (size > MAX_MESHY_METADATA_BYTES) {
|
|
9
|
+
throw new Error(`Meshy metadata exceeds ${MAX_MESHY_METADATA_BYTES} bytes: ${path}`);
|
|
10
|
+
}
|
|
11
|
+
let parsed;
|
|
12
|
+
try {
|
|
13
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
throw new Error(`Meshy metadata is not valid JSON: ${path}`);
|
|
17
|
+
}
|
|
18
|
+
const root = record(parsed, "Meshy metadata");
|
|
19
|
+
rejectCredentialFields(root, "metadata");
|
|
20
|
+
const task = optionalRecord(root.task, "metadata.task") ?? root;
|
|
21
|
+
const prompt = optionalString(first(task.prompt, root.prompt), "prompt");
|
|
22
|
+
const settingsValue = first(task.settings, task.parameters, root.settings, root.parameters);
|
|
23
|
+
const settings = settingsValue === undefined ? undefined : sanitizeRecord(record(settingsValue, "settings"), 0);
|
|
24
|
+
return compact({
|
|
25
|
+
providerCli: optionalString(first(root.providerCli, root.provider_cli, root.cliVersion, root.cli_version), "providerCli"),
|
|
26
|
+
taskId: optionalString(first(task.taskId, task.task_id, task.id, root.taskId, root.task_id), "taskId"),
|
|
27
|
+
parentTaskIds: stringList(first(task.parentTaskIds, task.parent_task_ids, task.parentTaskId, task.parent_task_id), "parentTaskIds"),
|
|
28
|
+
operation: optionalString(first(task.operation, task.type, root.operation, root.type), "operation"),
|
|
29
|
+
promptHash: prompt ? `sha256-${createHash("sha256").update(prompt).digest("hex")}` : undefined,
|
|
30
|
+
model: optionalString(first(task.model, task.modelVersion, task.model_version, root.model), "model"),
|
|
31
|
+
settings,
|
|
32
|
+
createdAt: isoString(first(task.createdAt, task.created_at, root.createdAt, root.created_at), "createdAt"),
|
|
33
|
+
finishedAt: isoString(first(task.finishedAt, task.finished_at, task.completedAt, task.completed_at, root.finishedAt, root.finished_at), "finishedAt"),
|
|
34
|
+
consumedCredits: nonNegativeNumber(first(task.consumedCredits, task.consumed_credits, task.credits, root.consumedCredits, root.consumed_credits), "consumedCredits")
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export function validateMeshyEvidenceJson(path) {
|
|
38
|
+
const size = statSync(path).size;
|
|
39
|
+
if (size > MAX_MESHY_METADATA_BYTES)
|
|
40
|
+
throw new Error(`Meshy rights evidence exceeds ${MAX_MESHY_METADATA_BYTES} bytes: ${path}`);
|
|
41
|
+
let parsed;
|
|
42
|
+
try {
|
|
43
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
throw new Error(`Meshy rights evidence is not valid JSON: ${path}`);
|
|
47
|
+
}
|
|
48
|
+
const value = record(parsed, "Meshy rights evidence");
|
|
49
|
+
if (Object.keys(value).length === 0)
|
|
50
|
+
throw new Error("Meshy rights evidence must be a non-empty JSON object.");
|
|
51
|
+
rejectCredentialFields(value, "rights evidence");
|
|
52
|
+
const licenseName = boundedRightsString(value.licenseName, "licenseName", 512);
|
|
53
|
+
const licenseUrl = rightsHttpsUrl(first(value.termsUrl, value.licenseUrl), "termsUrl/licenseUrl");
|
|
54
|
+
const licenseRaw = boundedRightsString(first(value.rightsBasis, value.licenseRaw), "rightsBasis/licenseRaw", 4096);
|
|
55
|
+
return compact({ licenseName, licenseUrl, licenseRaw });
|
|
56
|
+
}
|
|
57
|
+
export function stripSignedUrl(value) {
|
|
58
|
+
try {
|
|
59
|
+
const url = new URL(value);
|
|
60
|
+
if ([...url.searchParams.keys()].some((key) => SIGNED_QUERY_KEY.test(key)))
|
|
61
|
+
url.search = "";
|
|
62
|
+
return url.toString();
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function rejectCredentialFields(value, path) {
|
|
69
|
+
if (Array.isArray(value)) {
|
|
70
|
+
value.forEach((item, index) => rejectCredentialFields(item, `${path}[${index}]`));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (!value || typeof value !== "object")
|
|
74
|
+
return;
|
|
75
|
+
for (const [key, child] of Object.entries(value)) {
|
|
76
|
+
const normalized = key.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[- ]/g, "_");
|
|
77
|
+
if (SECRET_KEY.test(normalized))
|
|
78
|
+
throw new Error(`Meshy input contains forbidden credential field at ${path}.${key}.`);
|
|
79
|
+
rejectCredentialFields(child, `${path}.${key}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function sanitizeRecord(value, depth) {
|
|
83
|
+
if (depth > 4)
|
|
84
|
+
throw new Error("Meshy settings exceed the maximum nesting depth.");
|
|
85
|
+
if (Object.keys(value).length > 100)
|
|
86
|
+
throw new Error("Meshy settings contain too many fields.");
|
|
87
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, sanitizeValue(child, depth + 1)]));
|
|
88
|
+
}
|
|
89
|
+
function sanitizeValue(value, depth) {
|
|
90
|
+
if (value === null || typeof value === "boolean" || typeof value === "number")
|
|
91
|
+
return value;
|
|
92
|
+
if (typeof value === "string") {
|
|
93
|
+
if (value.length > 4096)
|
|
94
|
+
throw new Error("Meshy metadata contains an unexpectedly long string.");
|
|
95
|
+
return stripSignedUrl(value);
|
|
96
|
+
}
|
|
97
|
+
if (Array.isArray(value)) {
|
|
98
|
+
if (value.length > 100)
|
|
99
|
+
throw new Error("Meshy metadata contains an unexpectedly large array.");
|
|
100
|
+
return value.map((entry) => sanitizeValue(entry, depth));
|
|
101
|
+
}
|
|
102
|
+
return sanitizeRecord(record(value, "settings value"), depth);
|
|
103
|
+
}
|
|
104
|
+
function record(value, label) {
|
|
105
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
106
|
+
throw new Error(`${label} must be a JSON object.`);
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
function optionalRecord(value, label) { return value === undefined ? undefined : record(value, label); }
|
|
110
|
+
function first(...values) { return values.find((value) => value !== undefined && value !== null); }
|
|
111
|
+
function optionalString(value, label) {
|
|
112
|
+
if (value === undefined)
|
|
113
|
+
return undefined;
|
|
114
|
+
if (typeof value !== "string" || value.trim() === "" || value.length > 4096)
|
|
115
|
+
throw new Error(`Meshy metadata field ${label} must be a non-empty bounded string.`);
|
|
116
|
+
return stripSignedUrl(value.trim());
|
|
117
|
+
}
|
|
118
|
+
function boundedRightsString(value, label, maximumLength) {
|
|
119
|
+
if (value === undefined || value === null)
|
|
120
|
+
return undefined;
|
|
121
|
+
if (typeof value !== "string" || value.trim() === "" || value.length > maximumLength) {
|
|
122
|
+
throw new Error(`Meshy rights evidence field ${label} must be a non-empty string of at most ${maximumLength} characters.`);
|
|
123
|
+
}
|
|
124
|
+
return value.trim();
|
|
125
|
+
}
|
|
126
|
+
function rightsHttpsUrl(value, label) {
|
|
127
|
+
const text = boundedRightsString(value, label, 2048);
|
|
128
|
+
if (!text)
|
|
129
|
+
return undefined;
|
|
130
|
+
let url;
|
|
131
|
+
try {
|
|
132
|
+
url = new URL(text);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
throw new Error(`Meshy rights evidence field ${label} must be an HTTPS URL.`);
|
|
136
|
+
}
|
|
137
|
+
if (url.protocol !== "https:" || url.username || url.password) {
|
|
138
|
+
throw new Error(`Meshy rights evidence field ${label} must be an HTTPS URL without embedded credentials.`);
|
|
139
|
+
}
|
|
140
|
+
return stripSignedUrl(url.toString());
|
|
141
|
+
}
|
|
142
|
+
function isoString(value, label) {
|
|
143
|
+
if (value === undefined)
|
|
144
|
+
return undefined;
|
|
145
|
+
if (typeof value === "number") {
|
|
146
|
+
if (!Number.isFinite(value) || value < 0)
|
|
147
|
+
throw new Error(`Meshy metadata field ${label} must be an ISO-8601 timestamp or non-negative epoch milliseconds.`);
|
|
148
|
+
const timestamp = new Date(value);
|
|
149
|
+
if (Number.isNaN(timestamp.getTime()))
|
|
150
|
+
throw new Error(`Meshy metadata field ${label} must be an ISO-8601 timestamp or non-negative epoch milliseconds.`);
|
|
151
|
+
return timestamp.toISOString();
|
|
152
|
+
}
|
|
153
|
+
const text = optionalString(value, label);
|
|
154
|
+
if (!text)
|
|
155
|
+
return undefined;
|
|
156
|
+
if (Number.isNaN(Date.parse(text)))
|
|
157
|
+
throw new Error(`Meshy metadata field ${label} must be an ISO-8601 timestamp or non-negative epoch milliseconds.`);
|
|
158
|
+
return text;
|
|
159
|
+
}
|
|
160
|
+
function nonNegativeNumber(value, label) {
|
|
161
|
+
if (value === undefined)
|
|
162
|
+
return undefined;
|
|
163
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
|
|
164
|
+
throw new Error(`Meshy metadata field ${label} must be a non-negative number.`);
|
|
165
|
+
return value;
|
|
166
|
+
}
|
|
167
|
+
function stringList(value, label) {
|
|
168
|
+
if (value === undefined)
|
|
169
|
+
return undefined;
|
|
170
|
+
const values = Array.isArray(value) ? value : [value];
|
|
171
|
+
if (values.length > 100)
|
|
172
|
+
throw new Error(`Meshy metadata field ${label} contains too many values.`);
|
|
173
|
+
return values.map((entry) => optionalString(entry, label));
|
|
174
|
+
}
|
|
175
|
+
function compact(value) { return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined)); }
|
|
176
|
+
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../../../packages/aura3d-cli/src/meshy/metadata.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AuraCliGeneratedAssetProvenance } from "../asset-core-types.js";
|
|
2
|
+
import type { SanitizedMeshyMetadata } from "./metadata.js";
|
|
3
|
+
export declare function createMeshyProvenance(projectDir: string, metadataPath: string, rightsPath: string, metadata: SanitizedMeshyMetadata): AuraCliGeneratedAssetProvenance;
|
|
4
|
+
//# sourceMappingURL=provenance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../../../../../packages/aura3d-cli/src/meshy/provenance.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAE5D,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,sBAAsB,GAAG,+BAA+B,CAgBrK"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs";
|
|
2
|
+
import { relative } from "node:path";
|
|
3
|
+
export function createMeshyProvenance(projectDir, metadataPath, rightsPath, metadata) {
|
|
4
|
+
return {
|
|
5
|
+
provider: "meshy",
|
|
6
|
+
providerCli: metadata.providerCli ?? "@meshy-ai/cli@0.2.0",
|
|
7
|
+
...(metadata.taskId ? { taskId: metadata.taskId } : {}),
|
|
8
|
+
...(metadata.parentTaskIds?.length ? { parentTaskIds: metadata.parentTaskIds } : {}),
|
|
9
|
+
...(metadata.operation ? { operation: metadata.operation } : {}),
|
|
10
|
+
...(metadata.promptHash ? { promptHash: metadata.promptHash } : {}),
|
|
11
|
+
...(metadata.model ? { model: metadata.model } : {}),
|
|
12
|
+
...(metadata.settings ? { settings: metadata.settings } : {}),
|
|
13
|
+
...(metadata.createdAt ? { createdAt: metadata.createdAt } : {}),
|
|
14
|
+
...(metadata.finishedAt ? { finishedAt: metadata.finishedAt } : {}),
|
|
15
|
+
...(metadata.consumedCredits !== undefined ? { consumedCredits: metadata.consumedCredits } : {}),
|
|
16
|
+
localMetadata: normalized(relative(realpathSync(projectDir), metadataPath)),
|
|
17
|
+
rightsEvidence: normalized(relative(realpathSync(projectDir), rightsPath))
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function normalized(path) { return path.replaceAll("\\", "/"); }
|
|
21
|
+
//# sourceMappingURL=provenance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provenance.js","sourceRoot":"","sources":["../../../../../packages/aura3d-cli/src/meshy/provenance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAIrC,MAAM,UAAU,qBAAqB,CAAC,UAAkB,EAAE,YAAoB,EAAE,UAAkB,EAAE,QAAgC;IAClI,OAAO;QACL,QAAQ,EAAE,OAAO;QACjB,WAAW,EAAE,QAAQ,CAAC,WAAW,IAAI,qBAAqB;QAC1D,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,GAAG,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpF,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,GAAG,CAAC,QAAQ,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,QAAQ,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChG,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,YAAY,CAAC,CAAC;QAC3E,cAAc,EAAE,UAAU,CAAC,QAAQ,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC,CAAC;KAC3E,CAAC;AACJ,CAAC;AACD,SAAS,UAAU,CAAC,IAAY,IAAY,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AuraCliRenderedProbe } from "../asset-core-types.js";
|
|
2
|
+
export declare const MAX_MESHY_THUMBNAIL_BYTES: number;
|
|
3
|
+
export interface RetainedMeshyThumbnail {
|
|
4
|
+
readonly sourceFile: string;
|
|
5
|
+
readonly outputPath: string;
|
|
6
|
+
readonly url: string;
|
|
7
|
+
readonly sha256: string;
|
|
8
|
+
readonly renderedProbe: AuraCliRenderedProbe;
|
|
9
|
+
}
|
|
10
|
+
export declare function findMeshyThumbnail(inputDir: string, requested: string | undefined): string | undefined;
|
|
11
|
+
export declare function retainMeshyThumbnail(options: {
|
|
12
|
+
readonly projectDir: string;
|
|
13
|
+
readonly inputDir: string;
|
|
14
|
+
readonly requested?: string;
|
|
15
|
+
readonly assetName: string;
|
|
16
|
+
readonly outputDir: string;
|
|
17
|
+
readonly publicPath: string;
|
|
18
|
+
}): RetainedMeshyThumbnail | undefined;
|
|
19
|
+
//# sourceMappingURL=thumbnail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thumbnail.d.ts","sourceRoot":"","sources":["../../../../../packages/aura3d-cli/src/meshy/thumbnail.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAKnE,eAAO,MAAM,yBAAyB,QAAmB,CAAC;AAE1D,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,aAAa,EAAE,oBAAoB,CAAC;CAC9C;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAOtG;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,GAAG,sBAAsB,GAAG,SAAS,CA4BrC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
3
|
+
import { extname, join, resolve } from "node:path";
|
|
4
|
+
import { resolveConfinedPath } from "./validation.js";
|
|
5
|
+
const THUMBNAIL_NAMES = ["thumbnail", "thumb", "preview", "render"];
|
|
6
|
+
const THUMBNAIL_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".webp"]);
|
|
7
|
+
export const MAX_MESHY_THUMBNAIL_BYTES = 20 * 1024 * 1024;
|
|
8
|
+
export function findMeshyThumbnail(inputDir, requested) {
|
|
9
|
+
if (requested)
|
|
10
|
+
return resolveConfinedPath(inputDir, requested, "thumbnail");
|
|
11
|
+
const matches = readdirSync(inputDir, { withFileTypes: true })
|
|
12
|
+
.filter((entry) => entry.isFile() && THUMBNAIL_EXTENSIONS.has(extname(entry.name).toLowerCase()) && THUMBNAIL_NAMES.includes(entry.name.replace(/\.[^.]+$/, "").toLowerCase()))
|
|
13
|
+
.map((entry) => resolve(inputDir, entry.name));
|
|
14
|
+
if (matches.length > 1)
|
|
15
|
+
throw new Error("Meshy output contains multiple candidate thumbnails. Select exactly one with --thumbnail <relative-image>.");
|
|
16
|
+
return matches[0];
|
|
17
|
+
}
|
|
18
|
+
export function retainMeshyThumbnail(options) {
|
|
19
|
+
const source = findMeshyThumbnail(options.inputDir, options.requested);
|
|
20
|
+
if (!source)
|
|
21
|
+
return undefined;
|
|
22
|
+
const stat = statSync(source);
|
|
23
|
+
const extension = extname(source).toLowerCase();
|
|
24
|
+
if (!stat.isFile() || !THUMBNAIL_EXTENSIONS.has(extension))
|
|
25
|
+
throw new Error("Meshy thumbnail must be a PNG, JPEG, or WebP regular file.");
|
|
26
|
+
if (stat.size === 0 || stat.size > MAX_MESHY_THUMBNAIL_BYTES)
|
|
27
|
+
throw new Error("Meshy thumbnail must be non-empty and no larger than " + MAX_MESHY_THUMBNAIL_BYTES + " bytes.");
|
|
28
|
+
const bytes = readFileSync(source);
|
|
29
|
+
validateImageSignature(bytes, extension);
|
|
30
|
+
const hash = createHash("sha256").update(bytes).digest("hex");
|
|
31
|
+
const fileName = options.assetName + ".meshy-candidate." + hash.slice(0, 8) + extension;
|
|
32
|
+
const outputPath = join(options.outputDir, fileName).replaceAll("\\", "/");
|
|
33
|
+
const destination = resolve(options.projectDir, outputPath);
|
|
34
|
+
mkdirSync(resolve(options.projectDir, options.outputDir), { recursive: true });
|
|
35
|
+
if (!existsSync(destination))
|
|
36
|
+
copyFileSync(source, destination);
|
|
37
|
+
const url = options.publicPath.replace(/\/?$/, "/") + fileName;
|
|
38
|
+
return {
|
|
39
|
+
sourceFile: source,
|
|
40
|
+
outputPath,
|
|
41
|
+
url,
|
|
42
|
+
sha256: "sha256-" + hash,
|
|
43
|
+
renderedProbe: {
|
|
44
|
+
url,
|
|
45
|
+
kind: "manual-inspection",
|
|
46
|
+
sha256: "sha256-" + hash,
|
|
47
|
+
checkedAt: new Date().toISOString()
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function validateImageSignature(bytes, extension) {
|
|
52
|
+
const png = bytes.length >= 8 && bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
|
|
53
|
+
const jpeg = bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff;
|
|
54
|
+
const webp = bytes.length >= 12 && bytes.toString("ascii", 0, 4) === "RIFF" && bytes.toString("ascii", 8, 12) === "WEBP";
|
|
55
|
+
const valid = extension === ".png" ? png : extension === ".webp" ? webp : jpeg;
|
|
56
|
+
if (!valid)
|
|
57
|
+
throw new Error("Meshy thumbnail content does not match its image extension.");
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=thumbnail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thumbnail.js","sourceRoot":"","sources":["../../../../../packages/aura3d-cli/src/meshy/thumbnail.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const MAX_MESHY_GLB_BYTES: number;
|
|
2
|
+
export declare function resolveConfinedPath(allowedRoot: string, candidate: string, label: string): string;
|
|
3
|
+
export declare function selectMeshyGlb(inputDir: string, file: string | undefined): string;
|
|
4
|
+
export declare function validateMeshyGlb(path: string): void;
|
|
5
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../../../packages/aura3d-cli/src/meshy/validation.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,QAAoB,CAAC;AAErD,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAgBjG;AACD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAcjF;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAWnD"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { closeSync, existsSync, openSync, readSync, realpathSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { extname, isAbsolute, relative, resolve } from "node:path";
|
|
3
|
+
export const MAX_MESHY_GLB_BYTES = 100 * 1024 * 1024;
|
|
4
|
+
export function resolveConfinedPath(allowedRoot, candidate, label) {
|
|
5
|
+
if (!existsSync(allowedRoot))
|
|
6
|
+
throw new Error(`Meshy allowed root does not exist: ${allowedRoot}`);
|
|
7
|
+
const lexicalRoot = resolve(allowedRoot);
|
|
8
|
+
const target = resolve(isAbsolute(candidate) ? candidate : resolve(lexicalRoot, candidate));
|
|
9
|
+
const lexicalRelative = relative(lexicalRoot, target);
|
|
10
|
+
if (lexicalRelative === ".." || lexicalRelative.startsWith("../") || isAbsolute(lexicalRelative)) {
|
|
11
|
+
throw new Error(`Meshy ${label} escapes the allowed root: ${candidate}`);
|
|
12
|
+
}
|
|
13
|
+
if (!existsSync(target))
|
|
14
|
+
throw new Error(`Meshy ${label} does not exist: ${candidate}`);
|
|
15
|
+
const root = realpathSync(lexicalRoot);
|
|
16
|
+
const realTarget = realpathSync(target);
|
|
17
|
+
const realRelative = relative(root, realTarget);
|
|
18
|
+
if (realRelative === ".." || realRelative.startsWith("../") || isAbsolute(realRelative)) {
|
|
19
|
+
throw new Error(`Meshy ${label} escapes the allowed root through a symbolic link: ${candidate}`);
|
|
20
|
+
}
|
|
21
|
+
return realTarget;
|
|
22
|
+
}
|
|
23
|
+
export function selectMeshyGlb(inputDir, file) {
|
|
24
|
+
if (!statSync(inputDir).isDirectory())
|
|
25
|
+
throw new Error(`Meshy input must be a directory: ${inputDir}`);
|
|
26
|
+
if (file) {
|
|
27
|
+
const selected = resolveConfinedPath(inputDir, file, "selected GLB");
|
|
28
|
+
validateMeshyGlb(selected);
|
|
29
|
+
return selected;
|
|
30
|
+
}
|
|
31
|
+
const glbs = readdirSync(inputDir, { withFileTypes: true })
|
|
32
|
+
.filter((entry) => entry.isFile() && extname(entry.name).toLowerCase() === ".glb")
|
|
33
|
+
.map((entry) => resolve(inputDir, entry.name));
|
|
34
|
+
if (glbs.length === 0)
|
|
35
|
+
throw new Error("Meshy output contains no GLB. Supply a completed local output directory.");
|
|
36
|
+
if (glbs.length > 1)
|
|
37
|
+
throw new Error("Meshy output contains multiple GLBs. Select exactly one with --file <relative.glb>.");
|
|
38
|
+
validateMeshyGlb(glbs[0]);
|
|
39
|
+
return glbs[0];
|
|
40
|
+
}
|
|
41
|
+
export function validateMeshyGlb(path) {
|
|
42
|
+
const stat = statSync(path);
|
|
43
|
+
if (!stat.isFile())
|
|
44
|
+
throw new Error(`Meshy GLB is not a regular file: ${path}`);
|
|
45
|
+
if (extname(path).toLowerCase() !== ".glb")
|
|
46
|
+
throw new Error(`Meshy selected file must use the .glb extension: ${path}`);
|
|
47
|
+
if (stat.size < 20)
|
|
48
|
+
throw new Error(`Meshy GLB is too small to be valid: ${path}`);
|
|
49
|
+
if (stat.size > MAX_MESHY_GLB_BYTES)
|
|
50
|
+
throw new Error(`Meshy GLB exceeds ${MAX_MESHY_GLB_BYTES} bytes: ${path}`);
|
|
51
|
+
const bytes = Buffer.alloc(12);
|
|
52
|
+
const handle = openSync(path, "r");
|
|
53
|
+
try {
|
|
54
|
+
readSync(handle, bytes, 0, 12, 0);
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
closeSync(handle);
|
|
58
|
+
}
|
|
59
|
+
if (bytes.toString("utf8", 0, 4) !== "glTF")
|
|
60
|
+
throw new Error(`Meshy GLB has invalid magic bytes: ${path}`);
|
|
61
|
+
if (bytes.readUInt32LE(8) !== stat.size)
|
|
62
|
+
throw new Error(`Meshy GLB declared length does not match its file size: ${path}`);
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../../../../packages/aura3d-cli/src/meshy/validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import type { AnimationAssetProfile, AuraCanonicalAsset, ResolveCandidate, ResolveConstraints } from "@aura3d/asset-index";
|
|
2
2
|
import type { CliAssetSearchProfile, CliResolveConstraints } from "./types.js";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Triangle budgets applied to inferred character/vehicle queries.
|
|
5
|
+
*
|
|
6
|
+
* These mirror the Meshy admission maxima (humanoid 150k, vehicle 250k): a
|
|
7
|
+
* general-profile query that reads as a character or vehicle stops defaulting
|
|
8
|
+
* to an unbounded `general` fetch and resolves under the same budget the
|
|
9
|
+
* quality gates enforce downstream. Explicit caller values always win.
|
|
10
|
+
*/
|
|
11
|
+
export declare const INFERRED_CHARACTER_MAX_TRIANGLES = 150000;
|
|
12
|
+
export declare const INFERRED_VEHICLE_MAX_TRIANGLES = 250000;
|
|
13
|
+
export declare function toResolveConstraints(cli: CliResolveConstraints, redistributableOnly: boolean, query?: string): ResolveConstraints;
|
|
14
|
+
export declare function rankForProfile(candidates: readonly ResolveCandidate[], profile: CliAssetSearchProfile, query?: string): readonly ResolveCandidate[];
|
|
5
15
|
export declare function isAnimationCliProfile(profile: CliAssetSearchProfile | undefined): profile is AnimationAssetProfile;
|
|
6
16
|
export declare function evaluateAssetProfile(asset: AuraCanonicalAsset, profile: Exclude<CliAssetSearchProfile, "general">): {
|
|
7
17
|
readonly suitable: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../../../../packages/aura3d-cli/src/pull-bridge/profiles.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../../../../packages/aura3d-cli/src/pull-bridge/profiles.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AASpB;;;;;;;GAOG;AACH,eAAO,MAAM,gCAAgC,SAAU,CAAC;AACxD,eAAO,MAAM,8BAA8B,SAAU,CAAC;AAEtD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,qBAAqB,EAC1B,mBAAmB,EAAE,OAAO,EAC5B,KAAK,CAAC,EAAE,MAAM,GACb,kBAAkB,CAgCpB;AAED,wBAAgB,cAAc,CAC5B,UAAU,EAAE,SAAS,gBAAgB,EAAE,EACvC,OAAO,EAAE,qBAAqB,EAC9B,KAAK,CAAC,EAAE,MAAM,GACb,SAAS,gBAAgB,EAAE,CA2B7B;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,SAAS,GAAG,OAAO,IAAI,qBAAqB,CAElH;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,OAAO,CAAC,qBAAqB,EAAE,SAAS,CAAC,GACjD;IAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAI/L;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAWnF"}
|
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
import * as assetIndex from "@aura3d/asset-index";
|
|
2
|
+
import { inferQueryRole } from "./scoring.js";
|
|
2
3
|
const { evaluateAnimationAssetProfile, evaluateGameAssetProfile, isAnimationAssetProfile, } = assetIndex;
|
|
3
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Triangle budgets applied to inferred character/vehicle queries.
|
|
6
|
+
*
|
|
7
|
+
* These mirror the Meshy admission maxima (humanoid 150k, vehicle 250k): a
|
|
8
|
+
* general-profile query that reads as a character or vehicle stops defaulting
|
|
9
|
+
* to an unbounded `general` fetch and resolves under the same budget the
|
|
10
|
+
* quality gates enforce downstream. Explicit caller values always win.
|
|
11
|
+
*/
|
|
12
|
+
export const INFERRED_CHARACTER_MAX_TRIANGLES = 150_000;
|
|
13
|
+
export const INFERRED_VEHICLE_MAX_TRIANGLES = 250_000;
|
|
14
|
+
export function toResolveConstraints(cli, redistributableOnly, query) {
|
|
4
15
|
const constraints = {};
|
|
16
|
+
if (cli.profile === "general" || cli.profile === undefined) {
|
|
17
|
+
const inferred = typeof query === "string" && query.trim().length > 0 ? inferQueryRole(query) : undefined;
|
|
18
|
+
if ((inferred === "character" || inferred === "vehicle") && typeof cli.maxTriangles !== "number") {
|
|
19
|
+
constraints.maxTriangles = inferred === "character" ? INFERRED_CHARACTER_MAX_TRIANGLES : INFERRED_VEHICLE_MAX_TRIANGLES;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
5
22
|
if (cli.profile === "fighting-character") {
|
|
6
23
|
constraints.license = cli.license && cli.license.length > 0 ? cli.license : ["CC0", "CC-BY"];
|
|
7
24
|
constraints.animated = true;
|
|
@@ -29,9 +46,23 @@ export function toResolveConstraints(cli, redistributableOnly) {
|
|
|
29
46
|
constraints.redistributableOnly = true;
|
|
30
47
|
return constraints;
|
|
31
48
|
}
|
|
32
|
-
export function rankForProfile(candidates, profile) {
|
|
33
|
-
if (profile === "general")
|
|
34
|
-
|
|
49
|
+
export function rankForProfile(candidates, profile, query) {
|
|
50
|
+
if (profile === "general") {
|
|
51
|
+
// A general-profile character/vehicle query stops defaulting to unfiltered
|
|
52
|
+
// catalog order: candidates whose catalog role matches the inferred query
|
|
53
|
+
// role rank first (by resolver score, then id), exactly like an explicit
|
|
54
|
+
// profile's suitability sort. Any other query returns catalog order.
|
|
55
|
+
const inferred = typeof query === "string" && query.trim().length > 0 ? inferQueryRole(query) : undefined;
|
|
56
|
+
if (inferred !== "character" && inferred !== "vehicle")
|
|
57
|
+
return candidates;
|
|
58
|
+
return [...candidates].sort((a, b) => {
|
|
59
|
+
const aMatch = a.asset.intendedRole === inferred ? 1 : 0;
|
|
60
|
+
const bMatch = b.asset.intendedRole === inferred ? 1 : 0;
|
|
61
|
+
return (bMatch - aMatch ||
|
|
62
|
+
b.score - a.score ||
|
|
63
|
+
a.asset.id.localeCompare(b.asset.id));
|
|
64
|
+
});
|
|
65
|
+
}
|
|
35
66
|
return [...candidates].sort((a, b) => {
|
|
36
67
|
const aEval = evaluateAssetProfile(a.asset, profile);
|
|
37
68
|
const bEval = evaluateAssetProfile(b.asset, profile);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../../../../packages/aura3d-cli/src/pull-bridge/profiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../../../../packages/aura3d-cli/src/pull-bridge/profiles.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,21 @@ import type { AuraAssetIntendedRole, AuraCanonicalAsset, ResolveCandidate } from
|
|
|
2
2
|
import type { AssetInspectionReport, AuraCliAssetRole, AuraCliResolveCandidateProvenance } from "../index.js";
|
|
3
3
|
import type { AssetResolveCandidateScore } from "./scoring.js";
|
|
4
4
|
import type { CliAssetSearchProfile } from "./types.js";
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Structural triangle floors mirrored from the Meshy admission profiles.
|
|
7
|
+
*
|
|
8
|
+
* `meshy/admission.ts` enforces a 1,000-triangle floor for vehicles and a
|
|
9
|
+
* 3,000-triangle floor for humanoids; the role-admission hero floor is 3,000.
|
|
10
|
+
* The same measured-and-below-floor classes are blocked here pre-download so a
|
|
11
|
+
* no-key resolve refuses a distant-prop shell (e.g. the 792-triangle body
|
|
12
|
+
* shell) without spending a download on it. A missing triangle count never
|
|
13
|
+
* blocks: absent metadata is unproven, not unfit.
|
|
14
|
+
*/
|
|
15
|
+
export declare const PROVENANCE_VEHICLE_MIN_TRIANGLES = 1000;
|
|
16
|
+
export declare const PROVENANCE_HUMANOID_MIN_TRIANGLES = 3000;
|
|
17
|
+
/** Bounds aspect ratio above this is degenerate (mirrors Meshy `bounds-degeneracy`). */
|
|
18
|
+
export declare const PROVENANCE_MAX_BOUNDS_ASPECT_RATIO = 1000;
|
|
19
|
+
export declare function createPreDownloadCandidateBlockingWarnings(asset: AuraCanonicalAsset, profile?: CliAssetSearchProfile): readonly string[];
|
|
6
20
|
export declare function createPostDownloadCandidateBlockingWarnings(asset: AuraCanonicalAsset, inspection: AssetInspectionReport, profile: CliAssetSearchProfile): readonly string[];
|
|
7
21
|
export declare function createResolveCandidateProvenance(candidate: ResolveCandidate, query: string, score: AssetResolveCandidateScore, inspection: AssetInspectionReport): AuraCliResolveCandidateProvenance;
|
|
8
22
|
export declare function mapCanonicalRoleToCli(role: AuraAssetIntendedRole | undefined): AuraCliAssetRole;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../../../../../packages/aura3d-cli/src/pull-bridge/provenance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,qBAAqB,EACrB,gBAAgB,EAChB,iCAAiC,EAClC,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAGxD,wBAAgB,0CAA0C,
|
|
1
|
+
{"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../../../../../packages/aura3d-cli/src/pull-bridge/provenance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,qBAAqB,EACrB,gBAAgB,EAChB,iCAAiC,EAClC,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAGxD;;;;;;;;;GASG;AACH,eAAO,MAAM,gCAAgC,OAAQ,CAAC;AACtD,eAAO,MAAM,iCAAiC,OAAQ,CAAC;AACvD,wFAAwF;AACxF,eAAO,MAAM,kCAAkC,OAAQ,CAAC;AA4BxD,wBAAgB,0CAA0C,CACxD,KAAK,EAAE,kBAAkB,EACzB,OAAO,GAAE,qBAAiC,GACzC,SAAS,MAAM,EAAE,CAkBnB;AAED,wBAAgB,2CAA2C,CACzD,KAAK,EAAE,kBAAkB,EACzB,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE,qBAAqB,GAC7B,SAAS,MAAM,EAAE,CA2CnB;AAED,wBAAgB,gCAAgC,CAC9C,SAAS,EAAE,gBAAgB,EAC3B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,0BAA0B,EACjC,UAAU,EAAE,qBAAqB,GAChC,iCAAiC,CAuDnC;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,qBAAqB,GAAG,SAAS,GAAG,gBAAgB,CAE/F"}
|
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
import { isPositiveVector3 } from "./vector3.js";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Structural triangle floors mirrored from the Meshy admission profiles.
|
|
4
|
+
*
|
|
5
|
+
* `meshy/admission.ts` enforces a 1,000-triangle floor for vehicles and a
|
|
6
|
+
* 3,000-triangle floor for humanoids; the role-admission hero floor is 3,000.
|
|
7
|
+
* The same measured-and-below-floor classes are blocked here pre-download so a
|
|
8
|
+
* no-key resolve refuses a distant-prop shell (e.g. the 792-triangle body
|
|
9
|
+
* shell) without spending a download on it. A missing triangle count never
|
|
10
|
+
* blocks: absent metadata is unproven, not unfit.
|
|
11
|
+
*/
|
|
12
|
+
export const PROVENANCE_VEHICLE_MIN_TRIANGLES = 1_000;
|
|
13
|
+
export const PROVENANCE_HUMANOID_MIN_TRIANGLES = 3_000;
|
|
14
|
+
/** Bounds aspect ratio above this is degenerate (mirrors Meshy `bounds-degeneracy`). */
|
|
15
|
+
export const PROVENANCE_MAX_BOUNDS_ASPECT_RATIO = 1_000;
|
|
16
|
+
function catalogTriangles(asset) {
|
|
17
|
+
const value = asset.triangleCount ?? asset.triangles;
|
|
18
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
|
|
19
|
+
}
|
|
20
|
+
function provenanceMinTriangles(asset, profile) {
|
|
21
|
+
if (asset.intendedRole === "vehicle")
|
|
22
|
+
return PROVENANCE_VEHICLE_MIN_TRIANGLES;
|
|
23
|
+
if (asset.intendedRole === "character")
|
|
24
|
+
return PROVENANCE_HUMANOID_MIN_TRIANGLES;
|
|
25
|
+
if (profile === "fighting-character" || profile === "animation-character")
|
|
26
|
+
return PROVENANCE_HUMANOID_MIN_TRIANGLES;
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
function boundsAspectWarnings(size, label) {
|
|
30
|
+
if (!size)
|
|
31
|
+
return [];
|
|
32
|
+
if (!(size.length === 3 && size.every((value) => typeof value === "number" && Number.isFinite(value) && value > 0))) {
|
|
33
|
+
return [`${label} bounds are not positive and finite; inspect export scale and stray geometry`];
|
|
34
|
+
}
|
|
35
|
+
const ratio = Math.max(...size) / Math.min(...size);
|
|
36
|
+
return ratio > PROVENANCE_MAX_BOUNDS_ASPECT_RATIO
|
|
37
|
+
? [`${label} bounds aspect ratio ${ratio.toFixed(1)} is degenerate; check export scale and stray geometry`]
|
|
38
|
+
: [];
|
|
39
|
+
}
|
|
40
|
+
export function createPreDownloadCandidateBlockingWarnings(asset, profile = "general") {
|
|
3
41
|
const warnings = [];
|
|
4
42
|
if (asset.duplicateHash && !asset.duplicateOkReason) {
|
|
5
43
|
warnings.push(`duplicate hash ${asset.duplicateHash} has no allowlist/explanation`);
|
|
@@ -7,6 +45,13 @@ export function createPreDownloadCandidateBlockingWarnings(asset) {
|
|
|
7
45
|
if (asset.access === "direct-download" && !(asset.downloadUrl ?? asset.url)) {
|
|
8
46
|
warnings.push("direct-download candidate is missing a download URL");
|
|
9
47
|
}
|
|
48
|
+
const triangles = catalogTriangles(asset);
|
|
49
|
+
const floor = provenanceMinTriangles(asset, profile);
|
|
50
|
+
if (triangles !== undefined && floor !== undefined && triangles < floor) {
|
|
51
|
+
const role = asset.intendedRole === "vehicle" ? "vehicle" : "humanoid";
|
|
52
|
+
warnings.push(`catalog reports ${triangles} triangles, below the ${role} structural floor of ${floor}; this is a distant-prop shell, not a hero subject`);
|
|
53
|
+
}
|
|
54
|
+
warnings.push(...boundsAspectWarnings(asset.bounds?.size ?? asset.dimensions, "catalog"));
|
|
10
55
|
return warnings;
|
|
11
56
|
}
|
|
12
57
|
export function createPostDownloadCandidateBlockingWarnings(asset, inspection, profile) {
|
|
@@ -35,6 +80,20 @@ export function createPostDownloadCandidateBlockingWarnings(asset, inspection, p
|
|
|
35
80
|
if (inspection.warnings.some((warning) => warning.includes("invisible or unreadable"))) {
|
|
36
81
|
warnings.push("downloaded file contains invisible or unreadable materials");
|
|
37
82
|
}
|
|
83
|
+
// Mirror the Meshy triangle floor post-download: a catalog count that looked
|
|
84
|
+
// fine (or was absent) still refuses when the role needs a hero subject and
|
|
85
|
+
// the measured catalog count is a shell. Profile-aware so an explicit
|
|
86
|
+
// character profile enforces the humanoid floor even when the catalog left
|
|
87
|
+
// `intendedRole` blank.
|
|
88
|
+
const postTriangles = catalogTriangles(asset);
|
|
89
|
+
const postFloor = provenanceMinTriangles(asset, profile);
|
|
90
|
+
if (postTriangles !== undefined && postFloor !== undefined && postTriangles < postFloor) {
|
|
91
|
+
const role = postFloor === PROVENANCE_VEHICLE_MIN_TRIANGLES && asset.intendedRole === "vehicle" ? "vehicle" : "humanoid";
|
|
92
|
+
warnings.push(`catalog reports ${postTriangles} triangles, below the ${role} structural floor of ${postFloor}; this is a distant-prop shell, not a hero subject`);
|
|
93
|
+
}
|
|
94
|
+
if (inspection.bounds) {
|
|
95
|
+
warnings.push(...boundsAspectWarnings(inspection.bounds, "downloaded file"));
|
|
96
|
+
}
|
|
38
97
|
return warnings;
|
|
39
98
|
}
|
|
40
99
|
export function createResolveCandidateProvenance(candidate, query, score, inspection) {
|