CommunityEscrita e Ediçãogithub.com

publisher-skill/publisher

Publish HTML frontend games to the AI Game MCP platform.

O que é publisher?

publisher is a Codex agent skill that publish HTML frontend games to the AI Game MCP platform.

Funciona com~Claude CodeCodex CLI~Cursor
npx skills add publisher-skill/publisher

Installed? Explore more Escrita e Edição skills: steipete/notion, affaan-m/seo, affaan-m/brand-voice · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Publisher Skill

Publish HTML frontend projects to the publishing platform and optionally package them as Android APKs.

Platform URL:

https://pushwebly.com

Use "project" in user-facing messages. The canonical API paths are /api/projects and canonical tool names are project_*. Deprecated game_* aliases and gameId response fields may still appear; treat gameId as the project id.

Codex Execution Rules

  • Prefer mounted project_* MCP tools if they exist in the current environment.
  • If remote MCP tools are not mounted, use the HTTP APIs with curl.
  • Do not require Python. Optional scripts, if present, are for CLI/CI users only.
  • Ask only for genuinely missing information: credentials, zip path, project name, public/private visibility, or private access password preference.
  • Never write credentials into a project zip or repository file. Store them only in the user's local ~/.publisher/config.json after consent.
  • On Windows/PowerShell, use a temp JSON file for request bodies instead of shell-escaping inline JSON.

Intent Mapping

User intentActionMissing information to ask for
Publish, ship, upload, put onlinePublish projectzip path, project name
Make an installable phone app, APK, Android packagePublish and build APKzip path, project name
Build APK for an already published projectBuild APKproject id; use project list if needed
Make an app private/public, add/remove access passwordChange visibilityapp URL, target visibility (optional password when private)
Show my projects, list published projectsList projectsnone if token is available
Log in, register, account setupLogin/registerusername and password

If the intent is ambiguous, ask one short question: "Just publish it, or publish it and build an Android APK too?"

Publishing Conversation Process

When users express intentions such as publishing, deploying, launching, sharing applications, generating access links, etc., they must enter this process.

General Principles of Finite-State Machine

  • On entering the flow, first run S-1 (version check, non-blocking), then proceed in the order of S0 -> S1 -> S2 -> S3 -> S4.
  • Skipping steps is not allowed (except S-1).
  • Multiple fields cannot be queried in a single request. Information that the user has clearly provided can be recorded, and there is no need to ask repeatedly. When the user's response is ambiguous, only follow up once for the current field.
  • Only after the current step is completed can we proceed to the next step.
  • If waiting for user input is required, do not append explanations, suggestions, or previews of the next step at the end of the response.

S-1: Version Check (first node of the publish flow, non-blocking)

When entering the publish flow, run a version self-check before S0. This step is NOT part of the <HARD-GATE> blocking rules: whatever the result — even if the endpoint is unreachable — always continue. Never interrupt publishing because of a version issue.

Steps:

  1. Read the local skill version: the version field in this file's frontmatter.

  2. Query the latest platform version (no login required):

    curl -s https://ai-pub.pushwebly.com/api/skill/latest-version
    

    The response data contains: version (latest), downloadUrl (cross-platform doc package, unzip-and-use), downloadUrlWindows (Windows installer .exe), and downloadUrlMac (macOS installer .zip).

  3. Compare local version with the returned version.

    Comparison rule: a version is major.minor.patch. Compare segment by segment as integers, left to right — major first, then minor, then patch; pad missing segments with 0 (1.1 means 1.1.0). Compare as integers, NOT as strings: 1.10.0 is greater than 1.9.0 (second segment 10 > 9); a string compare would get this wrong. Ascending example: 1.0.0 < 1.0.9 < 1.1.0 < 1.2.0 < 1.10.0 < 2.0.0.

    • Local is older (local's integer-segment sequence is less than the returned one): you MUST output an update notice to the user before continuing, and the notice MUST include the actual download URL (this does not block publishing, but the notice must not be omitted). Pick the download URL by the user's OS:

      • Windows → use downloadUrlWindows
      • macOS → use downloadUrlMac
      • OS unknown, or the matching field is empty → fall back to the generic downloadUrl

      Notice text (output it in the SAME language the user is conversing in — Chinese if they write Chinese, English if English; do not hard-code one language; the example below is English only as a template; replace the URL with the real selected one; write the download URL as a clickable Markdown link [URL](URL) — do NOT wrap it in backticks as inline code, or the user cannot click it; never leave a placeholder, never say "a new version exists" without giving the link):

      Note: a newer Publisher skill version <version> is available (you are on <local version>). Download: https://ai-pub.pushwebly.com/downloads/AI_Publisher_skill.exe (updating does not affect the current publish).

    • Local equals or is newer: stay silent and continue.

    • Request fails/times out/cannot parse: silently skip, go straight to S0, do not surface an error.

  4. In all cases, proceed to S0 next. Because this step only notifies (no input, no blocking), the "at most one user-facing question per reply" rule does not apply to it — the version note may share the same reply as the first S0 question.


S0: Check Account Status

First, check if there is already an available token/local credential.

  • If you already have account credentials: go to S2.
  • If there are no account credentials: Enter S1_USERNAME.
  • If unable to check the account status: ask the user if they already have a publishing account.

Ask the user a question:

I need to confirm the publishing account first: Do you already have an available account or token?

User answer:

  • Yes: Require the user to provide or confirm the token/login credentials, then proceed to S2.
  • No: Enter S1_USERNAME.

S1: Registration and Login Process

S1_USERNAME: Collect registered username

If there is no username, only ask:

Please provide the username to be registered.

After receiving the username, record it as username and enter S1_PASSWORD.

S1_PASSWORD: Collection registration password

If there is no password, only ask:

Please provide the registration password.

After receiving the password, record it as password and enter S1_VALIDATE.

S1_VALIDATE: Joint Validation

Only after having both username and password can the backend interface be called for joint verification.

Verification passed:

  • Create Account
  • Complete Login
  • Prompt: Registration and login successful
  • Enter S2

Verification failed:

  • Display the specific error reason returned by the backend
  • Only inquire about the item that needs to be corrected
  • Do not terminate the registration process

S2: Inquire about application visibility

If the public or private status has not been clearly defined, only ask:

Is this application public? Public means anyone can access it via a link; private requires a 6-digit access password.

User answer:

  • Public / Yes / public: Record visibility=public, enter S4
  • Private / No / private: Record visibility=private, enter S3
  • Unclear: Continue to ask only about this item

S3: Private Access Password

If visibility=private, it is necessary to ask whether to customize the password, asking only:

Do you want to customize a 6-digit access password? You can also reply "system-generated".

User answer:

  • Provide password: verification must be 6 digits or letters; after passing, record accessPassword=<user password> and enter S4
  • Reply system-generated / Not required / Random: Do not pass accessPassword, let the backend generate it, and enter S4
  • Password format error: Only prompt for the format requirements and re-ask for the password

S4: Execute Release

Calling the publishing interface is only allowed if one of the following conditions is met:

  • visibility=public
  • visibility=private and the user has selected a custom password or a system-generated password

Release Parameters:

Public Application:

{
  "visibility": "public"
}

Private application, custom password: 
{

Default to `public` if the user does not specify visibility. A private access
password must match `^[A-Za-z0-9]{6}$`; otherwise ask for a valid password or let
the backend generate one.

## Token Handling

A Bearer token is required for all publishing APIs.

Use this order:

1. If the user provided a token in the conversation, use it directly.
2. If `~/.publisher/config.json` exists, read the stored username/password and
   log in again to refresh the token.
3. Otherwise ask for username and password. The login endpoint automatically
   registers new accounts.

Login/register endpoint:

```bash
curl -s -X POST https://ai-pub.pushwebly.com/api/auth/login-or-register \
  -H "Content-Type: application/json" \
  -d @login.json

Write login.json as:

{"username":"alice","password":"plain-text-password"}

Use data.token from the response as:

Authorization: Bearer <TOKEN>

Delete the temp login file after use.

Optional local credential file:

{
  "baseUrl": "https://ai-pub.pushwebly.com",
  "username": "alice",
  "password": "plain-text-password",
  "authorization": "Bearer <TOKEN>",
  "userId": 1
}

On Linux/macOS, set mode 600 after writing. On 401, refresh the token once by logging in again, then retry the failed operation once.

Verify token:

curl -s https://ai-pub.pushwebly.com/api/auth/verify-token \
  -H "Authorization: Bearer <TOKEN>"

Pre-Publish Checks

Before publishing:

  1. Confirm the file exists and has a .zip extension.
  2. Confirm the zip contains index.html or index.htm.
  3. If the zip has nested directories, confirm the entry file can be located.
  4. Confirm the project name is non-empty.
  5. Confirm a valid token is available.

HTTP Operations

Publish Project

Public:

curl -s -X POST https://ai-pub.pushwebly.com/api/projects/publish \
  -H "Authorization: Bearer <TOKEN>" \
  -F "project_name=demo" \
  -F "[email protected]"

Private with optional custom password:

curl -s -X POST https://ai-pub.pushwebly.com/api/projects/publish \
  -H "Authorization: Bearer <TOKEN>" \
  -F "project_name=demo" \
  -F "[email protected]" \
  -F "visibility=private" \
  -F "access_password=a1b2c3"

For private apps, omit access_password to let the backend generate a password. Always relay the final accessPassword from the response to the user.

List Projects

curl -s https://ai-pub.pushwebly.com/api/projects/my \
  -H "Authorization: Bearer <TOKEN>"

Change Visibility (public <-> private)

Toggle an already-published app between public and private. The input is the app URL; the backend parses the app identifier from it and verifies the app belongs to the current user — you can only change your own apps, not others'.

curl -s -X PATCH https://ai-pub.pushwebly.com/api/projects/visibility \
  -H "Authorization: Bearer <TOKEN>" \
  -H "Content-Type: application/json" \
  -d @visibility.json

Write visibility.json (to private):

{"url":"https://ai-pub.pushwebly.com/play/aB3xYz/","visibility":"private","accessPassword":"a1b2c3"}

Or to public (no password needed; the old password is cleared):

{"url":"https://ai-pub.pushwebly.com/play/aB3xYz/","visibility":"public"}

accessPassword is optional and only used when switching to private (6 digits or letters; auto-generated if omitted). The response data includes the final visibility, url, and — when private — the accessPassword. Relay a private app's URL and password separately, never with ?pwd=. If the app is not found, already deleted, or belongs to another user, state the concrete reason and that only your own apps can be changed.

Build APK for Existing Project

curl -s -X POST https://ai-pub.pushwebly.com/api/apk/build \
  -H "Authorization: Bearer <TOKEN>" \
  -H "Content-Type: application/json" \
  -d @build-apk.json

Write build-apk.json as:

{"projectId":12}

Use data.apkUrl from the response as the APK download URL.

Publish and Build APK

This is the recommended operation when the user wants an installable Android app.

curl -s -X POST https://ai-pub.pushwebly.com/api/apk/publish-and-build \
  -H "Authorization: Bearer <TOKEN>" \
  -F "project_name=airplane-war" \
  -F "[email protected]"

For a private app, add:

-F "visibility=private" -F "access_password=a1b2c3"

Omit access_password to auto-generate it.

Expected response fields may include:

{
  "gameId": 12,
  "projectName": "airplane-war",
  "playUrl": "https://ai-pub.pushwebly.com/play/aB3xYz/",
  "status": "enabled",
  "apkUrl": "https://ai-pub.pushwebly.com/api/apk/download/airplane-war_aB3xYz.apk",
  "apkFileName": "airplane-war_aB3xYz.apk",
  "apkFileSize": 2300000
}

Read gameId as the project id.

MCP Tool Parameters

If project_* tools are mounted, use these canonical parameter shapes.

project_publish:

{
  "authorization": "Bearer <TOKEN>",
  "projectName": "project-name",
  "zipFilePath": "server-side local zip path",
  "visibility": "public",
  "accessPassword": ""
}

project_list:

{"authorization": "Bearer <TOKEN>"}

project_update_visibility:

{
  "authorization": "Bearer <TOKEN>",
  "url": "https://ai-pub.pushwebly.com/play/aB3xYz/",
  "visibility": "private",
  "accessPassword": "a1b2c3"
}

project_build_apk:

{
  "authorization": "Bearer <TOKEN>",
  "projectId": 12
}

project_publish_and_build_apk:

{
  "authorization": "Bearer <TOKEN>",
  "projectName": "project-name",
  "zipFilePath": "server-side local zip path",
  "visibility": "public",
  "accessPassword": ""
}

User Reply Formats

Publish only, public:

Project published successfully.

Project name: <projectName>
Access URL: <url>
Visibility: public
Status: enabled

Publish only, private:

Private project published successfully.

Project name: <projectName>
Access URL: <url>
Access password: <accessPassword>
Visibility: private

Share the URL and the access password separately. The recipient opens the URL and enters the password on the page. Do not append the password to the URL.

Publish and build APK:

Project published successfully.

Project name: <projectName>
Access URL: <playUrl>
APK download: <apkUrl>

Build APK only:

APK build complete.

Project name: <projectName>
APK download: <apkUrl>

On failure, state the concrete reason and the next retry action. Common reasons: missing index.html/index.htm, invalid or expired token, missing zip file, empty project name, invalid private password, APK timeout, or server missing Android SDK configuration.

Notes

  • Private apps are opened by entering the password on the access page; share the clean URL and the password separately, never appended together. (The backend still accepts ?pwd=<password>, but do not promote that suffixed-link form.) After successful entry, the browser stores a cookie.
  • APK builds usually take about 1-3 minutes.
  • Duplicate APK build requests during an active build are deduplicated.
  • Each APK has a unique package name: com.pushwebly.g{playToken}.
  • Use project_publish_and_build_apk for one-step publish plus APK generation.

Habilidades Relacionadas

steipete/notion

Notion CLI/API for pages, Markdown content, data sources, files, comments, search, Workers, and raw API calls.

community

affaan-m/seo

Audit, plan, and implement SEO improvements across technical SEO, on-page optimization, structured data, Core Web Vitals, and content strategy. Use when the user wants better search visibility, SEO remediation, schema markup, sitemap/robots work, or keyword mapping.

community

affaan-m/brand-voice

Build a source-derived writing style profile from real posts, essays, launch notes, docs, or site copy, then reuse that profile across content, outreach, and social workflows. Use when the user wants voice consistency without generic AI writing tropes.

community

affaan-m/crosspost

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

community

affaan-m/x-api

X/Twitter API integration for posting tweets, threads, reading timelines, search, and analytics. Covers OAuth auth patterns, rate limits, and platform-native content posting. Use when the user wants to interact with X programmatically.

community

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

community