Skip to content

Marketplace extension

Publishing puts your extension in the catalog every WAMP install browses, under a slug you own from then on. This page covers what has to be true of your manifest first, what actually gets uploaded, and the parts of the model that surprise people — chiefly that catalog scoping is curation and not access control.

Three manifest facts are checked, and two of them are checked only by the catalog, so an extension that runs on your machine can still be rejected.

Requirement Where it is enforced
engines.wamp — a semver range such as "^12.0.0" required by the catalog; optional to the host
version — exactly MAJOR.MINOR.PATCH required by the catalog
The slug is your extension’s id — its directory name, lowercase kebab-case the id is the address; the manifest has no separate slug field

engines.wamp is worth being precise about, because its name suggests more than it does. The catalog stores it and the listing displays it as Requires Wamp <range>, and that is all it does — no host compares its own version against that range at install time. The range that is enforced is compat.pluginApi: an extension whose declared plugin-API range does not match the host refuses to activate, with the mismatch reported as the reason. Declare engines.wamp because the upload demands it; declare compat.pluginApi if you actually need a floor. Both are described in The manifest.

Everything else the listing shows comes from fields you already have, with defaults where you left them out: name (required), description (empty), author (Wamp Team), icon (a Lucide icon name, Package), plus counts derived from your contributes block. longDescription is optional Markdown and becomes the overview on the detail page.

This is the path that needs nothing but the app you already have.

  1. Sign in. The listing is owned by your account — that is what lets you update or withdraw it later, so publishing without a session is refused.
  2. Open the Marketplace view and find your app among your own.
  3. From its menu choose Publish. The host zips the app, uploads it, and the listing is live immediately.

Publishing again is how you ship an update: the catalog keeps one listing per slug and replaces its contents. Withdraw takes the listing out of the catalog without deleting it, so people who already installed the app keep their copy and keep getting update checks.

The name is the address. Once your account has published a slug, nobody else can publish under it, and there is no way to hand it over. If you intend to rename the app, rename it before the first publish.

The package is not your working directory. One staging step decides its shape, and the same shape is what a branded product bundles, so an extension has one published form rather than one per destination.

An extension is classified as editable if it has no build script of its own and does have source (a page entry point or a main-process entry point). Editable apps ship their sourceui/, main/, shared/, tsconfig.json, and CLAUDE.md / AGENTS.md if present — with no dist/, because the host rebuilds them from source with its own bundler, and because a recipient can then open and fork the app. Their runtime dependencies are vendored into node_modules/ so that rebuild needs no npm on the user’s machine.

Everything else is sealed and ships dist/ only — the built bundles are the only JavaScript that runs.

Both forms additionally carry extension.json, a sanitized package.json with scripts and devDependencies stripped, icons/, assets/, and the declarative content per-component loaders read: .md and .json files under agents/, skills/, commands/, prompts/, hooks/, and mcp/. Nothing else is included. If a file you need at runtime is not in that list, it will not be there after install.

The in-app publish is one client of a plain HTTP endpoint. Use the endpoint directly if you are automating releases.

Terminal window
curl -X POST "https://api.vampikez.fun/marketplace/extensions/reading-list/upload" \
-H "Authorization: Bearer $WAMP_TOKEN" \
-F "version=1.2.0" \
-F "file=@reading-list.zip;type=application/zip"

version must precede file. The storage key is chosen while the bytes are still streaming — before anything can read the manifest inside the zip — so a version field that arrives after the file is not seen, and the package lands under 1.0.0.

Authorization: Bearer takes a signed-in WAMP account token; that account claims the slug on first publish and owns it afterwards. The zip must contain extension.json at its root, and must be under 500 MB.

Response Meaning
200 with { success: true, item } published; the catalog cache is invalidated, so the listing is visible at once
400 ZIP must contain extension.json at root you zipped the parent directory
400 Manifest failed validation: … a schema failure, most often a missing engines.wamp or a non-X.Y.Z version
401 Sign in to publish an extension no credential
403 The slug "…" belongs to someone else the slug is taken

Re-uploading the same version is allowed and overwrites the stored package. The catalog keeps no version history — one row per slug, one version column — so there is nothing to roll back to. Keep your own build artifacts.

contributes and products do less than they look like

Section titled “contributes and products do less than they look like”

contributes is your declaration of what the extension adds — views, pages, agents, tools, MCP servers, project templates, and the rest. For the catalog it does two things: the counts drive the badge and the “primary contribution” facet, and the full manifest is served on the detail page so a visitor can see what they are installing. It grants nothing. What each contribution means at runtime is the host’s business, described in Extensions and apps.

One divergence to know about: the catalog accepts and counts a contributes.tools array, and the host ignores it. Tools are registered in code at activation — see Contributing tools — so declaring them in the manifest changes the badge and nothing else.

products is an array of product slugs, and it scopes which catalogs list your extension. Absent or empty means every catalog lists it; ["forge"] means only a product whose id is forge shows it while browsing.

That is curation, not access control. The scope is applied to one query — the browse listing — and only when the request names a product. The detail route, the manifest route, and the download route consult products nowhere, and the download route does not even require a signed-in account. Anyone who knows the slug can fetch and install a listing that no catalog shows them. Do not use products to keep an extension private.

Honest state, because it decides whether the catalog fits your plans:

  • Ownership is first-come. The first account to publish a slug owns it. There is no publisher namespace — listings are reading-list, not you.reading-list — so the global slug space is the only namespace, and renaming means claiming a second slug.
  • The operator can publish anything. A build-time admin token bypasses ownership; that is how the curated first-party catalog is maintained.
  • There is no review queue and no signing. A published listing is live immediately, and what a user installs is whatever bytes you last uploaded.
  • There is no transfer, and no per-listing collaborator. One account, one slug, forever.

Users find your extension in the Marketplace view inside WAMP and install it there; the host fetches a presigned download of the package and installs it the same way it installs a sideloaded extension. Your listing carries the permissions your manifest declared, and the user sees them before installing — which is the main reason to declare no more than you use. See Permissions.

If your extension turns out to want its own window, its own name, and its own installer rather than a place inside WAMP, that is the next page: Branded desktop product.