← Files Biological Sequence & Alignment ViewerARCHIVED FILE

scripts/validate-public-bundle.mjs

7.16 KB · Sep 30, 2026 · 23:01 UTC

↓ Download file

import { access, readFile, readdir } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";

import {
  validateSequenceViewerExamplePrompts,
} from "./validate-example-prompts.mjs";

const REQUIRED_PATHS = [
  ".codex-plugin/plugin.json",
  ".mcp.json",
  "LICENSE",
  "README.md",
  "STARTER_EXAMPLES.md",
  "THIRD_PARTY_NOTICES.md",
  "assets/icon.png",
  "assets/logo.png",
  "dist/server.mjs",
  "dist/views/app.js.gz",
  "dist/views/styles.css",
  "skills",
  "starter-examples.json",
];
const PUBLIC_EXAMPLE_TOOL_NAME = "sequence.acquire_public_example";
const CHAT_OPEN_TOOL_NAME = "sequence.open_from_chat";
const REMOVED_FASTQ_EXAMPLE_TOOL_NAME = "sequence.open_fastq_example";
const FORBIDDEN_ROOT_ENTRIES = new Set([
  "PUBLIC_README.md",
  "node_modules",
  "package.json",
  "pnpm-lock.yaml",
  "scripts",
  "smoke-fixtures",
  "src",
]);
const FORBIDDEN_TEXT = [
  "/Users/",
  "file:../../lib/js",
  "openai-internal",
  "row-privacy-policy",
  "row-terms-of-use",
];
const MARKETPLACE_SHORT_DESCRIPTION =
  "Inspect, analyze, and compare biological sequences and alignments in Codex.";

export async function validatePublicBundle(bundleRoot, expectedName) {
  await Promise.all(
    REQUIRED_PATHS.map((entry) => access(path.join(bundleRoot, entry))),
  );

  const rootEntries = await readdir(bundleRoot);
  const forbiddenEntries = rootEntries.filter((entry) =>
    FORBIDDEN_ROOT_ENTRIES.has(entry),
  );
  assert(
    forbiddenEntries.length === 0,
    `Public bundle contains source-only root entries: ${forbiddenEntries.join(", ")}`,
  );

  const manifest = JSON.parse(
    await readFile(path.join(bundleRoot, ".codex-plugin/plugin.json"), "utf8"),
  );
  assert(manifest.name === expectedName, `Expected manifest name ${expectedName}`);
  assert(manifest.author?.name === "OpenAI", "Manifest must identify OpenAI as author");
  assert(manifest.homepage === "https://openai.com/", "Manifest homepage must be public");
  assert(
    manifest.repository === "https://github.com/openai/plugins",
    "Manifest repository must point at the public plugin repository",
  );
  assert(manifest.license === "MIT", "Manifest must declare the public license");
  assert(
    manifest.interface?.shortDescription === MARKETPLACE_SHORT_DESCRIPTION,
    `Manifest shortDescription must be: ${MARKETPLACE_SHORT_DESCRIPTION}`,
  );
  assert(
    manifest.interface?.privacyPolicyURL ===
      "https://openai.com/policies/privacy-policy/",
    "Manifest must use the public privacy policy",
  );
  assert(
    manifest.interface?.termsOfServiceURL ===
      "https://openai.com/policies/terms-of-use/",
    "Manifest must use the public terms of use",
  );
  const [viewerSkill, starterContractText, runbook, publicReadme] =
    await Promise.all([
      readFile(
        path.join(bundleRoot, "skills/biological-sequence-viewer/SKILL.md"),
        "utf8",
      ),
      readFile(path.join(bundleRoot, "starter-examples.json"), "utf8"),
      readFile(path.join(bundleRoot, "STARTER_EXAMPLES.md"), "utf8"),
      readFile(path.join(bundleRoot, "README.md"), "utf8"),
    ]);
  const starterContract = JSON.parse(starterContractText);
  validateSequenceViewerExamplePrompts(manifest, viewerSkill, starterContract, {
    publicReadme,
    runbook,
  });

  const mcpConfig = JSON.parse(
    await readFile(path.join(bundleRoot, ".mcp.json"), "utf8"),
  );
  const serverConfig = mcpConfig.mcpServers?.[expectedName];
  assert(serverConfig?.command === "node", "MCP server must use the Node runtime");
  assert(
    Array.isArray(serverConfig?.args) &&
      serverConfig.args.includes("./dist/server.mjs"),
    "MCP server must run the bundled server entrypoint",
  );

  for (const sourceOnlyReference of [
    "codex-plugin-skill-authoring",
    "pnpm install",
    "scripts/build.mjs",
    "src/",
  ]) {
    assert(
      !publicReadme.includes(sourceOnlyReference),
      `Public README contains source-only guidance: ${sourceOnlyReference}`,
    );
  }

  const [serverBundle, sequenceViewerSkill] = await Promise.all([
    readFile(path.join(bundleRoot, "dist/server.mjs"), "utf8"),
    readFile(
      path.join(
        bundleRoot,
        "skills/biological-sequence-viewer/SKILL.md",
      ),
      "utf8",
    ),
  ]);
  validateBundledStarterToolSurface(serverBundle, sequenceViewerSkill);
  assert(
    serverBundle.includes("uniprot-human-ras-sv1") &&
      serverBundle.includes("https://rest.uniprot.org/uniprotkb/") &&
      serverBundle.includes("builtin-center-star"),
    "Bundled server must include the bounded reviewed UniProt RAS alignment route",
  );
  for (const [label, contents] of [
    ["server", serverBundle],
    ["skill", sequenceViewerSkill],
    ["manifest", JSON.stringify(manifest)],
    ["README", publicReadme],
    ["starter contract", starterContractText],
    ["starter runbook", runbook],
  ]) {
    assert(
      !contents.includes(REMOVED_FASTQ_EXAMPLE_TOOL_NAME) &&
        !/bundled sample FASTQ/iu.test(contents),
      `Bundled ${label} still exposes the removed synthetic FASTQ starter`,
    );
  }

  const notices = await readFile(
    path.join(bundleRoot, "THIRD_PARTY_NOTICES.md"),
    "utf8",
  );
  assert(
    notices.startsWith("# Third-Party Notices"),
    "Third-party notices were not generated",
  );

  for (const filePath of await listFiles(bundleRoot)) {
    const contents = await readFile(filePath);
    for (const forbidden of FORBIDDEN_TEXT) {
      assert(
        !contents.includes(forbidden),
        `${path.relative(bundleRoot, filePath)} contains private or internal-only text: ${forbidden}`,
      );
    }
  }
}

export function validateBundledStarterToolSurface(
  serverBundle,
  sequenceViewerSkill,
) {
  assert(
    serverBundle.includes(PUBLIC_EXAMPLE_TOOL_NAME),
    "Bundled server must retain optional root-authorized public example acquisition",
  );
  assert(
    serverBundle.includes(CHAT_OPEN_TOOL_NAME),
    "Bundled server must expose absolute-path chat opening for Codex-acquired examples",
  );
  assert(
    sequenceViewerSkill.includes(CHAT_OPEN_TOOL_NAME),
    "Bundled skill must route Codex-acquired starters through chat opening",
  );
}

async function listFiles(root) {
  const files = [];
  for (const entry of await readdir(root, { withFileTypes: true })) {
    const entryPath = path.join(root, entry.name);
    if (entry.isDirectory()) {
      files.push(...(await listFiles(entryPath)));
      continue;
    }

    if (entry.isFile()) {
      files.push(entryPath);
    }
  }
  return files;
}

function assert(condition, message) {
  if (!condition) {
    throw new Error(message);
  }
}

const currentFilePath = fileURLToPath(import.meta.url);
if (
  process.argv[1] != null &&
  path.resolve(process.argv[1]) === currentFilePath
) {
  const bundleArgIndex = process.argv.indexOf("--bundle-dir");
  const pluginArgIndex = process.argv.indexOf("--plugin");
  const bundleRoot = process.argv[bundleArgIndex + 1];
  const expectedName = process.argv[pluginArgIndex + 1];
  if (
    bundleArgIndex === -1 ||
    pluginArgIndex === -1 ||
    bundleRoot == null ||
    expectedName == null
  ) {
    throw new Error(
      "Usage: node validate-public-bundle.mjs --bundle-dir <path> --plugin <name>",
    );
  }
  await validatePublicBundle(bundleRoot, expectedName);
}

SHA-256: 95ed660bb995c41178fffe90be844a261e594e74e967860315d0846b70ef9e6e