Files
jhonny-editor/bm/premiere-pro-mcp-main/scripts/validate-project-intake-host-report.mjs
T
João Henrique b541f502ba feat: initial commit - Jhonny Editor
- Adicionado estrutura completa do projeto
- Configurado MCP server para Premiere Pro
- Adicionado documentação e skills
- Configurado Gitignore para o projeto
2026-09-08 09:59:31 -04:00

212 lines
9.7 KiB
JavaScript
Executable File

import fs from "node:fs";
import path from "node:path";
const SCHEMA_VERSION = "project-intake-host-report/v1";
const NOT_RUN_SHA256 = "0".repeat(64);
const NOT_RUN_COMMIT = "0".repeat(40);
const ALLOWED_STATUSES = new Set(["passed", "failed", "unsupported", "not_run"]);
const CASE_REQUIREMENTS = {
"PIP-CONNECT-001": {
tool: "verify_premiere_connection",
evidence: ["structured_connection_response"],
assertions: { overall: "ready" },
},
"PIP-PREVIEW-001": {
tool: "preview_project_intake",
evidence: ["structured_preview_response"],
assertions: {
applied: false,
pathDisclosure: "redacted",
organizationPlanApplied: false,
},
},
"PIP-NO-MUTATION-001": {
tool: "preview_project_intake",
evidence: ["before_project_panel", "after_project_panel", "structured_preview_response"],
assertions: {
projectMutated: false,
projectSaved: false,
},
},
};
const SENSITIVE_CONTENT = /(?:(?:^|[^A-Za-z0-9])[A-Za-z]:[\\/]|\\\\|file:\/\/|\/(?:Users|home|private)\/|authorization|bearer\s+[A-Za-z0-9._-]+|api[_-]?key|password|secret|access[_-]?token|refresh[_-]?token|project(?:Name|Path)|media(?:Name|Path)|transcript|prompt)/i;
function isObject(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function hasOnlyKeys(value, allowedKeys, label, errors) {
if (!isObject(value)) {
errors.push(`${label} must be an object`);
return false;
}
for (const key of Object.keys(value)) {
if (!allowedKeys.has(key)) errors.push(`${label} contains unsupported field: ${key}`);
}
return true;
}
function isNonEmptyString(value, maxLength = 128) {
return typeof value === "string" && value.trim().length > 0 && value.length <= maxLength;
}
function isSha256(value) {
return /^[0-9a-f]{64}$/i.test(value ?? "");
}
function isCommit(value) {
return /^[0-9a-f]{40}$/i.test(value ?? "");
}
function isRfc3339(value) {
return typeof value === "string" && !Number.isNaN(Date.parse(value)) && /(?:Z|[+-]\d\d:\d\d)$/.test(value);
}
function serializedValues(value) {
if (typeof value === "string") return value;
if (Array.isArray(value)) return value.map(serializedValues).join("\n");
if (isObject(value)) return Object.values(value).map(serializedValues).join("\n");
return "";
}
function validateEvidence(evidence, label, errors) {
if (!Array.isArray(evidence)) {
errors.push(`${label}.evidence must be an array`);
return [];
}
const kinds = [];
for (const [index, item] of evidence.entries()) {
const evidenceLabel = `${label}.evidence[${index}]`;
hasOnlyKeys(item, new Set(["kind", "reference", "sha256"]), evidenceLabel, errors);
if (!isNonEmptyString(item?.kind, 64)) errors.push(`${evidenceLabel}.kind is required`);
else kinds.push(item.kind);
if (typeof item?.reference !== "string" || !/^evidence:\/\/[a-z0-9][a-z0-9._-]{0,127}$/i.test(item.reference)) {
errors.push(`${evidenceLabel}.reference must be an opaque evidence:// reference`);
}
if (!isSha256(item?.sha256)) errors.push(`${evidenceLabel}.sha256 must be a SHA-256 hash`);
}
if (new Set(kinds).size !== kinds.length) errors.push(`${label}.evidence must not repeat a kind`);
return kinds;
}
function validatePassedCase(entry, requirement, errors) {
const label = `case ${entry.id}`;
if (!isRfc3339(entry.executedAt)) errors.push(`${label}.executedAt must be an RFC 3339 timestamp`);
hasOnlyKeys(entry.assertions, new Set(["tool", ...Object.keys(requirement.assertions)]), `${label}.assertions`, errors);
if (entry.assertions?.tool !== requirement.tool) errors.push(`${label}.assertions.tool must be ${requirement.tool}`);
for (const [key, expected] of Object.entries(requirement.assertions)) {
if (entry.assertions?.[key] !== expected) errors.push(`${label}.assertions.${key} must be ${JSON.stringify(expected)}`);
}
const evidenceKinds = validateEvidence(entry.evidence, label, errors);
for (const requiredKind of requirement.evidence) {
if (!evidenceKinds.includes(requiredKind)) errors.push(`${label} requires ${requiredKind} evidence`);
}
for (const kind of evidenceKinds) {
if (!requirement.evidence.includes(kind)) errors.push(`${label} has unsupported evidence kind: ${kind}`);
}
}
function validateReport(report) {
const errors = [];
hasOnlyKeys(report, new Set(["schemaVersion", "sourceCommit", "host", "client", "fixture", "privacy", "cases"]), "report", errors);
if (report?.schemaVersion !== SCHEMA_VERSION) errors.push(`schemaVersion must be ${SCHEMA_VERSION}`);
if (!isCommit(report?.sourceCommit)) errors.push("sourceCommit must be a 40-character commit SHA");
hasOnlyKeys(report?.host, new Set(["os", "premiereVersion", "premiereBuild", "connector"]), "host", errors);
if (!new Set(["Windows", "macOS"]).has(report?.host?.os)) errors.push("host.os must be Windows or macOS");
if (!isNonEmptyString(report?.host?.premiereVersion, 64)) errors.push("host.premiereVersion is required");
if (!isNonEmptyString(report?.host?.premiereBuild, 64)) errors.push("host.premiereBuild is required");
hasOnlyKeys(report?.host?.connector, new Set(["type", "buildHash"]), "host.connector", errors);
if (report?.host?.connector?.type !== "cep") errors.push("host.connector.type must be cep for v1.13.0 Project Intake validation");
if (!/^[0-9a-f]{7,64}$/i.test(report?.host?.connector?.buildHash ?? "")) errors.push("host.connector.buildHash must be a build hash");
hasOnlyKeys(report?.client, new Set(["name", "version"]), "client", errors);
if (!isNonEmptyString(report?.client?.name)) errors.push("client.name is required");
if (!isNonEmptyString(report?.client?.version)) errors.push("client.version is required");
hasOnlyKeys(report?.fixture, new Set(["revision", "sha256"]), "fixture", errors);
if (!/^[a-z0-9][a-z0-9._-]{0,63}$/i.test(report?.fixture?.revision ?? "")) errors.push("fixture.revision must be a non-sensitive identifier");
if (!isSha256(report?.fixture?.sha256)) errors.push("fixture.sha256 must be a SHA-256 hash");
const requiredPrivacyFlags = ["containsOnlyGeneratedFixtureData", "localPathsRemoved", "mediaNamesRemoved", "promptsRemoved", "transcriptsRemoved", "credentialsRemoved"];
hasOnlyKeys(report?.privacy, new Set(requiredPrivacyFlags), "privacy", errors);
for (const flag of requiredPrivacyFlags) {
if (report?.privacy?.[flag] !== true) errors.push(`privacy.${flag} must be true`);
}
if (!Array.isArray(report?.cases) || report.cases.length !== Object.keys(CASE_REQUIREMENTS).length) {
errors.push(`cases must contain exactly ${Object.keys(CASE_REQUIREMENTS).length} Project Intake cases`);
}
const seenCaseIds = new Set();
for (const entry of report?.cases ?? []) {
const label = `case ${entry?.id ?? "<unknown>"}`;
hasOnlyKeys(entry, new Set(["id", "status", "executedAt", "assertions", "evidence"]), label, errors);
if (!Object.hasOwn(CASE_REQUIREMENTS, entry?.id)) errors.push(`${label} is not a supported Project Intake case`);
if (seenCaseIds.has(entry?.id)) errors.push(`${label} is duplicated`);
seenCaseIds.add(entry?.id);
if (!ALLOWED_STATUSES.has(entry?.status)) errors.push(`${label} has an invalid status`);
if (entry?.status === "passed" && CASE_REQUIREMENTS[entry.id]) {
validatePassedCase(entry, CASE_REQUIREMENTS[entry.id], errors);
} else {
if (entry?.executedAt !== undefined) errors.push(`${label}.executedAt is only recorded for passed cases`);
if (entry?.assertions !== undefined) errors.push(`${label}.assertions are only accepted for passed cases`);
const evidenceKinds = validateEvidence(entry?.evidence, label, errors);
if (evidenceKinds.length > 0) errors.push(`${label} must not include evidence unless it passed; retain failure evidence outside this minimal shared report`);
}
}
for (const id of Object.keys(CASE_REQUIREMENTS)) {
if (!seenCaseIds.has(id)) errors.push(`cases must include ${id}`);
}
const previewCase = report?.cases?.find((entry) => entry?.id === "PIP-PREVIEW-001");
const noMutationCase = report?.cases?.find((entry) => entry?.id === "PIP-NO-MUTATION-001");
if (previewCase?.status === "passed" && noMutationCase?.status !== "passed") {
errors.push("PIP-PREVIEW-001 cannot pass unless PIP-NO-MUTATION-001 also passes");
}
if (report?.cases?.some((entry) => entry?.status === "passed")) {
if (report.sourceCommit === NOT_RUN_COMMIT) errors.push("passed cases require a real sourceCommit, not the template sentinel");
if (report.fixture?.sha256 === NOT_RUN_SHA256) errors.push("passed cases require a real fixture checksum, not the template sentinel");
}
if (SENSITIVE_CONTENT.test(serializedValues(report))) {
errors.push("report appears to contain project data, a local path, or credential-like content; redact it before sharing");
}
return errors;
}
const reportPath = process.argv[2];
if (!reportPath) {
throw new Error("Usage: node scripts/validate-project-intake-host-report.mjs <redacted-report.json>");
}
const resolvedPath = path.resolve(reportPath);
let report;
try {
report = JSON.parse(fs.readFileSync(resolvedPath, "utf8"));
} catch (error) {
throw new Error(`Unable to parse Project Intake host report: ${error instanceof Error ? error.message : String(error)}`);
}
const errors = validateReport(report);
if (errors.length > 0) {
throw new Error(`Project Intake host report is invalid:\n- ${errors.join("\n- ")}`);
}
console.log(JSON.stringify({
report: path.basename(resolvedPath),
schemaVersion: SCHEMA_VERSION,
sourceCommit: report.sourceCommit,
host: report.host,
totals: Object.fromEntries([...ALLOWED_STATUSES].map((status) => [
status,
report.cases.filter((entry) => entry.status === status).length,
])),
humanReviewRequired: true,
licensedHostVerifiedByValidator: false,
}, null, 2));