#Packaging & Distributing

Once your pack works locally, you can hand it to other people. A bundle is just your pack folder, zipped.

#Building a bundle

Zip the contents of your pack folder so that info.json and phrases.json sit at the root of the archive — not nested inside an extra directory.

text
my-pack.zip
    info.json
    phrases.json
    setVolume.js
    hello.js
    package.json     (if you have dependencies)

Distribute it as a .zip. Don't include node_modules/ — dependencies are installed on the user's machine at install time.

#What gets validated

Whichever way a pack is installed, Personal Goober checks it before committing. A bundle is rejected if:

  • info.json or phrases.json is missing, or isn't valid JSON;
  • info.json is missing id (a string) or name;
  • any action listed in phrases.json has no matching .js file;
  • a package.json is present but isn't valid JSON.

If a pack with the same id is already installed, the install stops with a conflict rather than overwriting the existing one. Bump behavior aside, this means your id is effectively your pack's identity — choose it once and keep it.

#How users install

There are two paths, both of which end in a confirmation prompt:

  • From a file. The user opens the install page and uploads your .zip. Everything is validated and installed locally. This is the simplest thing to point people at — see the user guide on action installation.
  • From a website. A site can offer a one-click "Install" button that beams a bundle straight into a running Personal Goober. That's the API below.

However it arrives, the app installs the pack, and if it has a package.json, runs npm install --ignore-scripts inside the pack folder to fetch dependencies.

#The website install API

Personal Goober runs a local WebSocket server that trusted websites can use to install bundles. This is what powers the "Install" buttons on the official site, and you can build the same flow on your own.

  • Endpoint: ws://127.0.0.1:5577
  • Origins: the connection is only accepted from an allowlisted origin. The official site's origins are allowlisted; http://localhost:3000 is allowed for local development.

Every message is JSON and carries a requestId you generate, which is echoed back on the matching reply.

1. Ask for access. Before anything else, request permission. The user sees a system dialog and can choose to remember the decision for your origin.

json
{ "type": "request-access", "appName": "My Action Site", "requestId": "..." }

Reply: { "type": "access-result", "granted": true|false, "requestId": "..." }. Until access is granted, other requests are rejected.

2. Send the bundle. Base64-encode the .zip and send it with its filename.

json
{ "type": "install", "filename": "my-pack.zip", "data": "<base64>", "requestId": "..." }

If the bundle is valid you get back an install-confirm describing what's about to be installed (so you can show your own confirmation UI):

json
{
    "type": "install-confirm",
    "name": "My Pack",
    "id": "my-pack",
    "description": "...",
    "dependencies": ["loudness"],
    "requestId": "..."
}

3. Confirm or cancel.

json
{ "type": "confirm-install", "requestId": "..." }

Reply: { "type": "install-result", "status": "ok" | "error", "requestId": "..." } (with an error string when something went wrong). Send cancel-install instead to drop the staged install.

There's also a ping / pong pair you can use to check whether Personal Goober is running before you try any of this.

#The trust model, and your responsibility

Installing a pack is a big deal, and the app treats it that way:

  • The user must explicitly approve every install, and the prompt lists your pack's name, description, and dependencies.
  • Website access is gated per-origin behind a separate permission prompt.
  • Dependencies install with --ignore-scripts, so a dependency can't run code during installation.

None of that changes the core fact from the overview: once installed, your action code runs with full access to the user's computer. Distribute packs you'd trust yourself, be clear about what they do, and don't ask for dependencies you don't need.

That's the whole model. Go build something.