too-many-claw 1.0.4 → 1.0.5

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/dist/cli.js CHANGED
@@ -1212,19 +1212,57 @@ var ConfigManager = class {
1212
1212
  return !!config?.gateway?.discord?.token;
1213
1213
  }
1214
1214
  /**
1215
- * Get Discord settings from OpenClaw config
1215
+ * Get raw Discord settings from OpenClaw config
1216
1216
  */
1217
1217
  getOpenClawDiscordConfig() {
1218
1218
  const config = this.readOpenClawConfig();
1219
- return config?.gateway?.discord ?? null;
1219
+ return config?.gateway?.discord ?? config?.discord ?? null;
1220
+ }
1221
+ /**
1222
+ * Extract and normalize Discord settings from OpenClaw config
1223
+ * Returns a clean, normalized structure regardless of how OpenClaw stores it
1224
+ */
1225
+ extractOpenClawDiscordSettings() {
1226
+ const discord = this.getOpenClawDiscordConfig();
1227
+ if (!discord) return null;
1228
+ const settings = {};
1229
+ if (discord.token) {
1230
+ settings.token = discord.token;
1231
+ }
1232
+ if (discord.guildId) {
1233
+ settings.guildId = discord.guildId;
1234
+ } else if (discord.serverId) {
1235
+ settings.guildId = discord.serverId;
1236
+ } else if (discord.allowlist?.guilds && discord.allowlist.guilds.length > 0) {
1237
+ settings.guildId = discord.allowlist.guilds[0];
1238
+ }
1239
+ if (discord.channels?.chat) {
1240
+ settings.chatChannelId = discord.channels.chat;
1241
+ } else if (discord.channels?.default) {
1242
+ settings.chatChannelId = discord.channels.default;
1243
+ } else if (discord.allowlist?.channels && discord.allowlist.channels.length > 0) {
1244
+ settings.chatChannelId = discord.allowlist.channels[0];
1245
+ }
1246
+ if (discord.channels?.status) {
1247
+ settings.statusChannelId = discord.channels.status;
1248
+ } else if (discord.allowlist?.channels && discord.allowlist.channels.length > 1) {
1249
+ settings.statusChannelId = discord.allowlist.channels[1];
1250
+ }
1251
+ if (discord.allowlist?.channels) {
1252
+ settings.allowedChannels = [...discord.allowlist.channels];
1253
+ }
1254
+ if (discord.allowlist?.users) {
1255
+ settings.allowedUsers = [...discord.allowlist.users];
1256
+ }
1257
+ return settings;
1220
1258
  }
1221
1259
  /**
1222
1260
  * Import Discord settings from OpenClaw configuration
1223
1261
  * Returns true if successful, false if no OpenClaw Discord config found
1224
1262
  */
1225
1263
  importFromOpenClaw() {
1226
- const openclawDiscord = this.getOpenClawDiscordConfig();
1227
- if (!openclawDiscord) {
1264
+ const extracted = this.extractOpenClawDiscordSettings();
1265
+ if (!extracted) {
1228
1266
  return {
1229
1267
  success: false,
1230
1268
  imported: {},
@@ -1232,14 +1270,17 @@ var ConfigManager = class {
1232
1270
  };
1233
1271
  }
1234
1272
  const imported = {};
1235
- if (openclawDiscord.token) {
1236
- imported.token = openclawDiscord.token;
1273
+ if (extracted.token) {
1274
+ imported.token = extracted.token;
1237
1275
  }
1238
- if (openclawDiscord.allowlist?.channels && openclawDiscord.allowlist.channels.length > 0) {
1239
- imported.chatChannelId = openclawDiscord.allowlist.channels[0];
1240
- if (openclawDiscord.allowlist.channels.length > 1) {
1241
- imported.statusChannelId = openclawDiscord.allowlist.channels[1];
1242
- }
1276
+ if (extracted.guildId) {
1277
+ imported.guildId = extracted.guildId;
1278
+ }
1279
+ if (extracted.chatChannelId) {
1280
+ imported.chatChannelId = extracted.chatChannelId;
1281
+ }
1282
+ if (extracted.statusChannelId) {
1283
+ imported.statusChannelId = extracted.statusChannelId;
1243
1284
  }
1244
1285
  if (Object.keys(imported).length === 0) {
1245
1286
  return {
@@ -1261,6 +1302,46 @@ var ConfigManager = class {
1261
1302
  message: `Imported ${Object.keys(imported).length} setting(s) from OpenClaw`
1262
1303
  };
1263
1304
  }
1305
+ /**
1306
+ * Update guildId after bot connects and detects it
1307
+ * This is called when the bot auto-detects the guild from connection
1308
+ */
1309
+ updateGuildId(guildId) {
1310
+ const currentDiscord = this.getDiscordConfig();
1311
+ if (!currentDiscord.guildId) {
1312
+ this.setDiscordConfig({
1313
+ ...currentDiscord,
1314
+ guildId
1315
+ });
1316
+ }
1317
+ }
1318
+ /**
1319
+ * Get summary of what can be imported from OpenClaw
1320
+ * Useful for displaying to user before import
1321
+ */
1322
+ getOpenClawImportSummary() {
1323
+ const extracted = this.extractOpenClawDiscordSettings();
1324
+ if (!extracted) {
1325
+ return {
1326
+ hasConfig: false,
1327
+ availableSettings: [],
1328
+ extracted: null
1329
+ };
1330
+ }
1331
+ const availableSettings = [];
1332
+ if (extracted.token) availableSettings.push("Bot Token");
1333
+ if (extracted.guildId) availableSettings.push("Server (Guild) ID");
1334
+ if (extracted.chatChannelId) availableSettings.push("Chat Channel");
1335
+ if (extracted.statusChannelId) availableSettings.push("Status Channel");
1336
+ if (extracted.allowedChannels && extracted.allowedChannels.length > 0) {
1337
+ availableSettings.push(`${extracted.allowedChannels.length} Allowed Channel(s)`);
1338
+ }
1339
+ return {
1340
+ hasConfig: true,
1341
+ availableSettings,
1342
+ extracted
1343
+ };
1344
+ }
1264
1345
  /**
1265
1346
  * Get OpenClaw config file path
1266
1347
  */
@@ -1274,7 +1355,8 @@ import {
1274
1355
  Client,
1275
1356
  GatewayIntentBits,
1276
1357
  TextChannel,
1277
- Partials
1358
+ Partials,
1359
+ PermissionFlagsBits
1278
1360
  } from "discord.js";
1279
1361
  var Bot = class {
1280
1362
  client;
@@ -1409,6 +1491,126 @@ var Bot = class {
1409
1491
  get isConnected() {
1410
1492
  return this.client.isReady();
1411
1493
  }
1494
+ /**
1495
+ * Detect guild ID from connected guilds
1496
+ * Returns the first guild the bot is connected to, or the configured guildId
1497
+ */
1498
+ async detectGuildId() {
1499
+ if (!this.client.isReady()) {
1500
+ return this.config.guildId || null;
1501
+ }
1502
+ if (this.config.guildId) {
1503
+ const guild = this.client.guilds.cache.get(this.config.guildId);
1504
+ if (guild) {
1505
+ return this.config.guildId;
1506
+ }
1507
+ }
1508
+ const firstGuild = this.client.guilds.cache.first();
1509
+ return firstGuild?.id || null;
1510
+ }
1511
+ /**
1512
+ * Get all connected guilds
1513
+ */
1514
+ getConnectedGuilds() {
1515
+ return this.client.guilds.cache;
1516
+ }
1517
+ /**
1518
+ * Get existing webhooks in a channel
1519
+ */
1520
+ async getExistingWebhooks(channelId) {
1521
+ const channel = await this.client.channels.fetch(channelId);
1522
+ if (!(channel instanceof TextChannel)) {
1523
+ throw new Error("Channel is not a text channel");
1524
+ }
1525
+ const permissions = channel.permissionsFor(this.client.user);
1526
+ if (!permissions?.has(PermissionFlagsBits.ManageWebhooks)) {
1527
+ throw new Error("Bot does not have MANAGE_WEBHOOKS permission in this channel");
1528
+ }
1529
+ const webhooks = await channel.fetchWebhooks();
1530
+ return Array.from(webhooks.values());
1531
+ }
1532
+ /**
1533
+ * Check if bot has webhook management permission in a channel
1534
+ */
1535
+ async hasWebhookPermission(channelId) {
1536
+ try {
1537
+ const channel = await this.client.channels.fetch(channelId);
1538
+ if (!(channel instanceof TextChannel)) {
1539
+ return false;
1540
+ }
1541
+ const permissions = channel.permissionsFor(this.client.user);
1542
+ return permissions?.has(PermissionFlagsBits.ManageWebhooks) ?? false;
1543
+ } catch {
1544
+ return false;
1545
+ }
1546
+ }
1547
+ /**
1548
+ * Auto-create webhooks for agents in a channel
1549
+ * @param channelId - The channel to create webhooks in
1550
+ * @param agents - Array of agent definitions to create webhooks for
1551
+ * @param onProgress - Optional callback for progress updates
1552
+ * @returns Record of agentId -> webhook URL
1553
+ */
1554
+ async autoCreateWebhooks(channelId, agents, onProgress) {
1555
+ const channel = await this.client.channels.fetch(channelId);
1556
+ if (!(channel instanceof TextChannel)) {
1557
+ throw new Error("Channel is not a text channel");
1558
+ }
1559
+ const permissions = channel.permissionsFor(this.client.user);
1560
+ if (!permissions?.has(PermissionFlagsBits.ManageWebhooks)) {
1561
+ throw new Error("Bot does not have MANAGE_WEBHOOKS permission in this channel");
1562
+ }
1563
+ const existingWebhooks = await channel.fetchWebhooks();
1564
+ const webhooksByName = /* @__PURE__ */ new Map();
1565
+ for (const webhook of existingWebhooks.values()) {
1566
+ if (webhook.name) {
1567
+ webhooksByName.set(webhook.name, webhook);
1568
+ }
1569
+ }
1570
+ const result = {};
1571
+ let current = 0;
1572
+ const total = agents.length;
1573
+ for (const agent of agents) {
1574
+ current++;
1575
+ const webhookName = `${agent.emoji} ${agent.name}`;
1576
+ if (onProgress) {
1577
+ onProgress(current, total, agent.name);
1578
+ }
1579
+ const existing = webhooksByName.get(webhookName);
1580
+ if (existing) {
1581
+ result[agent.id] = existing.url;
1582
+ continue;
1583
+ }
1584
+ try {
1585
+ const webhook = await channel.createWebhook({
1586
+ name: webhookName,
1587
+ reason: `Too Many Claw - Auto-created webhook for agent: ${agent.id}`
1588
+ });
1589
+ result[agent.id] = webhook.url;
1590
+ await new Promise((resolve) => setTimeout(resolve, 500));
1591
+ } catch (error) {
1592
+ console.error(`Failed to create webhook for ${agent.name}:`, error);
1593
+ }
1594
+ }
1595
+ return result;
1596
+ }
1597
+ /**
1598
+ * Delete a webhook by URL
1599
+ */
1600
+ async deleteWebhook(webhookUrl) {
1601
+ try {
1602
+ const match = webhookUrl.match(/webhooks\/(\d+)\/([\w-]+)/);
1603
+ if (!match) {
1604
+ return false;
1605
+ }
1606
+ const [, id, token] = match;
1607
+ const webhook = await this.client.fetchWebhook(id, token);
1608
+ await webhook.delete("Too Many Claw - Webhook cleanup");
1609
+ return true;
1610
+ } catch {
1611
+ return false;
1612
+ }
1613
+ }
1412
1614
  };
1413
1615
 
1414
1616
  // src/discord/WebhookManager.ts
@@ -1574,6 +1776,51 @@ var DiscordAdapter = class {
1574
1776
  get isConnected() {
1575
1777
  return this.bot.isConnected;
1576
1778
  }
1779
+ /**
1780
+ * Detect guild ID from connected bot
1781
+ */
1782
+ async detectGuildId() {
1783
+ return this.bot.detectGuildId();
1784
+ }
1785
+ /**
1786
+ * Get all connected guilds
1787
+ */
1788
+ getConnectedGuilds() {
1789
+ return this.bot.getConnectedGuilds();
1790
+ }
1791
+ /**
1792
+ * Check if bot has webhook permission in channel
1793
+ */
1794
+ async hasWebhookPermission(channelId) {
1795
+ return this.bot.hasWebhookPermission(channelId);
1796
+ }
1797
+ /**
1798
+ * Get existing webhooks in a channel
1799
+ */
1800
+ async getExistingWebhooks(channelId) {
1801
+ return this.bot.getExistingWebhooks(channelId);
1802
+ }
1803
+ /**
1804
+ * Auto-create webhooks for all agents
1805
+ * @param channelId - Channel to create webhooks in
1806
+ * @param onProgress - Progress callback
1807
+ * @returns Record of agentId -> webhook URL
1808
+ */
1809
+ async autoCreateWebhooks(channelId, onProgress) {
1810
+ return this.bot.autoCreateWebhooks(channelId, AGENT_DEFINITIONS, onProgress);
1811
+ }
1812
+ /**
1813
+ * Auto-create webhooks for specific agents
1814
+ */
1815
+ async autoCreateWebhooksForAgents(channelId, agents, onProgress) {
1816
+ return this.bot.autoCreateWebhooks(channelId, agents, onProgress);
1817
+ }
1818
+ /**
1819
+ * Delete a webhook
1820
+ */
1821
+ async deleteWebhook(webhookUrl) {
1822
+ return this.bot.deleteWebhook(webhookUrl);
1823
+ }
1577
1824
  };
1578
1825
 
1579
1826
  // src/cli.ts
@@ -1641,16 +1888,26 @@ async function checkAndImportOpenClaw(config) {
1641
1888
  console.log(chalk3.green("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
1642
1889
  const openclawDiscord = config.getOpenClawDiscordConfig();
1643
1890
  if (!openclawDiscord) return false;
1891
+ const summary = config.getOpenClawImportSummary();
1892
+ const extracted = summary.extracted;
1644
1893
  console.log(chalk3.white("\nThe following settings can be imported:\n"));
1645
- if (openclawDiscord.token) {
1646
- const maskedToken = openclawDiscord.token.substring(0, 10) + "..." + openclawDiscord.token.slice(-4);
1894
+ if (extracted?.token) {
1895
+ const maskedToken = extracted.token.substring(0, 10) + "..." + extracted.token.slice(-4);
1647
1896
  console.log(chalk3.gray(` \u2022 Bot Token: ${maskedToken}`));
1648
1897
  }
1649
- if (openclawDiscord.allowlist?.channels && openclawDiscord.allowlist.channels.length > 0) {
1650
- console.log(chalk3.gray(` \u2022 Allowed Channels: ${openclawDiscord.allowlist.channels.length} channel(s)`));
1651
- openclawDiscord.allowlist.channels.forEach((ch, i) => {
1652
- const label = i === 0 ? "(will use as chat channel)" : i === 1 ? "(will use as status channel)" : "";
1653
- console.log(chalk3.gray(` - ${ch} ${label}`));
1898
+ if (extracted?.guildId) {
1899
+ console.log(chalk3.gray(` \u2022 Server (Guild) ID: ${extracted.guildId}`));
1900
+ }
1901
+ if (extracted?.chatChannelId) {
1902
+ console.log(chalk3.gray(` \u2022 Chat Channel: ${extracted.chatChannelId}`));
1903
+ }
1904
+ if (extracted?.statusChannelId) {
1905
+ console.log(chalk3.gray(` \u2022 Status Channel: ${extracted.statusChannelId}`));
1906
+ }
1907
+ if (extracted?.allowedChannels && extracted.allowedChannels.length > 0) {
1908
+ console.log(chalk3.gray(` \u2022 All Allowed Channels: ${extracted.allowedChannels.length} channel(s)`));
1909
+ extracted.allowedChannels.forEach((ch) => {
1910
+ console.log(chalk3.gray(` - ${ch}`));
1654
1911
  });
1655
1912
  }
1656
1913
  console.log();
@@ -1673,6 +1930,9 @@ async function checkAndImportOpenClaw(config) {
1673
1930
  if (result.imported.token) {
1674
1931
  console.log(chalk3.green(" \u2713 Bot Token"));
1675
1932
  }
1933
+ if (result.imported.guildId) {
1934
+ console.log(chalk3.green(` \u2713 Server (Guild) ID: ${result.imported.guildId}`));
1935
+ }
1676
1936
  if (result.imported.chatChannelId) {
1677
1937
  console.log(chalk3.green(` \u2713 Chat Channel: ${result.imported.chatChannelId}`));
1678
1938
  }
@@ -1838,26 +2098,40 @@ async function runDiscordSetup(config) {
1838
2098
  console.log(chalk3.green("\n\u2713 Discord settings saved successfully!"));
1839
2099
  }
1840
2100
  async function runWebhookSetup(config) {
1841
- console.log(chalk3.gray("\nTo create a webhook:"));
1842
- console.log(chalk3.gray(" 1. Go to your Discord channel settings"));
1843
- console.log(chalk3.gray(' 2. Click "Integrations" \u2192 "Webhooks"'));
1844
- console.log(chalk3.gray(" 3. Create a new webhook and copy its URL\n"));
2101
+ const discordConfig = config.getDiscordConfig();
2102
+ const hasDiscordConfig = !!(discordConfig.token && discordConfig.chatChannelId);
2103
+ const choices = [
2104
+ ...hasDiscordConfig ? [{ name: "\u{1F916} Auto-create webhooks (requires bot connection)", value: "auto" }] : [],
2105
+ { name: "\u{1F4DD} Use a single webhook for all agents", value: "single" },
2106
+ { name: "\u{1F4C1} Configure webhooks per category", value: "category" },
2107
+ { name: "\u{1F527} Configure webhooks per agent", value: "individual" },
2108
+ { name: "\u23ED\uFE0F Skip webhook configuration", value: "skip" }
2109
+ ];
2110
+ if (!hasDiscordConfig) {
2111
+ console.log(chalk3.yellow("\n\u26A0 Auto-create webhooks requires Discord to be configured first."));
2112
+ console.log(chalk3.gray(" Run Discord setup to enable this feature.\n"));
2113
+ }
2114
+ console.log(chalk3.gray("\nWebhooks allow each agent to have a unique name and avatar in Discord."));
2115
+ console.log(chalk3.gray("You can create them automatically or manually.\n"));
1845
2116
  const { webhookMode } = await inquirer.prompt([
1846
2117
  {
1847
2118
  type: "list",
1848
2119
  name: "webhookMode",
1849
2120
  message: "How would you like to configure webhooks?",
1850
- choices: [
1851
- { name: "Use a single webhook for all agents", value: "single" },
1852
- { name: "Configure webhooks per category", value: "category" },
1853
- { name: "Configure webhooks per agent", value: "individual" },
1854
- { name: "Skip webhook configuration", value: "skip" }
1855
- ]
2121
+ choices
1856
2122
  }
1857
2123
  ]);
1858
2124
  if (webhookMode === "skip") {
1859
2125
  return;
1860
2126
  }
2127
+ if (webhookMode === "auto") {
2128
+ await runAutoWebhookSetup(config);
2129
+ return;
2130
+ }
2131
+ console.log(chalk3.gray("\nTo create a webhook manually:"));
2132
+ console.log(chalk3.gray(" 1. Go to your Discord channel settings"));
2133
+ console.log(chalk3.gray(' 2. Click "Integrations" \u2192 "Webhooks"'));
2134
+ console.log(chalk3.gray(" 3. Create a new webhook and copy its URL\n"));
1861
2135
  if (webhookMode === "single") {
1862
2136
  const { webhookUrl } = await inquirer.prompt([
1863
2137
  {
@@ -1931,6 +2205,114 @@ async function runWebhookSetup(config) {
1931
2205
  }
1932
2206
  }
1933
2207
  }
2208
+ async function runAutoWebhookSetup(config) {
2209
+ console.log(chalk3.cyan("\n\u{1F916} Auto-Create Webhooks\n"));
2210
+ console.log(chalk3.gray("This will connect to Discord and automatically create webhooks for all 35 agents."));
2211
+ console.log(chalk3.gray("The bot needs MANAGE_WEBHOOKS permission in the target channel.\n"));
2212
+ const discordConfig = config.getDiscordConfig();
2213
+ if (!discordConfig.token) {
2214
+ console.log(chalk3.red("\u274C Discord bot token not configured."));
2215
+ console.log(chalk3.gray("Run `tmc setup` to configure Discord settings first.\n"));
2216
+ return;
2217
+ }
2218
+ if (!discordConfig.chatChannelId) {
2219
+ console.log(chalk3.red("\u274C Chat channel not configured."));
2220
+ console.log(chalk3.gray("Run `tmc setup` to configure Discord settings first.\n"));
2221
+ return;
2222
+ }
2223
+ console.log(chalk3.yellow("\u26A0 Important: Discord limits webhooks to 15 per channel."));
2224
+ console.log(chalk3.gray(` You have ${AGENT_DEFINITIONS.length} agents, so some will share webhooks or fail to create.`));
2225
+ console.log(chalk3.gray(" Consider using multiple channels or a single shared webhook if this is an issue.\n"));
2226
+ const { confirm } = await inquirer.prompt([
2227
+ {
2228
+ type: "confirm",
2229
+ name: "confirm",
2230
+ message: `Create webhooks for all ${AGENT_DEFINITIONS.length} agents in channel ${discordConfig.chatChannelId}?`,
2231
+ default: true
2232
+ }
2233
+ ]);
2234
+ if (!confirm) {
2235
+ console.log(chalk3.yellow("\nWebhook creation cancelled.\n"));
2236
+ return;
2237
+ }
2238
+ const connectSpinner = ora("Connecting to Discord...").start();
2239
+ let adapter;
2240
+ try {
2241
+ adapter = new DiscordAdapter({
2242
+ token: discordConfig.token,
2243
+ guildId: discordConfig.guildId || "0",
2244
+ // Placeholder, will be auto-detected
2245
+ chatChannelId: discordConfig.chatChannelId,
2246
+ statusChannelId: discordConfig.statusChannelId
2247
+ });
2248
+ await adapter.connect();
2249
+ connectSpinner.succeed("Connected to Discord");
2250
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
2251
+ } catch (error) {
2252
+ connectSpinner.fail("Failed to connect to Discord");
2253
+ console.log(chalk3.red(`
2254
+ Error: ${error instanceof Error ? error.message : "Unknown error"}`));
2255
+ console.log(chalk3.gray("\nMake sure your bot token is valid and the bot is invited to your server.\n"));
2256
+ return;
2257
+ }
2258
+ try {
2259
+ if (!discordConfig.guildId) {
2260
+ const detectSpinner = ora("Detecting server (guild) ID...").start();
2261
+ const detectedGuildId = await adapter.detectGuildId();
2262
+ if (detectedGuildId) {
2263
+ config.updateGuildId(detectedGuildId);
2264
+ detectSpinner.succeed(`Detected server ID: ${detectedGuildId}`);
2265
+ } else {
2266
+ detectSpinner.warn("Could not auto-detect server ID (bot may not be in any servers)");
2267
+ }
2268
+ }
2269
+ const permSpinner = ora("Checking webhook permissions...").start();
2270
+ const hasPermission = await adapter.hasWebhookPermission(discordConfig.chatChannelId);
2271
+ if (!hasPermission) {
2272
+ permSpinner.fail("Bot lacks MANAGE_WEBHOOKS permission");
2273
+ console.log(chalk3.red("\n\u274C The bot does not have permission to manage webhooks in this channel."));
2274
+ console.log(chalk3.gray("\nTo fix this:"));
2275
+ console.log(chalk3.gray(" 1. Go to your Discord server settings"));
2276
+ console.log(chalk3.gray(" 2. Navigate to Roles or Channel Permissions"));
2277
+ console.log(chalk3.gray(' 3. Grant the bot "Manage Webhooks" permission'));
2278
+ console.log(chalk3.gray(" 4. Try again\n"));
2279
+ await adapter.disconnect();
2280
+ return;
2281
+ }
2282
+ permSpinner.succeed("Bot has webhook permissions");
2283
+ console.log(chalk3.cyan(`
2284
+ Creating webhooks for ${AGENT_DEFINITIONS.length} agents...
2285
+ `));
2286
+ const progressSpinner = ora("Starting webhook creation...").start();
2287
+ const webhooks = await adapter.autoCreateWebhooks(
2288
+ discordConfig.chatChannelId,
2289
+ (current, total, agentName) => {
2290
+ progressSpinner.text = `Creating webhooks... (${current}/${total}) ${agentName}`;
2291
+ }
2292
+ );
2293
+ progressSpinner.succeed(`Created webhooks for ${Object.keys(webhooks).length} agents`);
2294
+ const saveSpinner = ora("Saving webhook configuration...").start();
2295
+ const existingWebhooks = config.getAllWebhooks();
2296
+ config.setAllWebhooks({ ...existingWebhooks, ...webhooks });
2297
+ saveSpinner.succeed("Webhook configuration saved");
2298
+ console.log(chalk3.green(`
2299
+ \u2705 Successfully configured ${Object.keys(webhooks).length} webhooks!
2300
+ `));
2301
+ const missingCount = AGENT_DEFINITIONS.length - Object.keys(webhooks).length;
2302
+ if (missingCount > 0) {
2303
+ console.log(chalk3.yellow(`\u26A0 ${missingCount} webhook(s) could not be created.`));
2304
+ console.log(chalk3.gray(" Possible reasons:"));
2305
+ console.log(chalk3.gray(" \u2022 Discord's 15 webhook per channel limit reached"));
2306
+ console.log(chalk3.gray(" \u2022 Rate limiting from Discord API"));
2307
+ console.log(chalk3.gray(" \u2022 Webhook already exists with the same name"));
2308
+ console.log(chalk3.gray("\n Consider using multiple channels or a shared webhook for remaining agents.\n"));
2309
+ }
2310
+ } finally {
2311
+ const disconnectSpinner = ora("Disconnecting from Discord...").start();
2312
+ await adapter.disconnect();
2313
+ disconnectSpinner.succeed("Disconnected from Discord");
2314
+ }
2315
+ }
1934
2316
  function viewConfiguration(config) {
1935
2317
  console.log(chalk3.cyan("\n\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
1936
2318
  console.log(chalk3.cyan("\u2502 \u{1F4CA} Current Configuration \u2502"));