Skip to content

feat(plugin): validate and pack the chatgpt upload package - #229

Open
Jerod Santo (jerodsanto) wants to merge 3 commits into
mainfrom
feat/chatgpt-plugin-pack
Open

Jerod Santo (jerodsanto) wants to merge 3 commits into
mainfrom
feat/chatgpt-plugin-pack

Conversation

@jerodsanto

@jerodsanto Jerod Santo (jerodsanto) commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

plugins/chatgpt/ had no repeatable way to check or build its upload archive - step 2 of the release checklist was "build the ZIP" by hand. This adds pnpm run plugin:chatgpt:pack, which checks the package against OpenAI's submission requirements and writes the archive.

Developer tooling only. No runtime path changes and the published package is untouched.

What it checks

The agent-plugins.org schemas type extensions as a bare {"type": "object"}, so nothing inside com.openai - the block submission review actually reads - is schema-checked. These rules cover that gap:

Area Checked
Listing metadata displayName 30, shortDescription 30, longDescription 4000, developerName 80, capabilities 20 items of 120
URLs websiteURL, supportURL, privacyPolicyURL, termsOfServiceURL present, HTTPS, under 1024
Review cases exactly 5 positive with all four fields, exactly 3 negative
Icon PNG, square, 48 to 4096 pixels, under 5 MiB
MCP config streamable-HTTP or SSE over HTTPS, and no headers block, which is where a credential would ride along into the upload
Errors block the ZIP, warnings do not

demo_recording_url is required for submission but is a warning here, because recording the walkthrough requires first installing this very ZIP in developer mode. Making it an error would deadlock the process. --strict promotes warnings to errors for the pre-submission gate.

Layout

requirements.mts holds the rules as pure functions over the package's in-memory contents; pack.mts is the IO shell and CLI. The split keeps the rules unit-testable without a fixture directory on disk.

The archive uses the fleet's existing dep-zero ZIP writer - no new dependency - and lands in the OS temp directory so a build artifact never enters the tracked tree. --out <dir> overrides the destination.

Validation

Ran: 19 tests in test/repo/unit/plugin-chatgpt-requirements.test.mts. They assert the shipped package passes with only the expected demo_recording_url warning, that each rule rejects a correspondingly broken manifest, and that the archive round-trips its three entries byte-identical at the root.

Both manifests were also validated against the live agent-plugins.org/schemas/1.0.0/* schemas, and the validator itself was checked against deliberately broken inputs so a pass means something.

tsc --noEmit reports 0 errors repo-wide; oxlint is clean on every file touched.

Known gap, tracked separately: the archive this writes is rejected by the ChatGPT upload dialog. createZipArchive emits a zeroed DOS timestamp (1980-00-00) and zero file-mode bits, which some extractors map to mode 0000. A conventionally-built archive with the same bytes is under test to confirm the cause before this script's writer is changed.


Note

Low Risk
Release-time developer tooling and docs only; no MCP server or published npm runtime changes.

Overview
Adds pnpm run plugin:chatgpt:pack so the ChatGPT plugin upload archive is validated and built repeatably instead of hand-zipping plugins/chatgpt/.

The CLI reads plugin.json, mcp.json, and assets/icon.png, runs OpenAI submission checks (listing limits, HTTPS URLs, 5/3 review cases, PNG icon rules, MCP over HTTPS with no headers), then writes a versioned ZIP to the OS temp dir by default (--out, --strict, --json). Errors block the ZIP; demo_recording_url is a warning until --strict because the demo needs the ZIP installed first.

Rules live in testable requirements.mts; pack.mts handles IO. Release docs step 2 now points at this script. PLUGIN_CHATGPT_DIR is added to repo paths. Unit tests cover the shipped package and each gate.

Reviewed by Cursor Bugbot for commit 30a7a3d. Configure here.

Encode the OpenAI plugin submission requirements as pure checks over the
package contents, and build the upload ZIP from plugins/chatgpt. The
archive is written outside the repository, with plugin.json, mcp.json, and
assets/icon.png at its root.

The rules and the IO are separate modules so the requirements stay
unit-testable without a fixture directory.

An error blocks the ZIP and a warning does not. demo_recording_url is a
warning because recording the walkthrough requires first installing the
ZIP in developer mode; --strict promotes warnings for the pre-submission
gate.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix is ON, but none of the reported issues met your autofix severity threshold.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 30a7a3d. Configure here.

Comment thread scripts/repo/plugin/chatgpt/pack.mts Outdated
Comment thread scripts/repo/plugin/chatgpt/requirements.mts
… document

SCRIPT_META declares json: 'result', so the fleet runner already renders a
JSON document from what main() returns. Printing one as well put two
documents on stdout and left --json unparseable.

Carry the findings and output path on ScriptResult.data, which puts them at
.data in the runner's single document.
A presence check that accepts '' let a blank demo_recording_url skip its
warning, and blank review-case fields, capability entries, and category
values pass as set, so --strict could pass a package the portal rejects.

stringField now reads an empty string as absent, matching how envString
treats an empty environment variable, and a blank capability entry is
rejected explicitly.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant