* refactor: multi-channel infrastructure with explicit channel/is_group tracking - Add channels[] array and findChannel() routing in index.ts, replacing hardcoded whatsapp.* calls with channel-agnostic callbacks - Add channel TEXT and is_group INTEGER columns to chats table with COALESCE upsert to protect existing values from null overwrites - is_group defaults to 0 (safe: unknown chats excluded from groups) - WhatsApp passes explicit channel='whatsapp' and isGroup to onChatMetadata - getAvailableGroups filters on is_group instead of JID pattern matching - findChannel logs warnings instead of silently dropping unroutable JIDs - Migration backfills channel/is_group from JID patterns for existing DBs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: skills engine v0.1 — deterministic skill packages with rerere resolution Three-way merge engine for applying skill packages on top of a core codebase. Skills declare which files they add/modify, and the engine uses git merge-file for conflict detection with git rerere for automatic resolution of previously-seen conflicts. Key components: - apply: three-way merge with backup/rollback safety net - replay: clean-slate replay for uninstall and rebase - update: core version updates with deletion detection - rebase: bake applied skills into base (one-way) - manifest: validation with path traversal protection - resolution-cache: pre-computed rerere resolutions - structured: npm deps, env vars, docker-compose merging - CI: per-skill test matrix with conflict detection 151 unit tests covering merge, rerere, backup, replay, uninstall, update, rebase, structured ops, and edge cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add Discord and Telegram skill packages Skill packages for adding Discord and Telegram channels to NanoClaw. Each package includes: - Channel implementation (add/src/channels/) - Three-way merge targets for index.ts, config.ts, routing.test.ts - Intent docs explaining merge invariants - Standalone integration tests - manifest.yaml with dependency/conflict declarations Applied via: npx tsx scripts/apply-skill.ts .claude/skills/add-discord These are inert until applied — no runtime impact. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * remove unused docs (skills-system-status, implementation-guide) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
105 lines
3.0 KiB
TypeScript
105 lines
3.0 KiB
TypeScript
export interface AdditionalMount {
|
|
hostPath: string; // Absolute path on host (supports ~ for home)
|
|
containerPath?: string; // Optional — defaults to basename of hostPath. Mounted at /workspace/extra/{value}
|
|
readonly?: boolean; // Default: true for safety
|
|
}
|
|
|
|
/**
|
|
* Mount Allowlist - Security configuration for additional mounts
|
|
* This file should be stored at ~/.config/nanoclaw/mount-allowlist.json
|
|
* and is NOT mounted into any container, making it tamper-proof from agents.
|
|
*/
|
|
export interface MountAllowlist {
|
|
// Directories that can be mounted into containers
|
|
allowedRoots: AllowedRoot[];
|
|
// Glob patterns for paths that should never be mounted (e.g., ".ssh", ".gnupg")
|
|
blockedPatterns: string[];
|
|
// If true, non-main groups can only mount read-only regardless of config
|
|
nonMainReadOnly: boolean;
|
|
}
|
|
|
|
export interface AllowedRoot {
|
|
// Absolute path or ~ for home (e.g., "~/projects", "/var/repos")
|
|
path: string;
|
|
// Whether read-write mounts are allowed under this root
|
|
allowReadWrite: boolean;
|
|
// Optional description for documentation
|
|
description?: string;
|
|
}
|
|
|
|
export interface ContainerConfig {
|
|
additionalMounts?: AdditionalMount[];
|
|
timeout?: number; // Default: 300000 (5 minutes)
|
|
}
|
|
|
|
export interface RegisteredGroup {
|
|
name: string;
|
|
folder: string;
|
|
trigger: string;
|
|
added_at: string;
|
|
containerConfig?: ContainerConfig;
|
|
requiresTrigger?: boolean; // Default: true for groups, false for solo chats
|
|
}
|
|
|
|
export interface NewMessage {
|
|
id: string;
|
|
chat_jid: string;
|
|
sender: string;
|
|
sender_name: string;
|
|
content: string;
|
|
timestamp: string;
|
|
is_from_me?: boolean;
|
|
is_bot_message?: boolean;
|
|
}
|
|
|
|
export interface ScheduledTask {
|
|
id: string;
|
|
group_folder: string;
|
|
chat_jid: string;
|
|
prompt: string;
|
|
schedule_type: 'cron' | 'interval' | 'once';
|
|
schedule_value: string;
|
|
context_mode: 'group' | 'isolated';
|
|
next_run: string | null;
|
|
last_run: string | null;
|
|
last_result: string | null;
|
|
status: 'active' | 'paused' | 'completed';
|
|
created_at: string;
|
|
}
|
|
|
|
export interface TaskRunLog {
|
|
task_id: string;
|
|
run_at: string;
|
|
duration_ms: number;
|
|
status: 'success' | 'error';
|
|
result: string | null;
|
|
error: string | null;
|
|
}
|
|
|
|
// --- Channel abstraction ---
|
|
|
|
export interface Channel {
|
|
name: string;
|
|
connect(): Promise<void>;
|
|
sendMessage(jid: string, text: string): Promise<void>;
|
|
isConnected(): boolean;
|
|
ownsJid(jid: string): boolean;
|
|
disconnect(): Promise<void>;
|
|
// Optional: typing indicator. Channels that support it implement it.
|
|
setTyping?(jid: string, isTyping: boolean): Promise<void>;
|
|
}
|
|
|
|
// Callback type that channels use to deliver inbound messages
|
|
export type OnInboundMessage = (chatJid: string, message: NewMessage) => void;
|
|
|
|
// Callback for chat metadata discovery.
|
|
// name is optional — channels that deliver names inline (Telegram) pass it here;
|
|
// channels that sync names separately (WhatsApp syncGroupMetadata) omit it.
|
|
export type OnChatMetadata = (
|
|
chatJid: string,
|
|
timestamp: string,
|
|
name?: string,
|
|
channel?: string,
|
|
isGroup?: boolean,
|
|
) => void;
|