Install
openclaw skills install @seiriosplus/miaoda-app-builderCreate, modify, generate, and deploy websites, web apps, dashboards, SaaS products, internal tools, interactive web pages, Weixin mini program, native iOS / Android mobile apps, games on the Baidu Miaoda (秒哒) platform using natural-language instructions.
openclaw skills install @seiriosplus/miaoda-app-builderMiaoda (秒哒) is a chat-driven full-stack application builder. Official website: https://www.miaoda.cn
Users describe what they want in natural language and Miaoda generates a production-ready web product, including:
Typical outputs include:
This skill enables AI agents to interact with the Miaoda(秒哒) platform to create, iterate, generate, and deploy applications.
All platform operations must be executed through the packaged CLI script:
python scripts/miaoda_api.py <command> [options]
Do not call platform APIs directly. Always use the CLI commands provided by this skill.
Use this skill whenever the user wants to:
Do not use this skill for unrelated programming tasks.
Trigger this skill if the request includes concepts such as:
Examples that should route to this skill:
Miaoda can generate native iOS and Android mobile applications, not just web products. The output is a real mobile app project rather than a mobile-adapted web page.
How to request it: state the target platform explicitly in the chat text.
There is no separate CLI command or flag — the platform decides the output form
from the natural-language description.
python scripts/miaoda_api.py chat --text "创建一个原生 iOS 记账 App,支持分类统计和月度报表"
python scripts/miaoda_api.py chat --text "创建一个安卓原生 App,用于扫码记录仓库出入库"
python scripts/miaoda_api.py chat --text "创建一个 iOS 和 Android 双端原生 App,功能是每日习惯打卡"
Guidance for agents:
chat message. Vague wording like "做个 App" is often interpreted
as a web app.chat → PRD refinement → generate-app --watch →
publish --wait.app-detail and the
post-publish trajectory output for the download / distribution entry the
platform returns; do not assume or construct a web URL.The CLI script is stateless.
It does not store workflow state between calls.
Application workflow state is maintained by the Miaoda platform and must be inferred from:
appIdconversationIdAgents must pass the appropriate identifiers when continuing conversations or modifying applications.
Miaoda applications follow a strict lifecycle.
Agents must follow these rules.
For a new application:
chat request describing the product.type":"button" and event":{"name":"generateApp"} in result.artifact.parts[].data.actions[]), trigger application generation using generate-app.Generation is required only once during the initial creation.
After an application has already been generated:
generate-app again.chat with the same appId and conversationId.Normal chat messages modify the existing application.
Publishing is allowed after the application has been generated at least once.
Rules:
--wait flag)publish after a successful execution. A publish is
successful when the CLI exits successfully and the final status is SUCCESS.
Missing or delayed downstream output, missing frontend events, an incomplete
conversation reply, or any other display/transport issue is not a publish
failure; investigate that separately instead of publishing again.publish only when the command itself fails or the final deployment
status is explicitly FAILED. Do not retry while the status is
PROCESSING, RUNNING, or UNDER_RELEASE; keep polling.Typical deployment flow:
publish → publish-status polling
Or use the --wait flag to auto-poll:
publish --wait
Stop polling when the status becomes:
SUCCESSFAILEDFor general tasks such as research reports, PPT, or analysis documents, the platform processes the request entirely in the chat stage.
Rules:
generate-app steppublish stepchat returns {"needGenerateApp": false, "appType": "TASK"}Do not call generate-app for general tasks.
Publishing returns the application's public URL in the platform response. Use
the value returned by the CLI; do not infer a URL from appId, because test and
production environments use different domains.
For a deployment that should wait for completion, use:
python scripts/miaoda_api.py publish --app-id <app_id> --wait
chat → PRD refinement → generate-app --watch → publish --wait
chat → chat → chat
(no additional generation step required)
publish → publish-status polling
Or:
publish --wait
chat → state:completed (needGenerateApp: false)
No generate-app or publish step required.
All commands are executed via the CLI script.
Authentication:
MIAODA_API_KEY is set, the CLI uses it for direct Miaoda API access.DUMATE_SESSION_ID and the desktop
gateway/scheduler environment are available.MIAODA_API_KEY explicitly before running commands.Examples below show the explicit-key form for clarity.
export MIAODA_API_KEY="your_api_key_here"
List all applications belonging to the authenticated user.
Usage:
python scripts/miaoda_api.py list-apps [--brief]
Optional Parameters:
--brief: Output only key fields: appId, name, type, appFocus, host, updatedAt. Recommended for agents to reduce token usage.--name NAME: Filter by app name (substring)--page PAGE: Page number (default: 1)--size SIZE: Page size (default: 12)Example:
export MIAODA_API_KEY="sk_xxxxx"
# Brief mode (recommended for agents)
python scripts/miaoda_api.py list-apps --brief
# Full mode
python scripts/miaoda_api.py list-apps
Returns: JSON array of applications with appId, name, type, etc.
Get detailed information about a specific application. Automatically injects conversationId into the response by default — no need to call get-context-id separately.
Usage:
python scripts/miaoda_api.py app-detail --app-id <app_id> [--no-context]
Required Parameters:
--app-id APP_ID: Application IDOptional Parameters:
--no-context: Skip auto-fetching conversationId from trajectory (faster, but response will not contain conversationId)Example:
export MIAODA_API_KEY="sk_xxxxx"
python scripts/miaoda_api.py app-detail --app-id app-abc123xyz
Returns: JSON object with application details, configuration, and status. data.conversationId is automatically populated.
appFocus — Publish Readiness Guide:
After calling app-detail, check data.appFocus to determine whether publishing is allowed:
appFocus | Meaning | Can Publish? |
|---|---|---|
NOT_GENERATE | Not yet generated | No — call generate-app first |
WAITING | Queued for generation | No — wait for generation to complete |
UNDER_CREATING | Generation in progress | No — wait for generation to complete |
CREATE_FAILED | Generation failed | No — retry generate-app |
DESIGNING | Generated, ready to edit/deploy | Yes |
RELEASED | Already published | Yes (only for an intentional new deployment after changes; not for retrying a successful publish) |
RELEASE_FAILED | Last publish explicitly failed | Yes (retry allowed only after confirming the failed deployment) |
UNDER_RELEASE | Publish currently in progress | Wait — do not call publish again |
Rule: Only proceed with publish when appFocus is DESIGNING, RELEASED, or RELEASE_FAILED.
When appFocus is RELEASED, do not publish again merely because downstream
UI output or frontend events are missing; check the existing publish result and
the event/rendering path first.
Recover the conversationId for an existing app by reading its trajectory. Useful when the conversationId has been lost after a session reset.
Usage:
python scripts/miaoda_api.py get-context-id --app-id <app_id>
Required Parameters:
--app-id APP_ID: Application IDOptional Parameters:
--fetch-timeout SECONDS: Request timeout in seconds (default: 10)Example:
export MIAODA_API_KEY="sk_xxxxx"
python scripts/miaoda_api.py get-context-id --app-id app-abc123xyz
Returns: {"appId": "app-abc123xyz", "conversationId": "conv-def456uvw"}
Use Cases:
conversationId is lostconversationId with chat --app-id --context-id to resume modificationShow a human/agent-readable summary of past interactions for an app. More convenient than trajectory or fetch-trajectory for quickly understanding what happened in previous sessions.
Usage:
python scripts/miaoda_api.py conversation-history --app-id <app_id> [options]
Required Parameters:
--app-id APP_ID: Application IDOptional Parameters:
--full: Show full content instead of truncated summaries (default: truncate at 200 chars)--limit N: Only show the last N conversation turns--fetch-timeout SECONDS: Request timeout in seconds (default: 10)Example:
export MIAODA_API_KEY="sk_xxxxx"
# View conversation history summary
python scripts/miaoda_api.py conversation-history --app-id app-abc123xyz
# Only show the last 3 turns
python scripts/miaoda_api.py conversation-history --app-id app-abc123xyz --limit 3
# Show full content without truncation
python scripts/miaoda_api.py conversation-history --app-id app-abc123xyz --full
Returns: JSON Lines, one entry per meaningful turn:
{"eventId": 5, "role": "user", "type": "message", "content": "创建一个待办事项应用..."}
{"eventId": 865, "role": "agent", "type": "file", "content": "[file: 需求文档.md]"}
{"eventId": 880, "role": "user", "type": "message", "content": "生成应用"}
Use Cases:
Start or continue a conversation to create or modify an application.
Usage:
python scripts/miaoda_api.py chat --text "description" [options]
Required Parameters:
--text TEXT: The message/instruction to sendOptional Parameters:
--context-id CONTEXT_ID: Conversation ID of an existing app.--app-id APP_ID: Application ID of an existing app.--query-mode QUERY_MODE: Query mode (default: deep_mode)--input-field-type INPUT_FIELD_TYPE: Input field type (default: web)--poll-interval SECONDS: Seconds between trajectory polls (default: 2.0)--fetch-timeout SECONDS: Per-request timeout for each trajectory fetch (default: 10)--no-stream: Return raw chat POST response without trajectory polling--prompt-generate: After polling, interactively ask whether to submit app generation if text was returned--node-json JSON: LGUI mode — pass a JSON string describing the currently-selected UI node (file path, line range, tag, current styles, etc.). When set, an extra kind: "data" part is appended to the request so the Agent can pinpoint the exact node instead of searching the codebase. See the LGUI (Live-GUI) Node-Scoped Editing section below.⚠️ IMPORTANT —
--app-idand--context-idmust always be used together.
Intent --app-id--context-idCreate a brand-new app omit omit Continue / modify an existing app required required (conversationId) Passing
--app-idwithout--context-idwill NOT modify the existing app. The platform will silently create a new app every time. The CLI will now raise an error in this case to prevent accidental app proliferation.
Examples:
1. Create a new application:
export MIAODA_API_KEY="sk_xxxxx"
python scripts/miaoda_api.py chat --text "创建一个待办事项管理应用"
Response includes appId and contextId for subsequent calls.
2. Continue conversation (refine PRD):
python scripts/miaoda_api.py chat \
--text "添加优先级标记功能" \
--app-id app-abc123xyz \
--context-id conv-def456uvw
3. Modify existing generated app:
python scripts/miaoda_api.py chat \
--text "把按钮颜色改成蓝色" \
--app-id app-abc123xyz \
--context-id conv-def456uvw
4. LGUI mode — modify one specific UI node:
python scripts/miaoda_api.py chat \
--text "改成蓝底黑字" \
--app-id app-abc123xyz \
--context-id conv-def456uvw \
--node-json '{"type":"node","node":{"id":"src/pages/HomePage.tsx:30:8","path":"src/pages/HomePage.tsx","line":"30","endLine":"35","tag":"H1","content":{"text":"HelloWorld"},"style":{"color":"rgb(46, 26, 15)","backgroundColor":"rgba(0, 0, 0, 0)"}}}'
Important Notes:
appId and contextId from the response and save themchat creates the app and starts PRD refinementchat directly modifies the app (no generate-app needed)LGUI mode lets a chat message carry the exact UI node the user is pointing
at, so the platform edits that node directly instead of inferring the target
from natural language alone.
A plain chat sends only text:
"parts": [
{"kind": "text", "text": "把整体配色换成暖色调的橙粉渐变风格"}
]
The Agent must search the codebase itself to work out what to change.
LGUI appends one extra kind: "data" part alongside the text:
"parts": [
{"kind": "text", "text": "改成蓝底黑字"},
{
"kind": "data",
"data": {
"type": "node",
"node": {
"id": "src/pages/HomePage.tsx:30:8",
"path": "src/pages/HomePage.tsx",
"line": "30",
"endLine": "35",
"isRoot": false,
"tag": "H1",
"content": {
"text": "HelloWorld",
"className": "gradient-text text-5xl font-bold",
"allowLink": true
},
"customAttr": {"href": null, "target": null},
"style": {
"color": "rgb(46, 26, 15)",
"fontSize": "72px",
"fontFamily": "\"Space Grotesk\"",
"margin": "0px",
"radius": "0px",
"backgroundColor": "rgba(0, 0, 0, 0)",
"backgroundImage": "linear-gradient(135deg, rgb(249, 115, 22), rgb(236, 72, 153))",
"borderColor": "rgb(245, 230, 214)",
"borderWidth": "0px",
"borderStyle": "solid"
}
}
}
}
]
Everything else in the request — contextId, metadata, queryMode, the
lifecycle, the trajectory polling — is identical to a plain chat. LGUI is
purely additive.
| Field | Meaning |
|---|---|
id | Node identifier, conventionally <path>:<line>:<column> |
path | Source file containing the node |
line / endLine | Line range of the node in that file |
isRoot | Whether this is the root node of the page |
tag | HTML/JSX tag name (e.g. H1, DIV, BUTTON) |
content.text | Current visible text |
content.className | Current class list |
content.allowLink | Whether the node may carry a link |
customAttr | Element attributes such as href / target |
style | Current computed styles (color, font, spacing, border, background…) |
Only path plus a line range is strictly needed to locate a node; the more
fields you supply, the less the Agent has to re-derive, and the more reliably a
relative instruction ("改成蓝底黑字", "字号再小一点") resolves.
python scripts/miaoda_api.py chat \
--text "改成蓝底黑字" \
--app-id <app_id> \
--context-id <conversation_id> \
--node-json '<json>'
--node-json accepts either form:
{"type":"node","node":{...}}{"id":"...","path":"...","line":"30",...} (the CLI
wraps it automatically)Invalid JSON, or JSON that is not an object, exits with an error before any request is sent.
| Situation | Use |
|---|---|
| User selected/clicked a specific element and you have its node data | --node-json |
| Change is scoped to one element ("这个标题改成蓝底黑字") | --node-json |
| Global change ("整体配色换成暖色调") | plain chat |
| Change spans multiple pages or components | plain chat |
| Adding a feature or new page | plain chat |
| You do not have real node data | plain chat — never fabricate a node |
Do not invent node data. A wrong path or line range points the Agent at
the wrong code. Node data must come from an actual selection source (the Miaoda
editor's element picker, or a caller that captured it). Without it, send a plain
chat and let the platform locate the target.
Also note:
--node-json carries one node. For several elements, send one chat per
node, updating conversationId from each response, or describe the change in
plain text.Poll trajectory events until the task reaches a terminal state.
Usage:
python scripts/miaoda_api.py trajectory --app-id <app_id> [options]
Required Parameters:
--app-id APP_ID: Application IDOptional Parameters:
--last-event-id EVENT_ID: Start eventId; -1 = all events from beginning (default: -1)--poll-interval SECONDS: Seconds between polls (default: 2.0)--fetch-timeout SECONDS: Per-request timeout in seconds (default: 10)--sse: Use legacy SSE streaming instead of pollingExample:
export MIAODA_API_KEY="sk_xxxxx"
python scripts/miaoda_api.py trajectory --app-id app-abc123xyz
Use Cases:
result.artifact.parts[].data.actions[])Fetch one batch of trajectory events (single request, no polling loop).
Usage:
python scripts/miaoda_api.py fetch-trajectory --app-id <app_id> [options]
Required Parameters:
--app-id APP_ID: Application IDOptional Parameters:
--last-event-id EVENT_ID: Fetch events after this eventId; -1 = all (default: -1)--fetch-timeout SECONDS: Request timeout in seconds (default: 10)Example:
# First call — get all events (note maxEventId from stderr)
python scripts/miaoda_api.py fetch-trajectory --app-id app-abc123xyz
# Subsequent calls — incremental fetch
python scripts/miaoda_api.py fetch-trajectory --app-id app-abc123xyz --last-event-id 345
Events are printed to stdout as JSON lines; {"maxEventId": N, "isTerminal": bool} is printed to stderr.
Generate App readiness: Inspect result.artifact.parts[].data.actions[] for an action with "type":"button" and "event":{"name":"generateApp"}. Example:
{"type":"button","label":"Generate App","value":"Generate App","event":{"name":"generateApp"}}
When this button appears, PRD is ready and generate-app may be called.
Submit app-generation confirmation and return immediately with appId/conversationId.
Usage:
python scripts/miaoda_api.py generate-app --app-id <app_id> --context-id <context_id> [options]
Required Parameters:
--app-id APP_ID: Application IDOptional Parameters:
--context-id CONTEXT_ID: Conversation ID (default: "")--query-mode QUERY_MODE: Query mode (default: deep_mode)--watch: Block and poll trajectory until generation completes (recommended) (default: return immediately)--poll-interval SECONDS: Seconds between polls when --watch is set (default: 2.0)--fetch-timeout SECONDS: Per-request timeout in seconds when --watch is set (default: 10)Example:
export MIAODA_API_KEY="sk_xxxxx"
# Recommended: submit and block until generation finishes
python scripts/miaoda_api.py generate-app \
--app-id app-abc123xyz \
--context-id conv-def456uvw \
--watch
# Alternative: submit and return immediately — check status later with fetch-trajectory
python scripts/miaoda_api.py generate-app \
--app-id app-abc123xyz \
--context-id conv-def456uvw
Important:
result.artifact.parts[].data.actions[]: an action with "type":"button" and "event":{"name":"generateApp"} (label may vary by locale; use event name for the check). This indicates PRD is ready.chat to modify the generated app
- Never call this command when
chatreturns"needGenerateApp": false. General tasks (reports, PPT, analysis) complete in thechatstep and do not have a generation stage.
Trigger deployment to production.
Usage:
python scripts/miaoda_api.py publish --app-id <app_id> [options]
Required Parameters:
--app-id APP_ID: Application ID to publishOptional Parameters:
--env ENV: Target environment (default: PRODUCE)--wait: Auto-poll publish status until SUCCESS or FAILEDExamples:
1. Publish and manually check status:
export MIAODA_API_KEY="sk_xxxxx"
python scripts/miaoda_api.py publish --app-id app-abc123xyz
Returns releaseId immediately. Then poll with publish-status.
2. Publish and auto-wait for completion (recommended):
export MIAODA_API_KEY="sk_xxxxx"
python scripts/miaoda_api.py publish --app-id app-abc123xyz --wait
This polls automatically and exits when deployment succeeds or fails.
Important:
appId.SUCCESS status is terminal for this publish attempt. Do not invoke
publish again because downstream UI output or events were not observed.
Retry only after a command error or an explicit final FAILED status.Check the status of a deployment.
Usage:
python scripts/miaoda_api.py publish-status --release-id <release_id>
Required Parameters:
--release-id RELEASE_ID: Release ID from publish commandExample:
export MIAODA_API_KEY="sk_xxxxx"
python scripts/miaoda_api.py publish-status --release-id app_release_record-xyz789abc
Returns: JSON with status: PROCESSING, RUNNING, SUCCESS, or FAILED
Usage Pattern:
# Get release ID from publish
RELEASE_ID=$(python scripts/miaoda_api.py publish --app-id app-abc123xyz | jq -r '.releaseId')
# Poll status
while true; do
STATUS=$(python scripts/miaoda_api.py publish-status --release-id $RELEASE_ID | jq -r '.status')
echo "Status: $STATUS"
if [[ "$STATUS" == "SUCCESS" || "$STATUS" == "FAILED" ]]; then
break
fi
sleep 5
done
Tip: Use publish --wait to avoid manual polling.
export MIAODA_API_KEY="sk_xxxxx"
cd ~/.openclaw/skills/miaoda-app-builder
# Step 1: Create app via chat (returns appId + conversationId on first line)
FIRST=$(python scripts/miaoda_api.py chat --text "创建一个简单的计数器应用" | head -1)
APP_ID=$(echo $FIRST | jq -r '.appId')
CONTEXT_ID=$(echo $FIRST | jq -r '.conversationId')
# Step 2: Generate and wait for completion (recommended: --watch auto-polls until done)
python scripts/miaoda_api.py generate-app \
--app-id $APP_ID \
--context-id $CONTEXT_ID \
--watch
# Step 3: Publish (with auto-wait)
python scripts/miaoda_api.py publish --app-id $APP_ID --wait
IMPORTANT — Always update
CONTEXT_IDfrom eachchatresponse.Each
chatcall prints a JSON header line{"appId": "...", "conversationId": "..."}. The platform may return an updatedconversationIdin this response. Always capture it and use it for the nextchatcall, or the next round will create a brand-new app.
export MIAODA_API_KEY="sk_xxxxx"
cd ~/.openclaw/skills/miaoda-app-builder
APP_ID="app-abc123xyz"
# Step 1: Get app detail — conversationId is auto-injected
DETAIL=$(python scripts/miaoda_api.py app-detail --app-id $APP_ID)
CONTEXT_ID=$(echo $DETAIL | jq -r '.data.conversationId')
# Abort early if conversationId is missing (app may have no history yet)
if [ -z "$CONTEXT_ID" ] || [ "$CONTEXT_ID" = "null" ]; then
echo "Error: could not retrieve conversationId. Try: get-context-id --app-id $APP_ID"
exit 1
fi
# Step 2 (optional): Review conversation history to understand previous work
python scripts/miaoda_api.py conversation-history --app-id $APP_ID
# Step 3a: First modification — capture the UPDATED conversationId from the response header
CHAT_RESULT=$(python scripts/miaoda_api.py chat \
--text "把背景颜色改成深色模式" \
--app-id $APP_ID \
--context-id $CONTEXT_ID | head -1)
CONTEXT_ID=$(echo $CHAT_RESULT | jq -r '.conversationId') # ← UPDATE for next round
# Step 3b: Second modification — uses the updated CONTEXT_ID
CHAT_RESULT=$(python scripts/miaoda_api.py chat \
--text "添加暗黑模式切换按钮" \
--app-id $APP_ID \
--context-id $CONTEXT_ID | head -1)
CONTEXT_ID=$(echo $CHAT_RESULT | jq -r '.conversationId') # ← UPDATE again
# Step 4: Re-publish
python scripts/miaoda_api.py publish --app-id $APP_ID --wait
Used to:
For a new application, chat creates the project and begins the PRD stage.
For an existing application, chat performs iterative modifications.
Streams conversation progress and system events.
Use this to determine:
Triggers application generation.
Call only when trajectory contains a Generate App button in result.artifact.parts[].data.actions[]: an action with "type":"button" and "event":{"name":"generateApp"}. This indicates PRD is ready. Do not rely on text alone—the button structure is the authoritative signal.
Call once during initial creation. Do not call again for modifications.
Triggers application deployment.
Use --wait flag to auto-poll status (recommended).
Treat SUCCESS as terminal. Missing or malformed downstream output is not
evidence that deployment failed and must not trigger another publish.
Polls deployment progress until the release completes.
Not needed if using publish --wait.
Symptom:
IAM access key validation failed.
Cause:
The configured MIAODA_API_KEY is invalid or missing.
Resolution:
Go to the Miaoda(秒哒) official website:
In the left navigation panel, apply for an available access key.
Set the key as the environment variable:
export MIAODA_API_KEY="sk_xxxxx"
Or create a .env file in the skill directory:
echo "MIAODA_API_KEY=sk_xxxxx" > ~/.openclaw/skills/miaoda-app-builder/.env
Symptom:
NotOpenSSLWarning: urllib3 v2 only supports OpenSSL 1.1.1+,
currently the 'ssl' module is compiled with 'LibreSSL 2.8.3'
Cause:
macOS uses LibreSSL by default, but urllib3 v2 recommends OpenSSL.
Impact:
This is a warning only. API calls still work correctly.
Resolution (optional):
If you want to suppress the warning:
pip3 install 'urllib3<2'
Or ignore it - it doesn't affect functionality.
Symptom:
[秒点不足] 您的秒点余额不足,无法继续操作。
请前往秒哒官网充值:https://www.miaoda.cn
Or within an error message:
秒点不足,您可明日再来,或邀请新人立即获得秒点奖励。
Cause:
Your Miaoda account does not have enough 秒点 (Credits) to perform this operation (e.g., generating or publishing an app).
Resolution:
Visit the official Miaoda website to recharge your credits:
Log in → navigate to the recharge page → complete payment. Credits are credited immediately.
Agents should handle the following situations:
appId or conversationId (use get-context-id to recover a lost conversationId)After a publish command returns successfully, do not republish solely because
downstream UI output or a frontend event is missing. Only a command-level
failure or an explicit final FAILED deployment status permits a retry.
If workflow state is unclear, inspect the trajectory or application detail before taking the next action.
Pro Tips:
--help when unsure about parameters--wait flag with publish to simplify deploymentappId and contextId from first chat responsegenerate-app once during initial creationchat directly for modificationsThe final response rules are defined in Final Output Contract above. Do not add a second sharing rule or expose editor/production URLs in ordinary text.