From 8ca3ebffde0779d5293c1c549c79ea42f617fb52 Mon Sep 17 00:00:00 2001 From: Nick Tabeling Date: Wed, 8 Apr 2026 14:24:45 +0200 Subject: [PATCH] dist-folder --- .gitignore | 1 - dist/agent/identity.d.ts | 41 ++++++ dist/agent/identity.d.ts.map | 1 + dist/agent/identity.js | 162 +++++++++++++++++++++ dist/agent/identity.js.map | 1 + dist/agent/runner.d.ts | 22 +++ dist/agent/runner.d.ts.map | 1 + dist/agent/runner.js | 243 +++++++++++++++++++++++++++++++ dist/agent/runner.js.map | 1 + dist/bot.d.ts | 5 + dist/bot.d.ts.map | 1 + dist/bot.js | 105 +++++++++++++ dist/bot.js.map | 1 + dist/commands/new-agent.d.ts | 6 + dist/commands/new-agent.d.ts.map | 1 + dist/commands/new-agent.js | 101 +++++++++++++ dist/commands/new-agent.js.map | 1 + dist/config/loader.d.ts | 14 ++ dist/config/loader.d.ts.map | 1 + dist/config/loader.js | 74 ++++++++++ dist/config/loader.js.map | 1 + dist/db/database.d.ts | 30 ++++ dist/db/database.d.ts.map | 1 + dist/db/database.js | 123 ++++++++++++++++ dist/db/database.js.map | 1 + dist/index.d.ts | 2 + dist/index.d.ts.map | 1 + dist/index.js | 66 +++++++++ dist/index.js.map | 1 + dist/router.d.ts | 10 ++ dist/router.d.ts.map | 1 + dist/router.js | 103 +++++++++++++ dist/router.js.map | 1 + 33 files changed, 1123 insertions(+), 1 deletion(-) create mode 100644 dist/agent/identity.d.ts create mode 100644 dist/agent/identity.d.ts.map create mode 100644 dist/agent/identity.js create mode 100644 dist/agent/identity.js.map create mode 100644 dist/agent/runner.d.ts create mode 100644 dist/agent/runner.d.ts.map create mode 100644 dist/agent/runner.js create mode 100644 dist/agent/runner.js.map create mode 100644 dist/bot.d.ts create mode 100644 dist/bot.d.ts.map create mode 100644 dist/bot.js create mode 100644 dist/bot.js.map create mode 100644 dist/commands/new-agent.d.ts create mode 100644 dist/commands/new-agent.d.ts.map create mode 100644 dist/commands/new-agent.js create mode 100644 dist/commands/new-agent.js.map create mode 100644 dist/config/loader.d.ts create mode 100644 dist/config/loader.d.ts.map create mode 100644 dist/config/loader.js create mode 100644 dist/config/loader.js.map create mode 100644 dist/db/database.d.ts create mode 100644 dist/db/database.d.ts.map create mode 100644 dist/db/database.js create mode 100644 dist/db/database.js.map create mode 100644 dist/index.d.ts create mode 100644 dist/index.d.ts.map create mode 100644 dist/index.js create mode 100644 dist/index.js.map create mode 100644 dist/router.d.ts create mode 100644 dist/router.d.ts.map create mode 100644 dist/router.js create mode 100644 dist/router.js.map diff --git a/.gitignore b/.gitignore index 86ae1c6..ea00b93 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ node_modules/ -dist/ .env data/ *.db diff --git a/dist/agent/identity.d.ts b/dist/agent/identity.d.ts new file mode 100644 index 0000000..6fddcf6 --- /dev/null +++ b/dist/agent/identity.d.ts @@ -0,0 +1,41 @@ +/** + * Metadata stored in agent.yaml -- DisClaw-specific fields only. + * Identity, personality, and instructions now live in CLAUDE.md, + * which Claude Code reads automatically. + */ +export interface AgentIdentity { + name: string; + display_name: string; + role: string; + channel_id: string; +} +/** + * Loads agent.yaml from the workspace folder and returns the parsed identity. + * Re-read on every call so edits take effect immediately. + */ +export declare function loadAgentIdentity(workspacePath: string): AgentIdentity; +/** + * Creates the agent.yaml file for a new agent workspace. + * This file contains only DisClaw routing metadata. + * All identity/personality/instructions go in CLAUDE.md. + */ +export declare function createAgentYaml(workspacePath: string, name: string, role: string, channelId: string): void; +/** + * Creates the CLAUDE.md file that serves as the agent's identity. + * Claude Code reads this file automatically when invoked with cwd + * set to the workspace directory. This is the primary identity mechanism. + */ +export declare function createClaudeMd(workspacePath: string, name: string, role: string): void; +/** + * Creates the .claude/ directory structure for a workspace. + * This provides per-agent Claude Code configuration. + */ +export declare function createClaudeConfig(workspacePath: string): void; +/** + * Sets up a complete agent workspace with all required files: + * - agent.yaml (DisClaw metadata) + * - CLAUDE.md (agent identity for Claude Code) + * - .claude/ directory structure + */ +export declare function setupAgentWorkspace(workspacePath: string, name: string, role: string, channelId: string): void; +//# sourceMappingURL=identity.d.ts.map \ No newline at end of file diff --git a/dist/agent/identity.d.ts.map b/dist/agent/identity.d.ts.map new file mode 100644 index 0000000..301daad --- /dev/null +++ b/dist/agent/identity.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/agent/identity.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,aAAa,CAStE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,IAAI,CAaN;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,IAAI,CA8CN;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CA0B9D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,IAAI,CAQN"} \ No newline at end of file diff --git a/dist/agent/identity.js b/dist/agent/identity.js new file mode 100644 index 0000000..e28d01d --- /dev/null +++ b/dist/agent/identity.js @@ -0,0 +1,162 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.loadAgentIdentity = loadAgentIdentity; +exports.createAgentYaml = createAgentYaml; +exports.createClaudeMd = createClaudeMd; +exports.createClaudeConfig = createClaudeConfig; +exports.setupAgentWorkspace = setupAgentWorkspace; +const fs = __importStar(require("fs")); +const path = __importStar(require("path")); +const YAML = __importStar(require("yaml")); +/** + * Loads agent.yaml from the workspace folder and returns the parsed identity. + * Re-read on every call so edits take effect immediately. + */ +function loadAgentIdentity(workspacePath) { + const yamlPath = path.join(workspacePath, "agent.yaml"); + if (!fs.existsSync(yamlPath)) { + throw new Error(`agent.yaml not found at ${yamlPath}`); + } + const raw = fs.readFileSync(yamlPath, "utf-8"); + return YAML.parse(raw); +} +/** + * Creates the agent.yaml file for a new agent workspace. + * This file contains only DisClaw routing metadata. + * All identity/personality/instructions go in CLAUDE.md. + */ +function createAgentYaml(workspacePath, name, role, channelId) { + const identity = { + name, + display_name: name + .split("-") + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .join(" "), + role, + channel_id: channelId, + }; + const yamlContent = YAML.stringify(identity); + fs.writeFileSync(path.join(workspacePath, "agent.yaml"), yamlContent, "utf-8"); +} +/** + * Creates the CLAUDE.md file that serves as the agent's identity. + * Claude Code reads this file automatically when invoked with cwd + * set to the workspace directory. This is the primary identity mechanism. + */ +function createClaudeMd(workspacePath, name, role) { + const displayName = name + .split("-") + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .join(" "); + const content = `# ${displayName} + +You are **${displayName}**, a specialized agent in the DisClaw multi-agent workspace. + +## Your Role + +${role} + +## Your Identity + +- **Name:** ${displayName} +- **Workspace:** This directory is your workspace. All your files and context live here. +- **Communication:** You receive messages from users via Discord. Your responses are sent back to the Discord channel. + +## Personality + +- Pragmatic and solution-oriented +- Explain your decisions clearly +- Prefer simple, maintainable solutions over clever ones +- Stay focused on your role and area of expertise + +## Guidelines + +- You work with the files in this workspace directory +- Answer questions related to your role +- When asked to create or modify files, do so within this workspace +- Be concise in your responses -- they are displayed in Discord (2000 char limit per message) +- If a task is outside your area of expertise, say so clearly + +## Workspace Contents + +Add project-specific context below. Describe the files, conventions, +and architecture relevant to this agent's work. + +--- + +*This file defines your identity. Edit it to customize your agent's behavior.* +`; + fs.writeFileSync(path.join(workspacePath, "CLAUDE.md"), content, "utf-8"); +} +/** + * Creates the .claude/ directory structure for a workspace. + * This provides per-agent Claude Code configuration. + */ +function createClaudeConfig(workspacePath) { + const claudeDir = path.join(workspacePath, ".claude"); + const commandsDir = path.join(claudeDir, "commands"); + // Create directory structure + fs.mkdirSync(commandsDir, { recursive: true }); + // Create settings.json with agent-specific settings + const settings = { + permissions: { + allow: [ + "Read", + "Write", + "Edit", + "Bash", + "Glob", + "Grep" + ] + } + }; + fs.writeFileSync(path.join(claudeDir, "settings.json"), JSON.stringify(settings, null, 2), "utf-8"); +} +/** + * Sets up a complete agent workspace with all required files: + * - agent.yaml (DisClaw metadata) + * - CLAUDE.md (agent identity for Claude Code) + * - .claude/ directory structure + */ +function setupAgentWorkspace(workspacePath, name, role, channelId) { + // Ensure workspace directory exists + fs.mkdirSync(workspacePath, { recursive: true }); + // Create all required files + createAgentYaml(workspacePath, name, role, channelId); + createClaudeMd(workspacePath, name, role); + createClaudeConfig(workspacePath); +} +//# sourceMappingURL=identity.js.map \ No newline at end of file diff --git a/dist/agent/identity.js.map b/dist/agent/identity.js.map new file mode 100644 index 0000000..4021689 --- /dev/null +++ b/dist/agent/identity.js.map @@ -0,0 +1 @@ +{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/agent/identity.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBA,8CASC;AAOD,0CAkBC;AAOD,wCAkDC;AAMD,gDA0BC;AAQD,kDAaC;AApKD,uCAAyB;AACzB,2CAA6B;AAC7B,2CAA6B;AAc7B;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,aAAqB;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IAExD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,2BAA2B,QAAQ,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAkB,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,SAAgB,eAAe,CAC7B,aAAqB,EACrB,IAAY,EACZ,IAAY,EACZ,SAAiB;IAEjB,MAAM,QAAQ,GAAkB;QAC9B,IAAI;QACJ,YAAY,EAAE,IAAI;aACf,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;aAClD,IAAI,CAAC,GAAG,CAAC;QACZ,IAAI;QACJ,UAAU,EAAE,SAAS;KACtB,CAAC;IAEF,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC7C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;AACjF,CAAC;AAED;;;;GAIG;AACH,SAAgB,cAAc,CAC5B,aAAqB,EACrB,IAAY,EACZ,IAAY;IAEZ,MAAM,WAAW,GAAG,IAAI;SACrB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAClD,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,MAAM,OAAO,GAAG,KAAK,WAAW;;YAEtB,WAAW;;;;EAIrB,IAAI;;;;cAIQ,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BxB,CAAC;IAEA,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC5E,CAAC;AAED;;;GAGG;AACH,SAAgB,kBAAkB,CAAC,aAAqB;IACtD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IACtD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAErD,6BAA6B;IAC7B,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/C,oDAAoD;IACpD,MAAM,QAAQ,GAAG;QACf,WAAW,EAAE;YACX,KAAK,EAAE;gBACL,MAAM;gBACN,OAAO;gBACP,MAAM;gBACN,MAAM;gBACN,MAAM;gBACN,MAAM;aACP;SACF;KACF,CAAC;IAEF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,EACrC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EACjC,OAAO,CACR,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAgB,mBAAmB,CACjC,aAAqB,EACrB,IAAY,EACZ,IAAY,EACZ,SAAiB;IAEjB,oCAAoC;IACpC,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEjD,4BAA4B;IAC5B,eAAe,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;IACtD,cAAc,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC1C,kBAAkB,CAAC,aAAa,CAAC,CAAC;AACpC,CAAC"} \ No newline at end of file diff --git a/dist/agent/runner.d.ts b/dist/agent/runner.d.ts new file mode 100644 index 0000000..1a88388 --- /dev/null +++ b/dist/agent/runner.d.ts @@ -0,0 +1,22 @@ +import type { Conversation } from "../db/database"; +export interface RunAgentOptions { + workspacePath: string; + channelName: string; + userMessage: string; + conversationHistory: Conversation[]; + abortController?: AbortController; +} +/** + * Runs the Claude Code CLI as a child process and returns the text response. + * + * The CLI is invoked with: + * claude -p "prompt" --output-format json + * + * The working directory is set to the workspace path so Claude Code + * automatically reads the CLAUDE.md file for agent identity and context. + * + * Timeout defaults to 120 seconds. The process is killed if it exceeds + * the timeout. + */ +export declare function runAgent(options: RunAgentOptions): Promise; +//# sourceMappingURL=runner.d.ts.map \ No newline at end of file diff --git a/dist/agent/runner.d.ts.map b/dist/agent/runner.d.ts.map new file mode 100644 index 0000000..43ff547 --- /dev/null +++ b/dist/agent/runner.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/agent/runner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAInD,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,YAAY,EAAE,CAAC;IACpC,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAkID;;;;;;;;;;;GAWG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CA2GxE"} \ No newline at end of file diff --git a/dist/agent/runner.js b/dist/agent/runner.js new file mode 100644 index 0000000..876d491 --- /dev/null +++ b/dist/agent/runner.js @@ -0,0 +1,243 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.runAgent = runAgent; +const child_process_1 = require("child_process"); +const path = __importStar(require("path")); +const identity_1 = require("./identity"); +const loader_1 = require("../config/loader"); +/** + * Resolves the Claude CLI command and timeout from configuration. + * Priority: CLAUDE_PATH env var > disclaw.yaml claude_command > "claude" default. + */ +function resolveClaudeSettings() { + let command = "claude"; + let timeoutMs = 120_000; + try { + const rootDir = path.resolve(__dirname, "../.."); + const config = (0, loader_1.loadDisclawConfig)(rootDir); + if (config.claude_command) { + command = config.claude_command; + } + if (config.claude_timeout_seconds) { + timeoutMs = config.claude_timeout_seconds * 1000; + } + } + catch { + // Config not available, use defaults + } + // CLAUDE_PATH env var takes highest priority for the command + if (process.env.CLAUDE_PATH) { + command = process.env.CLAUDE_PATH; + } + return { command, timeoutMs }; +} +/** + * Builds the full prompt string including conversation history context. + * The agent identity is handled by CLAUDE.md in the workspace directory, + * which Claude Code reads automatically. + */ +function buildPrompt(userMessage, conversationHistory, channelName) { + const parts = []; + // Add conversation history if present (oldest first) + const history = [...conversationHistory].reverse(); + if (history.length > 0) { + parts.push("Previous conversation context:"); + parts.push(""); + for (const msg of history) { + const author = msg.author_name ?? msg.role; + parts.push(`[${msg.role}] ${author}: ${msg.content}`); + parts.push(""); + } + parts.push("---"); + parts.push(""); + } + // Add runtime context + parts.push(`Discord channel: #${channelName}`); + parts.push(`Date: ${new Date().toISOString().split("T")[0]}`); + parts.push(""); + // Add the actual user message + parts.push(`New message:`); + parts.push(userMessage); + return parts.join("\n"); +} +/** + * Parses the JSON output from `claude -p --output-format json`. + * Extracts the text content from the response. + * + * The JSON output format contains a "result" field with the response text, + * or an array of content blocks with type "text". + */ +function parseClaudeJsonOutput(raw) { + const trimmed = raw.trim(); + if (!trimmed) { + return "(no response from agent)"; + } + try { + const parsed = JSON.parse(trimmed); + // Format 1: { result: "text" } + if (typeof parsed.result === "string") { + return parsed.result; + } + // Format 2: { content: [{ type: "text", text: "..." }] } or similar + if (Array.isArray(parsed.content)) { + const texts = []; + for (const block of parsed.content) { + if (block && block.type === "text" && typeof block.text === "string") { + texts.push(block.text); + } + } + if (texts.length > 0) { + return texts.join("\n"); + } + } + // Format 3: Direct array of content blocks + if (Array.isArray(parsed)) { + const texts = []; + for (const block of parsed) { + if (block && block.type === "text" && typeof block.text === "string") { + texts.push(block.text); + } + } + if (texts.length > 0) { + return texts.join("\n"); + } + } + // Fallback: stringify the whole response + return typeof parsed === "string" ? parsed : JSON.stringify(parsed, null, 2); + } + catch { + // Not valid JSON -- return the raw output as plain text + return trimmed; + } +} +/** + * Runs the Claude Code CLI as a child process and returns the text response. + * + * The CLI is invoked with: + * claude -p "prompt" --output-format json + * + * The working directory is set to the workspace path so Claude Code + * automatically reads the CLAUDE.md file for agent identity and context. + * + * Timeout defaults to 120 seconds. The process is killed if it exceeds + * the timeout. + */ +async function runAgent(options) { + const { workspacePath, channelName, userMessage, conversationHistory, abortController, } = options; + // Verify the workspace has an agent.yaml (validates it exists) + (0, identity_1.loadAgentIdentity)(workspacePath); + const settings = resolveClaudeSettings(); + const prompt = buildPrompt(userMessage, conversationHistory, channelName); + const timeoutMs = settings.timeoutMs; + return new Promise((resolve, reject) => { + const args = [ + "-p", + prompt, + "--output-format", + "json", + ]; + const child = (0, child_process_1.spawn)(settings.command, args, { + cwd: workspacePath, + env: { + ...process.env, + // Ensure Claude Code does not prompt for interactive input + CI: "true", + }, + stdio: ["pipe", "pipe", "pipe"], + shell: true, + }); + let stdout = ""; + let stderr = ""; + let killed = false; + // Collect stdout + child.stdout.on("data", (data) => { + stdout += data.toString("utf-8"); + }); + // Collect stderr + child.stderr.on("data", (data) => { + stderr += data.toString("utf-8"); + }); + // Timeout handling + const timer = setTimeout(() => { + killed = true; + child.kill("SIGTERM"); + // Give it a moment to die gracefully, then force kill + setTimeout(() => { + try { + child.kill("SIGKILL"); + } + catch { + // Already dead + } + }, 5000); + }, timeoutMs); + // Abort controller support + if (abortController) { + abortController.signal.addEventListener("abort", () => { + killed = true; + child.kill("SIGTERM"); + }); + } + child.on("error", (err) => { + clearTimeout(timer); + if (err.message.includes("ENOENT")) { + reject(new Error(`Claude Code CLI not found at "${settings.command}". ` + + `Install it with: npm install -g @anthropic-ai/claude-code\n` + + `Or set CLAUDE_PATH in your .env file.`)); + } + else { + reject(new Error(`Failed to start Claude Code CLI: ${err.message}`)); + } + }); + child.on("close", (code) => { + clearTimeout(timer); + if (killed) { + reject(new Error(`Claude Code CLI timed out after ${timeoutMs / 1000} seconds.`)); + return; + } + if (code !== 0) { + const errorDetail = stderr.trim() || stdout.trim() || `exit code ${code}`; + reject(new Error(`Claude Code CLI error: ${errorDetail}`)); + return; + } + const response = parseClaudeJsonOutput(stdout); + resolve(response); + }); + // Close stdin immediately since we pass everything via args + child.stdin.end(); + }); +} +//# sourceMappingURL=runner.js.map \ No newline at end of file diff --git a/dist/agent/runner.js.map b/dist/agent/runner.js.map new file mode 100644 index 0000000..0c0aad8 --- /dev/null +++ b/dist/agent/runner.js.map @@ -0,0 +1 @@ +{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/agent/runner.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/bot.d.ts b/dist/bot.d.ts new file mode 100644 index 0000000..5c3fa7a --- /dev/null +++ b/dist/bot.d.ts @@ -0,0 +1,5 @@ +import { Client } from "discord.js"; +import { DisclawDatabase } from "./db/database"; +import { DisclawConfig, EnvConfig } from "./config/loader"; +export declare function startBot(envConfig: EnvConfig, disclawConfig: DisclawConfig, db: DisclawDatabase): Promise; +//# sourceMappingURL=bot.d.ts.map \ No newline at end of file diff --git a/dist/bot.d.ts.map b/dist/bot.d.ts.map new file mode 100644 index 0000000..50ed7b1 --- /dev/null +++ b/dist/bot.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"bot.d.ts","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EAOP,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI3D,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,SAAS,EACpB,aAAa,EAAE,aAAa,EAC5B,EAAE,EAAE,eAAe,GAClB,OAAO,CAAC,MAAM,CAAC,CAoIjB"} \ No newline at end of file diff --git a/dist/bot.js b/dist/bot.js new file mode 100644 index 0000000..eee68ed --- /dev/null +++ b/dist/bot.js @@ -0,0 +1,105 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.startBot = startBot; +const discord_js_1 = require("discord.js"); +const new_agent_1 = require("./commands/new-agent"); +const router_1 = require("./router"); +async function startBot(envConfig, disclawConfig, db) { + const client = new discord_js_1.Client({ + intents: [ + discord_js_1.GatewayIntentBits.Guilds, + discord_js_1.GatewayIntentBits.GuildMessages, + discord_js_1.GatewayIntentBits.MessageContent, + ], + }); + // Track the management channel id once resolved + let managementChannelId = null; + // --- Event: Ready --- + client.once("ready", async () => { + if (!client.user) + return; + console.log(`Bot online as ${client.user.tag}`); + const guild = envConfig.DISCORD_GUILD_ID + ? client.guilds.cache.get(envConfig.DISCORD_GUILD_ID) + : client.guilds.cache.first(); + if (!guild) { + console.error("No guild found. Is the bot added to a server?"); + process.exit(1); + } + console.log(`Connected to guild: ${guild.name} (${guild.id})`); + // --- Ensure management channel exists --- + const managementName = disclawConfig.management_channel; + let existingWorkspace = db.getWorkspaceByName(managementName); + if (existingWorkspace) { + // Verify the channel still exists on Discord + const existingChannel = guild.channels.cache.get(existingWorkspace.channel_id); + if (existingChannel) { + managementChannelId = existingWorkspace.channel_id; + console.log(`Management channel found: #${managementName}`); + } + else { + // Channel was deleted from Discord, recreate it + existingWorkspace = undefined; + } + } + if (!existingWorkspace) { + // Look for an existing channel with that name first + let mgmtChannel; + const found = guild.channels.cache.find((ch) => ch.type === discord_js_1.ChannelType.GuildText && ch.name === managementName); + if (found) { + db.createWorkspace(found.id, guild.id, managementName, "__management__"); + managementChannelId = found.id; + mgmtChannel = found; + console.log(`Adopted existing channel #${managementName}`); + } + else { + const newChannel = await guild.channels.create({ + name: managementName, + type: discord_js_1.ChannelType.GuildText, + topic: "DisClaw Management -- verwende /new-agent um Agenten zu erstellen", + }); + db.createWorkspace(newChannel.id, guild.id, managementName, "__management__"); + managementChannelId = newChannel.id; + mgmtChannel = newChannel; + console.log(`Created management channel #${managementName}`); + } + // Send welcome message using the direct channel reference + await mgmtChannel + .send("DisClaw ist bereit. Verwende `/new-agent` um einen neuen Agenten zu erstellen.") + .catch(() => { }); + } + // --- Register slash commands --- + const rest = new discord_js_1.REST({ version: "10" }).setToken(envConfig.DISCORD_BOT_TOKEN); + try { + await rest.put(discord_js_1.Routes.applicationGuildCommands(client.user.id, guild.id), { + body: [new_agent_1.newAgentCommand.toJSON()], + }); + console.log("Slash commands registered."); + } + catch (err) { + console.error("Failed to register slash commands:", err); + } + }); + // --- Event: interactionCreate (slash commands) --- + client.on("interactionCreate", async (interaction) => { + if (!interaction.isChatInputCommand()) + return; + if (interaction.commandName === "new-agent") { + await (0, new_agent_1.handleNewAgent)(interaction, db, disclawConfig); + } + }); + // --- Event: messageCreate (agent routing) --- + client.on("messageCreate", async (message) => { + // Ignore bot messages + if (message.author.bot) + return; + // Only handle text channels in guilds + if (!message.guild || message.channel.type !== discord_js_1.ChannelType.GuildText) + return; + await (0, router_1.routeMessage)(message, db, disclawConfig, managementChannelId); + }); + // --- Login --- + await client.login(envConfig.DISCORD_BOT_TOKEN); + return client; +} +//# sourceMappingURL=bot.js.map \ No newline at end of file diff --git a/dist/bot.js.map b/dist/bot.js.map new file mode 100644 index 0000000..f2bedce --- /dev/null +++ b/dist/bot.js.map @@ -0,0 +1 @@ +{"version":3,"file":"bot.js","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/commands/new-agent.d.ts b/dist/commands/new-agent.d.ts new file mode 100644 index 0000000..3499a69 --- /dev/null +++ b/dist/commands/new-agent.d.ts @@ -0,0 +1,6 @@ +import { ChatInputCommandInteraction } from "discord.js"; +import { DisclawDatabase } from "../db/database"; +import { DisclawConfig } from "../config/loader"; +export declare const newAgentCommand: import("discord.js").SlashCommandOptionsOnlyBuilder; +export declare function handleNewAgent(interaction: ChatInputCommandInteraction, db: DisclawDatabase, config: DisclawConfig): Promise; +//# sourceMappingURL=new-agent.d.ts.map \ No newline at end of file diff --git a/dist/commands/new-agent.d.ts.map b/dist/commands/new-agent.d.ts.map new file mode 100644 index 0000000..a559fe4 --- /dev/null +++ b/dist/commands/new-agent.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"new-agent.d.ts","sourceRoot":"","sources":["../../src/commands/new-agent.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAI5B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAMjD,eAAO,MAAM,eAAe,qDAczB,CAAC;AAEJ,wBAAsB,cAAc,CAClC,WAAW,EAAE,2BAA2B,EACxC,EAAE,EAAE,eAAe,EACnB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,IAAI,CAAC,CA2Df"} \ No newline at end of file diff --git a/dist/commands/new-agent.js b/dist/commands/new-agent.js new file mode 100644 index 0000000..89203a9 --- /dev/null +++ b/dist/commands/new-agent.js @@ -0,0 +1,101 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.newAgentCommand = void 0; +exports.handleNewAgent = handleNewAgent; +const discord_js_1 = require("discord.js"); +const path = __importStar(require("path")); +const identity_1 = require("../agent/identity"); +// Allowed characters for agent names: lowercase letters, numbers, hyphens +const NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,30}[a-z0-9]$/; +exports.newAgentCommand = new discord_js_1.SlashCommandBuilder() + .setName("new-agent") + .setDescription("Erstellt einen neuen Agenten mit eigenem Kanal und Workspace") + .addStringOption((option) => option + .setName("name") + .setDescription("Eindeutiger Name fuer den Agenten (z.B. frontend-dev)") + .setRequired(true)) + .addStringOption((option) => option + .setName("role") + .setDescription("Rolle des Agenten (z.B. Frontend-Entwickler)") + .setRequired(true)); +async function handleNewAgent(interaction, db, config) { + const name = interaction.options.getString("name", true).toLowerCase(); + const role = interaction.options.getString("role", true); + const guild = interaction.guild; + // Validate name format + if (!NAME_PATTERN.test(name)) { + await interaction.reply({ + content: "Ungueltiger Name. Erlaubt sind Kleinbuchstaben, Zahlen und Bindestriche. " + + "Mindestens 2 Zeichen, muss mit Buchstabe oder Zahl beginnen und enden.", + ephemeral: true, + }); + return; + } + // Check uniqueness + if (db.getWorkspaceByName(name)) { + await interaction.reply({ + content: `Ein Agent mit dem Namen **${name}** existiert bereits.`, + ephemeral: true, + }); + return; + } + await interaction.deferReply(); + try { + // 1. Create Discord channel + const channel = await guild.channels.create({ + name, + type: discord_js_1.ChannelType.GuildText, + topic: `Agent: ${name} | Rolle: ${role}`, + }); + // 2. Create workspace with full Claude Code environment + const workspacePath = path.resolve(config.workspaces_root, name); + (0, identity_1.setupAgentWorkspace)(workspacePath, name, role, channel.id); + // 3. Save to database + db.createWorkspace(channel.id, guild.id, name, workspacePath); + // 4. Reply with confirmation + await interaction.editReply(`Agent **${name}** erstellt in <#${channel.id}>.\n` + + `Rolle: ${role}\n` + + `Workspace: \`${workspacePath}\``); + // 5. Send welcome message in the new channel + await channel.send(`Hallo! Ich bin **${name}** (${role}).\n` + + `Schreib mir eine Nachricht und ich antworte dir.`); + } + catch (error) { + const msg = error instanceof Error ? error.message : "Unbekannter Fehler"; + await interaction.editReply(`Fehler beim Erstellen des Agenten: ${msg}`); + } +} +//# sourceMappingURL=new-agent.js.map \ No newline at end of file diff --git a/dist/commands/new-agent.js.map b/dist/commands/new-agent.js.map new file mode 100644 index 0000000..79351f7 --- /dev/null +++ b/dist/commands/new-agent.js.map @@ -0,0 +1 @@ +{"version":3,"file":"new-agent.js","sourceRoot":"","sources":["../../src/commands/new-agent.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/config/loader.d.ts b/dist/config/loader.d.ts new file mode 100644 index 0000000..589f3a1 --- /dev/null +++ b/dist/config/loader.d.ts @@ -0,0 +1,14 @@ +export interface DisclawConfig { + workspaces_root: string; + management_channel: string; + claude_command: string; + claude_timeout_seconds: number; +} +export interface EnvConfig { + DISCORD_BOT_TOKEN: string; + DISCORD_GUILD_ID?: string; + CLAUDE_PATH?: string; +} +export declare function loadEnv(rootDir: string): EnvConfig; +export declare function loadDisclawConfig(rootDir: string): DisclawConfig; +//# sourceMappingURL=loader.d.ts.map \ No newline at end of file diff --git a/dist/config/loader.d.ts.map b/dist/config/loader.d.ts.map new file mode 100644 index 0000000..7064e7a --- /dev/null +++ b/dist/config/loader.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAclD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAwBhE"} \ No newline at end of file diff --git a/dist/config/loader.js b/dist/config/loader.js new file mode 100644 index 0000000..40570a5 --- /dev/null +++ b/dist/config/loader.js @@ -0,0 +1,74 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.loadEnv = loadEnv; +exports.loadDisclawConfig = loadDisclawConfig; +const fs = __importStar(require("fs")); +const path = __importStar(require("path")); +const dotenv = __importStar(require("dotenv")); +const YAML = __importStar(require("yaml")); +function loadEnv(rootDir) { + dotenv.config({ path: path.join(rootDir, ".env") }); + const token = process.env.DISCORD_BOT_TOKEN; + if (!token) { + throw new Error("DISCORD_BOT_TOKEN is not set in .env"); + } + return { + DISCORD_BOT_TOKEN: token, + DISCORD_GUILD_ID: process.env.DISCORD_GUILD_ID, + CLAUDE_PATH: process.env.CLAUDE_PATH, + }; +} +function loadDisclawConfig(rootDir) { + const configPath = path.join(rootDir, "disclaw.yaml"); + if (!fs.existsSync(configPath)) { + throw new Error(`disclaw.yaml not found at ${configPath}`); + } + const raw = fs.readFileSync(configPath, "utf-8"); + const parsed = YAML.parse(raw); + // Apply defaults + const config = { + workspaces_root: parsed.workspaces_root ?? "./workspaces", + management_channel: parsed.management_channel ?? "disclaw", + claude_command: parsed.claude_command ?? "claude", + claude_timeout_seconds: parsed.claude_timeout_seconds ?? 120, + }; + // Resolve workspaces_root to absolute path relative to project root + if (!path.isAbsolute(config.workspaces_root)) { + config.workspaces_root = path.resolve(rootDir, config.workspaces_root); + } + return config; +} +//# sourceMappingURL=loader.js.map \ No newline at end of file diff --git a/dist/config/loader.js.map b/dist/config/loader.js.map new file mode 100644 index 0000000..449f7a8 --- /dev/null +++ b/dist/config/loader.js.map @@ -0,0 +1 @@ +{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkBA,0BAcC;AAED,8CAwBC;AA1DD,uCAAyB;AACzB,2CAA6B;AAC7B,+CAAiC;AACjC,2CAA6B;AAe7B,SAAgB,OAAO,CAAC,OAAe;IACrC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IAEpD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAE5C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO;QACL,iBAAiB,EAAE,KAAK;QACxB,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB;QAC9C,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW;KACrC,CAAC;AACJ,CAAC;AAED,SAAgB,iBAAiB,CAAC,OAAe;IAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAEtD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,6BAA6B,UAAU,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA2B,CAAC;IAEzD,iBAAiB;IACjB,MAAM,MAAM,GAAkB;QAC5B,eAAe,EAAE,MAAM,CAAC,eAAe,IAAI,cAAc;QACzD,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,IAAI,SAAS;QAC1D,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,QAAQ;QACjD,sBAAsB,EAAE,MAAM,CAAC,sBAAsB,IAAI,GAAG;KAC7D,CAAC;IAEF,oEAAoE;IACpE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC;QAC7C,MAAM,CAAC,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;IACzE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"} \ No newline at end of file diff --git a/dist/db/database.d.ts b/dist/db/database.d.ts new file mode 100644 index 0000000..b095c0c --- /dev/null +++ b/dist/db/database.d.ts @@ -0,0 +1,30 @@ +export interface Workspace { + id: number; + channel_id: string; + guild_id: string; + agent_name: string; + workspace_path: string; + created_at: string; +} +export interface Conversation { + id: number; + workspace_id: number; + discord_msg_id: string; + role: "user" | "assistant"; + content: string; + author_name: string | null; + created_at: string; +} +export declare class DisclawDatabase { + private db; + constructor(dbPath: string); + private runMigrations; + getWorkspaceByChannelId(channelId: string): Workspace | undefined; + getWorkspaceByName(name: string): Workspace | undefined; + getAllWorkspaces(): Workspace[]; + createWorkspace(channelId: string, guildId: string, agentName: string, workspacePath: string): Workspace; + addConversation(workspaceId: number, discordMsgId: string, role: "user" | "assistant", content: string, authorName: string | null): void; + getConversationHistory(workspaceId: number, limit?: number): Conversation[]; + close(): void; +} +//# sourceMappingURL=database.d.ts.map \ No newline at end of file diff --git a/dist/db/database.d.ts.map b/dist/db/database.d.ts.map new file mode 100644 index 0000000..d768b8e --- /dev/null +++ b/dist/db/database.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,EAAE,CAAoB;gBAElB,MAAM,EAAE,MAAM;IAY1B,OAAO,CAAC,aAAa;IAMrB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAMjE,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAMvD,gBAAgB,IAAI,SAAS,EAAE;IAM/B,eAAe,CACb,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,SAAS;IAYZ,eAAe,CACb,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GAAG,WAAW,EAC1B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,IAAI;IAQP,sBAAsB,CACpB,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAW,GACjB,YAAY,EAAE;IAWjB,KAAK,IAAI,IAAI;CAGd"} \ No newline at end of file diff --git a/dist/db/database.js b/dist/db/database.js new file mode 100644 index 0000000..85b1e2d --- /dev/null +++ b/dist/db/database.js @@ -0,0 +1,123 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DisclawDatabase = void 0; +const better_sqlite3_1 = __importDefault(require("better-sqlite3")); +const fs = __importStar(require("fs")); +const path = __importStar(require("path")); +const SCHEMA_SQL = ` +CREATE TABLE IF NOT EXISTS workspaces ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + channel_id TEXT NOT NULL UNIQUE, + guild_id TEXT NOT NULL, + agent_name TEXT NOT NULL UNIQUE, + workspace_path TEXT NOT NULL, + created_at TEXT NOT NULL DEFAULT (datetime('now')) +); + +CREATE TABLE IF NOT EXISTS conversations ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + workspace_id INTEGER NOT NULL REFERENCES workspaces(id), + discord_msg_id TEXT NOT NULL UNIQUE, + role TEXT NOT NULL, + content TEXT NOT NULL, + author_name TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')) +); + +CREATE INDEX IF NOT EXISTS idx_conversations_workspace + ON conversations(workspace_id, created_at); +`; +class DisclawDatabase { + db; + constructor(dbPath) { + const dir = path.dirname(dbPath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + this.db = new better_sqlite3_1.default(dbPath); + this.db.pragma("journal_mode = WAL"); + this.db.pragma("foreign_keys = ON"); + this.runMigrations(); + } + runMigrations() { + this.db.exec(SCHEMA_SQL); + } + // --- Workspace queries --- + getWorkspaceByChannelId(channelId) { + return this.db + .prepare("SELECT * FROM workspaces WHERE channel_id = ?") + .get(channelId); + } + getWorkspaceByName(name) { + return this.db + .prepare("SELECT * FROM workspaces WHERE agent_name = ?") + .get(name); + } + getAllWorkspaces() { + return this.db + .prepare("SELECT * FROM workspaces ORDER BY created_at") + .all(); + } + createWorkspace(channelId, guildId, agentName, workspacePath) { + const stmt = this.db.prepare("INSERT INTO workspaces (channel_id, guild_id, agent_name, workspace_path) VALUES (?, ?, ?, ?)"); + const result = stmt.run(channelId, guildId, agentName, workspacePath); + return this.db + .prepare("SELECT * FROM workspaces WHERE id = ?") + .get(result.lastInsertRowid); + } + // --- Conversation queries --- + addConversation(workspaceId, discordMsgId, role, content, authorName) { + this.db + .prepare("INSERT OR IGNORE INTO conversations (workspace_id, discord_msg_id, role, content, author_name) VALUES (?, ?, ?, ?, ?)") + .run(workspaceId, discordMsgId, role, content, authorName); + } + getConversationHistory(workspaceId, limit = 50) { + return this.db + .prepare(`SELECT * FROM conversations + WHERE workspace_id = ? + ORDER BY created_at DESC + LIMIT ?`) + .all(workspaceId, limit); + } + close() { + this.db.close(); + } +} +exports.DisclawDatabase = DisclawDatabase; +//# sourceMappingURL=database.js.map \ No newline at end of file diff --git a/dist/db/database.js.map b/dist/db/database.js.map new file mode 100644 index 0000000..5e6c01d --- /dev/null +++ b/dist/db/database.js.map @@ -0,0 +1 @@ +{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,oEAAsC;AACtC,uCAAyB;AACzB,2CAA6B;AAE7B,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;CAsBlB,CAAC;AAqBF,MAAa,eAAe;IAClB,EAAE,CAAoB;IAE9B,YAAY,MAAc;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,IAAI,CAAC,EAAE,GAAG,IAAI,wBAAQ,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACrC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;QACpC,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC;IAEO,aAAa;QACnB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC3B,CAAC;IAED,4BAA4B;IAE5B,uBAAuB,CAAC,SAAiB;QACvC,OAAO,IAAI,CAAC,EAAE;aACX,OAAO,CAAC,+CAA+C,CAAC;aACxD,GAAG,CAAC,SAAS,CAA0B,CAAC;IAC7C,CAAC;IAED,kBAAkB,CAAC,IAAY;QAC7B,OAAO,IAAI,CAAC,EAAE;aACX,OAAO,CAAC,+CAA+C,CAAC;aACxD,GAAG,CAAC,IAAI,CAA0B,CAAC;IACxC,CAAC;IAED,gBAAgB;QACd,OAAO,IAAI,CAAC,EAAE;aACX,OAAO,CAAC,8CAA8C,CAAC;aACvD,GAAG,EAAiB,CAAC;IAC1B,CAAC;IAED,eAAe,CACb,SAAiB,EACjB,OAAe,EACf,SAAiB,EACjB,aAAqB;QAErB,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAC1B,+FAA+F,CAChG,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC,EAAE;aACX,OAAO,CAAC,uCAAuC,CAAC;aAChD,GAAG,CAAC,MAAM,CAAC,eAAe,CAAc,CAAC;IAC9C,CAAC;IAED,+BAA+B;IAE/B,eAAe,CACb,WAAmB,EACnB,YAAoB,EACpB,IAA0B,EAC1B,OAAe,EACf,UAAyB;QAEzB,IAAI,CAAC,EAAE;aACJ,OAAO,CACN,uHAAuH,CACxH;aACA,GAAG,CAAC,WAAW,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IAC/D,CAAC;IAED,sBAAsB,CACpB,WAAmB,EACnB,QAAgB,EAAE;QAElB,OAAO,IAAI,CAAC,EAAE;aACX,OAAO,CACN;;;iBAGS,CACV;aACA,GAAG,CAAC,WAAW,EAAE,KAAK,CAAmB,CAAC;IAC/C,CAAC;IAED,KAAK;QACH,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;CACF;AAvFD,0CAuFC"} \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..e26a57a --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/index.d.ts.map b/dist/index.d.ts.map new file mode 100644 index 0000000..535b86d --- /dev/null +++ b/dist/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..6fbb6f4 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,66 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __importStar(require("path")); +const loader_1 = require("./config/loader"); +const database_1 = require("./db/database"); +const bot_1 = require("./bot"); +const ROOT_DIR = path.resolve(__dirname, ".."); +async function main() { + console.log("DisClaw starting..."); + // 1. Load configuration + const envConfig = (0, loader_1.loadEnv)(ROOT_DIR); + const disclawConfig = (0, loader_1.loadDisclawConfig)(ROOT_DIR); + console.log(`Config loaded. Workspaces root: ${disclawConfig.workspaces_root}`); + // 2. Initialize database + const dbPath = path.join(ROOT_DIR, "data", "disclaw.db"); + const db = new database_1.DisclawDatabase(dbPath); + console.log(`Database initialized at ${dbPath}`); + // 3. Start Discord bot + await (0, bot_1.startBot)(envConfig, disclawConfig, db); + // Graceful shutdown + const shutdown = () => { + console.log("\nShutting down..."); + db.close(); + process.exit(0); + }; + process.on("SIGINT", shutdown); + process.on("SIGTERM", shutdown); +} +main().catch((err) => { + console.error("Fatal error:", err); + process.exit(1); +}); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map new file mode 100644 index 0000000..8655666 --- /dev/null +++ b/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAA6B;AAC7B,4CAA6D;AAC7D,4CAAgD;AAChD,+BAAiC;AAEjC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAE/C,KAAK,UAAU,IAAI;IACjB,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAEnC,wBAAwB;IACxB,MAAM,SAAS,GAAG,IAAA,gBAAO,EAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,aAAa,GAAG,IAAA,0BAAiB,EAAC,QAAQ,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CACT,mCAAmC,aAAa,CAAC,eAAe,EAAE,CACnE,CAAC;IAEF,yBAAyB;IACzB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;IACzD,MAAM,EAAE,GAAG,IAAI,0BAAe,CAAC,MAAM,CAAC,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,2BAA2B,MAAM,EAAE,CAAC,CAAC;IAEjD,uBAAuB;IACvB,MAAM,IAAA,cAAQ,EAAC,SAAS,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC;IAE7C,oBAAoB;IACpB,MAAM,QAAQ,GAAG,GAAS,EAAE;QAC1B,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAClC,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/router.d.ts b/dist/router.d.ts new file mode 100644 index 0000000..b22ffe1 --- /dev/null +++ b/dist/router.d.ts @@ -0,0 +1,10 @@ +import { Message } from "discord.js"; +import { DisclawDatabase } from "./db/database"; +import { DisclawConfig } from "./config/loader"; +/** + * Routes a Discord message to the correct agent based on channel_id. + * Returns true if the message was handled, false if the channel is not + * mapped to any agent. + */ +export declare function routeMessage(message: Message, db: DisclawDatabase, config: DisclawConfig, managementChannelId: string | null): Promise; +//# sourceMappingURL=router.d.ts.map \ No newline at end of file diff --git a/dist/router.d.ts.map b/dist/router.d.ts.map new file mode 100644 index 0000000..491cadd --- /dev/null +++ b/dist/router.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAe,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAmDhD;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,eAAe,EACnB,MAAM,EAAE,aAAa,EACrB,mBAAmB,EAAE,MAAM,GAAG,IAAI,GACjC,OAAO,CAAC,OAAO,CAAC,CAyElB"} \ No newline at end of file diff --git a/dist/router.js b/dist/router.js new file mode 100644 index 0000000..d106f54 --- /dev/null +++ b/dist/router.js @@ -0,0 +1,103 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.routeMessage = routeMessage; +const runner_1 = require("./agent/runner"); +const DISCORD_MAX_LENGTH = 2000; +/** + * Splits a message at line breaks so each chunk stays under the Discord + * character limit. If a single line exceeds the limit, it is split at + * the character boundary as a last resort. + */ +function splitMessage(text) { + if (text.length <= DISCORD_MAX_LENGTH) { + return [text]; + } + const chunks = []; + const lines = text.split("\n"); + let current = ""; + for (const line of lines) { + // If adding this line would exceed the limit, flush current chunk + if (current.length + line.length + 1 > DISCORD_MAX_LENGTH) { + if (current.length > 0) { + chunks.push(current); + current = ""; + } + // Handle lines that are themselves too long + if (line.length > DISCORD_MAX_LENGTH) { + let remaining = line; + while (remaining.length > DISCORD_MAX_LENGTH) { + chunks.push(remaining.slice(0, DISCORD_MAX_LENGTH)); + remaining = remaining.slice(DISCORD_MAX_LENGTH); + } + if (remaining.length > 0) { + current = remaining; + } + continue; + } + } + current += (current.length > 0 ? "\n" : "") + line; + } + if (current.length > 0) { + chunks.push(current); + } + return chunks; +} +/** + * Routes a Discord message to the correct agent based on channel_id. + * Returns true if the message was handled, false if the channel is not + * mapped to any agent. + */ +async function routeMessage(message, db, config, managementChannelId) { + const channelId = message.channelId; + // Ignore messages in the management channel (commands only) + if (channelId === managementChannelId) { + return false; + } + // Look up workspace for this channel + const workspace = db.getWorkspaceByChannelId(channelId); + if (!workspace) { + return false; + } + const channel = message.channel; + // Show typing indicator while the agent works + const typingInterval = setInterval(() => { + channel.sendTyping().catch(() => { }); + }, 5000); + // Send immediately as well + await channel.sendTyping().catch(() => { }); + try { + // Load recent conversation history for context + const history = db.getConversationHistory(workspace.id, 30); + // Run the agent + const response = await (0, runner_1.runAgent)({ + workspacePath: workspace.workspace_path, + channelName: channel.name, + userMessage: message.content, + conversationHistory: history, + }); + // Store user message + db.addConversation(workspace.id, message.id, "user", message.content, message.author.username); + // Split and send response + const chunks = splitMessage(response); + let firstMsgId = null; + for (const chunk of chunks) { + const sent = await channel.send(chunk); + if (!firstMsgId) { + firstMsgId = sent.id; + } + } + // Store assistant response (use first message id for dedup) + if (firstMsgId) { + db.addConversation(workspace.id, firstMsgId, "assistant", response, null); + } + } + catch (error) { + const msg = error instanceof Error ? error.message : "Unbekannter Fehler"; + await channel.send(`Fehler bei der Verarbeitung: ${msg}`).catch(() => { }); + } + finally { + clearInterval(typingInterval); + } + return true; +} +//# sourceMappingURL=router.js.map \ No newline at end of file diff --git a/dist/router.js.map b/dist/router.js.map new file mode 100644 index 0000000..2fd9480 --- /dev/null +++ b/dist/router.js.map @@ -0,0 +1 @@ +{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"} \ No newline at end of file