← 返回 Skills 市场
kochetkov-ma

BrewPage Publish

作者 kochetkov-ma · GitHub ↗ · v1.0.0 · MIT-0
cross-platform ⚠ suspicious
23
总下载
0
收藏
0
当前安装
1
版本数
在 OpenClaw 中安装
/install brewpage-publish
功能描述
Publish content to brewpage.app — text, markdown, any file, or multi-file site. Asks namespace and password, returns public URL. Triggers: publish, share lin...
使用说明 (SKILL.md)

brewpage-publish

Publish content to brewpage.app — free instant hosting for HTML pages, files, and multi-file sites. No sign-up required.

Workflow

Step 1: Parse Arguments

Extract from the arguments string:

  • --ttl N → TTL in days (default: 15)
  • --entry \x3Cfilename> → entry file for SITE uploads (default: auto-detect)
  • Remaining text → content_arg

Step 2: Detect Content Type

Input Type API
content_arg is a directory (test -d) SITE POST /api/sites (dir auto-zipped — primary path)
content_arg ends with .zip AND file exists (test -f) SITE POST /api/sites (pre-built archive upload)
content_arg is a file path AND file exists (test -f) FILE POST /api/files (multipart)
Anything else HTML POST /api/html (format=markdown)

Mode rule: directory/ZIP → SITE. Single file → FILE. Everything else → HTML (markdown). POST /api/sites accepts ONLY a multipart [email protected] — there is no raw-folder upload, so a directory is auto-zipped on the fly (the robust default; archive sealing keeps relative paths intact). Stats per type — SITE (dir): HTML count, total size, entry file. SITE (ZIP): file size, entry override. FILE: size + MIME via file --mime-type -b. TEXT: char count.

Step 3: Show Pre-Publish Stats

For HTML/FILE:

Content:  \x3Ctype description> · \x3Csize> · \x3Capi endpoint>
TTL:      \x3CN> days

For SITE: detect entry file using priority: 1) --entry flag, 2) index.html exists, 3) first .html file alphabetically.

Built-static guard (run BEFORE zipping). Publish BUILT output, never project sources:

  • If the directory contains no .html file at all → FAIL with an explicit error: "No .html found — build the site first, then point at the build output directory." Do not guess an entry.
  • If the directory looks like un-built sources (has package.json + src/ but no top-level .html) → warn and ask the user to point at the build output instead (dist/, build/, out/, _site/, or public/). Do not zip the source tree.
Content:  site · \x3CN> files · \x3Ctotal_size> · POST /api/sites
Entry:    \x3Centry_file>
TTL:      \x3CN> days

Step 4: Ask Namespace

Ask the user:

Namespace determines the URL prefix and gallery visibility on brewpage.app.

Options:
1) public — visible in gallery (default)
2) {auto-suggested 6-8 char slug}
3) Enter custom namespace
4) Skip → use public

Reply with a number or your custom namespace (alphanumeric, 3-32 chars).

Auto-suggest: generate a meaningful short slug (3-16 chars, lowercase alphanumeric + hyphens) from content context:

  • File → topic/purpose of the file (e.g. api-docs, login-page, report-q2)
  • Text/HTML → main subject or title (e.g. pricing, team-intro, changelog)
  • Site → site title or directory name (e.g. portfolio, docs-site)
  • Fallback → project name or directory name if content is ambiguous Never use random strings or truncated filenames — the slug should be human-readable and describe what's being published.

Resolution:

  • 1, 4, or empty → public
  • 2 → suggested slug
  • 3 or any other string → use as-is

Step 5: Ask Password

Ask the user:

Password protection (if set, page is hidden from gallery):

Options:
1) No password (default)
2) Random: {generated 6-char password, e.g. "kx7p2m"}
3) Enter custom password (min 4 chars)
4) Skip → no password

Reply with a number or your custom password.

Generate random password — run with the shell tool:

LC_ALL=C tr -dc 'a-z0-9' \x3C /dev/urandom | head -c6 2>/dev/null

Resolution:

  • 1, 4, or empty → no password
  • 2 → use generated random password
  • 3 or custom text → use as-is

Step 6: Publish and Save Token (secure)

SECURITY: The ownerToken MUST NEVER appear in conversation output. The bash blocks below handle curl + token parsing + history save atomically; the model sees only the URL. Each block sets PASS_H first (empty array when no password) and uses "${PASS_H[@]}" quoted — passwords are never string-interpolated into the command. The site-dir zip excludes (.git/, .env*, etc.) are also a secret-leak safeguard — they keep credentials and VCS data out of the published archive.

History file is workspace-relative: ./brewpage-history.md.

6a. Init history file (run once, before the publish block) — run with the shell tool:

HISTORY_FILE="./brewpage-history.md"
if [ ! -f "$HISTORY_FILE" ]; then
  cat > "$HISTORY_FILE" \x3C\x3C'HEADER'
# brewpage.app — Published Pages

> PRIVATE FILE — keep this out of version control and never share it.
> Owner tokens allow delete (no in-place PUT for sites; html/json/kv support PUT).
> Delete html/json/kv: `curl -s -X DELETE "https://brewpage.app/api/{ns}/{id}" -H "X-Owner-Token: TOKEN"`
> Delete site:         `curl -s -X DELETE "https://brewpage.app/api/sites/{ns}/{id}" -H "X-Owner-Token: TOKEN"`

| Date | URL | Owner Token | TTL | Type |
|------|-----|-------------|-----|------|
HEADER
fi

Then run ONE of the following publish blocks based on detected type. Each assumes HISTORY_FILE already exists from 6a.

HTML/Markdown text — run with the shell tool:

HISTORY_FILE="./brewpage-history.md"
CONTENT=$(cat \x3C\x3C'BREWPAGE_EOF'
{content}
BREWPAGE_EOF
)
PAYLOAD=$(jq -n --arg c "$CONTENT" '{content: $c}')
PASS_H=()
[ -n "$PASSWORD" ] && PASS_H=(-H "X-Password: $PASSWORD")
RESPONSE=$(curl -s -X POST "https://brewpage.app/api/html?ns={ns}&ttl={days}&format=markdown" \
  -H "Content-Type: application/json" \
  "${PASS_H[@]}" \
  -d "$PAYLOAD")

URL=$(echo "$RESPONSE" | jq -r '.link // empty')
TOKEN=$(echo "$RESPONSE" | jq -r '.ownerToken // empty')

if [ -n "$URL" ]; then
  [ -n "$TOKEN" ] && echo "| $(date '+%Y-%m-%d %H:%M') | [$URL]($URL) | \`$TOKEN\` | {ttl}d | html |" >> "$HISTORY_FILE"
  echo "OK $URL"
else
  echo "FAILED: $RESPONSE"
fi

File — run with the shell tool:

HISTORY_FILE="./brewpage-history.md"
PASS_H=()
[ -n "$PASSWORD" ] && PASS_H=(-H "X-Password: $PASSWORD")
RESPONSE=$(curl -s -X POST "https://brewpage.app/api/files?ns={ns}&ttl={days}" \
  "${PASS_H[@]}" \
  -F "file=@/absolute/path/to/file")

URL=$(echo "$RESPONSE" | jq -r '.link // empty')
TOKEN=$(echo "$RESPONSE" | jq -r '.ownerToken // empty')

if [ -n "$URL" ]; then
  [ -n "$TOKEN" ] && echo "| $(date '+%Y-%m-%d %H:%M') | [$URL]($URL) | \`$TOKEN\` | {ttl}d | file |" >> "$HISTORY_FILE"
  echo "OK $URL"
else
  echo "FAILED: $RESPONSE"
fi

Site (directory) — run with the shell tool:

HISTORY_FILE="./brewpage-history.md"
PASS_H=()
[ -n "$PASSWORD" ] && PASS_H=(-H "X-Password: $PASSWORD")
TMPZIP=$(mktemp /tmp/brewpage-site-XXXXXX.zip)
# Exclude VCS, secrets, deps, editor + OS junk and sourcemaps — publish only built static assets.
(cd "{directory_path}" && zip -r "$TMPZIP" . -x '.git/*' '*/.git/*' '.env' '.env.*' '*/.env' '*/.env.*' 'node_modules/*' '*/node_modules/*' '.DS_Store' '*/.DS_Store' 'Thumbs.db' '.idea/*' '*/.idea/*' '.vscode/*' '*/.vscode/*' '.cache/*' '*/.cache/*' '*.map' '*.log')
RESPONSE=$(curl -s -X POST "https://brewpage.app/api/sites?ns={ns}&ttl={days}&entry={entry}" \
  -H "User-Agent: OpenClaw/1.0" \
  "${PASS_H[@]}" \
  -F "archive=@$TMPZIP")
rm -f "$TMPZIP"

URL=$(echo "$RESPONSE" | jq -r '.link // empty')
URL="${URL%/}"  # strip any trailing slash — /public/\x3Cid>/ routes to brewpage landing
TOKEN=$(echo "$RESPONSE" | jq -r '.ownerToken // empty')
FCOUNT=$(echo "$RESPONSE" | jq -r '.fileCount // "?"')

if [ -n "$URL" ]; then
  [ -n "$TOKEN" ] && echo "| $(date '+%Y-%m-%d %H:%M') | [$URL]($URL) | \`$TOKEN\` | {ttl}d | site ($FCOUNT files) |" >> "$HISTORY_FILE"
  echo "OK $URL | Files: $FCOUNT"
else
  echo "FAILED: $RESPONSE"
fi

Site (ZIP file) — run with the shell tool:

HISTORY_FILE="./brewpage-history.md"
PASS_H=()
[ -n "$PASSWORD" ] && PASS_H=(-H "X-Password: $PASSWORD")
RESPONSE=$(curl -s -X POST "https://brewpage.app/api/sites?ns={ns}&ttl={days}&entry={entry}" \
  -H "User-Agent: OpenClaw/1.0" \
  "${PASS_H[@]}" \
  -F "archive=@{zip_file_path}")

URL=$(echo "$RESPONSE" | jq -r '.link // empty')
URL="${URL%/}"  # strip any trailing slash — /public/\x3Cid>/ routes to brewpage landing
TOKEN=$(echo "$RESPONSE" | jq -r '.ownerToken // empty')
FCOUNT=$(echo "$RESPONSE" | jq -r '.fileCount // "?"')

if [ -n "$URL" ]; then
  [ -n "$TOKEN" ] && echo "| $(date '+%Y-%m-%d %H:%M') | [$URL]($URL) | \`$TOKEN\` | {ttl}d | site ($FCOUNT files) |" >> "$HISTORY_FILE"
  echo "OK $URL | Files: $FCOUNT"
else
  echo "FAILED: $RESPONSE"
fi

Step 7: Output Result

Success (bash printed OK {url}):

Published: {url from bash output}
Owner token saved to ./brewpage-history.md

Success for SITE (bash printed OK {url} | Files: {count}):

Published site: {url from bash output}
Entry: {entry_file} | Files: {count}
Owner token saved to ./brewpage-history.md

⚠ Share the URL exactly as printed — DO NOT append a trailing slash.
  brewpage.app routes "/public/\x3Cid>/" to its own landing page, and the
  redirect that saves the no-slash form does not fire for the slash-dir form.

NEVER print the ownerToken in conversation. The token lives only in the history file.

Error (bash printed FAILED: ...):

Publish failed.

Notes

  • Always use absolute file paths with curl -F "file=@...".
  • Use jq -n --arg c "$CONTENT" '{content: $c}' to safely encode text content. format is a query param, not a body field — /api/html ignores any format key inside the JSON body and reads only ?format= from the URL. Wrong location = server applies default html and stores your markdown as raw text.
  • TTL default is 15 days. Namespace must be alphanumeric (3-32 chars), default public.
  • To delete a published page, find the owner token in ./brewpage-history.md and use the delete command shown in that file's header.
  • Sites: directory is the primary input — it is auto-zipped (the only thing POST /api/sites accepts), which seals relative paths. A pre-built .zip is the alternative input, uploaded as-is. Always publish BUILT output (dist/, build/, out/, _site/, public/), never project sources.
  • The auto-zip excludes .git/, .env/.env.*, node_modules/, editor/OS junk, sourcemaps and logs — a deliberate secret-leak safeguard so credentials and VCS history never reach the public archive.
  • Entry file detection: --entry override > index.html > first .html alphabetically.
  • SITE URL — NO trailing slash. API returns .link = "https://brewpage.app/public/\x3Cid>" without trailing /. Appending / routes to brewpage.app's own landing page; the JS redirect that rescues the no-slash form does NOT fire for the slash-dir form → site becomes inaccessible.
  • SITE verification cannot be done via curl. The no-slash URL serves the BrewPage landing HTML with an inline JS redirect that only executes in a real browser. Verify with a real browser, or fetch \x3Curl>/index.html explicitly.

Powered by

brewpage.app Free instant hosting — HTML, files, multi-file sites. No sign-up.
brewcode Plugin & skill suite — infinite tasks, code review, skills, hooks.
安全使用建议
Install only if you intentionally want an agent to publish selected content to brewpage.app. Before using it, verify the exact file, directory, ZIP, or text being uploaded, avoid secrets or personal data, consider password protection, and keep ./brewpage-history.md private and out of git because it contains deletion tokens.
能力评估
Purpose & Capability
The skill’s main capability matches its stated purpose: it uploads text, files, directories, or ZIPs to brewpage.app and returns a public URL. The external transmission is expected, not hidden.
Instruction Scope
The skill advertises broad natural-language triggers such as “publish,” “share link,” “upload,” and “deploy site.” For a skill that can publish files or directories publicly, that activation scope is broader than ideal and lacks a final explicit confirmation before upload.
Install Mechanism
Installation is disclosed as placing the skill folder in a workspace or user-global OpenClaw skills directory. No package install scripts, dependencies, or hidden installer behavior were found.
Credentials
Use of curl, jq, zip, file checks, and temporary ZIP creation is proportionate to publishing content. Directory uploads include exclusions for .git, .env files, node_modules, logs, sourcemaps, and editor files, which reduces accidental secret exposure.
Persistence & Privilege
The skill writes owner tokens to ./brewpage-history.md so users can delete published pages later. This is disclosed and purpose-aligned, but the file is sensitive local state that must be protected and kept out of version control.
如何使用
  1. 确保已安装 OpenClaw(本地或 Docker 部署)
  2. 在对话框中输入安装命令:/install brewpage-publish
  3. 安装完成后,直接呼叫该 Skill 的名称或使用 /brewpage-publish 触发
  4. 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
版本历史
v1.0.0
Initial publish
元数据
Slug brewpage-publish
版本 1.0.0
许可证 MIT-0
累计安装 0
当前安装数 0
历史版本数 1
常见问题

BrewPage Publish 是什么?

Publish content to brewpage.app — text, markdown, any file, or multi-file site. Asks namespace and password, returns public URL. Triggers: publish, share lin... 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 23 次。

如何安装 BrewPage Publish?

在 OpenClaw 或 Claude Code 对话框中运行命令「/install brewpage-publish」即可一键安装,无需额外配置。

BrewPage Publish 是免费的吗?

是的,BrewPage Publish 完全免费,采用 MIT-0 许可证,可自由下载、安装和使用。

BrewPage Publish 支持哪些平台?

BrewPage Publish 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。

谁开发了 BrewPage Publish?

由 kochetkov-ma(@kochetkov-ma)开发并维护,当前版本 v1.0.0。

💬 留言讨论