From b691288c9b7c887a2201b891f7ebb56039958455 Mon Sep 17 00:00:00 2001 From: stefan judis Date: Tue, 20 Oct 2026 15:23:12 +0200 Subject: [PATCH 1/3] fix: bring Codex listing in line with OpenAI submission limits - square PNG logo for composerIcon and logo (SVG was 64x67) - shortDescription under the 30 character final-submission limit - add supportURL, required for remote MCP plugins under public review - point privacy and terms at their final trailing-slash URLs - expand longDescription with tasks, intended users and limitations - add a test guarding these limits --- .codex-plugin/plugin.json | 13 +++++----- assets/checkly-logo.png | Bin 0 -> 7786 bytes test/codex-manifest.test.ts | 49 ++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 assets/checkly-logo.png create mode 100644 test/codex-manifest.test.ts diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 90dc86a..c967da8 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -23,21 +23,22 @@ "apps": "./.app.json", "interface": { "displayName": "Checkly", - "shortDescription": "Create and manage Checkly monitoring from Codex.", - "longDescription": "Use Checkly's agent skill to initialize monitoring-as-code projects, create API and browser checks, investigate failures, and manage account features with the Checkly CLI.", + "shortDescription": "Synthetic monitoring as code", + "longDescription": "Checkly is synthetic monitoring as code: API checks, browser checks, URL and uptime monitors, and Playwright Check Suites that run from global locations and alert you when something breaks.\n\nThis plugin bundles two skills and the Checkly MCP server. The skills teach Codex how to scaffold monitoring-as-code projects with the Checkly CLI, write API and browser checks, and apply Playwright best practices for stable end-to-end tests. The MCP server connects to your Checkly account so Codex can list and inspect checks, read check results and test sessions, trigger checks, run root cause analyses, and manage API checks, monitors, check groups, and status page incidents.\n\nIntended for developers and SREs who already use Checkly or want to add monitoring to a project without leaving their editor. A Checkly account is required; the MCP server authenticates with OAuth.\n\nLimitations: Playwright Check Suites and checks whose code lives in a repository are managed with the Checkly CLI, not through MCP tools. Environment variables can be read but not created or updated from this plugin.", "developerName": "Checkly", "category": "Developer Tools", "capabilities": ["Instructions", "Monitoring", "CLI workflows"], "websiteURL": "https://www.checklyhq.com", - "privacyPolicyURL": "https://www.checklyhq.com/privacy", - "termsOfServiceURL": "https://www.checklyhq.com/terms", + "supportURL": "https://www.checklyhq.com/support/", + "privacyPolicyURL": "https://www.checklyhq.com/privacy/", + "termsOfServiceURL": "https://www.checklyhq.com/terms/", "defaultPrompt": [ "Create a Checkly browser check for this flow.", "Add API monitoring for this endpoint.", "Investigate failing Checkly checks." ], "brandColor": "#0075FF", - "composerIcon": "./assets/checkly-logo.svg", - "logo": "./assets/checkly-logo.svg" + "composerIcon": "./assets/checkly-logo.png", + "logo": "./assets/checkly-logo.png" } } diff --git a/assets/checkly-logo.png b/assets/checkly-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..23a1e97628b0856106b4693dab873e6536833850 GIT binary patch literal 7786 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 { + const { interface: ui } = await readManifest(); + + assert.ok(ui.displayName.length <= 30, "displayName must be at most 30 characters"); + assert.ok(ui.shortDescription.length <= 30, "shortDescription must be at most 30 characters"); + assert.ok(ui.longDescription.length <= 4000, "longDescription must be at most 4000 characters"); + assert.ok(ui.developerName.length <= 80, "developerName must be at most 80 characters"); + assert.ok(ui.capabilities.length <= 20, "at most 20 capabilities"); + assert.ok(ui.defaultPrompt.length <= 3, "at most 3 starter prompts"); + for (const prompt of ui.defaultPrompt) { + assert.ok(prompt.length <= 128, `starter prompt too long: ${prompt}`); + } +}); + +test("Codex listing declares every URL OpenAI requires for a remote MCP plugin", async () => { + const { interface: ui } = await readManifest(); + + for (const key of ["websiteURL", "supportURL", "privacyPolicyURL", "termsOfServiceURL"]) { + assert.match(ui[key] ?? "", /^https:\/\//, `${key} must be an https URL`); + } +}); + +test("Codex icon and logo point at an existing square PNG", async () => { + const { interface: ui } = await readManifest(); + + for (const key of ["composerIcon", "logo"]) { + assert.match(ui[key], /^\.\/assets\/.+\.png$/, `${key} must be a ./assets PNG`); + const { width, height } = pngDimensions(await readFile(join(ROOT, ui[key]))); + assert.equal(width, height, `${key} must be square`); + assert.ok(width >= 48, `${key} must be at least 48px`); + } +}); From 7d438fdf161650531a762779a630ede83c1a3e92 Mon Sep 17 00:00:00 2001 From: stefan judis Date: Tue, 20 Oct 2026 15:24:21 +0200 Subject: [PATCH 2/3] feat: add npm run package to build the OpenAI submission ZIP Builds dist/checkly-plugin-.zip from git-tracked files only, so .env, node_modules, tests, CI and other agents' manifests never ship. --- .gitignore | 1 + README.md | 9 +++++ package.json | 1 + scripts/package.ts | 83 ++++++++++++++++++++++++++++++++++++++++++++ test/package.test.ts | 79 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 scripts/package.ts create mode 100644 test/package.test.ts diff --git a/.gitignore b/.gitignore index 22dc3af..aefb8c0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules/ *.log .env .claude/ +dist/ diff --git a/README.md b/README.md index 5441bf0..5e56e61 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Checkly skills for AI coding agents packaged as a single plugin that targets Cod skills/ Skills (one directory per skill) .mcp.json Checkly MCP server configuration scripts/sync.ts Pulls externally-authored skills from their source repos +scripts/package.ts Builds the ZIP for OpenAI plugin submission skills.config.ts Declares where each externally-authored skill comes from ``` @@ -54,6 +55,14 @@ Point Cursor at this repo to install the plugin. | [checkly](skills/checkly/) | [checkly/checkly-cli](https://github.com/checkly/checkly-cli/tree/main/skills/checkly) — tracks `main`, synced daily | | [playwright-best-practices-for-agents](skills/playwright-best-practices-for-agents/) | [checkly/docs](https://github.com/checkly/docs/tree/main/skills/playwright-best-practices-for-agents) — tracks `main`, synced daily | +## Packaging for OpenAI submission + +``` +npm run package +``` + +Writes `dist/checkly-plugin-.zip` containing only the files the Codex plugin needs: the Codex manifest, `.mcp.json`, `.app.json`, `skills/`, `assets/`, `LICENSE`, and `README.md`. The file list comes from `git ls-files`, so untracked files such as `.env` never end up in the archive. Upload the ZIP at the [OpenAI plugin dashboard](https://developers.openai.com/plugins/deploy/submission). + ## Adding more - **Externally-authored** (lives in another repo, synced here): add an entry to [`skills.config.ts`](skills.config.ts) and run `npm run sync`. diff --git a/package.json b/package.json index b796479..12775e5 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "type": "module", "scripts": { "sync": "node scripts/sync.ts", + "package": "node scripts/package.ts", "test": "node --test test/*.test.ts", "typecheck": "tsc --noEmit", "lint": "oxlint", diff --git a/scripts/package.ts b/scripts/package.ts new file mode 100644 index 0000000..ee47212 --- /dev/null +++ b/scripts/package.ts @@ -0,0 +1,83 @@ +import { execFile } from "node:child_process"; +import { mkdir, readFile, rm } from "node:fs/promises"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import { parseArgs, promisify } from "node:util"; + +const REPO_ROOT = resolve(fileURLToPath(import.meta.url), "..", ".."); +const run = promisify(execFile); + +/** + * Files that go into the Codex plugin ZIP. Everything else in the repo is + * tooling (sync script, tests, CI, other agents' manifests) and must not ship. + * + * OpenAI requires the ZIP to contain a single plugin root with no secrets, so + * the list is built from `git ls-files` and filtered here: untracked files + * such as `.env` or `node_modules/` can never slip in. + */ +const PACKAGE_FILES = new Set([ + ".codex-plugin/plugin.json", + ".mcp.json", + ".app.json", + "LICENSE", + "README.md", +]); +const PACKAGE_DIRS = ["assets/", "skills/"]; + +export function selectPackageFiles(trackedFiles: string[]): string[] { + return trackedFiles.filter( + (file) => PACKAGE_FILES.has(file) || PACKAGE_DIRS.some((dir) => file.startsWith(dir)), + ); +} + +async function listTrackedFiles(root: string): Promise { + const { stdout } = await run("git", ["-C", root, "ls-files", "-z"]); + return stdout.split("\0").filter(Boolean); +} + +/** + * Build the plugin ZIP at `outFile` from the tracked files under `root`. + * Returns the list of files that were packaged. + */ +export async function createPackage({ + root, + outFile, +}: { + root: string; + outFile: string; +}): Promise { + const files = selectPackageFiles(await listTrackedFiles(root)); + await mkdir(dirname(outFile), { recursive: true }); + await rm(outFile, { force: true }); + // -X drops extra file attributes so the archive is reproducible across machines. + await run("zip", ["-X", "-q", outFile, ...files], { cwd: root }); + return files; +} + +async function main() { + const { values } = parseArgs({ + options: { + out: { type: "string" }, + }, + }); + const manifest = JSON.parse( + await readFile(join(REPO_ROOT, ".codex-plugin", "plugin.json"), "utf8"), + ); + const outFile = resolve( + REPO_ROOT, + values.out ?? join("dist", `${manifest.name}-plugin-${manifest.version}.zip`), + ); + const files = await createPackage({ root: REPO_ROOT, outFile }); + for (const file of files) { + console.log(`packaged ${file}`); + } + console.log(`\n${files.length} file(s) written to ${outFile}`); +} + +const isMain = import.meta.url === pathToFileURL(process.argv[1] ?? "").href; +if (isMain) { + main().catch((err) => { + console.error(err); + process.exit(1); + }); +} diff --git a/test/package.test.ts b/test/package.test.ts new file mode 100644 index 0000000..71c6f62 --- /dev/null +++ b/test/package.test.ts @@ -0,0 +1,79 @@ +import { strict as assert } from "node:assert"; +import { execFile } from "node:child_process"; +import { mkdtemp, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, beforeEach, describe, test } from "node:test"; +import { promisify } from "node:util"; + +import { createPackage, selectPackageFiles } from "../scripts/package.ts"; + +const run = promisify(execFile); +const ROOT = join(import.meta.dirname, ".."); + +describe("selectPackageFiles", () => { + test("keeps only what the Codex plugin needs at runtime", () => { + const tracked = [ + ".codex-plugin/plugin.json", + ".mcp.json", + ".app.json", + "LICENSE", + "README.md", + "assets/checkly-logo.png", + "skills/checkly/SKILL.md", + "skills/playwright-best-practices-for-agents/references/auth.md", + // dev-only files that must never ship + ".env", + ".gitignore", + ".github/workflows/verify.yml", + ".claude-plugin/plugin.json", + ".cursor-plugin/plugin.json", + ".plugin/plugin.json", + "package.json", + "package-lock.json", + "scripts/sync.ts", + "skills.config.ts", + "test/sync.test.ts", + "tsconfig.json", + ]; + + assert.deepEqual(selectPackageFiles(tracked), [ + ".codex-plugin/plugin.json", + ".mcp.json", + ".app.json", + "LICENSE", + "README.md", + "assets/checkly-logo.png", + "skills/checkly/SKILL.md", + "skills/playwright-best-practices-for-agents/references/auth.md", + ]); + }); +}); + +describe("createPackage", () => { + let dir: string; + beforeEach(async () => { + dir = await mkdtemp(join(tmpdir(), "checkly-plugin-package-")); + }); + afterEach(async () => { + await rm(dir, { recursive: true, force: true }); + }); + + test("zips the tracked plugin files from the repo root", async () => { + const outFile = join(dir, "plugin.zip"); + const files = await createPackage({ root: ROOT, outFile }); + + const { stdout } = await run("unzip", ["-Z1", outFile]); + const entries = stdout.trim().split("\n").sort(); + + assert.deepEqual(entries, [...files].sort()); + assert.ok(entries.includes(".codex-plugin/plugin.json")); + assert.ok(entries.includes(".mcp.json")); + assert.ok(entries.includes("assets/checkly-logo.png")); + assert.ok(entries.includes("skills/checkly/SKILL.md")); + assert.ok( + !entries.some((e) => e === ".env" || e.startsWith("test/") || e.startsWith("node_modules/")), + ); + assert.ok(!entries.includes("package.json")); + }); +}); From 6e8748eb9973eaca15bafe0dbc39881a0a760a7d Mon Sep 17 00:00:00 2001 From: stefan judis Date: Tue, 20 Oct 2026 15:33:35 +0200 Subject: [PATCH 3/3] chore: remove unused SVG logo --- assets/checkly-logo.svg | 1 - 1 file changed, 1 deletion(-) delete mode 100644 assets/checkly-logo.svg diff --git a/assets/checkly-logo.svg b/assets/checkly-logo.svg deleted file mode 100644 index 27773cf..0000000 --- a/assets/checkly-logo.svg +++ /dev/null @@ -1 +0,0 @@ -