fix: provider startup order and enable flags
This commit is contained in:
@@ -45,6 +45,7 @@
|
|||||||
- Gateway launchd loop fixed by removing redundant `kickstart -k`.
|
- Gateway launchd loop fixed by removing redundant `kickstart -k`.
|
||||||
- CLI now hints when Peekaboo is unauthorized.
|
- CLI now hints when Peekaboo is unauthorized.
|
||||||
- WhatsApp web inbox listeners now clean up on close to avoid duplicate handlers.
|
- WhatsApp web inbox listeners now clean up on close to avoid duplicate handlers.
|
||||||
|
- Gateway startup now brings up browser control before external providers; WhatsApp/Telegram/Discord auto-start can be disabled with `web.enabled`, `telegram.enabled`, or `discord.enabled`.
|
||||||
|
|
||||||
### Providers & Routing
|
### Providers & Routing
|
||||||
- New Discord provider for DMs + guild text channels with allowlists and mention-gated replies by default.
|
- New Discord provider for DMs + guild text channels with allowlists and mention-gated replies by default.
|
||||||
|
|||||||
@@ -105,6 +105,48 @@ Controls how inbound messages behave when an agent run is already active.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `web` (WhatsApp web provider)
|
||||||
|
|
||||||
|
WhatsApp runs through the gateway’s web provider. It starts automatically when a linked session exists.
|
||||||
|
Set `web.enabled: false` to keep it off by default.
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
web: {
|
||||||
|
enabled: true,
|
||||||
|
heartbeatSeconds: 60,
|
||||||
|
reconnect: {
|
||||||
|
initialMs: 2000,
|
||||||
|
maxMs: 120000,
|
||||||
|
factor: 1.4,
|
||||||
|
jitter: 0.2,
|
||||||
|
maxAttempts: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `telegram` (bot transport)
|
||||||
|
|
||||||
|
Clawdis reads `TELEGRAM_BOT_TOKEN` or `telegram.botToken` to start the provider.
|
||||||
|
Set `telegram.enabled: false` to disable automatic startup.
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
telegram: {
|
||||||
|
enabled: true,
|
||||||
|
botToken: "your-bot-token",
|
||||||
|
requireMention: true,
|
||||||
|
allowFrom: ["123456789"],
|
||||||
|
mediaMaxMb: 5,
|
||||||
|
proxy: "socks5://localhost:9050",
|
||||||
|
webhookUrl: "https://example.com/telegram-webhook",
|
||||||
|
webhookSecret: "secret",
|
||||||
|
webhookPath: "/telegram-webhook"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### `discord` (bot transport)
|
### `discord` (bot transport)
|
||||||
|
|
||||||
Configure the Discord bot by setting the bot token and optional gating:
|
Configure the Discord bot by setting the bot token and optional gating:
|
||||||
@@ -112,6 +154,7 @@ Configure the Discord bot by setting the bot token and optional gating:
|
|||||||
```json5
|
```json5
|
||||||
{
|
{
|
||||||
discord: {
|
discord: {
|
||||||
|
enabled: true,
|
||||||
token: "your-bot-token",
|
token: "your-bot-token",
|
||||||
allowFrom: ["discord:1234567890", "*"], // optional DM allowlist (user ids)
|
allowFrom: ["discord:1234567890", "*"], // optional DM allowlist (user ids)
|
||||||
guildAllowFrom: {
|
guildAllowFrom: {
|
||||||
@@ -124,7 +167,7 @@ Configure the Discord bot by setting the bot token and optional gating:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Clawdis reads `DISCORD_BOT_TOKEN` or `discord.token` to start the provider. Use `user:<id>` (DM) or `channel:<id>` (guild channel) when specifying delivery targets for cron/CLI commands.
|
Clawdis reads `DISCORD_BOT_TOKEN` or `discord.token` to start the provider (unless `discord.enabled` is `false`). Use `user:<id>` (DM) or `channel:<id>` (guild channel) when specifying delivery targets for cron/CLI commands.
|
||||||
|
|
||||||
### `agent.workspace`
|
### `agent.workspace`
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ Status: ready for DM and guild text channels via the official Discord bot gatewa
|
|||||||
1. Create a Discord application → Bot, enable the intents you need (DMs + guild messages + message content), and grab the bot token.
|
1. Create a Discord application → Bot, enable the intents you need (DMs + guild messages + message content), and grab the bot token.
|
||||||
2. Invite the bot to your server with the permissions required to read/send messages where you want to use it.
|
2. Invite the bot to your server with the permissions required to read/send messages where you want to use it.
|
||||||
3. Configure Clawdis with `DISCORD_BOT_TOKEN` (or `discord.token` in `~/.clawdis/clawdis.json`).
|
3. Configure Clawdis with `DISCORD_BOT_TOKEN` (or `discord.token` in `~/.clawdis/clawdis.json`).
|
||||||
4. Run the gateway; it auto-starts the Discord provider when the token is set.
|
4. Run the gateway; it auto-starts the Discord provider when the token is set (unless `discord.enabled = false`).
|
||||||
5. Direct chats: use `user:<id>` (or a `<@id>` mention) when delivering; all turns land in the shared `main` session.
|
5. Direct chats: use `user:<id>` (or a `<@id>` mention) when delivering; all turns land in the shared `main` session.
|
||||||
6. Guild channels: use `channel:<channelId>` for delivery. Mentions are required by default; disable with `discord.requireMention = false`.
|
6. Guild channels: use `channel:<channelId>` for delivery. Mentions are required by default; disable with `discord.requireMention = false`.
|
||||||
7. Optional DM allowlist: reuse `discord.allowFrom` with user ids (`1234567890` or `discord:1234567890`). Use `"*"` to allow all DMs.
|
7. Optional DM allowlist: reuse `discord.allowFrom` with user ids (`1234567890` or `discord:1234567890`). Use `"*"` to allow all DMs.
|
||||||
@@ -37,6 +37,7 @@ Note: Discord does not provide a simple username → id lookup without extra gui
|
|||||||
```json5
|
```json5
|
||||||
{
|
{
|
||||||
discord: {
|
discord: {
|
||||||
|
enabled: true,
|
||||||
token: "abc.123",
|
token: "abc.123",
|
||||||
allowFrom: ["123456789012345678"],
|
allowFrom: ["123456789012345678"],
|
||||||
guildAllowFrom: {
|
guildAllowFrom: {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ Status: ready for bot-mode use with grammY (long-polling by default; webhook sup
|
|||||||
## How it will work (Bot API)
|
## How it will work (Bot API)
|
||||||
1) Create a bot with @BotFather and grab the token.
|
1) Create a bot with @BotFather and grab the token.
|
||||||
2) Configure Clawdis with `TELEGRAM_BOT_TOKEN` (or `telegram.botToken` in `~/.clawdis/clawdis.json`).
|
2) Configure Clawdis with `TELEGRAM_BOT_TOKEN` (or `telegram.botToken` in `~/.clawdis/clawdis.json`).
|
||||||
3) Run the gateway; it auto-starts Telegram when the bot token is set.
|
3) Run the gateway; it auto-starts Telegram when the bot token is set (unless `telegram.enabled = false`).
|
||||||
- **Long-polling** is the default.
|
- **Long-polling** is the default.
|
||||||
- **Webhook mode** is enabled by setting `telegram.webhookUrl` (optionally `telegram.webhookSecret` / `telegram.webhookPath`).
|
- **Webhook mode** is enabled by setting `telegram.webhookUrl` (optionally `telegram.webhookSecret` / `telegram.webhookPath`).
|
||||||
- The webhook listener currently binds to `0.0.0.0:8787` and serves `POST /telegram-webhook` by default.
|
- The webhook listener currently binds to `0.0.0.0:8787` and serves `POST /telegram-webhook` by default.
|
||||||
@@ -42,6 +42,7 @@ Example config:
|
|||||||
```json5
|
```json5
|
||||||
{
|
{
|
||||||
telegram: {
|
telegram: {
|
||||||
|
enabled: true,
|
||||||
botToken: "123:abc",
|
botToken: "123:abc",
|
||||||
requireMention: true,
|
requireMention: true,
|
||||||
allowFrom: ["123456789"], // direct chat ids allowed (or "*")
|
allowFrom: ["123456789"], // direct chat ids allowed (or "*")
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ Status: WhatsApp Web via Baileys only. Gateway owns the single session.
|
|||||||
- `agent.heartbeat.target`
|
- `agent.heartbeat.target`
|
||||||
- `agent.heartbeat.to`
|
- `agent.heartbeat.to`
|
||||||
- `session.*` (scope, idle, store, mainKey)
|
- `session.*` (scope, idle, store, mainKey)
|
||||||
|
- `web.enabled` (disable provider startup when false)
|
||||||
- `web.heartbeatSeconds`
|
- `web.heartbeatSeconds`
|
||||||
- `web.reconnect.*`
|
- `web.reconnect.*`
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ export type WebReconnectConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type WebConfig = {
|
export type WebConfig = {
|
||||||
|
/** If false, do not start the WhatsApp web provider. Default: true. */
|
||||||
|
enabled?: boolean;
|
||||||
heartbeatSeconds?: number;
|
heartbeatSeconds?: number;
|
||||||
reconnect?: WebReconnectConfig;
|
reconnect?: WebReconnectConfig;
|
||||||
};
|
};
|
||||||
@@ -126,6 +128,8 @@ export type HooksConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TelegramConfig = {
|
export type TelegramConfig = {
|
||||||
|
/** If false, do not start the Telegram provider. Default: true. */
|
||||||
|
enabled?: boolean;
|
||||||
botToken?: string;
|
botToken?: string;
|
||||||
requireMention?: boolean;
|
requireMention?: boolean;
|
||||||
allowFrom?: Array<string | number>;
|
allowFrom?: Array<string | number>;
|
||||||
@@ -137,6 +141,8 @@ export type TelegramConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type DiscordConfig = {
|
export type DiscordConfig = {
|
||||||
|
/** If false, do not start the Discord provider. Default: true. */
|
||||||
|
enabled?: boolean;
|
||||||
token?: string;
|
token?: string;
|
||||||
allowFrom?: Array<string | number>;
|
allowFrom?: Array<string | number>;
|
||||||
guildAllowFrom?: {
|
guildAllowFrom?: {
|
||||||
@@ -705,6 +711,7 @@ const ClawdisSchema = z.object({
|
|||||||
.optional(),
|
.optional(),
|
||||||
web: z
|
web: z
|
||||||
.object({
|
.object({
|
||||||
|
enabled: z.boolean().optional(),
|
||||||
heartbeatSeconds: z.number().int().positive().optional(),
|
heartbeatSeconds: z.number().int().positive().optional(),
|
||||||
reconnect: z
|
reconnect: z
|
||||||
.object({
|
.object({
|
||||||
@@ -719,6 +726,7 @@ const ClawdisSchema = z.object({
|
|||||||
.optional(),
|
.optional(),
|
||||||
telegram: z
|
telegram: z
|
||||||
.object({
|
.object({
|
||||||
|
enabled: z.boolean().optional(),
|
||||||
botToken: z.string().optional(),
|
botToken: z.string().optional(),
|
||||||
requireMention: z.boolean().optional(),
|
requireMention: z.boolean().optional(),
|
||||||
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
||||||
@@ -731,6 +739,7 @@ const ClawdisSchema = z.object({
|
|||||||
.optional(),
|
.optional(),
|
||||||
discord: z
|
discord: z
|
||||||
.object({
|
.object({
|
||||||
|
enabled: z.boolean().optional(),
|
||||||
token: z.string().optional(),
|
token: z.string().optional(),
|
||||||
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
||||||
guildAllowFrom: z
|
guildAllowFrom: z
|
||||||
|
|||||||
@@ -1829,6 +1829,17 @@ export async function startGatewayServer(
|
|||||||
|
|
||||||
const startWhatsAppProvider = async () => {
|
const startWhatsAppProvider = async () => {
|
||||||
if (whatsappTask) return;
|
if (whatsappTask) return;
|
||||||
|
const cfg = loadConfig();
|
||||||
|
if (cfg.web?.enabled === false) {
|
||||||
|
whatsappRuntime = {
|
||||||
|
...whatsappRuntime,
|
||||||
|
running: false,
|
||||||
|
connected: false,
|
||||||
|
lastError: "disabled",
|
||||||
|
};
|
||||||
|
logWhatsApp.info("skipping provider start (web.enabled=false)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!(await webAuthExists())) {
|
if (!(await webAuthExists())) {
|
||||||
whatsappRuntime = {
|
whatsappRuntime = {
|
||||||
...whatsappRuntime,
|
...whatsappRuntime,
|
||||||
@@ -1897,6 +1908,15 @@ export async function startGatewayServer(
|
|||||||
const startTelegramProvider = async () => {
|
const startTelegramProvider = async () => {
|
||||||
if (telegramTask) return;
|
if (telegramTask) return;
|
||||||
const cfg = loadConfig();
|
const cfg = loadConfig();
|
||||||
|
if (cfg.telegram?.enabled === false) {
|
||||||
|
telegramRuntime = {
|
||||||
|
...telegramRuntime,
|
||||||
|
running: false,
|
||||||
|
lastError: "disabled",
|
||||||
|
};
|
||||||
|
logTelegram.info("skipping provider start (telegram.enabled=false)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
const telegramToken =
|
const telegramToken =
|
||||||
process.env.TELEGRAM_BOT_TOKEN ?? cfg.telegram?.botToken ?? "";
|
process.env.TELEGRAM_BOT_TOKEN ?? cfg.telegram?.botToken ?? "";
|
||||||
if (!telegramToken.trim()) {
|
if (!telegramToken.trim()) {
|
||||||
@@ -1981,6 +2001,15 @@ export async function startGatewayServer(
|
|||||||
const startDiscordProvider = async () => {
|
const startDiscordProvider = async () => {
|
||||||
if (discordTask) return;
|
if (discordTask) return;
|
||||||
const cfg = loadConfig();
|
const cfg = loadConfig();
|
||||||
|
if (cfg.discord?.enabled === false) {
|
||||||
|
discordRuntime = {
|
||||||
|
...discordRuntime,
|
||||||
|
running: false,
|
||||||
|
lastError: "disabled",
|
||||||
|
};
|
||||||
|
logDiscord.info("skipping provider start (discord.enabled=false)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
const discordToken =
|
const discordToken =
|
||||||
process.env.DISCORD_BOT_TOKEN ?? cfg.discord?.token ?? "";
|
process.env.DISCORD_BOT_TOKEN ?? cfg.discord?.token ?? "";
|
||||||
if (!discordToken.trim()) {
|
if (!discordToken.trim()) {
|
||||||
@@ -2057,8 +2086,8 @@ export async function startGatewayServer(
|
|||||||
|
|
||||||
const startProviders = async () => {
|
const startProviders = async () => {
|
||||||
await startWhatsAppProvider();
|
await startWhatsAppProvider();
|
||||||
await startTelegramProvider();
|
|
||||||
await startDiscordProvider();
|
await startDiscordProvider();
|
||||||
|
await startTelegramProvider();
|
||||||
};
|
};
|
||||||
|
|
||||||
const broadcast = (
|
const broadcast = (
|
||||||
@@ -6066,14 +6095,20 @@ export async function startGatewayServer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start clawd browser control server (unless disabled via config).
|
// Start clawd browser control server (unless disabled via config).
|
||||||
void startBrowserControlServerIfEnabled().catch((err) => {
|
try {
|
||||||
|
await startBrowserControlServerIfEnabled();
|
||||||
|
} catch (err) {
|
||||||
logBrowser.error(`server failed to start: ${String(err)}`);
|
logBrowser.error(`server failed to start: ${String(err)}`);
|
||||||
});
|
}
|
||||||
|
|
||||||
// Launch configured providers (WhatsApp Web, Telegram) so gateway replies via the
|
// Launch configured providers (WhatsApp Web, Discord, Telegram) so gateway replies via the
|
||||||
// surface the message came from. Tests can opt out via CLAWDIS_SKIP_PROVIDERS.
|
// surface the message came from. Tests can opt out via CLAWDIS_SKIP_PROVIDERS.
|
||||||
if (process.env.CLAWDIS_SKIP_PROVIDERS !== "1") {
|
if (process.env.CLAWDIS_SKIP_PROVIDERS !== "1") {
|
||||||
void startProviders();
|
try {
|
||||||
|
await startProviders();
|
||||||
|
} catch (err) {
|
||||||
|
logProviders.error(`provider startup failed: ${String(err)}`);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logProviders.info("skipping provider start (CLAWDIS_SKIP_PROVIDERS=1)");
|
logProviders.info("skipping provider start (CLAWDIS_SKIP_PROVIDERS=1)");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ export async function buildProviderSummary(
|
|||||||
const effective = cfg ?? loadConfig();
|
const effective = cfg ?? loadConfig();
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
|
|
||||||
|
const webEnabled = effective.web?.enabled !== false;
|
||||||
|
if (!webEnabled) {
|
||||||
|
lines.push(chalk.cyan("WhatsApp: disabled"));
|
||||||
|
} else {
|
||||||
const webLinked = await webAuthExists();
|
const webLinked = await webAuthExists();
|
||||||
const authAgeMs = getWebAuthAgeMs();
|
const authAgeMs = getWebAuthAgeMs();
|
||||||
const authAge = authAgeMs === null ? "unknown" : formatAge(authAgeMs);
|
const authAge = authAgeMs === null ? "unknown" : formatAge(authAgeMs);
|
||||||
@@ -24,7 +28,12 @@ export async function buildProviderSummary(
|
|||||||
)
|
)
|
||||||
: chalk.red("WhatsApp: not linked"),
|
: chalk.red("WhatsApp: not linked"),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const telegramEnabled = effective.telegram?.enabled !== false;
|
||||||
|
if (!telegramEnabled) {
|
||||||
|
lines.push(chalk.cyan("Telegram: disabled"));
|
||||||
|
} else {
|
||||||
const telegramToken =
|
const telegramToken =
|
||||||
process.env.TELEGRAM_BOT_TOKEN ?? effective.telegram?.botToken;
|
process.env.TELEGRAM_BOT_TOKEN ?? effective.telegram?.botToken;
|
||||||
lines.push(
|
lines.push(
|
||||||
@@ -32,6 +41,7 @@ export async function buildProviderSummary(
|
|||||||
? chalk.green("Telegram: configured")
|
? chalk.green("Telegram: configured")
|
||||||
: chalk.cyan("Telegram: not configured"),
|
: chalk.cyan("Telegram: not configured"),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const allowFrom = effective.routing?.allowFrom?.length
|
const allowFrom = effective.routing?.allowFrom?.length
|
||||||
? effective.routing.allowFrom.map(normalizeE164).filter(Boolean)
|
? effective.routing.allowFrom.map(normalizeE164).filter(Boolean)
|
||||||
|
|||||||
Reference in New Issue
Block a user