Initial commit: Discord-Claude Gateway with event-driven agent runtime
This commit is contained in:
1
references/nanoclaw
Submodule
1
references/nanoclaw
Submodule
Submodule references/nanoclaw added at 1980d97d90
@@ -1,4 +1,4 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { spawn } from "node:child_process";
|
||||
import { writeFile, unlink } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
@@ -171,47 +171,65 @@ export class AgentRuntime {
|
||||
sessionId?: string,
|
||||
): Promise<ClaudeJsonResponse> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Build args — keep it minimal and match what works on the CLI directly
|
||||
const args: string[] = [
|
||||
"-p", promptText,
|
||||
"--output-format", "json",
|
||||
"--system-prompt-file", systemPromptFile,
|
||||
"--dangerously-skip-permissions",
|
||||
"--append-system-prompt-file", systemPromptFile,
|
||||
"--verbose",
|
||||
];
|
||||
|
||||
if (sessionId) {
|
||||
args.push("--resume", sessionId);
|
||||
}
|
||||
|
||||
if (this.config.allowedTools.length > 0) {
|
||||
args.push("--allowedTools", ...this.config.allowedTools);
|
||||
// --allowedTools expects each tool as a separate quoted arg
|
||||
for (const tool of this.config.allowedTools) {
|
||||
args.push("--allowedTools", tool);
|
||||
}
|
||||
|
||||
args.push("--max-turns", "25");
|
||||
|
||||
console.log(`[DEBUG] Spawning Claude CLI: ${this.config.claudeCliPath} -p "${promptText}" --output-format json --system-prompt-file ${systemPromptFile} ... (${args.length} args total)`);
|
||||
console.log(`[DEBUG] Spawning: ${this.config.claudeCliPath} args=${JSON.stringify(args.slice(0, 8))}... (${args.length} total)`);
|
||||
|
||||
const child = execFile(
|
||||
this.config.claudeCliPath,
|
||||
args,
|
||||
{
|
||||
timeout: this.config.queryTimeoutMs,
|
||||
maxBuffer: 10 * 1024 * 1024,
|
||||
encoding: "utf-8",
|
||||
},
|
||||
(error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`[DEBUG] Claude CLI error: code=${error.code}, killed=${error.killed}, stderr=${stderr?.slice(0, 500)}`);
|
||||
if (error.killed || error.code === "ETIMEDOUT") {
|
||||
const child = spawn(this.config.claudeCliPath, args, {
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
|
||||
child.stdout.on("data", (data: Buffer) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
|
||||
child.stderr.on("data", (data: Buffer) => {
|
||||
const chunk = data.toString();
|
||||
stderr += chunk;
|
||||
// Log stderr in real-time so we can see what's happening
|
||||
if (chunk.trim()) {
|
||||
console.log(`[DEBUG] Claude stderr: ${chunk.trim().slice(0, 200)}`);
|
||||
}
|
||||
});
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
console.log(`[DEBUG] Timeout reached, killing Claude CLI process`);
|
||||
child.kill("SIGTERM");
|
||||
reject(new Error("Query timed out"));
|
||||
return;
|
||||
}
|
||||
reject(new Error(`Claude CLI error: ${stderr || error.message}`));
|
||||
return;
|
||||
}, this.config.queryTimeoutMs);
|
||||
|
||||
child.on("close", (code) => {
|
||||
clearTimeout(timer);
|
||||
|
||||
console.log(`[DEBUG] Claude CLI exited: code=${code}, stdout=${stdout.length} chars`);
|
||||
if (stdout.length > 0) {
|
||||
console.log(`[DEBUG] Claude stdout preview: ${stdout.slice(0, 300)}`);
|
||||
}
|
||||
|
||||
console.log(`[DEBUG] Claude CLI stdout (${stdout.length} chars): ${stdout.slice(0, 300)}...`);
|
||||
if (stderr) {
|
||||
console.log(`[DEBUG] Claude CLI stderr: ${stderr.slice(0, 300)}`);
|
||||
if (code !== 0 && code !== null) {
|
||||
reject(new Error(`Claude CLI error (exit ${code}): ${stderr.slice(0, 500) || "unknown error"}`));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -225,16 +243,16 @@ export class AgentRuntime {
|
||||
}
|
||||
|
||||
resolve(parsed);
|
||||
} catch (parseError) {
|
||||
} catch {
|
||||
resolve({
|
||||
type: "result",
|
||||
result: stdout.trim(),
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
child.on("error", (err) => {
|
||||
clearTimeout(timer);
|
||||
console.error(`[DEBUG] Failed to spawn Claude CLI: ${err.message}`);
|
||||
reject(new Error(`Failed to spawn Claude CLI: ${err.message}`));
|
||||
});
|
||||
|
||||
@@ -21,7 +21,10 @@ export function shouldIgnoreMessage(message: { author: { bot: boolean } }): bool
|
||||
}
|
||||
|
||||
export function extractPromptFromMention(content: string, botId: string): string {
|
||||
return content.replace(new RegExp(`<@!?${botId}>`, "g"), "").trim();
|
||||
// Remove user mentions (<@ID> or <@!ID>) and role mentions (<@&ID>) for the bot
|
||||
return content
|
||||
.replace(/<@[!&]?\d+>/g, "")
|
||||
.trim();
|
||||
}
|
||||
|
||||
export class DiscordBot {
|
||||
|
||||
Reference in New Issue
Block a user