agentsclimarketplace

Agentpub publish

Skill agentpub-io/skill/skills/agentpub-publish

agentpub agent skills — publish, review, blueprints, and onboarding for agentpub.io

Install
npx -y skills add agentpub-io/skill --skill agentpub-publish

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when asked to publish, host, deploy, or share an HTML page, report, dashboard, visualization, or static files to a live URL, or when agentpub is mentioned. Static sites only (no server-side compute).

SKILL.md

14.3 KB, as published. Nobody here has run it

agentpub publishing

agentpub turns local HTML/static files into a live URL via a three-step API: create → upload → finalize. Base: https://agentpub.io.

When to use / when NOT

Use to put static HTML, CSS, JS, images, PDFs, or other static assets online fast. Do NOT use for server-side compute, databases, or dynamic backends — served sites are static only.

Step 0 — resolve a key and authenticate by default (DO THIS FIRST)

The single most common mistake is publishing anonymously by accident — that creates a throwaway 24-hour site and forces a separate claim. Before the three calls, resolve a key and decide ownership:

  1. Resolve a key, first match wins: (1) AGENTPUB_API_KEY env var; (2) ~/.config/agentpub/credentials (file mode 0600).
  2. If a key resolves → publish authenticated (send Authorization: Bearer <key> on the create call). The site is owned at creation: permanent, in the dashboard immediately, no per-page claim, and share-safe (no claim link a recipient could hijack).
  3. If no key resolves: if the user wants to keep the work, acquire one once (see "First use" below) and publish authenticated. Only publish anonymous for a deliberate zero-signup throwaway/trial.
  4. Anonymous is an explicit choice, never a fallback. Don't quietly create a 24h site because no key resolved — if the user wants to keep the work, acquire a key. Only go anonymous when the user explicitly asked for a throwaway. For durable/automated workflows set AGENTPUB_REQUIRE_AUTH=1 to forbid anonymous entirely (belt-and-suspenders).

Shortcut: the bundled agentpub.sh encodes all of this. ./agentpub.sh publish ./dir publishes owned when a key exists and hard-stops when none does (telling you to login or pass --anonymous) — it never creates a silent/accidental anonymous site. --anonymous is the explicit throwaway; AGENTPUB_REQUIRE_AUTH=1 forbids anonymous even then. MCP-host agents (Claude, Cursor) can instead use the agentpub.io/mcp endpoint, where the host manages auth.

The three calls

  1. Create — declare each file with its exact byte size. Authenticated by default:
curl -sX POST https://agentpub.io/api/v1/publish \
  -H "authorization: Bearer $AGENTPUB_API_KEY" \
  -H 'content-type: application/json' \
  -d '{"files":[{"path":"index.html","size":1234,"contentType":"text/html"}]}'

Returns upload.versionId, upload.uploads[] (each {path, method:"PUT", url}), upload.finalizeUrl, and the ownership state authenticated/anonymous/expiresAt. Omit the authorization header only for a deliberate anonymous site — anonymous responses also include claimToken, claimUrl, warning.

  1. Upload — PUT each file's bytes to its presigned url, sending content-type:
curl -sX PUT '<upload.uploads[].url>' \
  -H 'content-type: text/html' --data-binary @index.html
  1. Finalize — flip it live:
curl -sX POST '<upload.finalizeUrl>' \
  -H 'content-type: application/json' \
  -d '{"versionId":"<upload.versionId>"}'

Live at https://{slug}.agentpub.io/.

Name the site for humans. Pass "artifact":{"title":"…","description":"…"} on the create body so the owner's dashboard and list_my_sites show a meaningful label (not just the slug) and it's searchable. Omitted? The title auto-derives from the page's <title> (or first <h1>) and the description from <meta name="description"> — so ship a good <title>/meta-description either way.

Name it so you can update it in place (stable addressing)

Pass a name on the create body — an account-scoped handle YOU choose (e.g. "name":"ads-daily"):

curl -sX POST https://agentpub.io/api/v1/publish \
  -H "authorization: Bearer $AGENTPUB_API_KEY" -H 'content-type: application/json' \
  -d '{"name":"ads-daily","files":[{"path":"index.html","size":1234,"contentType":"text/html"}]}'
  • Re-publishing with the same name updates that site in place — you never have to discover, store, or guess the random slug. This is the right way to run any "updates daily/in place" routine: re-derive your name from context each run, no out-of-band slug tracking.
  • Resolve a name → slug anytime: GET /api/v1/publish/by-name/{name} (Bearer) → {slug, name}. list_my_sites also shows each site's name.
  • name is lowercase letters/digits/hyphens, unique per account; doing a fresh create with a name you already own returns 409 (publish-by-name to update it instead).
  • nametitle: name is the stable machine handle you address by; title (in artifact) is the human display label. The public URL stays a random {slug}.agentpub.io (random by design — not enumerable); name is how you find it, not the public link.

Confirm ownership after finalize (surface it loudly)

Every publish response carries the ownership state — check it and report it to the user so an accidental anonymous site is caught immediately:

authenticated: true
anonymous: false
expiresAt: null

If you see authenticated: false / anonymous: true / a non-null expiresAt when the user wanted to keep the site, you published the unsafe path — acquire a key and republish (or claim via the returned claimUrl).

First use — acquire and persist a key once (no browser)

Preferred for headless agents: device pairing. Run ./agentpub.sh pair (or use the /api/v1/pair/* endpoints directly): the agent starts a pairing, the human approves in their browser, and the key is delivered out-of-band on poll — so neither the user code nor the API key ever passes through chat. The deviceSecret stays with the agent and agentpub.sh pair persists the key to ~/.config/agentpub/credentials (0600). For headless integration, run agentpub.sh pair --json — machine-readable events go to stdout (start / poll pending|slow_down / approved with keyId/keyName, never the key), human text to stderr; exit codes: 0 ok, 2 denied, 3 expired, 4 timed out. The email-code flow below remains the fallback when a browser approve link is impractical.

When no key resolves and the user wants to keep their work:

  1. POST /api/auth/agent/request-code {"email":"[email protected]"} → a 6-digit code is emailed.
  2. User reads the code back to you → POST /api/auth/agent/verify-code {"email":"[email protected]","code":"482913"} → returns {"apiKey":"...","accountCreated":true|false}.
  3. Persist it so you never claim again:
mkdir -p ~/.config/agentpub && umask 177
printf '%s\n' "$APIKEY" > ~/.config/agentpub/credentials
chmod 600 ~/.config/agentpub/credentials
  1. Name the key for the tool holding it: POST /api/v1/keys {"name":"claude"} (use claude, cursor, hermes, …) — each tool keeps its own revocable key on the one account (least privilege; revoke one without breaking the others).
  2. Never echo, log, commit, or paste the key into chat history, code, or shared docs — only the 0600 file or env. Treat it like a password.

Anonymous (only when explicitly intended)

  • No auth → 24h site + a one-time claimToken/claimUrl. SURFACE the claimUrl to the user IMMEDIATELY and prominently — it is shown only once and is the only way to keep the site past 24h. Never log or paste the claimToken anywhere else.

Updating — full replace vs patch

Address an existing site by slug (or resolve it from your name). Two ways to update:

  • Full replacePUT /api/v1/publish/{slug} (same body shape as create). Authorize with the owner Bearer (or {"claimToken":"..."} for anonymous). Send a per-file hash (sha256 lowercase hex — shasum -a 256 file) so unchanged files skip upload (returned under upload.carried); only changed files get presigned URLs. Then finalize. Send hash on every publish incl. the first, or dedup can't compare.
  • Patch (overlay just some files)POST /api/v1/publish/{slug}/patch with only the files you want to change (same {files:[...]} shape; Bearer or {"claimToken"}). Unmentioned files are kept (carried forward byte-identical); patch never deletes. Then finalize. This is the cheap path for incremental updates — e.g. swapping one data.json without resending the rest of the site. MCP: patch_site.

Recurring reports / dashboards that update in place daily

The robust pattern for a page a scheduled agent refreshes (daily report, live dashboard):

  1. Publish once with a name and a static index.html whose markup never changes — it fetch('data.json')s its numbers at load and renders them client-side.
  2. Each run, patch only the data: POST /api/v1/publish/{slug}/patch with just [{"path":"data.json",...}], then finalize. The design stays byte-identical every day (no drift) and the payload is a few KB.
  3. Read back to compute deltas vs the last run: GET /api/v1/publish/{slug}/content?path=data.json (Bearer) → current file contents. MCP: get_site_content.

Why this matters: if a model re-emits a large HTML document each run it will drift (spacing, layout, bar heights wobble) — models reconstruct markup rather than reproduce it byte-for-byte. Static design + data-only patch eliminates that. (Generating the HTML with deterministic code also avoids drift — but patching only the data is still cheaper and is what get_site_content + patch_site are built for.)

Tips: agentpub is static hosting only — "live daily numbers" need an external scheduler (your cron/agent runner) calling patch+finalize. And don't bake the page's own public URL into the markup (you don't know the slug until after create) — read location.href client-side instead.

Quick reference

ActionCall
Site statusGET /api/v1/sites/{slug}
List my sitesGET /api/v1/sites (Bearer) — includes each site's name + versionNumber
Resolve a nameGET /api/v1/publish/by-name/{name} (Bearer) → {slug, name}
Patch (overlay)POST /api/v1/publish/{slug}/patch — change some files, keep the rest — MCP patch_site
Read contentGET /api/v1/publish/{slug}/content[?path=&version=] (Bearer) — MCP get_site_content
VersionsGET /api/v1/publish/{slug}/versions — each has a vN number — (Bearer or ?claimToken=)
RollbackPOST /api/v1/publish/{slug}/rollback {"version":"v2"} or {"versionId":"..."}
DeleteDELETE /api/v1/publish/{slug} (Bearer, or {"claimToken":"..."})
List keysGET /api/v1/keys (Bearer)
Mint named keyPOST /api/v1/keys {"name":"cursor"} (Bearer)
Revoke keyDELETE /api/v1/keys/{id} (Bearer)
Enable reviewPOST /api/v1/publish/{slug}/review {"enabled":true} (Bearer) — MCP enable_review
Get feedbackGET /api/v1/sites/{slug}/comments{slug, approved, comments[]} — MCP get_feedback
Mark addressedPOST /api/v1/sites/{slug}/comments/{id}/addressed (Bearer) — MCP mark_addressed

Enabling review mode turns on a feedback widget on the served page where reviewers leave page-level comments and approve. Comments can target a specific element via an anchor {selector, tag, text}, surfaced by get_feedback so the agent can locate and edit that exact element.

Applying review feedback

Run this loop whenever asked to "apply the agentpub comments for {slug}":

  1. Fetch comments — MCP get_feedback(slug) or GET /api/v1/sites/{slug}/comments (Bearer). Returns {slug, approved, approvedVersionId, comments[]}. Each comment has status ("open" | "addressed"), body (the requested change), and anchor: {selector, tag, text} | null.

  2. Filter — work only on comments where status === "open".

  3. Fetch the live page sourceGET https://{slug}.agentpub.io/ (or whichever file the comment targets). Strip the review widget before editing: the served HTML of a review-enabled site has a block injected immediately before </body> that begins with <button id="apb-approve-top". Remove everything from that <button> to and including </body>, then append a clean </body> — do NOT bake the widget back into the published source.

  4. Apply each change — for comments with an anchor, locate the element by matching tag + text first; use selector to disambiguate when multiple elements match. For anchorless comments, apply the change to the page as a whole. Edit the in-memory source.

  5. Republish — standard three-call flow against PUT /api/v1/publish/{slug} (Bearer $AGENTPUB_API_KEY): declare files with exact byte sizes and sha256 hashes → upload changed files → finalize.

  6. Mark addressed — for each comment you handled: MCP mark_addressed(slug, commentId) or POST /api/v1/sites/{slug}/comments/{commentId}/addressed (Bearer). Call after a successful finalize.

Gotchas

  • size is validated for format and limits at create (must be an integer, ≤ 25 MB), but a mismatch between declared and actual bytes is not enforced at upload — still declare exact bytes (wc -c < file) because the manifest is recorded metadata.
  • Send the content-type on the upload PUT — R2 records it and serves it back.
  • Finalize 409 means a declared file didn't upload — re-PUT that file, then re-finalize.
  • 429 → respect the Retry-After header before retrying.
  • Anonymous sites show a claim badge; claiming (via claimUrl) removes it.

Full reference: https://agentpub.io/llms.txt and https://agentpub.io/openapi.json.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.