miaoda-cli
v1.0.2
Published
秒哒 (Miaoda) Node.js CLI — chat-driven full-stack app builder
Readme
miaoda-cli (WorkBuddy skill)
WorkBuddy skill wrapping the 秒哒 (Miaoda) platform.
Login uses OAuth2 Device Authorization Grant (RFC 8628), mirroring the
cnb-skill / tencentmeeting-cli convention already used by WorkBuddy.
Requirements
- Node.js ≥ 18 (built-in
fetch,AbortController)
Install
cd node-cli
npm install
npm link # optional: expose `miaoda` globallyLogin / status / logout
miaoda login # opens browser, prints the device code and returns
miaoda login --no-browser # just prints the URL, no auto-open
miaoda login --debug # verbose device-auth trace
miaoda status # single-shot token exchange attempt; re-run until approved (auto-refreshes if expired)
miaoda logout # delete ~/.miaoda/tokenToken is stored at ~/.miaoda/token (mode 0600):
{
"access_token": "...",
"refresh_token": "...",
"expires_at": 1770000000,
"platform_url": "https://www.miaoda.cn",
"client_id": "miaoda_cli",
"login_host": "https://www.miaoda.cn"
}Token resolution order
- WorkBuddy sandbox — when
AGENTOS_RUNTIME_IDis set, token is fetched from the URL inWORKBUDDY_TOKEN_URL_MIAODA_APP(WorkBuddy-managed, no local file involved). MIAODA_TOKEN_FOR_CODEBUDDY— set by the CodeBuddy NPC runtime.MIAODA_TOKEN— plain env var override (CI/automation), no expiry check.~/.miaoda/tokenfile — normal interactive login; auto-refreshes viarefresh_tokenwhen expired, prompts re-login when refresh fails.
Exit codes follow the cnb-skill convention: 0 on success, 1 on any
failure (auth required, refresh failed, API error, etc).
Project layout
node-cli/
├── bin/miaoda.js CLI entry (commander)
├── src/
│ ├── api.js Miaoda platform HTTP client (chat/publish/...)
│ ├── exit-code.js Shared exit-code constants
│ ├── commands/
│ │ ├── login.js Device authorization grant flow
│ │ ├── logout.js Delete ~/.miaoda/token
│ │ └── status.js Check login state, auto-refresh
│ └── utils/
│ ├── token-path.js ~/.miaoda/token path
│ ├── load-token.js / save-token.js
│ ├── mask-token.js Redact tokens in --debug output
│ ├── open-browser.js Cross-platform `open`/`xdg-open`/`start`
│ ├── request-device-code.js POST /oauth2/device/auth
│ ├── do-token-request.js Single POST /oauth2/token attempt
│ ├── poll-token.js RFC 8628 §3.4/3.5 single exchange attempt (no loop)
│ ├── refresh-token.js POST grant_type=refresh_token
│ ├── get-token.js Sync token getter (legacy/back-compat)
│ ├── is-workbuddy-sandbox.js AGENTOS_RUNTIME_ID detection
│ ├── get-token-env-key.js WORKBUDDY_TOKEN_URL_<CONNECTOR> naming
│ ├── resolve-token-source.js 4-tier token source resolution
│ ├── resolve-token.js Resolve + auto-refresh + exit(1) on failure
│ ├── resolve-api-domain.js Pick API domain (login_host vs override)
│ └── device-auth.js Barrel re-export of the above
└── SKILL.md WorkBuddy skill descriptorSupported commands
login / logout / status
list-apps [--brief] [--name] [--page] [--size]
app-detail --app-id [--no-context]
chat --text [--app-id --context-id] [--no-stream] [--prompt-generate]
generate-app [--app-id] [--context-id] [--watch]
trajectory --app-id [--last-event-id]
fetch-trajectory --app-id [--last-event-id]
get-context-id --app-id
conversation-history --app-id [--full] [--limit]
publish --app-id [--wait]
publish-status --release-idEach command supports --help.
Application lifecycle (summary)
chat → (PRD refinement) → generate-app --watch → publish --wait
↓
https://<app_id>.appmiaoda.comAfter generation, iterate by re-issuing chat --app-id ... --context-id ....
Never re-run generate-app for the same app.
Placeholders to confirm with the platform team
The device-auth endpoints in src/utils/request-device-code.js /
poll-token.js / refresh-token.js assume:
POST {platformUrl}/oauth2/device/authreturns{ device_code, user_code, verification_uri, verification_uri_complete, expires_in, interval }POST {platformUrl}/oauth2/tokenwithgrant_type=urn:ietf:params:oauth:grant-type:device_codereturns{ access_token, refresh_token, expires_in, token_type, scope }on success, or{ error, error_description }withauthorization_pending/slow_down/access_denied/expired_tokenwhile pending.
Update these three files once the real Miaoda OAuth endpoint paths/fields are confirmed — the polling state machine and exit-code behavior do not need to change.
