项目文件夹

文件
wehub-resource-sync 426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

527 行
16 KiB
JavaScript
可执行文件

// Supports OS release manifests, checksums, and TEE evidence automation.
import { createHash } from "node:crypto";
import { createReadStream } from "node:fs";
import { access, mkdir, readFile, stat, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
export const repoRoot = path.resolve(
fileURLToPath(new URL("../../..", import.meta.url)),
);
export const defaultManifestPath = path.join(
repoRoot,
"packages/os/release/beta-2026-05-16/manifest.json",
);
const sha256Pattern = /^[a-f0-9]{64}$/;
const zeroSha256 = "0".repeat(64);
function isPlaceholderSha256(value) {
return value === zeroSha256;
}
const isoDatePattern = /^\d{4}-\d{2}-\d{2}$/;
const releaseChannels = new Set(["alpha", "beta", "stable", "nightly"]);
const artifactKinds = new Set([
"raw-image",
"vm-image",
"android-image",
"checksum-manifest",
"usb-installer",
]);
const releaseStatuses = new Set([
"planned",
"candidate",
"available",
"withdrawn",
]);
const artifactStatuses = new Set([
"planned",
"candidate",
"published",
"withdrawn",
]);
const teeProviders = new Set([
"dstack",
"tdx",
"sev-snp",
"nitro",
"cove",
"keystone",
"optee",
"eliza-vault",
]);
// Measurement names always required in a TEE-capable measurement set.
export const requiredTeeMeasurementNames = ["boot", "os", "agent", "policy"];
// Optional OS-side measurement names. These mirror the agent's canonical
// `TeeMeasurementName` union (packages/agent/src/services/tee-evidence.ts) plus
// the OS-only `modelWeights` and `monitor` measurements. The agent type accepts
// arbitrary names via its `(string & {})` fallback, so all of these round-trip
// through normalizeTeeEvidence without an agent-side change.
export const optionalTeeMeasurementNames = [
"device",
"container",
"compose",
"gpuFirmware",
"npuFirmware",
"modelWeights",
"monitor",
];
// Conditionally-required claims for on-device confidential inference: when the
// manifest declares an inference-bearing measurement, npuProtected and
// ioProtected must be asserted (plan §6, OS-2/OS-3).
const inferenceMeasurementNames = [
"gpuFirmware",
"npuFirmware",
"modelWeights",
];
function isValidIsoDate(value) {
if (typeof value !== "string" || !isoDatePattern.test(value)) {
return false;
}
const time = Date.parse(`${value}T00:00:00Z`);
return Number.isFinite(time);
}
export function parseArgs(argv) {
const args = {};
for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i];
if (!arg.startsWith("--")) {
args._ = [...(args._ ?? []), arg];
continue;
}
const key = arg.slice(2);
const next = argv[i + 1];
if (!next || next.startsWith("--")) {
args[key] = true;
} else {
args[key] = next;
i += 1;
}
}
return args;
}
export async function readJson(filePath) {
return JSON.parse(await readFile(filePath, "utf8"));
}
export async function writeJson(filePath, value) {
await mkdir(path.dirname(filePath), { recursive: true });
await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`);
}
export async function fileExists(filePath) {
try {
await access(filePath);
return true;
} catch {
return false;
}
}
export async function sha256File(filePath) {
const hash = createHash("sha256");
await new Promise((resolve, reject) => {
const stream = createReadStream(filePath);
stream.on("data", (chunk) => hash.update(chunk));
stream.on("error", reject);
stream.on("end", resolve);
});
return hash.digest("hex");
}
// Deterministic canonicalization: recursively sort object keys, preserve array
// order. The result serializes to identical bytes regardless of the source file's
// key ordering or whitespace, so the policy digest depends only on the policy's
// semantic content. Arrays are NOT reordered (order is meaningful for the policy).
export function canonicalize(value) {
if (Array.isArray(value)) return value.map(canonicalize);
if (value && typeof value === "object") {
const out = {};
for (const key of Object.keys(value).sort()) {
out[key] = canonicalize(value[key]);
}
return out;
}
return value;
}
// sha256 of the canonical JSON serialization of `value`, prefixed `sha256:`.
// This is the digest folded into measurements.policy: the confidential-policy.json
// bytes (canonicalized) ARE the policy measurement.
export function sha256CanonicalJson(value) {
const bytes = JSON.stringify(canonicalize(value));
return `sha256:${createHash("sha256").update(bytes, "utf8").digest("hex")}`;
}
export function artifactPath(artifactRoot, artifact) {
return path.join(artifactRoot, artifact.filename);
}
export async function artifactFileRecord(artifactRoot, artifact) {
const filePath = artifactPath(artifactRoot, artifact);
const stats = await stat(filePath);
return {
id: artifact.id,
filename: artifact.filename,
path: filePath,
sizeBytes: stats.size,
sha256: await sha256File(filePath),
};
}
function requireString(errors, value, field) {
if (typeof value !== "string" || value.length === 0) {
errors.push(`${field} must be a non-empty string`);
}
}
function requireDate(errors, value, field) {
requireString(errors, value, field);
if (typeof value === "string" && !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
errors.push(`${field} must use YYYY-MM-DD`);
}
}
export function validateManifest(manifest, options = {}) {
const errors = [];
const warnings = [];
const requirePublishableChecksums = Boolean(
options.requirePublishableChecksums,
);
if (manifest?.schemaVersion !== 1) {
errors.push("schemaVersion must be 1");
}
requireString(errors, manifest?.release?.id, "release.id");
if (!releaseChannels.has(manifest?.release?.channel)) {
errors.push(
`release.channel must be one of ${[...releaseChannels].join(", ")}`,
);
}
requireString(errors, manifest?.release?.version, "release.version");
requireDate(
errors,
manifest?.release?.availableDate,
"release.availableDate",
);
if (
typeof manifest?.release?.availableDate === "string" &&
!isValidIsoDate(manifest.release.availableDate)
) {
errors.push("release.availableDate must be a valid ISO date (YYYY-MM-DD)");
}
if (!releaseStatuses.has(manifest?.release?.status)) {
errors.push("release.status is invalid");
}
const presale = manifest?.commerce?.usbKeyPresale;
if (!presale?.enabled) {
errors.push("commerce.usbKeyPresale.enabled must be true");
}
if (typeof presale?.priceUsd !== "number" || presale.priceUsd <= 0) {
errors.push("commerce.usbKeyPresale.priceUsd must be a positive number");
}
if (!isValidIsoDate(presale?.saleStarts)) {
errors.push("commerce.usbKeyPresale.saleStarts must be a valid ISO date");
}
if (!isValidIsoDate(presale?.estimatedShipWindow?.starts)) {
errors.push(
"commerce.usbKeyPresale.estimatedShipWindow.starts must be a valid ISO date",
);
}
if (!isValidIsoDate(presale?.estimatedShipWindow?.ends)) {
errors.push(
"commerce.usbKeyPresale.estimatedShipWindow.ends must be a valid ISO date",
);
}
if (!Array.isArray(manifest?.artifacts) || manifest.artifacts.length === 0) {
errors.push("artifacts must be a non-empty array");
}
const requiredKinds = new Set(["raw-image", "vm-image", "android-image"]);
const seenKinds = new Set();
const seenIds = new Set();
const seenFilenames = new Set();
for (const [index, artifact] of (manifest.artifacts ?? []).entries()) {
const prefix = `artifacts[${index}]`;
requireString(errors, artifact?.id, `${prefix}.id`);
if (seenIds.has(artifact?.id)) {
errors.push(`${prefix}.id duplicates ${artifact.id}`);
}
seenIds.add(artifact?.id);
if (!artifactKinds.has(artifact?.kind)) {
errors.push(`${prefix}.kind is invalid`);
} else {
seenKinds.add(artifact.kind);
}
if (!artifactStatuses.has(artifact?.status)) {
errors.push(`${prefix}.status is invalid`);
}
requireString(
errors,
artifact?.target?.platform,
`${prefix}.target.platform`,
);
requireString(
errors,
artifact?.target?.architecture,
`${prefix}.target.architecture`,
);
requireString(errors, artifact?.filename, `${prefix}.filename`);
if (seenFilenames.has(artifact?.filename)) {
errors.push(`${prefix}.filename duplicates ${artifact.filename}`);
}
seenFilenames.add(artifact?.filename);
const hasDownloadUrl =
typeof artifact?.downloadUrl === "string" &&
artifact.downloadUrl.length > 0;
if (artifact?.downloadUrl !== null && !hasDownloadUrl) {
errors.push(`${prefix}.downloadUrl must be null or a non-empty string`);
}
if (artifact?.status === "published" && !hasDownloadUrl) {
errors.push(`${prefix}.downloadUrl is required for published artifacts`);
} else if (!hasDownloadUrl) {
warnings.push(`${artifact.id} is awaiting download URL`);
}
if (
artifact?.sha256 !== null &&
!sha256Pattern.test(artifact?.sha256 ?? "")
) {
errors.push(
`${prefix}.sha256 must be null or 64 lowercase hex characters`,
);
}
if (isPlaceholderSha256(artifact?.sha256)) {
errors.push(
`${prefix}.sha256 is an all-zero placeholder; use null until a real checksum is generated`,
);
}
if (
artifact?.sizeBytes !== null &&
(!Number.isInteger(artifact?.sizeBytes) || artifact.sizeBytes <= 0)
) {
errors.push(`${prefix}.sizeBytes must be null or a positive integer`);
}
if (requirePublishableChecksums && artifact?.status !== "withdrawn") {
if (!hasDownloadUrl) {
errors.push(
`${prefix}.downloadUrl is required for publishable validation`,
);
}
if (
!sha256Pattern.test(artifact?.sha256 ?? "") ||
isPlaceholderSha256(artifact?.sha256)
) {
errors.push(`${prefix}.sha256 is required for publishable validation`);
}
if (!Number.isInteger(artifact?.sizeBytes) || artifact.sizeBytes <= 0) {
errors.push(
`${prefix}.sizeBytes is required for publishable validation`,
);
}
}
if (!Array.isArray(artifact?.validation?.requiredEvidence)) {
errors.push(`${prefix}.validation.requiredEvidence must be an array`);
}
if (!Array.isArray(artifact?.validation?.evidence)) {
errors.push(`${prefix}.validation.evidence must be an array`);
}
if (
(artifact?.validation?.requiredEvidence ?? []).includes(
"sha256-generated",
) &&
!artifact.sha256
) {
warnings.push(`${artifact.id} is awaiting sha256 generation`);
}
}
for (const kind of requiredKinds) {
if (!seenKinds.has(kind)) {
errors.push(`artifacts must include at least one ${kind}`);
}
}
if (manifest?.checksumPolicy?.algorithm !== "sha256") {
errors.push("checksumPolicy.algorithm must be sha256");
}
requireString(
errors,
manifest?.checksumPolicy?.generatedFile,
"checksumPolicy.generatedFile",
);
requireString(
errors,
manifest?.checksumPolicy?.verificationScript,
"checksumPolicy.verificationScript",
);
requireString(
errors,
manifest?.validation?.evidenceDirectory,
"validation.evidenceDirectory",
);
if (!Array.isArray(manifest?.validation?.promotionGates)) {
errors.push("validation.promotionGates must be an array");
}
validateTeePolicy(errors, manifest?.tee);
return {
ok: errors.length === 0,
errors,
warnings,
};
}
export function validateTeeMeasurements(document) {
const errors = [];
if (document?.schemaVersion !== 1) {
errors.push("schemaVersion must be 1");
}
if (typeof document?.generatedBy !== "string" || !document.generatedBy) {
errors.push("generatedBy must be a non-empty string");
}
const measurements = document?.measurements;
if (!measurements || typeof measurements !== "object") {
errors.push("measurements must be an object");
return { ok: false, errors };
}
const knownNames = new Set([
...requiredTeeMeasurementNames,
...optionalTeeMeasurementNames,
]);
for (const field of requiredTeeMeasurementNames) {
if (!sha256DigestString(measurements[field])) {
errors.push(`measurements.${field} must be sha256:<64 lowercase hex>`);
}
}
for (const [field, digest] of Object.entries(measurements)) {
if (!knownNames.has(field)) {
errors.push(`measurements.${field} is not a known measurement name`);
continue;
}
if (!sha256DigestString(digest)) {
errors.push(`measurements.${field} must be sha256:<64 lowercase hex>`);
}
}
return { ok: errors.length === 0, errors };
}
function validateTeePolicy(errors, tee) {
if (tee === undefined) return;
if (!tee || typeof tee !== "object" || Array.isArray(tee)) {
errors.push("tee must be an object when present");
return;
}
if (typeof tee.enabled !== "boolean") {
errors.push("tee.enabled must be a boolean");
}
if (tee.enabled !== true) return;
if (!sha256DigestString(tee.policyDigest)) {
errors.push("tee.policyDigest must be sha256:<64 lowercase hex>");
}
if (!Array.isArray(tee.providers) || tee.providers.length === 0) {
errors.push("tee.providers must be a non-empty array when enabled");
} else {
for (const provider of tee.providers) {
if (!teeProviders.has(provider)) {
errors.push(`tee.providers contains unsupported provider ${provider}`);
}
}
}
const measurements = tee.measurements;
if (
!measurements ||
typeof measurements !== "object" ||
Array.isArray(measurements)
) {
errors.push("tee.measurements must be an object when enabled");
return;
}
const knownNames = new Set([
...requiredTeeMeasurementNames,
...optionalTeeMeasurementNames,
]);
// Fail closed: a declared TEE-capable manifest missing any required digest is
// rejected, never silently accepted.
for (const field of requiredTeeMeasurementNames) {
if (!sha256DigestString(measurements[field])) {
errors.push(
`tee.measurements.${field} must be sha256:<64 lowercase hex>`,
);
}
}
for (const [field, digest] of Object.entries(measurements)) {
if (!knownNames.has(field)) {
errors.push(`tee.measurements.${field} is not a known measurement name`);
continue;
}
if (!sha256DigestString(digest)) {
errors.push(
`tee.measurements.${field} must be sha256:<64 lowercase hex>`,
);
}
}
const requiredClaims = tee.requiredClaims;
if (
requiredClaims === undefined ||
!requiredClaims ||
typeof requiredClaims !== "object" ||
Array.isArray(requiredClaims)
) {
errors.push("tee.requiredClaims must be an object when enabled");
return;
}
for (const claim of ["debugDisabled", "secureBoot"]) {
if (requiredClaims[claim] !== true) {
errors.push(`tee.requiredClaims.${claim} must be true when enabled`);
}
}
// On-device confidential inference: any inference-bearing measurement requires
// npuProtected + ioProtected to be asserted (plan §6 / OS-2 / OS-3).
const declaresInference = inferenceMeasurementNames.some((name) =>
sha256DigestString(measurements[name]),
);
if (declaresInference) {
for (const claim of ["npuProtected", "ioProtected"]) {
if (requiredClaims[claim] !== true) {
errors.push(
`tee.requiredClaims.${claim} must be true when an inference measurement (gpuFirmware/npuFirmware/modelWeights) is declared`,
);
}
}
}
}
function sha256DigestString(value) {
return typeof value === "string" && /^sha256:[a-f0-9]{64}$/.test(value);
}
export function formatCheckEntry(record) {
return `${record.sha256} ${record.filename}`;
}
export function parseChecksumFile(contents) {
return contents
.split(/\r?\n/)
.map((line) => line.trim())
.filter((line) => line && !line.startsWith("#"))
.map((line) => {
const match = line.match(/^([a-f0-9]{64})\s+\*?(.+)$/);
if (!match) {
throw new Error(`Invalid checksum line: ${line}`);
}
return { sha256: match[1], filename: match[2] };
});
}