ralph-cli-sandboxed 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/dist/commands/action.js +47 -20
- package/dist/commands/chat.d.ts +1 -1
- package/dist/commands/chat.js +325 -62
- package/dist/commands/config.js +2 -1
- package/dist/commands/daemon.d.ts +2 -5
- package/dist/commands/daemon.js +118 -49
- package/dist/commands/docker.js +110 -73
- package/dist/commands/fix-config.js +2 -1
- package/dist/commands/fix-prd.js +2 -2
- package/dist/commands/help.js +19 -3
- package/dist/commands/init.js +78 -17
- package/dist/commands/listen.js +116 -5
- package/dist/commands/logo.d.ts +5 -0
- package/dist/commands/logo.js +41 -0
- package/dist/commands/notify.js +1 -1
- package/dist/commands/once.js +19 -9
- package/dist/commands/prd.js +20 -2
- package/dist/commands/run.js +111 -27
- package/dist/commands/slack.d.ts +10 -0
- package/dist/commands/slack.js +333 -0
- package/dist/config/responder-presets.json +69 -0
- package/dist/index.js +6 -1
- package/dist/providers/discord.d.ts +82 -0
- package/dist/providers/discord.js +697 -0
- package/dist/providers/slack.d.ts +79 -0
- package/dist/providers/slack.js +715 -0
- package/dist/providers/telegram.d.ts +30 -0
- package/dist/providers/telegram.js +190 -7
- package/dist/responders/claude-code-responder.d.ts +48 -0
- package/dist/responders/claude-code-responder.js +203 -0
- package/dist/responders/cli-responder.d.ts +62 -0
- package/dist/responders/cli-responder.js +298 -0
- package/dist/responders/llm-responder.d.ts +135 -0
- package/dist/responders/llm-responder.js +582 -0
- package/dist/templates/macos-scripts.js +2 -4
- package/dist/templates/prompts.js +4 -2
- package/dist/tui/ConfigEditor.js +42 -5
- package/dist/tui/components/ArrayEditor.js +1 -1
- package/dist/tui/components/EditorPanel.js +10 -6
- package/dist/tui/components/HelpPanel.d.ts +1 -1
- package/dist/tui/components/HelpPanel.js +1 -1
- package/dist/tui/components/JsonSnippetEditor.js +8 -5
- package/dist/tui/components/KeyValueEditor.js +69 -5
- package/dist/tui/components/LLMProvidersEditor.d.ts +22 -0
- package/dist/tui/components/LLMProvidersEditor.js +357 -0
- package/dist/tui/components/ObjectEditor.js +1 -1
- package/dist/tui/components/Preview.js +1 -1
- package/dist/tui/components/RespondersEditor.d.ts +22 -0
- package/dist/tui/components/RespondersEditor.js +437 -0
- package/dist/tui/components/SectionNav.js +27 -3
- package/dist/tui/utils/presets.js +15 -2
- package/dist/utils/chat-client.d.ts +33 -4
- package/dist/utils/chat-client.js +20 -1
- package/dist/utils/config.d.ts +100 -1
- package/dist/utils/config.js +78 -1
- package/dist/utils/daemon-actions.d.ts +19 -0
- package/dist/utils/daemon-actions.js +111 -0
- package/dist/utils/daemon-client.d.ts +21 -0
- package/dist/utils/daemon-client.js +28 -1
- package/dist/utils/llm-client.d.ts +82 -0
- package/dist/utils/llm-client.js +185 -0
- package/dist/utils/message-queue.js +6 -6
- package/dist/utils/notification.d.ts +10 -2
- package/dist/utils/notification.js +111 -4
- package/dist/utils/prd-validator.js +60 -19
- package/dist/utils/prompt.js +22 -12
- package/dist/utils/responder-logger.d.ts +47 -0
- package/dist/utils/responder-logger.js +129 -0
- package/dist/utils/responder-presets.d.ts +92 -0
- package/dist/utils/responder-presets.js +156 -0
- package/dist/utils/responder.d.ts +88 -0
- package/dist/utils/responder.js +207 -0
- package/dist/utils/stream-json.js +6 -6
- package/docs/CHAT-CLIENTS.md +520 -0
- package/docs/CHAT-RESPONDERS.md +785 -0
- package/docs/DEVELOPMENT.md +25 -0
- package/docs/USEFUL_ACTIONS.md +815 -0
- package/docs/chat-architecture.md +251 -0
- package/package.json +14 -1
package/dist/commands/chat.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Chat command for managing Telegram
|
|
2
|
+
* Chat command for managing Telegram, Slack, and other chat integrations.
|
|
3
3
|
* Allows ralph to receive commands and send notifications via chat services.
|
|
4
4
|
*/
|
|
5
5
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -7,8 +7,10 @@ import { join, basename } from "path";
|
|
|
7
7
|
import { spawn } from "child_process";
|
|
8
8
|
import { loadConfig, getRalphDir, isRunningInContainer } from "../utils/config.js";
|
|
9
9
|
import { createTelegramClient } from "../providers/telegram.js";
|
|
10
|
+
import { createSlackClient } from "../providers/slack.js";
|
|
11
|
+
import { createDiscordClient } from "../providers/discord.js";
|
|
10
12
|
import { generateProjectId, formatStatusMessage, formatStatusForChat, } from "../utils/chat-client.js";
|
|
11
|
-
import { getMessagesPath, sendMessage, waitForResponse
|
|
13
|
+
import { getMessagesPath, sendMessage, waitForResponse } from "../utils/message-queue.js";
|
|
12
14
|
const CHAT_STATE_FILE = "chat-state.json";
|
|
13
15
|
/**
|
|
14
16
|
* Load chat state from .ralph/chat-state.json
|
|
@@ -220,6 +222,23 @@ async function handleCommand(command, client, config, state, debug) {
|
|
|
220
222
|
saveChatState(state);
|
|
221
223
|
break;
|
|
222
224
|
}
|
|
225
|
+
case "stop": {
|
|
226
|
+
// Stop a running ralph run process in the sandbox
|
|
227
|
+
await client.sendMessage(chatId, `${state.projectName}: Stopping ralph run...`);
|
|
228
|
+
const response = await sendToSandbox("stop", [], debug, 10000);
|
|
229
|
+
if (response) {
|
|
230
|
+
if (response.success) {
|
|
231
|
+
await client.sendMessage(chatId, `${state.projectName}: ${response.output}`);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
await client.sendMessage(chatId, `${state.projectName}: ${response.error}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
await client.sendMessage(chatId, `${state.projectName}: No response from sandbox. Is 'ralph listen' running?`);
|
|
239
|
+
}
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
223
242
|
case "status": {
|
|
224
243
|
// Try sandbox first, fall back to host
|
|
225
244
|
const response = await sendToSandbox("status", [], debug, 5000);
|
|
@@ -253,7 +272,8 @@ async function handleCommand(command, client, config, state, debug) {
|
|
|
253
272
|
}
|
|
254
273
|
case "add": {
|
|
255
274
|
if (args.length === 0) {
|
|
256
|
-
|
|
275
|
+
const usage = client.provider === "slack" ? "/ralph add [task description]" : "/add [task description]";
|
|
276
|
+
await client.sendMessage(chatId, `${state.projectName}: Usage: ${usage}`);
|
|
257
277
|
return;
|
|
258
278
|
}
|
|
259
279
|
const description = args.join(" ");
|
|
@@ -268,7 +288,8 @@ async function handleCommand(command, client, config, state, debug) {
|
|
|
268
288
|
}
|
|
269
289
|
case "exec": {
|
|
270
290
|
if (args.length === 0) {
|
|
271
|
-
|
|
291
|
+
const usage = client.provider === "slack" ? "/ralph exec [command]" : "/exec [command]";
|
|
292
|
+
await client.sendMessage(chatId, `${state.projectName}: Usage: ${usage}`);
|
|
272
293
|
return;
|
|
273
294
|
}
|
|
274
295
|
// Send exec command to sandbox
|
|
@@ -286,22 +307,19 @@ async function handleCommand(command, client, config, state, debug) {
|
|
|
286
307
|
}
|
|
287
308
|
break;
|
|
288
309
|
}
|
|
289
|
-
case "stop": {
|
|
290
|
-
await client.sendMessage(chatId, `${state.projectName}: Stop command received (not implemented yet)`);
|
|
291
|
-
break;
|
|
292
|
-
}
|
|
293
310
|
case "action": {
|
|
294
311
|
// Reload config to pick up new actions
|
|
295
312
|
const freshConfig = loadConfig();
|
|
296
313
|
const actions = freshConfig.daemon?.actions || {};
|
|
297
|
-
const actionNames = Object.keys(actions).filter(name => name !== "notify" && name !== "telegram_notify");
|
|
314
|
+
const actionNames = Object.keys(actions).filter((name) => name !== "notify" && name !== "telegram_notify");
|
|
298
315
|
if (args.length === 0) {
|
|
299
316
|
// List available actions
|
|
317
|
+
const usage = client.provider === "slack" ? "/ralph action <name>" : "/action <name>";
|
|
300
318
|
if (actionNames.length === 0) {
|
|
301
319
|
await client.sendMessage(chatId, `${state.projectName}: No actions configured. Add actions to daemon.actions in config.json`);
|
|
302
320
|
}
|
|
303
321
|
else {
|
|
304
|
-
await client.sendMessage(chatId, `${state.projectName}: Available actions: ${actionNames.join(", ")}\nUsage:
|
|
322
|
+
await client.sendMessage(chatId, `${state.projectName}: Available actions: ${actionNames.join(", ")}\nUsage: ${usage}`);
|
|
305
323
|
}
|
|
306
324
|
return;
|
|
307
325
|
}
|
|
@@ -346,7 +364,8 @@ async function handleCommand(command, client, config, state, debug) {
|
|
|
346
364
|
}
|
|
347
365
|
case "claude": {
|
|
348
366
|
if (args.length === 0) {
|
|
349
|
-
|
|
367
|
+
const usage = client.provider === "slack" ? "/ralph <prompt>" : "/claude [prompt]";
|
|
368
|
+
await client.sendMessage(chatId, `${state.projectName}: Usage: ${usage}`);
|
|
350
369
|
return;
|
|
351
370
|
}
|
|
352
371
|
const prompt = args.join(" ");
|
|
@@ -380,15 +399,24 @@ async function handleCommand(command, client, config, state, debug) {
|
|
|
380
399
|
break;
|
|
381
400
|
}
|
|
382
401
|
case "help": {
|
|
383
|
-
const
|
|
384
|
-
|
|
402
|
+
const isSlack = client.provider === "slack";
|
|
403
|
+
const helpText = isSlack
|
|
404
|
+
? `/ralph help - This help
|
|
405
|
+
/ralph status - PRD progress
|
|
406
|
+
/ralph run [category] - Start automation
|
|
407
|
+
/ralph stop - Stop automation
|
|
408
|
+
/ralph add [desc] - Add task
|
|
409
|
+
/ralph exec [cmd] - Shell command
|
|
410
|
+
/ralph action [name] - Run action
|
|
411
|
+
/ralph <prompt> - Run Claude Code`
|
|
412
|
+
: `/help - This help
|
|
385
413
|
/status - PRD progress
|
|
414
|
+
/run - Start automation
|
|
415
|
+
/stop - Stop automation
|
|
386
416
|
/add [desc] - Add task
|
|
387
417
|
/exec [cmd] - Shell command
|
|
388
418
|
/action [name] - Run action
|
|
389
|
-
/claude [prompt] - Run Claude Code
|
|
390
|
-
/help - This help
|
|
391
|
-
`.trim();
|
|
419
|
+
/claude [prompt] - Run Claude Code`;
|
|
392
420
|
await client.sendMessage(chatId, helpText);
|
|
393
421
|
break;
|
|
394
422
|
}
|
|
@@ -397,21 +425,91 @@ async function handleCommand(command, client, config, state, debug) {
|
|
|
397
425
|
}
|
|
398
426
|
}
|
|
399
427
|
/**
|
|
400
|
-
*
|
|
428
|
+
* Create a chat client based on the provider configuration.
|
|
401
429
|
*/
|
|
402
|
-
|
|
403
|
-
|
|
430
|
+
function createChatClient(config, debug) {
|
|
431
|
+
const provider = config.chat?.provider || "telegram";
|
|
432
|
+
if (provider === "slack") {
|
|
433
|
+
// Check that Slack is configured
|
|
434
|
+
if (!config.chat?.slack?.botToken) {
|
|
435
|
+
console.error("Error: Slack bot token not configured");
|
|
436
|
+
console.error("Set chat.slack.botToken in .ralph/config.json");
|
|
437
|
+
console.error("Get a token from your Slack app settings: https://api.slack.com/apps");
|
|
438
|
+
process.exit(1);
|
|
439
|
+
}
|
|
440
|
+
if (!config.chat?.slack?.appToken) {
|
|
441
|
+
console.error("Error: Slack app token not configured");
|
|
442
|
+
console.error("Set chat.slack.appToken in .ralph/config.json");
|
|
443
|
+
console.error("Enable Socket Mode in your Slack app and generate an app token");
|
|
444
|
+
process.exit(1);
|
|
445
|
+
}
|
|
446
|
+
if (!config.chat?.slack?.signingSecret) {
|
|
447
|
+
console.error("Error: Slack signing secret not configured");
|
|
448
|
+
console.error("Set chat.slack.signingSecret in .ralph/config.json");
|
|
449
|
+
console.error("Find your signing secret in Slack app Basic Information");
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
if (config.chat.slack.enabled === false) {
|
|
453
|
+
console.error("Error: Slack is disabled in config (slack.enabled = false)");
|
|
454
|
+
process.exit(1);
|
|
455
|
+
}
|
|
456
|
+
return {
|
|
457
|
+
client: createSlackClient({
|
|
458
|
+
botToken: config.chat.slack.botToken,
|
|
459
|
+
appToken: config.chat.slack.appToken,
|
|
460
|
+
signingSecret: config.chat.slack.signingSecret,
|
|
461
|
+
allowedChannelIds: config.chat.slack.allowedChannelIds,
|
|
462
|
+
}, debug),
|
|
463
|
+
provider: "slack",
|
|
464
|
+
allowedChatIds: config.chat.slack.allowedChannelIds,
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
if (provider === "discord") {
|
|
468
|
+
// Check that Discord is configured
|
|
469
|
+
if (!config.chat?.discord?.botToken) {
|
|
470
|
+
console.error("Error: Discord bot token not configured");
|
|
471
|
+
console.error("Set chat.discord.botToken in .ralph/config.json");
|
|
472
|
+
console.error("Get a token from the Discord Developer Portal: https://discord.com/developers/applications");
|
|
473
|
+
process.exit(1);
|
|
474
|
+
}
|
|
475
|
+
if (config.chat.discord.enabled === false) {
|
|
476
|
+
console.error("Error: Discord is disabled in config (discord.enabled = false)");
|
|
477
|
+
process.exit(1);
|
|
478
|
+
}
|
|
479
|
+
return {
|
|
480
|
+
client: createDiscordClient({
|
|
481
|
+
botToken: config.chat.discord.botToken,
|
|
482
|
+
allowedGuildIds: config.chat.discord.allowedGuildIds,
|
|
483
|
+
allowedChannelIds: config.chat.discord.allowedChannelIds,
|
|
484
|
+
}, debug),
|
|
485
|
+
provider: "discord",
|
|
486
|
+
allowedChatIds: config.chat.discord.allowedChannelIds,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
// Default to Telegram
|
|
404
490
|
if (!config.chat?.telegram?.botToken) {
|
|
405
491
|
console.error("Error: Telegram bot token not configured");
|
|
406
492
|
console.error("Set chat.telegram.botToken in .ralph/config.json");
|
|
407
493
|
console.error("Get a token from @BotFather on Telegram");
|
|
408
494
|
process.exit(1);
|
|
409
495
|
}
|
|
410
|
-
// Check if Telegram is explicitly disabled
|
|
411
496
|
if (config.chat.telegram.enabled === false) {
|
|
412
497
|
console.error("Error: Telegram is disabled in config (telegram.enabled = false)");
|
|
413
498
|
process.exit(1);
|
|
414
499
|
}
|
|
500
|
+
return {
|
|
501
|
+
client: createTelegramClient({
|
|
502
|
+
botToken: config.chat.telegram.botToken,
|
|
503
|
+
allowedChatIds: config.chat.telegram.allowedChatIds,
|
|
504
|
+
}, debug),
|
|
505
|
+
provider: "telegram",
|
|
506
|
+
allowedChatIds: config.chat.telegram.allowedChatIds,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Start the chat daemon (listens for messages and handles commands).
|
|
511
|
+
*/
|
|
512
|
+
async function startChat(config, debug) {
|
|
415
513
|
// Create or load chat state
|
|
416
514
|
let state = loadChatState();
|
|
417
515
|
const projectId = getOrCreateProjectId();
|
|
@@ -424,15 +522,12 @@ async function startChat(config, debug) {
|
|
|
424
522
|
};
|
|
425
523
|
saveChatState(state);
|
|
426
524
|
}
|
|
427
|
-
// Create
|
|
428
|
-
const client =
|
|
429
|
-
botToken: config.chat.telegram.botToken,
|
|
430
|
-
allowedChatIds: config.chat.telegram.allowedChatIds,
|
|
431
|
-
}, debug);
|
|
525
|
+
// Create chat client based on provider
|
|
526
|
+
const { client, provider, allowedChatIds } = createChatClient(config, debug);
|
|
432
527
|
console.log("Ralph Chat Daemon");
|
|
433
528
|
console.log("-".repeat(40));
|
|
434
529
|
console.log(`Project: ${projectName}`);
|
|
435
|
-
console.log(`Provider: ${
|
|
530
|
+
console.log(`Provider: ${provider}`);
|
|
436
531
|
console.log("");
|
|
437
532
|
// Connect and start listening
|
|
438
533
|
try {
|
|
@@ -442,21 +537,34 @@ async function startChat(config, debug) {
|
|
|
442
537
|
return Promise.resolve();
|
|
443
538
|
}
|
|
444
539
|
: undefined);
|
|
445
|
-
|
|
540
|
+
const providerName = provider === "slack" ? "Slack" : provider === "discord" ? "Discord" : "Telegram";
|
|
541
|
+
console.log(`Connected to ${providerName}!`);
|
|
446
542
|
console.log("");
|
|
447
|
-
console.log(
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
543
|
+
console.log(`Commands (send in ${providerName}):`);
|
|
544
|
+
if (provider === "slack") {
|
|
545
|
+
console.log(" /ralph help - Show help");
|
|
546
|
+
console.log(" /ralph status - Show PRD progress");
|
|
547
|
+
console.log(" /ralph run - Start ralph automation");
|
|
548
|
+
console.log(" /ralph stop - Stop running automation");
|
|
549
|
+
console.log(" /ralph add ... - Add new task to PRD");
|
|
550
|
+
console.log(" /ralph exec ... - Execute shell command");
|
|
551
|
+
console.log(" /ralph action ... - Run daemon action");
|
|
552
|
+
console.log(" /ralph <prompt> - Run Claude Code with prompt");
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
console.log(" /run - Start ralph automation");
|
|
556
|
+
console.log(" /status - Show PRD progress");
|
|
557
|
+
console.log(" /add ... - Add new task to PRD");
|
|
558
|
+
console.log(" /exec ... - Execute shell command");
|
|
559
|
+
console.log(" /action ... - Run daemon action");
|
|
560
|
+
console.log(" /claude ... - Run Claude Code with prompt (YOLO mode)");
|
|
561
|
+
console.log(" /help - Show help");
|
|
562
|
+
}
|
|
455
563
|
console.log("");
|
|
456
564
|
console.log("Press Ctrl+C to stop the daemon.");
|
|
457
565
|
// Send connected message to all allowed chats
|
|
458
|
-
if (
|
|
459
|
-
for (const chatId of
|
|
566
|
+
if (allowedChatIds && allowedChatIds.length > 0) {
|
|
567
|
+
for (const chatId of allowedChatIds) {
|
|
460
568
|
try {
|
|
461
569
|
await client.sendMessage(chatId, `${projectName} connected`);
|
|
462
570
|
}
|
|
@@ -476,8 +584,8 @@ async function startChat(config, debug) {
|
|
|
476
584
|
const shutdown = async () => {
|
|
477
585
|
console.log("\nShutting down chat daemon...");
|
|
478
586
|
// Send disconnected message to all allowed chats
|
|
479
|
-
if (
|
|
480
|
-
for (const chatId of
|
|
587
|
+
if (allowedChatIds && allowedChatIds.length > 0) {
|
|
588
|
+
for (const chatId of allowedChatIds) {
|
|
481
589
|
try {
|
|
482
590
|
await client.sendMessage(chatId, `${projectName} disconnected`);
|
|
483
591
|
}
|
|
@@ -518,7 +626,51 @@ function showStatus(config) {
|
|
|
518
626
|
console.log("State: not initialized (run 'ralph chat start' to initialize)");
|
|
519
627
|
}
|
|
520
628
|
console.log("");
|
|
521
|
-
if (config.chat.provider === "
|
|
629
|
+
if (config.chat.provider === "slack") {
|
|
630
|
+
if (config.chat.slack?.botToken &&
|
|
631
|
+
config.chat.slack?.appToken &&
|
|
632
|
+
config.chat.slack?.signingSecret) {
|
|
633
|
+
console.log("Slack: configured");
|
|
634
|
+
if (config.chat.slack.allowedChannelIds && config.chat.slack.allowedChannelIds.length > 0) {
|
|
635
|
+
console.log(`Allowed channels: ${config.chat.slack.allowedChannelIds.join(", ")}`);
|
|
636
|
+
}
|
|
637
|
+
else {
|
|
638
|
+
console.log("Allowed channels: all (no restrictions)");
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
const missing = [];
|
|
643
|
+
if (!config.chat.slack?.botToken)
|
|
644
|
+
missing.push("botToken");
|
|
645
|
+
if (!config.chat.slack?.appToken)
|
|
646
|
+
missing.push("appToken");
|
|
647
|
+
if (!config.chat.slack?.signingSecret)
|
|
648
|
+
missing.push("signingSecret");
|
|
649
|
+
console.log(`Slack: not configured (missing: ${missing.join(", ")})`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
else if (config.chat.provider === "discord") {
|
|
653
|
+
if (config.chat.discord?.botToken) {
|
|
654
|
+
console.log("Discord: configured");
|
|
655
|
+
if (config.chat.discord.allowedGuildIds && config.chat.discord.allowedGuildIds.length > 0) {
|
|
656
|
+
console.log(`Allowed guilds: ${config.chat.discord.allowedGuildIds.join(", ")}`);
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
console.log("Allowed guilds: all (no restrictions)");
|
|
660
|
+
}
|
|
661
|
+
if (config.chat.discord.allowedChannelIds &&
|
|
662
|
+
config.chat.discord.allowedChannelIds.length > 0) {
|
|
663
|
+
console.log(`Allowed channels: ${config.chat.discord.allowedChannelIds.join(", ")}`);
|
|
664
|
+
}
|
|
665
|
+
else {
|
|
666
|
+
console.log("Allowed channels: all (no restrictions)");
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
console.log("Discord: not configured (missing botToken)");
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
else if (config.chat.provider === "telegram") {
|
|
522
674
|
if (config.chat.telegram?.botToken) {
|
|
523
675
|
console.log("Telegram: configured");
|
|
524
676
|
if (config.chat.telegram.allowedChatIds && config.chat.telegram.allowedChatIds.length > 0) {
|
|
@@ -541,23 +693,68 @@ async function testChat(config, chatId) {
|
|
|
541
693
|
console.error("Error: Chat is not enabled in config.json");
|
|
542
694
|
process.exit(1);
|
|
543
695
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
696
|
+
const provider = config.chat.provider || "telegram";
|
|
697
|
+
let client;
|
|
698
|
+
let targetChatId;
|
|
699
|
+
if (provider === "slack") {
|
|
700
|
+
if (!config.chat.slack?.botToken ||
|
|
701
|
+
!config.chat.slack?.appToken ||
|
|
702
|
+
!config.chat.slack?.signingSecret) {
|
|
703
|
+
console.error("Error: Slack configuration incomplete");
|
|
704
|
+
console.error("Required: botToken, appToken, signingSecret");
|
|
705
|
+
process.exit(1);
|
|
706
|
+
}
|
|
707
|
+
targetChatId = chatId || config.chat.slack.allowedChannelIds?.[0];
|
|
708
|
+
if (!targetChatId) {
|
|
709
|
+
console.error("Error: No channel ID specified and no allowed channel IDs configured");
|
|
710
|
+
console.error("Usage: ralph chat test <channel_id>");
|
|
711
|
+
console.error("Or add channel IDs to chat.slack.allowedChannelIds in config.json");
|
|
712
|
+
process.exit(1);
|
|
713
|
+
}
|
|
714
|
+
client = createSlackClient({
|
|
715
|
+
botToken: config.chat.slack.botToken,
|
|
716
|
+
appToken: config.chat.slack.appToken,
|
|
717
|
+
signingSecret: config.chat.slack.signingSecret,
|
|
718
|
+
allowedChannelIds: config.chat.slack.allowedChannelIds,
|
|
719
|
+
});
|
|
547
720
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
721
|
+
else if (provider === "discord") {
|
|
722
|
+
if (!config.chat.discord?.botToken) {
|
|
723
|
+
console.error("Error: Discord bot token not configured");
|
|
724
|
+
process.exit(1);
|
|
725
|
+
}
|
|
726
|
+
targetChatId = chatId || config.chat.discord.allowedChannelIds?.[0];
|
|
727
|
+
if (!targetChatId) {
|
|
728
|
+
console.error("Error: No channel ID specified and no allowed channel IDs configured");
|
|
729
|
+
console.error("Usage: ralph chat test <channel_id>");
|
|
730
|
+
console.error("Or add channel IDs to chat.discord.allowedChannelIds in config.json");
|
|
731
|
+
process.exit(1);
|
|
732
|
+
}
|
|
733
|
+
client = createDiscordClient({
|
|
734
|
+
botToken: config.chat.discord.botToken,
|
|
735
|
+
allowedGuildIds: config.chat.discord.allowedGuildIds,
|
|
736
|
+
allowedChannelIds: config.chat.discord.allowedChannelIds,
|
|
737
|
+
});
|
|
555
738
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
739
|
+
else {
|
|
740
|
+
// Telegram
|
|
741
|
+
if (!config.chat.telegram?.botToken) {
|
|
742
|
+
console.error("Error: Telegram bot token not configured");
|
|
743
|
+
process.exit(1);
|
|
744
|
+
}
|
|
745
|
+
targetChatId = chatId || config.chat.telegram.allowedChatIds?.[0];
|
|
746
|
+
if (!targetChatId) {
|
|
747
|
+
console.error("Error: No chat ID specified and no allowed chat IDs configured");
|
|
748
|
+
console.error("Usage: ralph chat test <chat_id>");
|
|
749
|
+
console.error("Or add chat IDs to chat.telegram.allowedChatIds in config.json");
|
|
750
|
+
process.exit(1);
|
|
751
|
+
}
|
|
752
|
+
client = createTelegramClient({
|
|
753
|
+
botToken: config.chat.telegram.botToken,
|
|
754
|
+
allowedChatIds: config.chat.telegram.allowedChatIds,
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
console.log(`Testing connection to ${provider} chat ${targetChatId}...`);
|
|
561
758
|
try {
|
|
562
759
|
// Just connect to verify credentials
|
|
563
760
|
await client.connect(() => Promise.resolve());
|
|
@@ -584,7 +781,7 @@ export async function chat(args) {
|
|
|
584
781
|
// Show help
|
|
585
782
|
if (subcommand === "help" || subcommand === "--help" || subcommand === "-h" || !subcommand) {
|
|
586
783
|
console.log(`
|
|
587
|
-
ralph chat - Chat client integration (Telegram,
|
|
784
|
+
ralph chat - Chat client integration (Telegram, Slack, Discord)
|
|
588
785
|
|
|
589
786
|
USAGE:
|
|
590
787
|
ralph chat start [--debug] Start the chat daemon
|
|
@@ -595,6 +792,7 @@ USAGE:
|
|
|
595
792
|
CONFIGURATION:
|
|
596
793
|
Configure chat in .ralph/config.json:
|
|
597
794
|
|
|
795
|
+
Telegram:
|
|
598
796
|
{
|
|
599
797
|
"chat": {
|
|
600
798
|
"enabled": true,
|
|
@@ -606,6 +804,33 @@ CONFIGURATION:
|
|
|
606
804
|
}
|
|
607
805
|
}
|
|
608
806
|
|
|
807
|
+
Slack:
|
|
808
|
+
{
|
|
809
|
+
"chat": {
|
|
810
|
+
"enabled": true,
|
|
811
|
+
"provider": "slack",
|
|
812
|
+
"slack": {
|
|
813
|
+
"botToken": "xoxb-YOUR-BOT-TOKEN",
|
|
814
|
+
"appToken": "xapp-YOUR-APP-TOKEN",
|
|
815
|
+
"signingSecret": "YOUR_SIGNING_SECRET",
|
|
816
|
+
"allowedChannelIds": ["C01234567"]
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
Discord:
|
|
822
|
+
{
|
|
823
|
+
"chat": {
|
|
824
|
+
"enabled": true,
|
|
825
|
+
"provider": "discord",
|
|
826
|
+
"discord": {
|
|
827
|
+
"botToken": "YOUR_BOT_TOKEN",
|
|
828
|
+
"allowedGuildIds": ["123456789"],
|
|
829
|
+
"allowedChannelIds": ["987654321"]
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
609
834
|
TELEGRAM SETUP:
|
|
610
835
|
1. Create a bot with @BotFather on Telegram
|
|
611
836
|
2. Copy the bot token to chat.telegram.botToken
|
|
@@ -616,8 +841,40 @@ TELEGRAM SETUP:
|
|
|
616
841
|
Example: https://api.telegram.org/bot123456:ABC-xyz/getUpdates
|
|
617
842
|
5. Add the chat ID to chat.telegram.allowedChatIds (optional security)
|
|
618
843
|
|
|
844
|
+
SLACK SETUP:
|
|
845
|
+
1. Create a Slack app at https://api.slack.com/apps
|
|
846
|
+
2. Enable Socket Mode in the app settings
|
|
847
|
+
3. Generate an App-Level Token with connections:write scope (xapp-...)
|
|
848
|
+
4. Under OAuth & Permissions, add these Bot Token Scopes:
|
|
849
|
+
- chat:write (send messages)
|
|
850
|
+
- channels:history (read public channel messages)
|
|
851
|
+
- groups:history (read private channel messages)
|
|
852
|
+
- im:history (read direct messages)
|
|
853
|
+
- commands (for slash commands, optional)
|
|
854
|
+
5. Install the app to your workspace
|
|
855
|
+
6. Copy the Bot User OAuth Token (xoxb-...) to chat.slack.botToken
|
|
856
|
+
7. Copy the App Token (xapp-...) to chat.slack.appToken
|
|
857
|
+
8. Copy the Signing Secret to chat.slack.signingSecret
|
|
858
|
+
9. Invite the bot to channels: /invite @your-bot-name
|
|
859
|
+
10. Add channel IDs to chat.slack.allowedChannelIds (optional security)
|
|
860
|
+
|
|
861
|
+
DISCORD SETUP:
|
|
862
|
+
1. Create an application at https://discord.com/developers/applications
|
|
863
|
+
2. Go to "Bot" section and click "Add Bot"
|
|
864
|
+
3. Enable these Privileged Gateway Intents:
|
|
865
|
+
- MESSAGE CONTENT INTENT (to read message content)
|
|
866
|
+
4. Copy the bot token to chat.discord.botToken
|
|
867
|
+
5. Go to "OAuth2" > "URL Generator":
|
|
868
|
+
- Select scopes: bot, applications.commands
|
|
869
|
+
- Select permissions: Send Messages, Read Message History, Use Slash Commands
|
|
870
|
+
6. Use the generated URL to invite the bot to your server
|
|
871
|
+
7. Get your guild (server) ID: Enable Developer Mode in Discord settings,
|
|
872
|
+
then right-click your server and "Copy Server ID"
|
|
873
|
+
8. Get channel IDs: Right-click a channel and "Copy Channel ID"
|
|
874
|
+
9. Add IDs to allowedGuildIds and allowedChannelIds (optional security)
|
|
875
|
+
|
|
619
876
|
CHAT COMMANDS:
|
|
620
|
-
Once connected, send commands to your
|
|
877
|
+
Once connected, send commands to your bot:
|
|
621
878
|
|
|
622
879
|
/run - Start ralph automation
|
|
623
880
|
/status - Show PRD progress
|
|
@@ -629,8 +886,8 @@ CHAT COMMANDS:
|
|
|
629
886
|
/help - Show help
|
|
630
887
|
|
|
631
888
|
SECURITY:
|
|
632
|
-
- Use allowedChatIds to restrict
|
|
633
|
-
- Never share your bot
|
|
889
|
+
- Use allowedChatIds/allowedChannelIds/allowedGuildIds to restrict access
|
|
890
|
+
- Never share your bot tokens
|
|
634
891
|
- The daemon should run on the host, not in the container
|
|
635
892
|
|
|
636
893
|
DAEMON ACTIONS:
|
|
@@ -651,16 +908,22 @@ DAEMON ACTIONS:
|
|
|
651
908
|
}
|
|
652
909
|
}
|
|
653
910
|
|
|
654
|
-
Then trigger them via
|
|
911
|
+
Then trigger them via chat: /action build or /action deploy
|
|
655
912
|
|
|
656
913
|
EXAMPLES:
|
|
657
914
|
# Start the chat daemon
|
|
658
915
|
ralph chat start
|
|
659
916
|
|
|
660
|
-
# Test the connection
|
|
917
|
+
# Test the connection (Telegram)
|
|
661
918
|
ralph chat test 123456789
|
|
662
919
|
|
|
663
|
-
#
|
|
920
|
+
# Test the connection (Slack)
|
|
921
|
+
ralph chat test C01234567
|
|
922
|
+
|
|
923
|
+
# Test the connection (Discord)
|
|
924
|
+
ralph chat test 123456789012345678
|
|
925
|
+
|
|
926
|
+
# In Telegram/Slack/Discord:
|
|
664
927
|
/run # Start ralph automation
|
|
665
928
|
/status # Show task progress
|
|
666
929
|
/add Fix login # Add new task
|
package/dist/commands/config.js
CHANGED
|
@@ -44,8 +44,9 @@ SECTIONS:
|
|
|
44
44
|
Docker Ports, volumes, environment, packages
|
|
45
45
|
Daemon Actions, socket path
|
|
46
46
|
Claude MCP servers, skills
|
|
47
|
-
Chat Telegram integration
|
|
47
|
+
Chat Telegram, Slack, Discord integration
|
|
48
48
|
Notify Notification settings
|
|
49
|
+
LLM LLM provider configuration (Anthropic, OpenAI, Ollama)
|
|
49
50
|
`;
|
|
50
51
|
console.log(helpText.trim());
|
|
51
52
|
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
description?: string;
|
|
4
|
-
ntfyUrl?: string;
|
|
5
|
-
}
|
|
1
|
+
import { DaemonAction } from "../utils/daemon-actions.js";
|
|
2
|
+
export type { DaemonAction };
|
|
6
3
|
export interface DaemonConfig {
|
|
7
4
|
enabled?: boolean;
|
|
8
5
|
actions?: Record<string, DaemonAction>;
|