Guide · Debug & distribute

Debug & distribute

The last mile: iterate fast, ship without a marketplace, keep the trust model honest.

Debugging

Petty logs all plugin output to stderr with the prefix [Plugin <id>]. When developing:

  1. Launch Petty from the terminal: ./start.sh or .build/debug/Petty.
  2. Watch the terminal for your plugin's output.
  3. Use petty.log(message) — or petty.log.info/warn/error for level-tagged structured logs — to print from JavaScript.
  4. JS runtime errors are caught and logged automatically with full stack traces.

Common issues

  • Plugin doesn't load. Check that manifest.id matches the folder name and that the script path is valid.
  • petty.xxx is undefined. You didn't declare the required permission in the manifest.
  • Exec returns “not in allowlist”. The command isn't in your manifest's exec.allowlist.
  • Fetch returns null. URL uses a non-HTTP scheme, or the request failed.

Distribution

Plugins are just folders. To distribute one:

  1. Zip your plugin folder: zip -r my-plugin.zip my-plugin/
  2. Host the zip anywhere with HTTPS (GitHub Releases, your own site, etc.).
  3. Link users to petty://install?url=<url-encoded-https-url-to-zip>. Petty downloads the zip, shows a confirmation dialog listing your plugin's declared permissions, and — on approval — drops it into ~/Library/Application Support/Petty/Plugins/ and reloads. No restart required.
  4. Fallback: users can always download the zip and extract it into ~/Library/Application Support/Petty/Plugins/ by hand.

The installer enforces an HTTPS origin, a 10 MB download cap, and a 50 MB extracted cap. Archives with symlinks or without a manifest.json at the root are rejected.

Plugin packs (collections)

A pack is a JSON manifest that lists several plugins so a user can install a curated setup with one click. Host the JSON anywhere with HTTPS (a gist works) and link users to petty://install-pack?url=<url-encoded-https-url-to-json>.

Pack manifest shape (the URLs below are illustrative — swap in real HTTPS-hosted zips):

{
  "id": "focus-mode",
  "name": "Focus Mode",
  "description": "Pomodoro + git watcher + ElevenLabs voice.",
  "author": "you",
  "version": "1.0.0",
  "plugins": [
    {
      "url": "https://example.com/builtin-pomodoro.zip",
      "settings": { "focusMinutes": 25, "breakMinutes": 5 }
    },
    { "url": "https://example.com/builtin-git.zip" },
    {
      "url": "https://example.com/elevenlabs-tts.zip",
      "requiredSecrets": [
        {
          "key": "apiKey",
          "label": "ElevenLabs API key",
          "description": "Get one at elevenlabs.io"
        }
      ]
    }
  ]
}

Notes on each field:

  • plugins[].url — same kind of HTTPS plugin zip the single-install URL accepts.
  • plugins[].settings — optional preset values, applied after install. Filtered against the plugin's own settings schema: unknown keys and type-mismatched values are silently dropped. Don't put secrets here — packs are public, and the value is plain JSON.
  • plugins[].requiredSecrets — hints for fields the user must fill in themselves (API keys, tokens). After install, Petty surfaces an alert listing every required secret across the pack with an “Open Settings” shortcut.

Installs are all-or-nothing: every listed plugin must download and validate before the single combined permission dialog appears. Existing plugins that share an ID are backed up before commit and restored on any failure. Pack manifests are capped at 256 KB and 32 plugins.

The marketplace

Petty's own marketplace page surfaces three layers: curated collections at the top (one-click multi-plugin packs that fire the petty://install-pack flow), the bundled plugins in the middle, and the community seed pack below. Every community plugin has an Install in Petty button that triggers the petty://install flow. A third-party submission flow is planned but not yet live — for now, host your plugin's zip on a GitHub release or project site and link to it from your repo, or roll a pack manifest as described above.

There is no in-app plugin browser and no auto-update: every install is an explicit conscious decision by the user, which is exactly the trust model the plugin runtime was built around.

Fast iteration loop

Petty reads plugins at launch. While iterating, restart the app after each change:

pkill -x Petty
./start.sh    # or .build/debug/Petty

Keep main.js and manifest.json small and focused — the shorter your feedback loop, the faster you'll find the right shape for the plugin.