napcat-cli
Standalone CLI and daemon for NapCat QQ bot management with skills-fs integration.
介绍博文: napcat-cli 群配置与管理速查 — 从零配置、群消息收发、Agent Wake 的完整链路。
napcat-cli provides a CLI for all NapCat API operations, a WebSocket daemon that bridges to a skills-fs HTTP provider, and an agent wake-up mechanism for integrating with Hermes or other AI agents.
napcat send group 123456 -m "Hello"
napcat send private 987654 -m "Hi"
napcat recall 1001
napcat events --limit 10
napcat daemon start
napcat wake --reason NEW_MESSAGE
Commands
| Command | Description |
|---|---|
napcat api <endpoint> |
Raw API access (like gh api) |
napcat send group <id> -m "msg" |
Send group message |
napcat send private <id> -m "msg" |
Send private message |
napcat reply <id> -m "msg" |
Reply to a message |
napcat recall <msg_id> |
Recall a message |
napcat group <sub> |
Group list, members, settings |
napcat friend <sub> |
Friend list, info |
napcat file <sub> |
File upload, download, list |
napcat events |
Read events from SQLite |
napcat alerts [--clear] |
Pending alerts |
napcat status |
Bot online status |
napcat config get/set |
Configuration management |
napcat daemon start/stop/status/restart |
Watch daemon |
napcat fs tree |
Skills-fs directory tree |
napcat wake [--reason R] [--prompt P] [--transport T] [--dry-run] |
Wake the configured agent (HTTP/CLI, auto-fallback) |
napcat setup |
Interactive setup wizard |
napcat phone |
Textual phone-style TUI |
Setup
uv tool install napcat-cli # recommended — isolated install via uv (alternative: pip install napcat-cli)
napcat setup # interactive — guides token, data dir, skills-fs, wake
napcat daemon start
The setup wizard writes two config files:
<data_dir>/config.json— API URL, token, self_id, ports, triggers<data_dir>/daemon.json— All fields consumed bywatch.pyincludingskills_fs_*settings
Non-interactive mode uses defaults:
napcat setup --non-interactive # no prompts, validates token
napcat setup --yes # skip token validation
napcat setup --force # overwrite existing config
Agent Wake
When a notable QQ event arrives, the daemon wakes an external agent (Hermes by default) carrying a contextual prompt so it can read the inbox and reply. The wake mechanism is generic and pluggable — Hermes is just the default preset; any HTTP endpoint or shell command works.
How it works
QQ Event (WS) --> EventProcessor --> WakeOrchestrator (debounce/cooldown/queue)
|
Waker (auto-fallback)
HTTP API (RECOMMENDED) CLI (LEGACY — 不推荐)
POST /chat hermes -z "..."
fixed session_id --continue <session>
- Debounce (
wake_debounce_seconds, default 3): coalesces a burst of same-reason events into one wake. - Cooldown (
wake_cooldown_seconds, default 30): suppresses repeat wakes for non-urgent reasons. - Serial queue:
WakeOrchestratorhas a single worker thread — wakes to the same session are processed one at a time, preventing concurrent "split personality" responses. - Timeout:
wake_timeout(default 300s) — how long to wait for the agent to respond.
Every wake is logged to daemon.log:
[WAKE] trigger reason=AT_ME who=Alice where=group123 text='hello'
[WAKE] queued reason=AT_ME pending=1 debounce=1.0s primary=auto
[WAKE] delivered reason=AT_ME transport=http detail=POST /api/sessions/.../chat -> 200
Event routing
| Trigger | Behavior |
|---|---|
AT_ME, REPLY_TO_ME, DM_ME |
Near-immediate wake (cooldown bypassed). Prompt includes who/where/text, image metadata, reply chain, and skill hints. |
GROUP_TRIGGER |
Debounced wake (group trigger-word match) |
NEW_MESSAGE (not @) |
Tracked, not woken; if unread longer than wake_new_message_idle_seconds -> NEW_MESSAGE_BACKLOG wake |
NEW_FRIEND, NEW_REQUEST, BOT_BANNED, NEW_POKE, GROUP_ADMIN_CHANGE, NEW_GROUP_MEMBER, BOT_OFFLINE, ... |
Debounced + cooldown-bounded wake |
AT_ME detection supports all NapCat message formats: CQ code ([CQ:at,qq=...]), display name (@name (qq)), and message segments ({"type":"at","data":{"qq":"..."}}).
Two transports — HTTP recommended, CLI legacy
⚠️ Wake transport recommendation. The HTTP API server is the recommended transport for agent wake. The CLI one-shot backend is kept as a legacy fallback for environments where running the API server isn't viable — it is known to be less reliable (process-spawn latency, quoting hazards, new-session drift, no idempotency). When
wake_primary=autofalls back to CLI because HTTP is unreachable, the wake may appear to succeed but never deliver the prompt (only HTTP returns a realdeliveredindaemon.log). Enable the HTTP API server first — only keep CLI for diagnostic--dry-runor as last-resort fallback. SeeAGENTS.md"Critical Lessons —hermes -z -" for the most common CLI failure mode.
| Transport | Status | When it picks | Needs |
|---|---|---|---|
| HTTP API server | RECOMMENDED | primary=http or primary=auto when configured + reachable |
wake_http_url + wake_http_key (bearer) |
| CLI one-shot | LEGACY / 不推荐 | primary=cli (forced) or primary=auto only if HTTP is not configured/unreachable as a last-resort fallback |
Agent CLI on PATH |
wake_primary=auto tries HTTP first (if configured + reachable), else falls back to CLI.
Configuring for Hermes Agent
Option A: HTTP API Server (recommended)
The HTTP API server is the recommended wake transport. It provides:
- Session continuity: all wakes go to the same session (no "split personality")
- Idempotency:
Idempotency-Keyheader prevents duplicate processing on retry - Persistence: session history is stored in
~/.hermes/state.db - Lower latency: no CLI process spawn overhead
Step 1: Enable Hermes API Server
Add to ~/.hermes/.env:
API_SERVER_ENABLED=true
API_SERVER_KEY=$(openssl rand -hex 32) # generate a random key
Restart the gateway:
sudo systemctl restart hermes-gateway.service
# Verify API server is listening:
curl http://127.0.0.1:8642/health
Step 2: Create a dedicated QQ session
curl -X POST http://127.0.0.1:8642/api/sessions \
-H "Authorization: Bearer <API_SERVER_KEY>" \
-H "Content-Type: application/json" \
-d '{"title": "napcat-qq"}'
# Response: {"session": {"id": "api_1784824379_59dd9495", ...}}
Step 3: Configure napcat-cli
napcat config set wake_enabled true
napcat config set wake_preset hermes
napcat config set wake_primary auto # try HTTP first, CLI fallback
napcat config set wake_http_url http://127.0.0.1:8642
napcat config set wake_http_key <API_SERVER_KEY>
napcat config set wake_http_session_id <SESSION_ID> # from Step 2
napcat config set wake_session napcat-qq # CLI fallback session name
napcat config set wake_timeout 300 # seconds
Or edit ~/.napcat-data/daemon.json directly:
{
"wake_enabled": true,
"wake_preset": "hermes",
"wake_primary": "auto",
"wake_http_url": "http://127.0.0.1:8642",
"wake_http_key": "<API_SERVER_KEY>",
"wake_http_session_id": "<SESSION_ID>",
"wake_timeout": 300.0,
"wake_cli_command": "hermes --continue {session} -z \"$(cat {prompt_file})\" --yolo --pass-session-id"
}
Key env vars (also checked by Hermes preset, in priority order):
NAPCAT_WAKE_HTTP_KEY— HTTP API bearer tokenHERMES_API_KEY— alias for the above
Option B: CLI one-shot — LEGACY / 不推荐
⚠️ Not recommended. Prefer Option A (HTTP). The CLI backend is kept for environments where running the API server isn't viable, but it has multiple known failure modes (process-spawn latency, quoting hazards, no idempotency, new-session drift). When
wake_primary=autofalls back to CLI after an HTTP probe fails, the wake often appears to succeed but never delivers the prompt to the agent. Diagnose by runninggrep '\[WAKE\]' ~/.napcat-data/daemon.log— only the HTTP backend writes adelivered ... transport=http -> 200line. For agents other than HTTP, configure Option A first; only fall back to CLI when there is no other choice.
If you don't want to run the API server, napcat-cli can invoke the Hermes CLI
directly. The CLI backend writes the prompt to a temp file and passes it via
$(cat {prompt_file}) to avoid shell quoting issues.
napcat config set wake_primary cli
napcat config set wake_session napcat-qq
napcat config set wake_cli_command 'hermes --continue {session} -z "$(cat {prompt_file})" --yolo --pass-session-id'
Limitations of CLI mode:
- Each invocation may create a new session if the name doesn't match an existing one
- No idempotency — retries may cause duplicate processing
- ~30-60s per invocation (CLI process startup + model inference)
- Large prompts with special characters require the
$(cat {prompt_file})pattern (not-z -, which Hermes treats as literal text "-")
Configuring for other agents
napcat-cli's wake system is agent-agnostic. Any HTTP endpoint or shell command works.
Custom HTTP endpoint
napcat config set wake_preset custom
napcat config set wake_primary http
napcat config set wake_http_url http://127.0.0.1:9000
napcat config set wake_http_key my-secret-key
# The HTTP backend POSTs to: {wake_http_url}/api/sessions/{session}/chat
# Body: {"input": "<prompt>"}
# Headers: Authorization: Bearer <key>, Idempotency-Key: <unique-id>
# If your endpoint uses a different path/body format, see wake_backend.py HttpWakeBackend
Custom CLI command — LEGACY / 不推荐
⚠️ Not recommended. The shell-command backend is kept for legacy / experimental setups only. Prefer Custom HTTP endpoint above — see the "Two transports — HTTP recommended, CLI legacy" note at the top of this section for why.
napcat config set wake_preset custom
napcat config set wake_primary cli
napcat config set wake_cli_command 'my-agent --prompt "$(cat {prompt_file})"'
# Placeholders: {prompt_file} (temp file path), {session}, {prompt} (inline, shell-quoted)
Disable wake entirely
napcat config set wake_preset none
Manual / debug
napcat wake # reason: manual, contextual default prompt
napcat wake --reason AT_ME --prompt "hello"
napcat wake --transport http # recommended; --transport cli is legacy
napcat wake --dry-run # render the HTTP request + CLI command without executing
napcat wake test # per-transport configured + reachable probe
napcat wake sessions # list Hermes sessions (HTTP backend)
grep '\[WAKE\]' ~/.napcat-data/daemon.log # see when/why/how wakes fired
The agent replies via napcat send / napcat reply (or the skills-fs write
path) — see napcat_cli/data/SKILL.md.
Troubleshooting & verification
Verify the full wake chain end-to-end
-
Check daemon is running:
ps aux | grep watch.py | grep -v grep tail -5 ~/.napcat-data/daemon.log # should show recent events
-
Check Hermes API server is listening:
curl -s http://127.0.0.1:8642/health # Expected: {"status": "ok", ...}
-
Test HTTP wake manually:
curl -X POST http://127.0.0.1:8642/api/sessions/<SESSION_ID>/chat \ -H "Authorization: Bearer <KEY>" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: test-001" \ -d '{"input": "ping"}' # Expected: {"message": {"content": "..."}} with a response
-
Test CLI wake manually (legacy — prefer HTTP test above):
⚠️ CLI is legacy / not recommended. This reference is kept for diagnosing environments where the HTTP API server is genuinely unavailable. Only test CLI as a last resort — the HTTP test (step 3) is the one that matters.
echo "hello" > /tmp/prompt.txt hermes --continue napcat-qq -z "$(cat /tmp/prompt.txt)" --yolo --pass-session-id # Expected: agent responds within 30-60s # NOTE: -z - does NOT read stdin. Always use -z "$(cat file)" or -z "literal".
-
Send a test @mention to the bot in a group, then check logs:
grep '\[WAKE\]' ~/.napcat-data/daemon.log | tail -10 # Should show: trigger -> queued -> delivered (or failed)
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Bot doesn't respond to @mentions | Daemon not running, or crash killed event loop | ps aux | grep watch.py; check daemon.log for errors; restart daemon |
[WAKE] delivered but no reply |
Agent received prompt but didn't act | Check Hermes session via hermes sessions list; verify agent has napcat skills/tools |
[WAKE] failed ... timeout |
Agent took > wake_timeout seconds |
Increase wake_timeout in daemon.json |
[WAKE] failed ... session resolution failed |
HTTP session ID not found | Run napcat wake sessions to list; recreate if needed |
| Multiple replies to one event ("split personality") | Concurrent wakes to same session | Set wake_primary=http (or ensure HTTP probe passes); the worker thread serializes wakes through HTTP's fixed session, but CLI one-shot can still create drift |
hermes -z - sends literal "-" |
-z takes literal string, not stdin |
Use -z "$(cat {prompt_file})" template |
| Events stop after a bad message | Unhandled exception in event handler | Check daemon.log for process() ERROR; the handler now has try/except |
Static analysis (prevents regressions)
The test suite includes tests/test_lint.py which runs pyflakes and
py_compile on every file in napcat_cli/. This catches undefined names,
syntax errors, and other issues that would crash the daemon at runtime.
python -m pytest tests/test_lint.py -v
# Should pass: test_no_undefined_names, test_all_modules_compile
Image & OCR
The wake prompt automatically includes image metadata when an image message is received:
file_id— image file IDurl— download URLsub_type— image type (0=normal, 1=sticker, 7=like)file_size— file size in bytessummary— PaddleOCR auto-recognized text (if available)reply_id— reply message ID (for tracking reply chains)
PaddleOCR is integrated. Image text is auto-recognized and included in the
summary field. The agent can also use multimodal vision to read the image URL
directly. NapCat's built-in OCR is unavailable — do not use /napcat/ocr.
| Operation | CLI | skills-fs |
|---|---|---|
| Download image | napcat get_image <url> |
/napcat/get_image |
| View message detail | napcat group <gid> get_message <mid> |
/napcat/groups/:gid/:range/:mid/:content |
| Send image | napcat send group <gid> -f <path> |
/napcat/groups/:gid/send/image |
Environment Variables
| Variable | Default | Description |
|---|---|---|
NAPCAT_API_URL |
http://127.0.0.1:18801 |
NapCat HTTP endpoint |
NAPCAT_TOKEN |
— | API auth token |
NAPCAT_DATA_DIR |
~/.napcat-data |
Data directory |
skills-fs Integration
The daemon runs an HTTP provider server (port 18820/18821) that skills-fs calls to read/write NapCat API endpoints through the virtual filesystem.
When skills_fs_enabled is true in daemon.json, the daemon spawns skills-fs
automatically with the configured mountpoint and config file.
Distribution note:
skills-fsis a compiled Go binary (git submodule pinned atcb13f37). It is not bundled in the pip/uv install ofnapcat-cli. If you need the FUSE mount (optional), build it separately:git clone https://github.com/yandu-app/skills-fs cd skills-fs && make build # produces ./skills-fs napcat config set skills_fs_binary /path/to/skills-fs napcat daemon stop && napcat daemon startOr place
skills-fson yourPATHand the daemon will find it. This feature is optional — the CLI, daemon, and wake system work without it.
Manual start:
skills-fs fuse --config ~/.napcat-data/skills-fs.json \
--mountpoint ~/.napcat-data/skills/napcat-cli --allow-other
Development
napcat-cli/
├── napcat_cli/ # Installable package
│ ├── cli.py # CLI entry point
│ ├── wake.py # Wake command-template renderer
│ ├── wake_backend.py # Generic HTTP/CLI wake transports + Waker (auto-fallback)
│ ├── wake_presets.py # Hermes/custom/none presets -> Waker
│ ├── wake_orchestrator.py # Debounce, cooldown, backlog sweep, contextual prompts
│ ├── setup_wizard.py # Setup wizard
│ ├── daemon/ # Watch daemon, schemas
│ ├── lib/ # API, config, events
│ ├── tui/ # Textual TUI
│ └── data/ # SKILL.md, persona.md
├── pyproject.toml
├── tests/
├── skills-fs/ # Go submodule
└── tools/ # Dev utilities
python -m pytest tests/ -v
python -m build --wheel
uv build && uv publish
License
MIT
Release files for napcat-cli 2.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| napcat_cli-2.2.0.tar.gz | 144.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| napcat_cli-2.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 267.7 kB
Release files / napcat_cli-2.2.0.tar.gz
| Download URL | napcat_cli-2.2.0.tar.gz |
|---|---|
| Size | 144.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
45d21d7ab171687655f4088c7c03cb1724ab94f7dfb5ff44f9fd2d1e502106ec
|
|
BLAKE2b-256 checksum How to use checksums |
7113bf533ad78f3de754fa45c9bd5b417588a6dc443e49e50bd647012afe3852
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / napcat_cli-2.2.0-py3-none-any.whl
| Download URL | napcat_cli-2.2.0-py3-none-any.whl |
|---|---|
| Size | 123.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3a0bffa21fc6c06c0c3aa60503e5258dd265af58adc7295e84d37fac37e59b32
|
|
BLAKE2b-256 checksum How to use checksums |
fcbf1ccb496ad20bb6e31434ed6fc10fc5ce60bd1a43809372c2d42a9ce12aad
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|