fix(skills): improve create-instance for cross-platform and add channel reference

- Make SKILL.md platform-agnostic (remove Windows-only path rules)
- Add 14-channel quick-reference table with required fields
- Create references/channels.md with detailed per-channel config
- Inherit model from parent config when not explicitly specified
- Consolidate duplicate file reads in _patch_config
- Add email channel consent_granted field documentation
- Fix auto_reply_enabled default value (true, not false)
- Add troubleshooting section to SKILL.md
This commit is contained in:
chengyongru 2026-04-27 11:35:28 +08:00 committed by chengyongru
parent a7aeb1d2ea
commit b815aa8c0e
3 changed files with 248 additions and 31 deletions

View File

@ -1,50 +1,64 @@
--- ---
name: create-instance name: create-instance
description: "Create a new nanobot instance with separate config and workspace. Use when the user wants to set up a new bot for a different channel, persona, or purpose." description: "Create a new nanobot instance with separate config and workspace. Use when the user wants to set up a new bot, create a new instance for a different channel, persona, or purpose. Triggers on: create instance, new bot, set up bot, add bot, create telegram/discord/feishu/slack/wechat/wecom/dingtalk/qq/email/matrix/msteams/whatsapp bot, multi-instance setup."
--- ---
# Create Instance # Create Instance
Set up a new nanobot instance with its own config and workspace. Set up a new nanobot instance with its own config and workspace.
## When to Use
When the user wants to create a new bot instance — typically for a different channel (Telegram, Discord, WeChat, etc.) or with different settings.
## Steps ## Steps
1. **Collect information from the user** (ask one at a time if not already provided): 1. **Collect information** (ask one at a time if not already provided):
- **Instance name** (required): a short identifier like `telegram-bot`, `discord-bot` - **Instance name** (required): short identifier, e.g. `telegram-bot`, `work-slack`
- **Channel type** (required): e.g. `telegram`, `discord`, `weixin`, `feishu`, `slack` - **Channel type** (required): see table below
- **Model** (optional): LLM model to use. Defaults to the same model as the current instance. - **Model** (optional): LLM model, defaults to current instance
2. **Do NOT collect sensitive information** in the chat (API keys, bot tokens, secrets). API keys are automatically copied from the current instance. Channel-specific tokens (e.g. `telegram.token`) still need to be filled in manually. 2. **Do NOT collect secrets** in the chat (API keys, bot tokens). API keys are automatically inherited from the current instance via `--inherit-config`. Channel-specific tokens must be filled in manually after creation.
3. **Run the creation script** using the exec tool — always pass `--inherit-config` with the current instance's config path so API keys are copied: 3. **Run the creation script**:
```bash ```bash
python D:/path/to/nanobot/skills/create-instance/scripts/create_instance.py --name <name> --channel <channel> --inherit-config ~/.nanobot/config.json [--model <model>] [--config-dir <path>] python <skill-dir>/scripts/create_instance.py --name <name> --channel <channel> --inherit-config <current-config>
``` ```
**Path rules (critical on Windows):** - `<skill-dir>` — the directory containing this SKILL.md
- Use **forward-slash absolute paths** to the script, e.g. `D:/path/to/create_instance.py` - `<current-config>` — current instance's config path, typically `~/.nanobot/config.json`
- Do **NOT** wrap paths in quotes — the exec tool will mangle them - Optional: `--model <model>`, `--config-dir <path>`
- Do **NOT** use `cd` — the exec tool ignores it; working directory stays as workspace
- Do **NOT** use backslash paths like `D:\path` — they will fail
Use `~/.nanobot/config.json` as the `--inherit-config` path unless the current instance uses a custom config location. **Exec tool constraints:**
- Use forward-slash paths (works on all platforms)
- Do not wrap paths in quotes
- Do not use `cd`; pass the full script path directly
4. **Report results to the user**: 4. **Report results** to the user:
- Where the config and workspace were created - Config and workspace paths (script outputs them)
- Which fields they need to fill in (the script will list them) - Required fields to fill in (script lists them)
- The command to start the instance: `nanobot gateway --config <config-path>` - Start command: `nanobot gateway --config <config-path>`
## Examples ## Available Channels
User: "help me create a Telegram bot" (or similar request) | Channel | Key | Required Fields |
|---------|-----|-----------------|
| Telegram | `telegram` | token |
| Discord | `discord` | token |
| Feishu / Lark | `feishu` | app_id, app_secret |
| DingTalk | `dingtalk` | client_id, client_secret |
| Slack | `slack` | bot_token, app_token |
| WeCom | `wecom` | bot_id, secret |
| WeChat OA | `weixin` | token |
| WhatsApp | `whatsapp` | bridge_token |
| QQ | `qq` | app_id, secret |
| Email | `email` | imap_host, imap_username, imap_password, smtp_host, smtp_username, smtp_password, from_address |
| Matrix | `matrix` | user_id, password or access_token |
| MS Teams | `msteams` | app_id, app_password, tenant_id |
| MoChat | `mochat` | claw_token |
| WebSocket | `websocket` | token |
→ Ask for an instance name if not obvious from context For detailed channel configuration including optional fields, see `references/channels.md`.
→ Ask which model to use (optional, can skip if user doesn't care)
→ Run: `python D:/path/to/nanobot/skills/create-instance/scripts/create_instance.py --name telegram-bot --channel telegram --inherit-config ~/.nanobot/config.json` ## Troubleshooting
(Replace `D:/path/to/nanobot` with the actual nanobot source directory)
→ Tell user: config created at `~/.nanobot-telegram/config.json`, fill in `channels.telegram.token`, then start with `nanobot gateway --config ~/.nanobot-telegram/config.json` - **"Unknown channel"**: Channel name must match the Key column exactly. Run the script without arguments to see usage.
- **"Config already exists"**: Use a different `--name` or `--config-dir` to create in a new location.
- **Port conflicts**: The script auto-assigns free ports for gateway and API if defaults are in use.

View File

@ -0,0 +1,194 @@
# Channel Configuration Reference
Detailed configuration for each supported channel.
## Field Types
- **Required**: defaults to empty string `""`, must be filled in before the instance can start
- **Optional**: has a sensible default, can be customized
---
## telegram
**Required:**
- `token` — Bot token from @BotFather
**Notable optional:**
- `proxy` — HTTP proxy URL
- `group_policy``"open"` (all messages) or `"mention"` (default, only when @mentioned)
- `streaming` — Enable streaming responses (default: true)
- `reply_to_message` — Reply to the triggering message (default: false)
- `react_emoji` — Emoji for "thinking" reaction (default: `"eyes"`)
- `inline_keyboards` — Enable inline keyboard buttons (default: false)
## discord
**Required:**
- `token` — Bot token from Discord Developer Portal
**Notable optional:**
- `allow_channels` — Restrict to specific channel IDs
- `group_policy``"mention"` (default) or `"open"`
- `streaming` — Enable streaming (default: true)
- `proxy` — HTTP proxy URL
- `intents` — Discord gateway intents (default: 37377)
- `read_receipt_emoji` — Emoji for read receipt
- `working_emoji` — Emoji for "working" indicator
## feishu
**Required:**
- `app_id` — Feishu app ID
- `app_secret` — Feishu app secret
**Notable optional:**
- `encrypt_key` — Event encryption key
- `verification_token` — Event verification token
- `domain``"feishu"` (default) or `"lark"`
- `group_policy``"mention"` (default) or `"open"`
- `streaming` — Enable streaming (default: true)
## dingtalk
**Required:**
- `client_id` — DingTalk app client ID
- `client_secret` — DingTalk app client secret
**Notable optional:**
- `allow_from` — Allowed user IDs
## slack
**Required:**
- `bot_token` — Bot OAuth token (`xoxb-...`)
- `app_token` — App-level token (`xapp-...`)
**Notable optional:**
- `mode``"socket"` (default, Socket Mode) or `"webhook"`
- `reply_in_thread` — Reply in thread (default: true)
- `react_emoji` — "thinking" emoji (default: `"eyes"`)
- `done_emoji` — "done" emoji (default: `"white_check_mark"`)
- `group_policy``"mention"` (default) or `"open"`
- `dm.enabled` — Enable DM support
- `dm.policy` — DM policy
- `dm.allow_from` — Allowed DM users
## wecom
**Required:**
- `bot_id` — WeCom bot ID
- `secret` — WeCom bot secret
**Notable optional:**
- `allow_from` — Allowed users
- `welcome_message` — Welcome message for new chats
## weixin
**Required:**
- `token` — WeChat Official Account token
**Notable optional:**
- `base_url` — API base URL
- `cdn_base_url` — CDN base URL
- `state_dir` — State persistence directory
- `poll_timeout` — Long polling timeout
## whatsapp
**Required:**
- `bridge_token` — WhatsApp bridge token (auto-generated if absent)
**Notable optional:**
- `bridge_url` — Bridge WebSocket URL (default: `"ws://localhost:3001"`)
- `group_policy``"open"` (default) or `"mention"`
## qq
**Required:**
- `app_id` — QQ bot app ID
- `secret` — QQ bot secret
**Notable optional:**
- `msg_format``"plain"` or `"markdown"`
- `ack_message` — Acknowledgment message text
- `media_dir` — Media file directory
## email
**Required:**
- `imap_host` — IMAP server hostname
- `imap_username` — IMAP login username
- `imap_password` — IMAP login password
- `smtp_host` — SMTP server hostname
- `smtp_username` — SMTP login username
- `smtp_password` — SMTP login password
- `from_address` — Sender email address
**Notable optional:**
- `imap_port` — IMAP port (default: 993)
- `smtp_port` — SMTP port (default: 587)
- `imap_use_ssl` — Use SSL for IMAP (default: true)
- `smtp_use_tls` — Use TLS for SMTP (default: true)
- `poll_interval_seconds` — Polling interval (default: 30)
- `mark_seen` — Mark emails as read (default: true)
- `max_body_chars` — Max email body length (default: 12000)
- `subject_prefix` — Reply subject prefix (default: `"Re: "`)
- `verify_dkim` — Verify DKIM signatures (default: true)
- `verify_spf` — Verify SPF records (default: true)
- `allowed_attachment_types` — Allowed file extensions
- `max_attachment_size` — Max attachment size in bytes
- `consent_granted` — Must be set to `true` for the channel to start (default: false)
- `auto_reply_enabled` — Enable auto-reply (default: true)
## matrix
**Required:**
- `user_id` — Matrix user ID (e.g. `@bot:matrix.org`)
- `password` or `access_token` — Login password OR access token
**Notable optional:**
- `homeserver` — Homeserver URL (default: `"https://matrix.org"`)
- `device_id` — Device ID
- `e2eeEnabled` — Enable end-to-end encryption (default: true)
- `group_policy``"open"`, `"mention"`, or `"allowlist"`
- `streaming` — Enable streaming (default: false)
- `max_media_bytes` — Max media file size (default: 20MB)
## msteams
**Required:**
- `app_id` — Azure AD app ID
- `app_password` — Azure AD app password/secret
- `tenant_id` — Azure AD tenant ID
**Notable optional:**
- `host` — Listen host (default: `"0.0.0.0"`)
- `port` — Listen port (default: 3978)
- `reply_in_thread` — Reply in thread (default: true)
- `validate_inbound_auth` — Validate incoming auth (default: true)
## mochat
**Required:**
- `claw_token` — MoChat Claw token
**Notable optional:**
- `base_url` — API base URL
- `socket_url` — WebSocket URL
- `refresh_interval_ms` — Refresh interval in ms
- `watch_timeout_ms` — Watch timeout in ms
## websocket
Built-in WebSocket channel for programmatic access.
**Required:**
- `token` — Authentication token (enabled by default; set `websocket_requires_token: false` to disable)
**Notable optional:**
- `host` — Listen host (default: `"127.0.0.1"`)
- `port` — Listen port (default: 8765)
- `allow_from` — Allowed origins (default: `["*"]`)
- `streaming` — Enable streaming (default: true)

View File

@ -73,18 +73,27 @@ def _patch_config(
"""Patch the generated config: enable channel, set workspace, optionally set model.""" """Patch the generated config: enable channel, set workspace, optionally set model."""
data = json.loads(config_path.read_text(encoding="utf-8")) data = json.loads(config_path.read_text(encoding="utf-8"))
# Inherit providers (API keys, api_base, etc.) from current instance # Inherit providers and model from current instance
if inherit_config_path and inherit_config_path.exists(): if inherit_config_path and inherit_config_path.exists():
try: try:
src = json.loads(inherit_config_path.read_text(encoding="utf-8")) src = json.loads(inherit_config_path.read_text(encoding="utf-8"))
# Inherit providers (API keys, api_base, etc.)
src_providers = src.get("providers", {}) src_providers = src.get("providers", {})
if src_providers: if src_providers:
data.setdefault("providers", {}) data.setdefault("providers", {})
for key, val in src_providers.items(): for key, val in src_providers.items():
if isinstance(val, dict) and val.get("apiKey"): if isinstance(val, dict) and val.get("apiKey"):
data["providers"][key] = val data["providers"][key] = val
# Inherit model if not explicitly overridden
if not model:
parent_model = src.get("agents", {}).get("defaults", {}).get("model")
if parent_model:
model = parent_model
except Exception as exc: except Exception as exc:
print(f"[WARN] Could not inherit providers from {inherit_config_path}: {exc}", file=sys.stderr) print(f"[WARN] Could not inherit from {inherit_config_path}: {exc}", file=sys.stderr)
# Set workspace and model # Set workspace and model
data.setdefault("agents", {}).setdefault("defaults", {}) data.setdefault("agents", {}).setdefault("defaults", {})