Skip to main content

Standalone CLI and daemon for NapCat QQ bot management with skills-fs integration

Project description

napcat-cli

Standalone CLI and daemon for NapCat QQ bot management with skills-fs integration.

PyPI Python versions

介绍博文: 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 by watch.py including skills_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: WakeOrchestrator has 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=auto falls back to CLI because HTTP is unreachable, the wake may appear to succeed but never deliver the prompt (only HTTP returns a real delivered in daemon.log). Enable the HTTP API server first — only keep CLI for diagnostic --dry-run or as last-resort fallback. See AGENTS.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-Key header 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 token
  • HERMES_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=auto falls back to CLI after an HTTP probe fails, the wake often appears to succeed but never delivers the prompt to the agent. Diagnose by running grep '\[WAKE\]' ~/.napcat-data/daemon.log — only the HTTP backend writes a delivered ... transport=http -> 200 line. 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

  1. Check daemon is running:

    ps aux | grep watch.py | grep -v grep
    tail -5 ~/.napcat-data/daemon.log   # should show recent events
    
  2. Check Hermes API server is listening:

    curl -s http://127.0.0.1:8642/health
    # Expected: {"status": "ok", ...}
    
  3. 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
    
  4. 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".
    
  5. 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 ID
  • url — download URL
  • sub_type — image type (0=normal, 1=sticker, 7=like)
  • file_size — file size in bytes
  • summary — 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-fs is a compiled Go binary (git submodule pinned at cb13f37). It is not bundled in the pip/uv install of napcat-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 start

Or place skills-fs on your PATH and 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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

napcat_cli-2.2.0.tar.gz (144.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

napcat_cli-2.2.0-py3-none-any.whl (123.8 kB view details)

Uploaded Python 3

File details

Details for the file napcat_cli-2.2.0.tar.gz.

File metadata

  • Download URL: napcat_cli-2.2.0.tar.gz
  • Upload date:
  • Size: 144.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for napcat_cli-2.2.0.tar.gz
Algorithm Hash digest
SHA256 45d21d7ab171687655f4088c7c03cb1724ab94f7dfb5ff44f9fd2d1e502106ec
MD5 61a43aa0439cb3661b18286147aaba33
BLAKE2b-256 7113bf533ad78f3de754fa45c9bd5b417588a6dc443e49e50bd647012afe3852

See more details on using hashes here.

File details

Details for the file napcat_cli-2.2.0-py3-none-any.whl.

File metadata

  • Download URL: napcat_cli-2.2.0-py3-none-any.whl
  • Upload date:
  • Size: 123.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for napcat_cli-2.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3a0bffa21fc6c06c0c3aa60503e5258dd265af58adc7295e84d37fac37e59b32
MD5 cb3462d88927a2d8ce26b56ed3a28d0e
BLAKE2b-256 fcbf1ccb496ad20bb6e31434ed6fc10fc5ce60bd1a43809372c2d42a9ce12aad

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page