Build for Dunea

Manifest reference

Every field of a plugin, theme, template or skill manifest, with the limits the marketplace enforces.

A manifest is a JSON object that describes your listing. The marketplace validates every submitted manifest with the schemas exported by @dunea/plugin-sdk (pluginManifestSchema, themeManifestSchema, templateManifestSchema, skillManifestSchema), and validateManifest() runs exactly the same checks locally.

Keep the manifest in dunea.plugin.json (or dunea.theme.json, dunea.template.json, dunea.skill.json) at the root of your project. The dunea-plugin CLI looks for those names, and also accepts a .js or .cjs file that exports the manifest.

dunea.plugin.json
{
  "schemaVersion": 1,
  "kind": "plugin",
  "id": "order-alerts",
  "version": "1.0.0",
  "...": "the rest of the fields below"
}

If you prefer TypeScript, the typed helpers definePlugin(), defineTheme(), defineTemplate() and defineSkill() fill in schemaVersion and kind and give you autocomplete. The CLI does not read .ts files, so compile the manifest to .js, or write it out as JSON:

manifest.ts
import { writeFileSync } from 'node:fs';
import { definePlugin } from '@dunea/plugin-sdk';

const manifest = definePlugin({
  id: 'order-alerts',
  version: '1.0.0',
  // ...the rest of the fields below
});

writeFileSync('dunea.plugin.json', `${JSON.stringify(manifest, null, 2)}\n`);

Shared fields

Every kind of listing has these fields.

FieldTypeRequiredConstraints
schemaVersionnumberYesMust be 1 (MANIFEST_SCHEMA_VERSION). Set for you by definePlugin / defineTheme.
kindstringYes"plugin", "theme", "template" or "skill". Set for you by the helpers.
idstringYesLowercase letters, digits and dashes, starting with a letter, 3 to 41 characters (^[a-z][a-z0-9-]{2,40}$).
versionstringYesSemver x.y.z, digits only (1.4.0). No pre-release or build suffixes.
namestringYes2 to 60 characters.
taglinestringYes10 to 120 characters. One line shown on listing cards.
descriptionstringYes40 to 4,000 characters, plain text. Shown on the listing page; blank lines start a new paragraph.
iconstringYesAn https:// URL, at most 500 characters.
screenshotsstring[]NoUp to 8 https:// URLs. Defaults to [].
developerobjectYesSee developer.
pricingobjectYesSee pricing.
categoriesstring[]Yes1 to 3 values. The allowed list differs by kind, see below.

Every URL field must start with https:// and be at most 500 characters.

developer

FieldTypeRequiredConstraints
namestringYes2 to 80 characters.
websitestringNohttps:// URL.
supportEmailstringYesA valid email address. Shown to owners on your listing.
privacyUrlstringYeshttps:// URL of your privacy policy.

pricing

pricing is one of three shapes, chosen by model. Amounts are in US cents.

{ "model": "free" }
{ "model": "one_time", "amount": 1900, "currency": "USD" }
{ "model": "monthly", "amount": 900, "currency": "USD", "trialDays": 14 }
FieldApplies toConstraints
modelall"free", "one_time" or "monthly".
amountone_time, monthlyInteger, 100 to 99,900 (that is, $1 to $999).
currencyone_time, monthlyMust be "USD".
trialDaysmonthlyOptional integer, 0 to 30.

Templates are free or one_time only. See Pricing and payouts for how paid listings are billed.

Plugin fields

FieldTypeRequiredConstraints
kindstringYes"plugin"
categoriesstring[]Yes1 to 3 of: marketing, sales, shipping, payments, analytics, customer-support, content, design, productivity, ai, integrations.
permissionsstring[]YesPermission IDs from the permissions table. Can be empty.
subscribesstring[]NoEvent IDs from the events table. Defaults to [].
webhookUrlstringConditionalhttps:// URL where Dunea sends signed events and tool calls. Required if subscribes or agentTools is not empty.
panelobjectNo{ url, title }: url is an https:// URL, title is 2 to 40 characters. See Workbench panels.
agentToolsobject[]NoUp to 10 tools. Defaults to []. See agentTools.
usageobjectNoMetered usage your service reports. See usage.
siteScriptsobject[]NoUp to 3 scripts. Defaults to []. See siteScripts.
settingsobject[]NoUp to 20 fields. Defaults to []. See settings.

agentTools

Each entry is a tool the Dunea AI can call. See AI tools.

FieldTypeConstraints
namestringLowercase letters, digits and underscores, starting with a letter, 3 to 41 characters (^[a-z][a-z0-9_]{2,40}$). Unique within the manifest.
descriptionstring20 to 600 characters.
inputobjectSee below.
priceobjectOptional. { credits }: what each successful call costs the owner, 0.05 to 50 credits with at most two decimals. Omit for a free tool. See Priced tools and metered usage.

input describes the tool's arguments:

FieldTypeConstraints
input.typestringMust be "object".
input.propertiesobjectKeys match ^[a-zA-Z][a-zA-Z0-9_]{0,40}$. Each value is { type, description, enum? }.
input.properties.*.typestring"string", "number", "integer" or "boolean". Nested objects and arrays are not supported.
input.properties.*.descriptionstringAt most 300 characters.
input.properties.*.enumstring[]Optional. Up to 30 values, each at most 80 characters.
input.requiredstring[]Optional. Up to 20 names, each of which must be a key in properties.

usage

Optional. Declares metered usage your service reports with POST /usage, charged to the owner's credits within their monthly cap.

FieldTypeConstraints
unitstring2 to 40 characters, e.g. "label".
maxCreditsPerUnitnumberThe most you may charge per unit: 0.05 to 50 credits, at most two decimals.
descriptionstring20 to 300 characters. Shown to the owner at install.
suggestedMonthlyCapCreditsnumber1 to 10,000. Defaults to 50. The cap Dunea suggests at install.

siteScripts

FieldTypeConstraints
srcstringhttps:// URL of the script.
consentstring"necessary", "analytics" or "marketing".
placementstring"head" or "body". Defaults to "body".

See Site scripts and consent.

settings

Fields the owner fills in when they install your plugin. Dunea renders the form and sends you the values.

FieldTypeConstraints
keystringStarts with a letter, then letters, digits or underscores, at most 41 characters (^[a-zA-Z][a-zA-Z0-9_]{0,40}$).
labelstring1 to 80 characters.
typestring"text", "secret", "select", "boolean", "number", "email" or "url".
helpstringOptional, at most 300 characters.
placeholderstringOptional, at most 120 characters.
requiredbooleanOptional.
optionsobject[]Optional. Up to 50 { value, label } pairs, each at most 80 characters. Use with select.

Values arrive as settings in the plugin.installed event, and you can read the current values any time with GET /settings. Owners can change them later; subscribe to plugin.settings.updated to hear when they do.

Cross-field rules

The validator also checks these rules across fields:

  • Every event in subscribes needs the permissions listed for it in the events table. For example, shop.order.paid needs orders:read.
  • webhookUrl is required if subscribes or agentTools is not empty.
  • siteScripts requires the site:scripts permission.
  • Tool names in agentTools must be unique.
  • Every name in a tool's input.required must exist in its input.properties.

Theme fields

FieldTypeRequiredConstraints
kindstringYes"theme"
categoriesstring[]Yes1 to 3 of: minimal, bold, editorial, playful, luxury, tech, retail, portfolio, local-business.
tokensobjectYesSee tokens.
guidestringYesMarkdown, 200 to 12,000 characters. Art direction for the Dunea AI.
sectionsobject[]NoUp to 20 { name, recipe }: name is 2 to 60 characters, recipe is markdown of 40 to 3,000 characters. Defaults to [].

tokens

FieldTypeConstraints
colorsobjectAll of background, surface, text, muted, primary, primaryText, accent, border. Each a #rrggbb hex colour.
darkColorsobjectOptional. The same eight keys, for a dark mode. Omit for a single-mode theme.
fonts.headingstringA family from the font allow-list.
fonts.bodystringA family from the font allow-list.
fonts.monostringOptional. A family from the font allow-list.
radiusnumberCorner radius in px, integer 0 to 32. 0 is sharp.
densitystring"compact", "comfortable" or "airy".
typeScalenumberHeading scale ratio, 1.1 to 1.6 (for example 1.25).

See the Themes guide for how to use each one.

Template fields

See Templates for the snapshot rules and the remix flow.

FieldTypeRequiredConstraints
kindstringYes"template"
categoriesstring[]Yes1 to 3 of: restaurant, retail, salon-beauty, health, education, professional-services, trades, events, portfolio, agency, community, saas.
sourceProjectIdstringYesUUID of the Dunea project to snapshot. It must be yours.
previewUrlstringYeshttps:// URL of a live demo.
pagesstring[]Yes1 to 30 page names, each 1 to 40 characters.
themestringNoSlug of a theme listing it's designed with.
recommendedPluginsstring[]NoUp to 10 plugin slugs. Defaults to []. Suggested after a remix, never installed automatically.
firstPromptstringNo10 to 300 characters. What the owner should tell the AI first.

Skill fields

See Skills for how the AI uses them.

FieldTypeRequiredConstraints
kindstringYes"skill"
categoriesstring[]Yes1 to 3 of: copywriting, seo, design, ecommerce, marketing, industry, localization, accessibility, conversion.
instructionsstringYesMarkdown, 200 to 20,000 characters. What the AI follows.
triggersstring[]Yes1 to 12 phrases, each 3 to 80 characters. When the AI should apply the skill.
recommendedThemesstring[]NoUp to 5 theme slugs. Defaults to [].
recommendedPluginsstring[]NoUp to 5 plugin slugs. Defaults to [].

Validate locally

import { readFileSync } from 'node:fs';
import { validateManifest } from '@dunea/plugin-sdk';

const manifest: unknown = JSON.parse(readFileSync('dunea.plugin.json', 'utf8'));
const result = validateManifest(manifest);

if (!result.ok) {
  // e.g. "subscribes: \"shop.order.paid\" needs the \"orders:read\" permission"
  console.error(result.errors.join('\n'));
  process.exit(1);
}

Each error is path: message. dunea-plugin validate runs the same check from the command line, and dunea-plugin check adds the rest of the review checklist (screenshots, descriptions, prices, contrast, skill instructions) with a rule id for each finding.

On this page