too-many-claw 1.0.3 → 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 +625 -33
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +164 -9
- package/dist/index.js +335 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1073,10 +1073,12 @@ var DEFAULT_CONFIG = {
|
|
|
1073
1073
|
};
|
|
1074
1074
|
var ConfigManager = class {
|
|
1075
1075
|
configPath;
|
|
1076
|
+
openclawConfigPath;
|
|
1076
1077
|
config;
|
|
1077
1078
|
constructor() {
|
|
1078
1079
|
const openclawDir = path.join(os.homedir(), ".openclaw");
|
|
1079
1080
|
this.configPath = path.join(openclawDir, "too-many-claw.json");
|
|
1081
|
+
this.openclawConfigPath = path.join(openclawDir, "openclaw.json");
|
|
1080
1082
|
this.config = this.load();
|
|
1081
1083
|
}
|
|
1082
1084
|
/**
|
|
@@ -1180,6 +1182,172 @@ var ConfigManager = class {
|
|
|
1180
1182
|
} catch {
|
|
1181
1183
|
}
|
|
1182
1184
|
}
|
|
1185
|
+
// ============================================
|
|
1186
|
+
// OpenClaw Integration
|
|
1187
|
+
// ============================================
|
|
1188
|
+
/**
|
|
1189
|
+
* Check if OpenClaw config file exists
|
|
1190
|
+
*/
|
|
1191
|
+
hasOpenClawConfig() {
|
|
1192
|
+
return fs.existsSync(this.openclawConfigPath);
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Read OpenClaw configuration file
|
|
1196
|
+
*/
|
|
1197
|
+
readOpenClawConfig() {
|
|
1198
|
+
try {
|
|
1199
|
+
if (!this.hasOpenClawConfig()) {
|
|
1200
|
+
return null;
|
|
1201
|
+
}
|
|
1202
|
+
return fs.readJsonSync(this.openclawConfigPath);
|
|
1203
|
+
} catch {
|
|
1204
|
+
return null;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Check if OpenClaw has Discord configuration
|
|
1209
|
+
*/
|
|
1210
|
+
hasOpenClawDiscordConfig() {
|
|
1211
|
+
const config = this.readOpenClawConfig();
|
|
1212
|
+
return !!config?.gateway?.discord?.token;
|
|
1213
|
+
}
|
|
1214
|
+
/**
|
|
1215
|
+
* Get raw Discord settings from OpenClaw config
|
|
1216
|
+
*/
|
|
1217
|
+
getOpenClawDiscordConfig() {
|
|
1218
|
+
const config = this.readOpenClawConfig();
|
|
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;
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Import Discord settings from OpenClaw configuration
|
|
1261
|
+
* Returns true if successful, false if no OpenClaw Discord config found
|
|
1262
|
+
*/
|
|
1263
|
+
importFromOpenClaw() {
|
|
1264
|
+
const extracted = this.extractOpenClawDiscordSettings();
|
|
1265
|
+
if (!extracted) {
|
|
1266
|
+
return {
|
|
1267
|
+
success: false,
|
|
1268
|
+
imported: {},
|
|
1269
|
+
message: "No OpenClaw Discord configuration found"
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
const imported = {};
|
|
1273
|
+
if (extracted.token) {
|
|
1274
|
+
imported.token = extracted.token;
|
|
1275
|
+
}
|
|
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;
|
|
1284
|
+
}
|
|
1285
|
+
if (Object.keys(imported).length === 0) {
|
|
1286
|
+
return {
|
|
1287
|
+
success: false,
|
|
1288
|
+
imported: {},
|
|
1289
|
+
message: "OpenClaw Discord config exists but has no usable settings"
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
const currentDiscord = this.getDiscordConfig();
|
|
1293
|
+
this.setDiscordConfig({
|
|
1294
|
+
...currentDiscord,
|
|
1295
|
+
// Base: existing settings
|
|
1296
|
+
...imported
|
|
1297
|
+
// Override with imported values
|
|
1298
|
+
});
|
|
1299
|
+
return {
|
|
1300
|
+
success: true,
|
|
1301
|
+
imported,
|
|
1302
|
+
message: `Imported ${Object.keys(imported).length} setting(s) from OpenClaw`
|
|
1303
|
+
};
|
|
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
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* Get OpenClaw config file path
|
|
1347
|
+
*/
|
|
1348
|
+
getOpenClawConfigPath() {
|
|
1349
|
+
return this.openclawConfigPath;
|
|
1350
|
+
}
|
|
1183
1351
|
};
|
|
1184
1352
|
|
|
1185
1353
|
// src/discord/Bot.ts
|
|
@@ -1187,7 +1355,8 @@ import {
|
|
|
1187
1355
|
Client,
|
|
1188
1356
|
GatewayIntentBits,
|
|
1189
1357
|
TextChannel,
|
|
1190
|
-
Partials
|
|
1358
|
+
Partials,
|
|
1359
|
+
PermissionFlagsBits
|
|
1191
1360
|
} from "discord.js";
|
|
1192
1361
|
var Bot = class {
|
|
1193
1362
|
client;
|
|
@@ -1322,6 +1491,126 @@ var Bot = class {
|
|
|
1322
1491
|
get isConnected() {
|
|
1323
1492
|
return this.client.isReady();
|
|
1324
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
|
+
}
|
|
1325
1614
|
};
|
|
1326
1615
|
|
|
1327
1616
|
// src/discord/WebhookManager.ts
|
|
@@ -1487,11 +1776,56 @@ var DiscordAdapter = class {
|
|
|
1487
1776
|
get isConnected() {
|
|
1488
1777
|
return this.bot.isConnected;
|
|
1489
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
|
+
}
|
|
1490
1824
|
};
|
|
1491
1825
|
|
|
1492
1826
|
// src/cli.ts
|
|
1493
1827
|
var program = new Command();
|
|
1494
|
-
program.name("tmc").description("Too Many Claw - 35 AI agents collaborating via Discord").version("1.0.
|
|
1828
|
+
program.name("tmc").description("Too Many Claw - 35 AI agents collaborating via Discord").version("1.0.4");
|
|
1495
1829
|
program.command("setup").description("Interactive setup wizard for Too Many Claw").action(async () => {
|
|
1496
1830
|
console.log(chalk3.cyan(`
|
|
1497
1831
|
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
@@ -1503,20 +1837,22 @@ program.command("setup").description("Interactive setup wizard for Too Many Claw
|
|
|
1503
1837
|
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
1504
1838
|
`));
|
|
1505
1839
|
const config = new ConfigManager();
|
|
1506
|
-
|
|
1840
|
+
await checkAndImportOpenClaw(config);
|
|
1841
|
+
const menuChoices = [
|
|
1842
|
+
{ name: "\u{1F527} Full Setup (Recommended for first time)", value: "full" },
|
|
1843
|
+
{ name: "\u{1F916} Discord Bot Settings", value: "discord" },
|
|
1844
|
+
{ name: "\u{1F517} Webhook Configuration", value: "webhooks" },
|
|
1845
|
+
...config.hasOpenClawConfig() ? [{ name: "\u{1F4E5} Import from OpenClaw", value: "openclaw" }] : [],
|
|
1846
|
+
{ name: "\u{1F4CA} View Current Configuration", value: "view" },
|
|
1847
|
+
{ name: "\u{1F5D1}\uFE0F Reset Configuration", value: "reset" },
|
|
1848
|
+
{ name: "\u274C Exit", value: "exit" }
|
|
1849
|
+
];
|
|
1507
1850
|
const { setupType } = await inquirer.prompt([
|
|
1508
1851
|
{
|
|
1509
1852
|
type: "list",
|
|
1510
1853
|
name: "setupType",
|
|
1511
1854
|
message: "What would you like to configure?",
|
|
1512
|
-
choices:
|
|
1513
|
-
{ name: "\u{1F527} Full Setup (Recommended for first time)", value: "full" },
|
|
1514
|
-
{ name: "\u{1F916} Discord Bot Settings", value: "discord" },
|
|
1515
|
-
{ name: "\u{1F517} Webhook Configuration", value: "webhooks" },
|
|
1516
|
-
{ name: "\u{1F4CA} View Current Configuration", value: "view" },
|
|
1517
|
-
{ name: "\u{1F5D1}\uFE0F Reset Configuration", value: "reset" },
|
|
1518
|
-
{ name: "\u274C Exit", value: "exit" }
|
|
1519
|
-
]
|
|
1855
|
+
choices: menuChoices
|
|
1520
1856
|
}
|
|
1521
1857
|
]);
|
|
1522
1858
|
switch (setupType) {
|
|
@@ -1529,6 +1865,9 @@ program.command("setup").description("Interactive setup wizard for Too Many Claw
|
|
|
1529
1865
|
case "webhooks":
|
|
1530
1866
|
await runWebhookSetup(config);
|
|
1531
1867
|
break;
|
|
1868
|
+
case "openclaw":
|
|
1869
|
+
await importFromOpenClawManual(config);
|
|
1870
|
+
break;
|
|
1532
1871
|
case "view":
|
|
1533
1872
|
viewConfiguration(config);
|
|
1534
1873
|
break;
|
|
@@ -1540,16 +1879,134 @@ program.command("setup").description("Interactive setup wizard for Too Many Claw
|
|
|
1540
1879
|
break;
|
|
1541
1880
|
}
|
|
1542
1881
|
});
|
|
1882
|
+
async function checkAndImportOpenClaw(config) {
|
|
1883
|
+
if (!config.hasOpenClawDiscordConfig()) {
|
|
1884
|
+
return false;
|
|
1885
|
+
}
|
|
1886
|
+
console.log(chalk3.green("\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
|
|
1887
|
+
console.log(chalk3.green("\u2502 \u2728 OpenClaw Discord configuration detected! \u2502"));
|
|
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"));
|
|
1889
|
+
const openclawDiscord = config.getOpenClawDiscordConfig();
|
|
1890
|
+
if (!openclawDiscord) return false;
|
|
1891
|
+
const summary = config.getOpenClawImportSummary();
|
|
1892
|
+
const extracted = summary.extracted;
|
|
1893
|
+
console.log(chalk3.white("\nThe following settings can be imported:\n"));
|
|
1894
|
+
if (extracted?.token) {
|
|
1895
|
+
const maskedToken = extracted.token.substring(0, 10) + "..." + extracted.token.slice(-4);
|
|
1896
|
+
console.log(chalk3.gray(` \u2022 Bot Token: ${maskedToken}`));
|
|
1897
|
+
}
|
|
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}`));
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
console.log();
|
|
1914
|
+
const { shouldImport } = await inquirer.prompt([
|
|
1915
|
+
{
|
|
1916
|
+
type: "confirm",
|
|
1917
|
+
name: "shouldImport",
|
|
1918
|
+
message: "Would you like to import these settings from OpenClaw?",
|
|
1919
|
+
default: true
|
|
1920
|
+
}
|
|
1921
|
+
]);
|
|
1922
|
+
if (!shouldImport) {
|
|
1923
|
+
console.log(chalk3.gray("\nSkipped OpenClaw import.\n"));
|
|
1924
|
+
return false;
|
|
1925
|
+
}
|
|
1926
|
+
const result = config.importFromOpenClaw();
|
|
1927
|
+
if (result.success) {
|
|
1928
|
+
console.log(chalk3.green("\n\u2713 Import successful!\n"));
|
|
1929
|
+
console.log(chalk3.white("Imported settings:"));
|
|
1930
|
+
if (result.imported.token) {
|
|
1931
|
+
console.log(chalk3.green(" \u2713 Bot Token"));
|
|
1932
|
+
}
|
|
1933
|
+
if (result.imported.guildId) {
|
|
1934
|
+
console.log(chalk3.green(` \u2713 Server (Guild) ID: ${result.imported.guildId}`));
|
|
1935
|
+
}
|
|
1936
|
+
if (result.imported.chatChannelId) {
|
|
1937
|
+
console.log(chalk3.green(` \u2713 Chat Channel: ${result.imported.chatChannelId}`));
|
|
1938
|
+
}
|
|
1939
|
+
if (result.imported.statusChannelId) {
|
|
1940
|
+
console.log(chalk3.green(` \u2713 Status Channel: ${result.imported.statusChannelId}`));
|
|
1941
|
+
}
|
|
1942
|
+
console.log();
|
|
1943
|
+
const currentConfig = config.getDiscordConfig();
|
|
1944
|
+
const missing = [];
|
|
1945
|
+
if (!currentConfig.guildId) missing.push("Server (Guild) ID");
|
|
1946
|
+
if (!currentConfig.chatChannelId) missing.push("Chat Channel ID");
|
|
1947
|
+
if (missing.length > 0) {
|
|
1948
|
+
console.log(chalk3.yellow("\u26A0 The following settings still need to be configured:"));
|
|
1949
|
+
missing.forEach((m) => console.log(chalk3.yellow(` \u2022 ${m}`)));
|
|
1950
|
+
console.log();
|
|
1951
|
+
}
|
|
1952
|
+
} else {
|
|
1953
|
+
console.log(chalk3.yellow(`
|
|
1954
|
+
\u26A0 ${result.message}
|
|
1955
|
+
`));
|
|
1956
|
+
}
|
|
1957
|
+
return result.success;
|
|
1958
|
+
}
|
|
1959
|
+
async function importFromOpenClawManual(config) {
|
|
1960
|
+
console.log(chalk3.cyan("\n\u{1F4E5} Import from OpenClaw\n"));
|
|
1961
|
+
if (!config.hasOpenClawConfig()) {
|
|
1962
|
+
console.log(chalk3.yellow("OpenClaw configuration file not found."));
|
|
1963
|
+
console.log(chalk3.gray(`Expected location: ${config.getOpenClawConfigPath()}`));
|
|
1964
|
+
console.log(chalk3.gray("\nMake sure OpenClaw is installed and configured.\n"));
|
|
1965
|
+
return;
|
|
1966
|
+
}
|
|
1967
|
+
if (!config.hasOpenClawDiscordConfig()) {
|
|
1968
|
+
console.log(chalk3.yellow("OpenClaw is installed but has no Discord configuration."));
|
|
1969
|
+
console.log(chalk3.gray("\nPlease configure Discord in OpenClaw first, then try again.\n"));
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1972
|
+
await checkAndImportOpenClaw(config);
|
|
1973
|
+
}
|
|
1543
1974
|
async function runFullSetup(config) {
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1975
|
+
const currentDiscord = config.getDiscordConfig();
|
|
1976
|
+
const hasToken = !!currentDiscord.token;
|
|
1977
|
+
const hasGuild = !!currentDiscord.guildId;
|
|
1978
|
+
const hasChatChannel = !!currentDiscord.chatChannelId;
|
|
1979
|
+
if (hasToken && hasGuild && hasChatChannel) {
|
|
1980
|
+
console.log(chalk3.green("\n\u2713 Discord is already configured!\n"));
|
|
1981
|
+
const { reconfigure } = await inquirer.prompt([
|
|
1982
|
+
{
|
|
1983
|
+
type: "confirm",
|
|
1984
|
+
name: "reconfigure",
|
|
1985
|
+
message: "Would you like to reconfigure Discord settings?",
|
|
1986
|
+
default: false
|
|
1987
|
+
}
|
|
1988
|
+
]);
|
|
1989
|
+
if (!reconfigure) {
|
|
1990
|
+
console.log(chalk3.cyan("\n\u{1F4CB} Step 1/3: Discord Bot Configuration - Skipped\n"));
|
|
1991
|
+
} else {
|
|
1992
|
+
console.log(chalk3.cyan("\n\u{1F4CB} Step 1/3: Discord Bot Configuration\n"));
|
|
1993
|
+
await runDiscordSetup(config);
|
|
1994
|
+
}
|
|
1995
|
+
} else {
|
|
1996
|
+
console.log(chalk3.cyan("\n\u{1F4CB} Step 1/3: Discord Bot Configuration\n"));
|
|
1997
|
+
if (hasToken) {
|
|
1998
|
+
console.log(chalk3.green("\u2713 Bot token already configured (imported from OpenClaw)\n"));
|
|
1999
|
+
} else {
|
|
2000
|
+
console.log(chalk3.gray("To use Too Many Claw, you need a Discord bot. Here's how to get one:"));
|
|
2001
|
+
console.log(chalk3.gray(" 1. Go to https://discord.com/developers/applications"));
|
|
2002
|
+
console.log(chalk3.gray(" 2. Create a new application"));
|
|
2003
|
+
console.log(chalk3.gray(' 3. Go to the "Bot" tab and create a bot'));
|
|
2004
|
+
console.log(chalk3.gray(" 4. Copy the bot token"));
|
|
2005
|
+
console.log(chalk3.gray(' 5. Enable "Message Content Intent" in the Bot settings'));
|
|
2006
|
+
console.log(chalk3.gray(" 6. Invite the bot to your server with appropriate permissions\n"));
|
|
2007
|
+
}
|
|
2008
|
+
await runDiscordSetup(config);
|
|
2009
|
+
}
|
|
1553
2010
|
console.log(chalk3.cyan("\n\u{1F4CB} Step 2/3: Webhook Configuration (Optional)\n"));
|
|
1554
2011
|
console.log(chalk3.gray("Webhooks allow each agent to have a unique name and avatar."));
|
|
1555
2012
|
console.log(chalk3.gray("Without webhooks, all agents will use the bot's identity.\n"));
|
|
@@ -1574,11 +2031,24 @@ async function runFullSetup(config) {
|
|
|
1574
2031
|
}
|
|
1575
2032
|
async function runDiscordSetup(config) {
|
|
1576
2033
|
const currentDiscord = config.getDiscordConfig();
|
|
2034
|
+
if (currentDiscord.token || currentDiscord.guildId || currentDiscord.chatChannelId) {
|
|
2035
|
+
console.log(chalk3.gray("Pre-filled values (press Enter to keep, or type new value):\n"));
|
|
2036
|
+
if (currentDiscord.token) {
|
|
2037
|
+
console.log(chalk3.gray(` \u2022 Token: ${currentDiscord.token.substring(0, 10)}...`));
|
|
2038
|
+
}
|
|
2039
|
+
if (currentDiscord.guildId) {
|
|
2040
|
+
console.log(chalk3.gray(` \u2022 Guild ID: ${currentDiscord.guildId}`));
|
|
2041
|
+
}
|
|
2042
|
+
if (currentDiscord.chatChannelId) {
|
|
2043
|
+
console.log(chalk3.gray(` \u2022 Chat Channel: ${currentDiscord.chatChannelId}`));
|
|
2044
|
+
}
|
|
2045
|
+
console.log();
|
|
2046
|
+
}
|
|
1577
2047
|
const answers = await inquirer.prompt([
|
|
1578
2048
|
{
|
|
1579
2049
|
type: "password",
|
|
1580
2050
|
name: "token",
|
|
1581
|
-
message: "Discord Bot Token:",
|
|
2051
|
+
message: currentDiscord.token ? "Discord Bot Token (press Enter to keep current):" : "Discord Bot Token:",
|
|
1582
2052
|
default: currentDiscord.token || "",
|
|
1583
2053
|
mask: "*",
|
|
1584
2054
|
validate: (input) => {
|
|
@@ -1591,7 +2061,7 @@ async function runDiscordSetup(config) {
|
|
|
1591
2061
|
{
|
|
1592
2062
|
type: "input",
|
|
1593
2063
|
name: "guildId",
|
|
1594
|
-
message: "Discord Server (Guild) ID:",
|
|
2064
|
+
message: currentDiscord.guildId ? "Discord Server (Guild) ID (press Enter to keep current):" : "Discord Server (Guild) ID:",
|
|
1595
2065
|
default: currentDiscord.guildId || "",
|
|
1596
2066
|
validate: (input) => {
|
|
1597
2067
|
if (!input || !/^\d{17,19}$/.test(input)) {
|
|
@@ -1603,7 +2073,7 @@ async function runDiscordSetup(config) {
|
|
|
1603
2073
|
{
|
|
1604
2074
|
type: "input",
|
|
1605
2075
|
name: "chatChannelId",
|
|
1606
|
-
message: "Chat Channel ID (main conversation channel):",
|
|
2076
|
+
message: currentDiscord.chatChannelId ? "Chat Channel ID (press Enter to keep current):" : "Chat Channel ID (main conversation channel):",
|
|
1607
2077
|
default: currentDiscord.chatChannelId || "",
|
|
1608
2078
|
validate: (input) => {
|
|
1609
2079
|
if (!input || !/^\d{17,19}$/.test(input)) {
|
|
@@ -1628,26 +2098,40 @@ async function runDiscordSetup(config) {
|
|
|
1628
2098
|
console.log(chalk3.green("\n\u2713 Discord settings saved successfully!"));
|
|
1629
2099
|
}
|
|
1630
2100
|
async function runWebhookSetup(config) {
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
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"));
|
|
1635
2116
|
const { webhookMode } = await inquirer.prompt([
|
|
1636
2117
|
{
|
|
1637
2118
|
type: "list",
|
|
1638
2119
|
name: "webhookMode",
|
|
1639
2120
|
message: "How would you like to configure webhooks?",
|
|
1640
|
-
choices
|
|
1641
|
-
{ name: "Use a single webhook for all agents", value: "single" },
|
|
1642
|
-
{ name: "Configure webhooks per category", value: "category" },
|
|
1643
|
-
{ name: "Configure webhooks per agent", value: "individual" },
|
|
1644
|
-
{ name: "Skip webhook configuration", value: "skip" }
|
|
1645
|
-
]
|
|
2121
|
+
choices
|
|
1646
2122
|
}
|
|
1647
2123
|
]);
|
|
1648
2124
|
if (webhookMode === "skip") {
|
|
1649
2125
|
return;
|
|
1650
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"));
|
|
1651
2135
|
if (webhookMode === "single") {
|
|
1652
2136
|
const { webhookUrl } = await inquirer.prompt([
|
|
1653
2137
|
{
|
|
@@ -1721,6 +2205,114 @@ async function runWebhookSetup(config) {
|
|
|
1721
2205
|
}
|
|
1722
2206
|
}
|
|
1723
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
|
+
}
|
|
1724
2316
|
function viewConfiguration(config) {
|
|
1725
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"));
|
|
1726
2318
|
console.log(chalk3.cyan("\u2502 \u{1F4CA} Current Configuration \u2502"));
|