mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-19 16:12:30 +00:00
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:
parent
a7aeb1d2ea
commit
b815aa8c0e
@ -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.
|
||||||
|
|||||||
194
nanobot/skills/create-instance/references/channels.md
Normal file
194
nanobot/skills/create-instance/references/channels.md
Normal 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)
|
||||||
@ -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", {})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user