refactor: implement multi-channel architecture (#500)
* refactor: implement channel architecture and dynamic setup - Introduced ChannelRegistry for dynamic channel loading - Decoupled WhatsApp from core index.ts and config.ts - Updated setup wizard to support ENABLED_CHANNELS selection - Refactored IPC and group registration to be channel-aware - Verified with 359 passing tests and clean typecheck * style: fix formatting in config.ts to pass CI * refactor(setup): full platform-agnostic transformation - Harmonized all instructional text and help prompts - Implemented conditional guards for WhatsApp-specific steps - Normalized CLI terminology across all 4 initial channels - Unified troubleshooting and verification logic - Verified 369 tests pass with clean typecheck * feat(skills): transform WhatsApp into a pluggable skill - Created .claude/skills/add-whatsapp with full 5-phase interactive setup - Fixed TS7006 'implicit any' error in IpcDeps - Added auto-creation of STORE_DIR to prevent crashes on fresh installs - Verified with 369 passing tests and clean typecheck * refactor(skills): move WhatsApp from core to pluggable skill - Move src/channels/whatsapp.ts to add-whatsapp skill add/ folder - Move src/channels/whatsapp.test.ts to skill add/ folder - Move src/whatsapp-auth.ts to skill add/ folder - Create modify/ for barrel file (src/channels/index.ts) - Create tests/ with skill package validation test - Update manifest with adds/modifies lists - Remove WhatsApp deps from core package.json (now skill-managed) - Remove WhatsApp-specific ghost language from types.ts - Update SKILL.md to reflect skill-apply workflow Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(skills): move setup/whatsapp-auth.ts into WhatsApp skill The WhatsApp auth setup step is channel-specific — move it from core to the add-whatsapp skill so core stays minimal. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(skills): convert Telegram skill to pluggable channel pattern Replace the old direct-integration approach (modifying src/index.ts, src/config.ts, src/routing.test.ts) with self-registration via the channel registry, matching the WhatsApp skill pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(skills): fix add-whatsapp build failure and improve auth flow - Add missing @types/qrcode-terminal to manifest npm_dependencies (build failed after skill apply without it) - Make QR-browser the recommended auth method (terminal QR too small, pairing codes expire too fast) - Remove "replace vs alongside" question — channels are additive - Add pairing code retry guidance and QR-browser fallback Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove hardcoded WhatsApp default and stale Baileys comment - ENABLED_CHANNELS now defaults to empty (fresh installs must configure channels explicitly via /setup; existing installs already have .env) - Remove Baileys-specific comment from storeMessageDirect() in db.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(skills): convert Discord, Slack, Gmail skills to pluggable channel pattern All channel skills now use the same self-registration pattern: - registerChannel() factory at module load time - Barrel file append (src/channels/index.ts) instead of orchestrator modifications - No more *_ONLY flags (DISCORD_ONLY, SLACK_ONLY) — use ENABLED_CHANNELS instead - Removed ~2500 lines of old modify/ files (src/index.ts, src/config.ts, src/routing.test.ts) Gmail retains its container-runner.ts and agent-runner modifications (MCP mount + server config) since those are independent of channel wiring. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: use getRegisteredChannels instead of ENABLED_CHANNELS Remove the ENABLED_CHANNELS env var entirely. The orchestrator now iterates getRegisteredChannelNames() from the channel registry — channels self-register via barrel imports and their factories return null when credentials are missing, so unconfigured channels are skipped automatically. Deleted setup/channels.ts (and its tests) since its sole purpose was writing ENABLED_CHANNELS to .env. Refactored verify, groups, and environment setup steps to detect channels by credential presence instead of reading ENABLED_CHANNELS. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add breaking change notice and whatsapp migration instructions CHANGELOG.md documents the pluggable channel architecture shift and provides migration steps for existing WhatsApp users. CLAUDE.md updated: Quick Context reflects multi-channel architecture, Key Files lists registry.ts instead of whatsapp.ts, and a new Troubleshooting section directs users to /add-whatsapp if WhatsApp stops connecting after upgrade. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: rewrite READMEs for pluggable multi-channel architecture Reflects the architectural shift from a hardcoded WhatsApp bot to a pluggable channel platform. Adds upgrading notice, Mermaid architecture diagram, CI/License/TypeScript/PRs badges, and clarifies that slash commands run inside the Claude Code CLI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: move pluggable channel architecture details to SPEC.md Revert READMEs to original tone with only two targeted changes: - Add upgrading notice for WhatsApp breaking change - Mention pluggable channels in "What It Supports" Move Mermaid diagram, channel registry internals, factory pattern explanation, and self-registration walkthrough into docs/SPEC.md. Update stale WhatsApp-specific references in SPEC.md to be channel-agnostic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: move upgrading notice to CHANGELOG, add changelog link Remove the "Upgrading from Pre-Pluggable Versions" section from README.md — breaking change details belong in the CHANGELOG. Add a Changelog section linking to CHANGELOG.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: expand CHANGELOG with full PR #500 changes Cover all changes: channel registry, WhatsApp moved to skill, removed core dependencies, all 5 skills simplified, orchestrator refactored, setup decoupled. Use Claude Code CLI instructions for migration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: bump version to 1.2.0 for pluggable channel architecture Minor version bump — new functionality (pluggable channels) with a managed migration path for existing WhatsApp users. Update version references in CHANGELOG and update skill. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix skill application * fix: use slotted barrel file to prevent channel merge conflicts Pre-allocate a named comment slot for each channel in src/channels/index.ts, separated by blank lines. Each skill's modify file only touches its own slot, so three-way merges never conflict when applying multiple channels. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve real chat ID during setup for token-based channels Instead of registering with `pending@telegram` (which never matches incoming messages), the setup skill now runs an inline bot that waits for the user to send /chatid, capturing the real chat ID before registration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: setup delegates to channel skills, fix group sync and Discord metadata - Restructure setup SKILL.md to delegate channel setup to individual channel skills (/add-whatsapp, /add-telegram, etc.) instead of reimplementing auth/registration inline with broken placeholder JIDs - Move channel selection to step 5 where it's immediately acted on - Fix setup/groups.ts: write sync script to temp file instead of passing via node -e which broke on shell escaping of newlines - Fix Discord onChatMetadata missing channel and isGroup parameters - Add .tmp-* to .gitignore for temp sync script cleanup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: align add-whatsapp skill with main setup patterns Add headless detection for auth method selection, structured inline error handling, dedicated number DM flow, and reorder questions to match main's trigger-first flow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add missing auth script to package.json The add-whatsapp skill adds src/whatsapp-auth.ts but doesn't add the corresponding npm script. Setup and SKILL.md reference `npm run auth` for WhatsApp QR terminal authentication. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: update Discord skill tests to match onChatMetadata signature The onChatMetadata callback now takes 5 arguments (jid, timestamp, name, channel, isGroup) but the Discord skill tests only expected 3. This caused skill application to roll back on test failure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: replace 'pluggable' jargon with clearer language User-facing text now says "multi-channel" or describes what it does. Developer-facing text uses "self-registering" or "channel registry". Also removes extra badge row from README. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: align Chinese README with English version Remove extra badges, replace pluggable jargon, remove upgrade section (now in CHANGELOG), add missing intro line and changelog section, fix setup FAQ answer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: warn on installed-but-unconfigured channels instead of silent skip Channels with missing credentials now emit WARN logs naming the exact missing variable, so misconfigurations surface instead of being hidden. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: simplify changelog to one-liner with compare link Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add isMain flag and channel-prefixed group folders Replace MAIN_GROUP_FOLDER constant with explicit isMain boolean on RegisteredGroup. Group folders now use channel prefix convention (e.g., whatsapp_main, telegram_family-chat) to prevent cross-channel collisions. - Add isMain to RegisteredGroup type and SQLite schema (with migration) - Replace all folder-based main group checks with group.isMain - Add --is-main flag to setup/register.ts - Strip isMain from IPC payload (defense in depth) - Update MCP tool description for channel-prefixed naming - Update all channel SKILL.md files and documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: gavrielc <gabicohen22@yahoo.com> Co-authored-by: Koshkoshinski <daniel.milliner@gmail.com>
This commit is contained in:
288
docs/SPEC.md
288
docs/SPEC.md
@@ -1,79 +1,81 @@
|
||||
# NanoClaw Specification
|
||||
|
||||
A personal Claude assistant accessible via WhatsApp, with persistent memory per conversation, scheduled tasks, and email integration.
|
||||
A personal Claude assistant with multi-channel support, persistent memory per conversation, scheduled tasks, and container-isolated agent execution.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Architecture](#architecture)
|
||||
2. [Folder Structure](#folder-structure)
|
||||
3. [Configuration](#configuration)
|
||||
4. [Memory System](#memory-system)
|
||||
5. [Session Management](#session-management)
|
||||
6. [Message Flow](#message-flow)
|
||||
7. [Commands](#commands)
|
||||
8. [Scheduled Tasks](#scheduled-tasks)
|
||||
9. [MCP Servers](#mcp-servers)
|
||||
10. [Deployment](#deployment)
|
||||
11. [Security Considerations](#security-considerations)
|
||||
2. [Architecture: Channel System](#architecture-channel-system)
|
||||
3. [Folder Structure](#folder-structure)
|
||||
4. [Configuration](#configuration)
|
||||
5. [Memory System](#memory-system)
|
||||
6. [Session Management](#session-management)
|
||||
7. [Message Flow](#message-flow)
|
||||
8. [Commands](#commands)
|
||||
9. [Scheduled Tasks](#scheduled-tasks)
|
||||
10. [MCP Servers](#mcp-servers)
|
||||
11. [Deployment](#deployment)
|
||||
12. [Security Considerations](#security-considerations)
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ HOST (macOS) │
|
||||
│ (Main Node.js Process) │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌────────────────────┐ │
|
||||
│ │ WhatsApp │────────────────────▶│ SQLite Database │ │
|
||||
│ │ (baileys) │◀────────────────────│ (messages.db) │ │
|
||||
│ └──────────────┘ store/send └─────────┬──────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
|
||||
│ │ Message Loop │ │ Scheduler Loop │ │ IPC Watcher │ │
|
||||
│ │ (polls SQLite) │ │ (checks tasks) │ │ (file-based) │ │
|
||||
│ └────────┬─────────┘ └────────┬─────────┘ └───────────────┘ │
|
||||
│ │ │ │
|
||||
│ └───────────┬───────────┘ │
|
||||
│ │ spawns container │
|
||||
│ ▼ │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ CONTAINER (Linux VM) │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ ┌──────────────────────────────────────────────────────────────┐ │
|
||||
│ │ AGENT RUNNER │ │
|
||||
│ │ │ │
|
||||
│ │ Working directory: /workspace/group (mounted from host) │ │
|
||||
│ │ Volume mounts: │ │
|
||||
│ │ • groups/{name}/ → /workspace/group │ │
|
||||
│ │ • groups/global/ → /workspace/global/ (non-main only) │ │
|
||||
│ │ • data/sessions/{group}/.claude/ → /home/node/.claude/ │ │
|
||||
│ │ • Additional dirs → /workspace/extra/* │ │
|
||||
│ │ │ │
|
||||
│ │ Tools (all groups): │ │
|
||||
│ │ • Bash (safe - sandboxed in container!) │ │
|
||||
│ │ • Read, Write, Edit, Glob, Grep (file operations) │ │
|
||||
│ │ • WebSearch, WebFetch (internet access) │ │
|
||||
│ │ • agent-browser (browser automation) │ │
|
||||
│ │ • mcp__nanoclaw__* (scheduler tools via IPC) │ │
|
||||
│ │ │ │
|
||||
│ └──────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ HOST (macOS / Linux) │
|
||||
│ (Main Node.js Process) │
|
||||
├──────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────────┐ ┌────────────────────┐ │
|
||||
│ │ Channels │─────────────────▶│ SQLite Database │ │
|
||||
│ │ (self-register │◀────────────────│ (messages.db) │ │
|
||||
│ │ at startup) │ store/send └─────────┬──────────┘ │
|
||||
│ └──────────────────┘ │ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
|
||||
│ │ Message Loop │ │ Scheduler Loop │ │ IPC Watcher │ │
|
||||
│ │ (polls SQLite) │ │ (checks tasks) │ │ (file-based) │ │
|
||||
│ └────────┬─────────┘ └────────┬─────────┘ └───────────────┘ │
|
||||
│ │ │ │
|
||||
│ └───────────┬───────────┘ │
|
||||
│ │ spawns container │
|
||||
│ ▼ │
|
||||
├──────────────────────────────────────────────────────────────────────┤
|
||||
│ CONTAINER (Linux VM) │
|
||||
├──────────────────────────────────────────────────────────────────────┤
|
||||
│ ┌──────────────────────────────────────────────────────────────┐ │
|
||||
│ │ AGENT RUNNER │ │
|
||||
│ │ │ │
|
||||
│ │ Working directory: /workspace/group (mounted from host) │ │
|
||||
│ │ Volume mounts: │ │
|
||||
│ │ • groups/{name}/ → /workspace/group │ │
|
||||
│ │ • groups/global/ → /workspace/global/ (non-main only) │ │
|
||||
│ │ • data/sessions/{group}/.claude/ → /home/node/.claude/ │ │
|
||||
│ │ • Additional dirs → /workspace/extra/* │ │
|
||||
│ │ │ │
|
||||
│ │ Tools (all groups): │ │
|
||||
│ │ • Bash (safe - sandboxed in container!) │ │
|
||||
│ │ • Read, Write, Edit, Glob, Grep (file operations) │ │
|
||||
│ │ • WebSearch, WebFetch (internet access) │ │
|
||||
│ │ • agent-browser (browser automation) │ │
|
||||
│ │ • mcp__nanoclaw__* (scheduler tools via IPC) │ │
|
||||
│ │ │ │
|
||||
│ └──────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Technology Stack
|
||||
|
||||
| Component | Technology | Purpose |
|
||||
|-----------|------------|---------|
|
||||
| WhatsApp Connection | Node.js (@whiskeysockets/baileys) | Connect to WhatsApp, send/receive messages |
|
||||
| Channel System | Channel registry (`src/channels/registry.ts`) | Channels self-register at startup |
|
||||
| Message Storage | SQLite (better-sqlite3) | Store messages for polling |
|
||||
| Container Runtime | Containers (Linux VMs) | Isolated environments for agent execution |
|
||||
| Agent | @anthropic-ai/claude-agent-sdk (0.2.29) | Run Claude with tools and MCP servers |
|
||||
@@ -82,6 +84,158 @@ A personal Claude assistant accessible via WhatsApp, with persistent memory per
|
||||
|
||||
---
|
||||
|
||||
## Architecture: Channel System
|
||||
|
||||
The core ships with no channels built in — each channel (WhatsApp, Telegram, Slack, Discord, Gmail) is installed as a [Claude Code skill](https://code.claude.com/docs/en/skills) that adds the channel code to your fork. Channels self-register at startup; installed channels with missing credentials emit a WARN log and are skipped.
|
||||
|
||||
### System Diagram
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph Channels["Channels"]
|
||||
WA[WhatsApp]
|
||||
TG[Telegram]
|
||||
SL[Slack]
|
||||
DC[Discord]
|
||||
New["Other Channel (Signal, Gmail...)"]
|
||||
end
|
||||
|
||||
subgraph Orchestrator["Orchestrator — index.ts"]
|
||||
ML[Message Loop]
|
||||
GQ[Group Queue]
|
||||
RT[Router]
|
||||
TS[Task Scheduler]
|
||||
DB[(SQLite)]
|
||||
end
|
||||
|
||||
subgraph Execution["Container Execution"]
|
||||
CR[Container Runner]
|
||||
LC["Linux Container"]
|
||||
IPC[IPC Watcher]
|
||||
end
|
||||
|
||||
%% Flow
|
||||
WA & TG & SL & DC & New -->|onMessage| ML
|
||||
ML --> GQ
|
||||
GQ -->|concurrency| CR
|
||||
CR --> LC
|
||||
LC -->|filesystem IPC| IPC
|
||||
IPC -->|tasks & messages| RT
|
||||
RT -->|Channel.sendMessage| Channels
|
||||
TS -->|due tasks| CR
|
||||
|
||||
%% DB Connections
|
||||
DB <--> ML
|
||||
DB <--> TS
|
||||
|
||||
%% Styling for the dynamic channel
|
||||
style New stroke-dasharray: 5 5,stroke-width:2px
|
||||
```
|
||||
|
||||
### Channel Registry
|
||||
|
||||
The channel system is built on a factory registry in `src/channels/registry.ts`:
|
||||
|
||||
```typescript
|
||||
export type ChannelFactory = (opts: ChannelOpts) => Channel | null;
|
||||
|
||||
const registry = new Map<string, ChannelFactory>();
|
||||
|
||||
export function registerChannel(name: string, factory: ChannelFactory): void {
|
||||
registry.set(name, factory);
|
||||
}
|
||||
|
||||
export function getChannelFactory(name: string): ChannelFactory | undefined {
|
||||
return registry.get(name);
|
||||
}
|
||||
|
||||
export function getRegisteredChannelNames(): string[] {
|
||||
return [...registry.keys()];
|
||||
}
|
||||
```
|
||||
|
||||
Each factory receives `ChannelOpts` (callbacks for `onMessage`, `onChatMetadata`, and `registeredGroups`) and returns either a `Channel` instance or `null` if that channel's credentials are not configured.
|
||||
|
||||
### Channel Interface
|
||||
|
||||
Every channel implements this interface (defined in `src/types.ts`):
|
||||
|
||||
```typescript
|
||||
interface Channel {
|
||||
name: string;
|
||||
connect(): Promise<void>;
|
||||
sendMessage(jid: string, text: string): Promise<void>;
|
||||
isConnected(): boolean;
|
||||
ownsJid(jid: string): boolean;
|
||||
disconnect(): Promise<void>;
|
||||
setTyping?(jid: string, isTyping: boolean): Promise<void>;
|
||||
syncGroups?(force: boolean): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
### Self-Registration Pattern
|
||||
|
||||
Channels self-register using a barrel-import pattern:
|
||||
|
||||
1. Each channel skill adds a file to `src/channels/` (e.g. `whatsapp.ts`, `telegram.ts`) that calls `registerChannel()` at module load time:
|
||||
|
||||
```typescript
|
||||
// src/channels/whatsapp.ts
|
||||
import { registerChannel, ChannelOpts } from './registry.js';
|
||||
|
||||
export class WhatsAppChannel implements Channel { /* ... */ }
|
||||
|
||||
registerChannel('whatsapp', (opts: ChannelOpts) => {
|
||||
// Return null if credentials are missing
|
||||
if (!existsSync(authPath)) return null;
|
||||
return new WhatsAppChannel(opts);
|
||||
});
|
||||
```
|
||||
|
||||
2. The barrel file `src/channels/index.ts` imports all channel modules, triggering registration:
|
||||
|
||||
```typescript
|
||||
import './whatsapp.js';
|
||||
import './telegram.js';
|
||||
// ... each skill adds its import here
|
||||
```
|
||||
|
||||
3. At startup, the orchestrator (`src/index.ts`) loops through registered channels and connects whichever ones return a valid instance:
|
||||
|
||||
```typescript
|
||||
for (const name of getRegisteredChannelNames()) {
|
||||
const factory = getChannelFactory(name);
|
||||
const channel = factory?.(channelOpts);
|
||||
if (channel) {
|
||||
await channel.connect();
|
||||
channels.push(channel);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Key Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `src/channels/registry.ts` | Channel factory registry |
|
||||
| `src/channels/index.ts` | Barrel imports that trigger channel self-registration |
|
||||
| `src/types.ts` | `Channel` interface, `ChannelOpts`, message types |
|
||||
| `src/index.ts` | Orchestrator — instantiates channels, runs message loop |
|
||||
| `src/router.ts` | Finds the owning channel for a JID, formats messages |
|
||||
|
||||
### Adding a New Channel
|
||||
|
||||
To add a new channel, contribute a skill to `.claude/skills/add-<name>/` that:
|
||||
|
||||
1. Adds a `src/channels/<name>.ts` file implementing the `Channel` interface
|
||||
2. Calls `registerChannel(name, factory)` at module load
|
||||
3. Returns `null` from the factory if credentials are missing
|
||||
4. Adds an import line to `src/channels/index.ts`
|
||||
|
||||
See existing skills (`/add-whatsapp`, `/add-telegram`, `/add-slack`, `/add-discord`, `/add-gmail`) for the pattern.
|
||||
|
||||
---
|
||||
|
||||
## Folder Structure
|
||||
|
||||
```
|
||||
@@ -100,7 +254,8 @@ nanoclaw/
|
||||
├── src/
|
||||
│ ├── index.ts # Orchestrator: state, message loop, agent invocation
|
||||
│ ├── channels/
|
||||
│ │ └── whatsapp.ts # WhatsApp connection, auth, send/receive
|
||||
│ │ ├── registry.ts # Channel factory registry
|
||||
│ │ └── index.ts # Barrel imports for channel self-registration
|
||||
│ ├── ipc.ts # IPC watcher and task processing
|
||||
│ ├── router.ts # Message formatting and outbound routing
|
||||
│ ├── config.ts # Configuration constants
|
||||
@@ -141,10 +296,10 @@ nanoclaw/
|
||||
│
|
||||
├── groups/
|
||||
│ ├── CLAUDE.md # Global memory (all groups read this)
|
||||
│ ├── main/ # Self-chat (main control channel)
|
||||
│ ├── {channel}_main/ # Main control channel (e.g., whatsapp_main/)
|
||||
│ │ ├── CLAUDE.md # Main channel memory
|
||||
│ │ └── logs/ # Task execution logs
|
||||
│ └── {Group Name}/ # Per-group folders (created on registration)
|
||||
│ └── {channel}_{group-name}/ # Per-group folders (created on registration)
|
||||
│ ├── CLAUDE.md # Group-specific memory
|
||||
│ ├── logs/ # Task logs for this group
|
||||
│ └── *.md # Files created by the agent
|
||||
@@ -205,7 +360,7 @@ Groups can have additional directories mounted via `containerConfig` in the SQLi
|
||||
```typescript
|
||||
registerGroup("1234567890@g.us", {
|
||||
name: "Dev Team",
|
||||
folder: "dev-team",
|
||||
folder: "whatsapp_dev-team",
|
||||
trigger: "@Andy",
|
||||
added_at: new Date().toISOString(),
|
||||
containerConfig: {
|
||||
@@ -221,6 +376,8 @@ registerGroup("1234567890@g.us", {
|
||||
});
|
||||
```
|
||||
|
||||
Folder names follow the convention `{channel}_{group-name}` (e.g., `whatsapp_family-chat`, `telegram_dev-team`). The main group has `isMain: true` set during registration.
|
||||
|
||||
Additional mounts appear at `/workspace/extra/{containerPath}` inside the container.
|
||||
|
||||
**Mount syntax note:** Read-write mounts use `-v host:container`, but readonly mounts require `--mount "type=bind,source=...,target=...,readonly"` (the `:ro` suffix may not work on all runtimes).
|
||||
@@ -314,10 +471,10 @@ Sessions enable conversation continuity - Claude remembers what you talked about
|
||||
### Incoming Message Flow
|
||||
|
||||
```
|
||||
1. User sends WhatsApp message
|
||||
1. User sends a message via any connected channel
|
||||
│
|
||||
▼
|
||||
2. Baileys receives message via WhatsApp Web protocol
|
||||
2. Channel receives message (e.g. Baileys for WhatsApp, Bot API for Telegram)
|
||||
│
|
||||
▼
|
||||
3. Message stored in SQLite (store/messages.db)
|
||||
@@ -349,7 +506,7 @@ Sessions enable conversation continuity - Claude remembers what you talked about
|
||||
└── Uses tools as needed (search, email, etc.)
|
||||
│
|
||||
▼
|
||||
9. Router prefixes response with assistant name and sends via WhatsApp
|
||||
9. Router prefixes response with assistant name and sends via the owning channel
|
||||
│
|
||||
▼
|
||||
10. Router updates last agent timestamp and saves session ID
|
||||
@@ -473,7 +630,7 @@ The `nanoclaw` MCP server is created dynamically per agent call with the current
|
||||
| `pause_task` | Pause a task |
|
||||
| `resume_task` | Resume a paused task |
|
||||
| `cancel_task` | Delete a task |
|
||||
| `send_message` | Send a WhatsApp message to the group |
|
||||
| `send_message` | Send a message to the group via its channel |
|
||||
|
||||
---
|
||||
|
||||
@@ -487,7 +644,8 @@ When NanoClaw starts, it:
|
||||
1. **Ensures container runtime is running** - Automatically starts it if needed; kills orphaned NanoClaw containers from previous runs
|
||||
2. Initializes the SQLite database (migrates from JSON files if they exist)
|
||||
3. Loads state from SQLite (registered groups, sessions, router state)
|
||||
4. Connects to WhatsApp (on `connection.open`):
|
||||
4. **Connects channels** — loops through registered channels, instantiates those with credentials, calls `connect()` on each
|
||||
5. Once at least one channel is connected:
|
||||
- Starts the scheduler loop
|
||||
- Starts the IPC watcher for container messages
|
||||
- Sets up the per-group queue with `processGroupMessages`
|
||||
|
||||
Reference in New Issue
Block a user