discord-ops 0.22.0 → 0.23.3

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +108 -29
  3. package/dist/cli/validate.d.ts.map +1 -1
  4. package/dist/cli/validate.js +48 -0
  5. package/dist/cli/validate.js.map +1 -1
  6. package/dist/config/index.d.ts +23 -4
  7. package/dist/config/index.d.ts.map +1 -1
  8. package/dist/config/index.js +84 -4
  9. package/dist/config/index.js.map +1 -1
  10. package/dist/config/profiles.d.ts +7 -0
  11. package/dist/config/profiles.d.ts.map +1 -1
  12. package/dist/config/profiles.js +23 -1
  13. package/dist/config/profiles.js.map +1 -1
  14. package/dist/config/schema.d.ts +166 -15
  15. package/dist/config/schema.d.ts.map +1 -1
  16. package/dist/config/schema.js +33 -2
  17. package/dist/config/schema.js.map +1 -1
  18. package/dist/config/validate.d.ts.map +1 -1
  19. package/dist/config/validate.js +25 -6
  20. package/dist/config/validate.js.map +1 -1
  21. package/dist/profiles/index.d.ts.map +1 -1
  22. package/dist/profiles/index.js +2 -0
  23. package/dist/profiles/index.js.map +1 -1
  24. package/dist/routing/resolver.d.ts +5 -0
  25. package/dist/routing/resolver.d.ts.map +1 -1
  26. package/dist/routing/resolver.js +79 -16
  27. package/dist/routing/resolver.js.map +1 -1
  28. package/dist/server.d.ts.map +1 -1
  29. package/dist/server.js +50 -2
  30. package/dist/server.js.map +1 -1
  31. package/dist/tools/index.d.ts.map +1 -1
  32. package/dist/tools/index.js +2 -0
  33. package/dist/tools/index.js.map +1 -1
  34. package/dist/tools/system/list-bots.d.ts +2 -0
  35. package/dist/tools/system/list-bots.d.ts.map +1 -0
  36. package/dist/tools/system/list-bots.js +49 -0
  37. package/dist/tools/system/list-bots.js.map +1 -0
  38. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,59 @@
1
1
  # discord-ops
2
2
 
3
+ ## 0.23.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 4faf83d: fix(release): pin npm to 11.5.1 for OIDC trusted-publishing auth
8
+
9
+ The previous corepack-based install activated npm 10.9.7 (corepack's
10
+ `npm@latest` alias is stale), which supports `--provenance` for sigstore
11
+ attestation but lacks native trusted-publishing OIDC auth. The publish
12
+ PUT to the registry therefore went out unauthenticated and was rejected
13
+ with a misleading E404. Pinning to npm 11.5.1 ensures both provenance
14
+ signing and TP auth function correctly.
15
+
16
+ ## 0.23.2
17
+
18
+ ### Patch Changes
19
+
20
+ - d227788: chore(deps): resolve npm audit findings via audit fix
21
+
22
+ Updates lockfile to clear 1 high-severity (fast-uri path traversal,
23
+ CVSS 7.5) and 7 moderate-severity transitive vulnerabilities in hono,
24
+ ip-address, postcss, and ws. No direct dependency changes; no behavioral
25
+ impact. Unblocks `prepublishOnly` audit gate for release pipeline.
26
+
27
+ ## 0.23.1
28
+
29
+ ### Patch Changes
30
+
31
+ - 2f303d6: chore: migrate npm publishing to Trusted Publishing (OIDC)
32
+
33
+ Removes long-lived `NPM_TOKEN` dependency from CI publish workflow.
34
+ Authentication now happens via GitHub Actions OIDC token federation,
35
+ in response to the npm Mini Shai-Hulud token rotation event. No
36
+ behavioral changes for package consumers — sigstore provenance
37
+ attestation is preserved.
38
+
39
+ ## 0.23.0
40
+
41
+ ### Minor Changes
42
+
43
+ - bbc870b: Multi-bot architecture: bot personas, per-channel bot assignment, and per-project tool profile enforcement.
44
+
45
+ **Bot personas:** Named bots with identity metadata (`name`, `role`, `description`) configured in a top-level `bots` section. Each bot references a `token_env` and can have a `default_profile` restricting which tools it can use.
46
+
47
+ **Channel-level bot assignment:** Channels accept `{ "id": "...", "bot": "bot-name" }` to override which bot operates in that channel. Token resolution follows: channel bot → project bot → project `token_env` → default token.
48
+
49
+ **Per-project tool profile enforcement:** Runtime gate in the MCP server checks per-project `tool_profile` and per-bot `default_profile` on every tool call. Tools not in the effective profile return an error. Supports `profile_add`/`profile_remove` overrides.
50
+
51
+ **New tool:** `list_bots` — returns all configured bot personas with project assignments, channel overrides, and connection status. Does not expose token values.
52
+
53
+ **Validation:** `discord-ops validate` now checks bot references, profile names, and bot token availability.
54
+
55
+ Backwards compatible — existing configs without `bots` work unchanged. Channels accept both plain snowflake strings and `{ id, bot }` objects.
56
+
3
57
  ## 0.22.0
4
58
 
5
59
  ### Minor Changes
package/README.md CHANGED
@@ -8,10 +8,11 @@ Agency-grade Discord MCP server with multi-guild project routing.
8
8
 
9
9
  ## Features
10
10
 
11
- - **48 MCP tools** — messaging, channels, moderation, roles, webhooks, audit log, threads, guilds, invites, permissions, search, 23 templates, OG embed unfurling, project introspection
11
+ - **49 MCP tools** — messaging, channels, moderation, roles, webhooks, audit log, threads, guilds, invites, permissions, search, 23 templates, OG embed unfurling, project introspection
12
12
  - **Multi-guild project routing** — `send_message({ project: "my-app", channel: "builds" })` instead of raw channel IDs
13
13
  - **Notification routing** — map notification types (`ci_build`, `deploy`, `error`) to channels per project
14
14
  - **Owner pings** — configure project owners so releases, errors, and alerts auto-mention the right people
15
+ - **Bot personas** — named bots with identity metadata, per-channel bot assignment, and per-bot tool profiles
15
16
  - **Multi-bot support** — manage multiple Discord bots from a single MCP server with per-project tokens
16
17
  - **Tool profiles** — load only the tools an agent needs; cut schema overhead by 85% with slim profiles
17
18
  - **Smart channel resolution** — channel params accept channel name or snowflake ID, with 4-layer fuzzy fallback
@@ -183,6 +184,56 @@ Projects can specify their own bot token via `token_env`:
183
184
 
184
185
  When all projects have `token_env`, the default `DISCORD_TOKEN` is optional. Each project connects with its own bot.
185
186
 
187
+ ### Bot personas
188
+
189
+ Give bots names, roles, and per-channel assignment. This is ideal when your Discord server runs multiple bots with distinct personas (e.g., a community helper vs. a tech ops bot).
190
+
191
+ ```json
192
+ {
193
+ "bots": {
194
+ "claire": {
195
+ "name": "Claire",
196
+ "role": "Community helper",
197
+ "description": "Handles support and community channels",
198
+ "token_env": "CLAIRE_TOKEN",
199
+ "default_profile": "messaging"
200
+ },
201
+ "courier": {
202
+ "name": "Clarity Courier",
203
+ "role": "Technical operations",
204
+ "token_env": "COURIER_TOKEN",
205
+ "default_profile": "full"
206
+ }
207
+ },
208
+ "projects": {
209
+ "clarity-house": {
210
+ "guild_id": "123456789012345678",
211
+ "bot": "courier",
212
+ "channels": {
213
+ "general": "111111111111111111",
214
+ "support": { "id": "222222222222222222", "bot": "claire" },
215
+ "dev-ops": "333333333333333333",
216
+ "ai-testing": { "id": "444444444444444444", "bot": "claire" }
217
+ },
218
+ "default_channel": "dev-ops",
219
+ "tool_profile": "full"
220
+ }
221
+ }
222
+ }
223
+ ```
224
+
225
+ **How it works:**
226
+
227
+ - **`bots`** — named bot definitions with identity metadata and `token_env`
228
+ - **`project.bot`** — default bot for the project (all channels use this bot unless overridden)
229
+ - **Channel `bot` override** — individual channels can use a different bot: `{ "id": "...", "bot": "claire" }`
230
+ - **`default_profile`** — per-bot tool profile (restricts which tools a bot can use at runtime)
231
+ - **Token resolution** — channel bot → project bot → project `token_env` → default `DISCORD_TOKEN`
232
+ - **Bot persona in routing** — resolved targets include `bot: { name, role }` metadata for agent context
233
+ - **Backwards compatible** — `bots` is optional; channels accept both `"ID"` and `{ "id": "ID", "bot": "name" }` formats
234
+
235
+ Use `list_bots` to see all configured bots, their project assignments, channel overrides, and connection status.
236
+
186
237
  ### Owner pings
187
238
 
188
239
  Configure project owners so that releases, errors, and alerts automatically prepend `@mentions`. This ensures the right people are always paged for critical events without hardcoding mentions in every message.
@@ -365,12 +416,13 @@ Useful for sharing GitHub PRs, npm releases, blog posts, or any URL with rich pr
365
416
  | `list_threads` | List active threads |
366
417
  | `archive_thread` | Archive (and optionally lock) a thread |
367
418
 
368
- ### System (2 tools)
419
+ ### System (3 tools)
369
420
 
370
- | Tool | Description |
371
- | --------------- | ------------------------------------------------------------------- |
372
- | `health_check` | Bot status, version, connected guilds, and permission audit |
373
- | `list_projects` | List all projects with guild mappings, token status, and validation |
421
+ | Tool | Description |
422
+ | --------------- | -------------------------------------------------------------------- |
423
+ | `health_check` | Bot status, version, connected guilds, and permission audit |
424
+ | `list_projects` | List all projects with guild mappings, token status, and validation |
425
+ | `list_bots` | List all bot personas with project assignments and channel overrides |
374
426
 
375
427
  ## Tool Profiles
376
428
 
@@ -380,9 +432,9 @@ Load only the tools an agent needs. Reduces schema token overhead by up to 85% f
380
432
 
381
433
  | Profile | Tools | Description |
382
434
  | ------------ | ----- | ------------------------------------------------------------------------------------------------------ |
383
- | `full` | 48 | All tools (default) |
384
- | `monitoring` | 6 | get_messages, send_message, add_reaction, create_thread, health_check, list_projects |
385
- | `readonly` | 6 | get_messages, list_channels, list_members, get_guild, health_check, list_projects |
435
+ | `full` | 49 | All tools (default) |
436
+ | `monitoring` | 7 | get_messages, send_message, add_reaction, create_thread, health_check, list_projects, list_bots |
437
+ | `readonly` | 7 | get_messages, list_channels, list_members, get_guild, health_check, list_projects, list_bots |
386
438
  | `moderation` | 7 | get_messages, kick_member, ban_member, timeout_member, delete_message, purge_messages, query_audit_log |
387
439
  | `messaging` | 5 | add_reaction, delete_message, edit_message, get_messages, send_message |
388
440
  | `channels` | 7 | create_channel, delete_channel, edit_channel, get_channel, list_channels, purge_messages, set_slowmode |
@@ -484,10 +536,12 @@ Any tool name accepted by the MCP server works here — `send_message`, `send_te
484
536
 
485
537
  ### Token resolution
486
538
 
487
- 1. If `DISCORD_OPS_TOKEN_ENV` is set, its value names the env var holding the default token (e.g., `DISCORD_OPS_TOKEN_ENV=MY_BOT_TOKEN` reads `MY_BOT_TOKEN`).
488
- 2. Otherwise, the default token comes from `DISCORD_TOKEN`.
489
- 3. Per-project tokens override the default: if a project config has `"token_env": "ORG_A_TOKEN"`, that project's bot uses `ORG_A_TOKEN`.
490
- 4. If all projects have `token_env` set with valid values, no default token is needed at all.
539
+ 1. **Channel-level bot** if the channel has a `bot` override, use that bot's `token_env`
540
+ 2. **Project-level bot** — if the project has a `bot`, use that bot's `token_env`
541
+ 3. **Project-level `token_env`** project's own token env var
542
+ 4. **Default token** `DISCORD_TOKEN` (or custom via `DISCORD_OPS_TOKEN_ENV`)
543
+
544
+ If `DISCORD_OPS_TOKEN_ENV` is set, its value names the env var holding the default token (e.g., `DISCORD_OPS_TOKEN_ENV=MY_BOT_TOKEN` reads `MY_BOT_TOKEN`). If all projects have `token_env` or `bot` set, no default token is needed.
491
545
 
492
546
  ## CI/CD Integration
493
547
 
@@ -720,22 +774,33 @@ Full `~/.discord-ops.json` schema with all options:
720
774
 
721
775
  ```json
722
776
  {
777
+ "bots": {
778
+ "my-bot": {
779
+ "name": "My Bot",
780
+ "role": "General purpose",
781
+ "description": "Handles all operations",
782
+ "token_env": "MY_BOT_TOKEN",
783
+ "default_profile": "full"
784
+ }
785
+ },
723
786
  "projects": {
724
787
  "my-app": {
725
788
  "guild_id": "123456789012345678",
726
789
  "token_env": "MY_APP_DISCORD_TOKEN",
790
+ "bot": "my-bot",
727
791
  "channels": {
728
792
  "dev": "CHANNEL_ID",
729
793
  "builds": "CHANNEL_ID",
730
794
  "releases": "CHANNEL_ID",
731
- "alerts": "CHANNEL_ID"
795
+ "alerts": "CHANNEL_ID",
796
+ "support": { "id": "CHANNEL_ID", "bot": "my-bot" }
732
797
  },
733
798
  "default_channel": "dev",
734
799
  "owners": ["USER_SNOWFLAKE_ID"],
735
800
  "notify_owners_on": ["release", "error", "alert"],
736
801
  "tool_profile": "full",
737
- "tool_profile_add": [],
738
- "tool_profile_remove": [],
802
+ "profile_add": [],
803
+ "profile_remove": [],
739
804
  "notification_routing": {
740
805
  "ci_build": "builds",
741
806
  "deploy": "builds",
@@ -755,20 +820,31 @@ Full `~/.discord-ops.json` schema with all options:
755
820
  }
756
821
  ```
757
822
 
758
- | Field | Description |
759
- | ---------------------- | ----------------------------------------------------------------- |
760
- | `guild_id` | Discord server (guild) snowflake ID |
761
- | `token_env` | Env var name for this project's bot token |
762
- | `channels` | Alias channel ID map; `channel: "builds"` resolves here first |
763
- | `default_channel` | Channel used when no `channel` param is provided |
764
- | `owners` | User snowflake IDs to mention on matching notification types |
765
- | `notify_owners_on` | Notification types that trigger owner pings (`"dev"` never pings) |
766
- | `tool_profile` | Base tool profile for this project (`full`, `monitoring`, etc.) |
767
- | `tool_profile_add` | Additional tools to load on top of the base profile |
768
- | `tool_profile_remove` | Tools to exclude from the base profile |
769
- | `notification_routing` | Per-project override of global notification → channel routing |
823
+ **Global fields:**
824
+
825
+ | Field | Description |
826
+ | ---------------------- | ------------------------------------------------------------------------------------- |
827
+ | `bots` | Named bot personas with `name`, `role`, `description`, `token_env`, `default_profile` |
828
+ | `default_project` | Project used when no `project` param is provided |
829
+ | `notification_routing` | Global notification type channel alias routing |
830
+
831
+ **Project fields:**
832
+
833
+ | Field | Description |
834
+ | ---------------------- | ------------------------------------------------------------------------------------- |
835
+ | `guild_id` | Discord server (guild) snowflake ID |
836
+ | `token_env` | Env var name for this project's bot token |
837
+ | `bot` | Default bot persona for this project (references a key in `bots`) |
838
+ | `channels` | Alias → channel ID or `{ id, bot }` map; `channel: "builds"` resolves here first |
839
+ | `default_channel` | Channel used when no `channel` param is provided |
840
+ | `owners` | User snowflake IDs to mention on matching notification types |
841
+ | `notify_owners_on` | Notification types that trigger owner pings (`"dev"` never pings) |
842
+ | `tool_profile` | Base tool profile for this project (`full`, `monitoring`, etc.) — enforced at runtime |
843
+ | `profile_add` | Additional tools to load on top of the base profile |
844
+ | `profile_remove` | Tools to exclude from the base profile |
845
+ | `notification_routing` | Per-project override of global notification → channel routing |
770
846
 
771
- > **Note:** `tool_profile`, `tool_profile_add`, and `tool_profile_remove` in project config are not yet implemented in the config schema. Use CLI flags `--profile` and `--tools` instead.
847
+ Per-project profiles are enforced at runtime all tools stay registered on the MCP server, but tool calls are filtered when the resolved project or bot has a profile set. This means agents can discover all tools via MCP schema, but per-project restrictions are applied on each call.
772
848
 
773
849
  ## Multi-Organization Troubleshooting
774
850
 
@@ -777,6 +853,9 @@ Full `~/.discord-ops.json` schema with all options:
777
853
  Run `discord-ops validate` to check your config without connecting to Discord. It detects:
778
854
 
779
855
  - Missing `token_env` values (env var not set)
856
+ - Bot references (`project.bot`, channel `bot`) pointing to undefined bots
857
+ - Invalid `default_profile` or `tool_profile` values
858
+ - Missing bot `token_env` environment variables
780
859
  - Duplicate guild IDs across projects with different tokens
781
860
  - `default_channel` referencing a nonexistent alias
782
861
  - `default_project` pointing to a nonexistent project
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/cli/validate.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CA0BjD"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/cli/validate.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAyFjD"}
@@ -1,4 +1,5 @@
1
1
  import { loadConfig } from "../config/index.js";
2
+ import { isProfileName } from "../profiles/index.js";
2
3
  /**
3
4
  * Validates the discord-ops configuration files.
4
5
  * Loads global and per-project configs and reports any issues.
@@ -7,8 +8,12 @@ export async function runValidate() {
7
8
  try {
8
9
  const config = loadConfig();
9
10
  const projectCount = Object.keys(config.global.projects).length;
11
+ const botCount = Object.keys(config.global.bots ?? {}).length;
10
12
  console.log("Configuration valid.");
11
13
  console.log(` Projects: ${projectCount}`);
14
+ if (botCount > 0) {
15
+ console.log(` Bots: ${botCount}`);
16
+ }
12
17
  if (config.global.default_project) {
13
18
  console.log(` Default project: ${config.global.default_project}`);
14
19
  }
@@ -21,6 +26,49 @@ export async function runValidate() {
21
26
  else {
22
27
  console.warn(" Warning: No default token configured (DISCORD_TOKEN not set)");
23
28
  }
29
+ // Validate bot references and profiles
30
+ const warnings = [];
31
+ const errors = [];
32
+ const bots = config.global.bots ?? {};
33
+ // Validate bot personas
34
+ for (const [botKey, bot] of Object.entries(bots)) {
35
+ if (!process.env[bot.token_env]) {
36
+ warnings.push(`Bot "${botKey}": token_env "${bot.token_env}" is not set in environment`);
37
+ }
38
+ if (bot.default_profile && !isProfileName(bot.default_profile)) {
39
+ errors.push(`Bot "${botKey}": default_profile "${bot.default_profile}" is not a valid profile name`);
40
+ }
41
+ }
42
+ // Validate project-level bot and profile references
43
+ for (const [projectName, project] of Object.entries(config.global.projects)) {
44
+ // Check project.bot reference
45
+ if (project.bot && !bots[project.bot]) {
46
+ errors.push(`Project "${projectName}": bot "${project.bot}" is not defined in the bots section`);
47
+ }
48
+ // Check project tool_profile
49
+ if (project.tool_profile && !isProfileName(project.tool_profile)) {
50
+ errors.push(`Project "${projectName}": tool_profile "${project.tool_profile}" is not a valid profile name`);
51
+ }
52
+ // Check channel-level bot overrides
53
+ for (const [alias, channelConfig] of Object.entries(project.channels)) {
54
+ if (typeof channelConfig === "object" && channelConfig.bot) {
55
+ if (!bots[channelConfig.bot]) {
56
+ errors.push(`Project "${projectName}", channel "${alias}": bot "${channelConfig.bot}" is not defined in the bots section`);
57
+ }
58
+ }
59
+ }
60
+ }
61
+ // Report warnings
62
+ for (const warning of warnings) {
63
+ console.warn(` Warning: ${warning}`);
64
+ }
65
+ // Report errors
66
+ if (errors.length > 0) {
67
+ for (const error of errors) {
68
+ console.error(` Error: ${error}`);
69
+ }
70
+ process.exit(1);
71
+ }
24
72
  }
25
73
  catch (err) {
26
74
  console.error("Configuration invalid:", err instanceof Error ? err.message : String(err));
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/cli/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAE5B,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC;QAEhE,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,eAAe,YAAY,EAAE,CAAC,CAAC;QAE3C,IAAI,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;QACrE,CAAC;QAED,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,iCAAiC,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;QACtC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/cli/validate.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,32 @@
1
- import { type GlobalConfig, type PerProjectConfig } from "./schema.js";
1
+ import { type GlobalConfig, type PerProjectConfig, type BotPersona } from "./schema.js";
2
2
  export interface LoadedConfig {
3
3
  global: GlobalConfig;
4
4
  perProject?: PerProjectConfig;
5
5
  defaultToken?: string;
6
6
  }
7
+ /**
8
+ * Resolves the token for a named bot from the bots config section.
9
+ */
10
+ export declare function getTokenForBot(botName: string, config: LoadedConfig): string;
11
+ /**
12
+ * Resolves the token for a specific channel within a project.
13
+ * Checks channel-level bot override first, then falls back to project-level.
14
+ */
15
+ export declare function getTokenForChannel(projectName: string, channelAlias: string, config: LoadedConfig): string;
16
+ /**
17
+ * Returns bot persona metadata for agent context.
18
+ * Checks channel-level bot override first, then project-level bot.
19
+ */
20
+ export declare function getBotPersona(projectName: string, channelAlias?: string, config?: LoadedConfig): (BotPersona & {
21
+ key: string;
22
+ }) | undefined;
7
23
  /**
8
24
  * Resolves the token for a given project.
9
- * If the project has `token_env`, reads that env var.
10
- * Otherwise falls back to the default DISCORD_TOKEN.
25
+ *
26
+ * Token resolution priority:
27
+ * 1. Project-level bot (project.bot → bots[name].token_env)
28
+ * 2. Project-level token_env (existing behavior)
29
+ * 3. Default DISCORD_TOKEN (existing behavior)
11
30
  */
12
31
  export declare function getTokenForProject(projectName: string, config: LoadedConfig): string;
13
32
  /**
@@ -19,5 +38,5 @@ export declare function getTokenForProject(projectName: string, config: LoadedCo
19
38
  * 3. Direct params always work regardless
20
39
  */
21
40
  export declare function loadConfig(): LoadedConfig;
22
- export { type GlobalConfig, type PerProjectConfig } from "./schema.js";
41
+ export { type GlobalConfig, type PerProjectConfig, type BotPersona } from "./schema.js";
23
42
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACtB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAgBpF;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,IAAI,YAAY,CAwCzC;AA4DD,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAU5E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,YAAY,GACnB,MAAM,CAcR;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE,YAAY,GACpB,CAAC,UAAU,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAyB5C;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CA2BpF;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,IAAI,YAAY,CAgDzC;AA4DD,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC"}
@@ -3,13 +3,85 @@ import { resolve, join } from "node:path";
3
3
  import { homedir } from "node:os";
4
4
  import { logger } from "../utils/logger.js";
5
5
  import { GlobalConfigSchema, PerProjectConfigSchema, } from "./schema.js";
6
+ /**
7
+ * Resolves the token for a named bot from the bots config section.
8
+ */
9
+ export function getTokenForBot(botName, config) {
10
+ const bot = config.global.bots?.[botName];
11
+ if (!bot) {
12
+ throw new Error(`Bot "${botName}" not found in bots config`);
13
+ }
14
+ const token = process.env[bot.token_env];
15
+ if (!token) {
16
+ throw new Error(`Bot "${botName}": token_env "${bot.token_env}" is not set in environment`);
17
+ }
18
+ return token;
19
+ }
20
+ /**
21
+ * Resolves the token for a specific channel within a project.
22
+ * Checks channel-level bot override first, then falls back to project-level.
23
+ */
24
+ export function getTokenForChannel(projectName, channelAlias, config) {
25
+ const project = config.global.projects[projectName];
26
+ if (!project) {
27
+ throw new Error(`Project "${projectName}" not found in config`);
28
+ }
29
+ // Check channel-level bot override
30
+ const channelConfig = project.channels[channelAlias];
31
+ if (channelConfig && typeof channelConfig === "object" && channelConfig.bot) {
32
+ return getTokenForBot(channelConfig.bot, config);
33
+ }
34
+ // Fall back to project-level resolution
35
+ return getTokenForProject(projectName, config);
36
+ }
37
+ /**
38
+ * Returns bot persona metadata for agent context.
39
+ * Checks channel-level bot override first, then project-level bot.
40
+ */
41
+ export function getBotPersona(projectName, channelAlias, config) {
42
+ if (!config)
43
+ return undefined;
44
+ const project = config.global.projects[projectName];
45
+ if (!project)
46
+ return undefined;
47
+ const bots = config.global.bots;
48
+ if (!bots)
49
+ return undefined;
50
+ // Check channel-level bot override
51
+ if (channelAlias) {
52
+ const channelConfig = project.channels[channelAlias];
53
+ if (channelConfig && typeof channelConfig === "object" && channelConfig.bot) {
54
+ const bot = bots[channelConfig.bot];
55
+ if (bot)
56
+ return { ...bot, key: channelConfig.bot };
57
+ }
58
+ }
59
+ // Check project-level bot
60
+ if (project.bot) {
61
+ const bot = bots[project.bot];
62
+ if (bot)
63
+ return { ...bot, key: project.bot };
64
+ }
65
+ return undefined;
66
+ }
6
67
  /**
7
68
  * Resolves the token for a given project.
8
- * If the project has `token_env`, reads that env var.
9
- * Otherwise falls back to the default DISCORD_TOKEN.
69
+ *
70
+ * Token resolution priority:
71
+ * 1. Project-level bot (project.bot → bots[name].token_env)
72
+ * 2. Project-level token_env (existing behavior)
73
+ * 3. Default DISCORD_TOKEN (existing behavior)
10
74
  */
11
75
  export function getTokenForProject(projectName, config) {
12
76
  const project = config.global.projects[projectName];
77
+ // Check project-level bot reference first
78
+ if (project?.bot && config.global.bots?.[project.bot]) {
79
+ const bot = config.global.bots[project.bot];
80
+ const token = process.env[bot.token_env];
81
+ if (token)
82
+ return token;
83
+ logger.warn(`Bot "${project.bot}" token_env "${bot.token_env}" for project "${projectName}" is not set, falling back`);
84
+ }
13
85
  if (project?.token_env) {
14
86
  const token = process.env[project.token_env];
15
87
  if (token)
@@ -42,14 +114,22 @@ export function loadConfig() {
42
114
  const defaultToken = process.env[tokenEnvName] || undefined;
43
115
  const global = loadGlobalConfig();
44
116
  const perProject = loadPerProjectConfig();
45
- // Option B: if no default token, validate that all projects have their own token_env
117
+ // Option B: if no default token, validate that all projects have their own token_env or bot reference
46
118
  if (!defaultToken) {
47
119
  const projectEntries = Object.entries(global.projects);
48
120
  if (projectEntries.length === 0) {
49
121
  throw new Error(`${tokenEnvName} environment variable is required (no projects with token_env configured)`);
50
122
  }
51
123
  const missing = projectEntries
52
- .filter(([, p]) => !p.token_env || !process.env[p.token_env])
124
+ .filter(([, p]) => {
125
+ // Project has a bot reference — check if the bot's token_env is set
126
+ if (p.bot && global.bots?.[p.bot]) {
127
+ const bot = global.bots[p.bot];
128
+ return !process.env[bot.token_env];
129
+ }
130
+ // Fallback to direct token_env check
131
+ return !p.token_env || !process.env[p.token_env];
132
+ })
53
133
  .map(([name]) => name);
54
134
  if (missing.length > 0) {
55
135
  throw new Error(`${tokenEnvName} is not set, and these projects lack a valid token_env: ${missing.join(", ")}`);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"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"}
@@ -3,12 +3,19 @@ export interface ResolvedProject {
3
3
  name: string;
4
4
  guildId: string;
5
5
  channels: Record<string, string>;
6
+ channelBots: Record<string, string>;
6
7
  defaultChannel?: string;
7
8
  notificationRouting?: Record<string, string>;
9
+ bot?: string;
10
+ toolProfile?: string;
11
+ toolProfileAdd?: string[];
12
+ toolProfileRemove?: string[];
8
13
  }
9
14
  /**
10
15
  * Resolves a project from global + per-project config.
11
16
  * Per-project notification_routing overrides global.
17
+ * Normalizes ChannelConfigSchema union values — extracts plain IDs into `channels`
18
+ * and collects bot overrides into `channelBots`.
12
19
  */
13
20
  export declare function resolveProject(projectName: string, globalConfig: GlobalConfig, perProjectConfig?: PerProjectConfig): ResolvedProject | undefined;
14
21
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../src/config/profiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAElE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9C;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,YAAY,EAC1B,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,eAAe,GAAG,SAAS,CAiB7B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,YAAY,EAC1B,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,MAAM,GAAG,SAAS,CAEpB"}
1
+ {"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../src/config/profiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAElE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AASD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,YAAY,EAC1B,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,eAAe,GAAG,SAAS,CAiC7B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,YAAY,EAC1B,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,MAAM,GAAG,SAAS,CAEpB"}
@@ -1,6 +1,14 @@
1
+ /**
2
+ * Normalizes a channel config value (string or object) to a plain channel ID.
3
+ */
4
+ function normalizeChannelId(value) {
5
+ return typeof value === "string" ? value : value.id;
6
+ }
1
7
  /**
2
8
  * Resolves a project from global + per-project config.
3
9
  * Per-project notification_routing overrides global.
10
+ * Normalizes ChannelConfigSchema union values — extracts plain IDs into `channels`
11
+ * and collects bot overrides into `channelBots`.
4
12
  */
5
13
  export function resolveProject(projectName, globalConfig, perProjectConfig) {
6
14
  const project = globalConfig.projects[projectName];
@@ -11,12 +19,26 @@ export function resolveProject(projectName, globalConfig, perProjectConfig) {
11
19
  ...globalConfig.notification_routing,
12
20
  ...perProjectConfig?.notification_routing,
13
21
  };
22
+ // Normalize channels: extract plain IDs and collect bot overrides
23
+ const channels = {};
24
+ const channelBots = {};
25
+ for (const [alias, value] of Object.entries(project.channels)) {
26
+ channels[alias] = normalizeChannelId(value);
27
+ if (typeof value === "object" && value.bot) {
28
+ channelBots[alias] = value.bot;
29
+ }
30
+ }
14
31
  return {
15
32
  name: projectName,
16
33
  guildId: project.guild_id,
17
- channels: project.channels,
34
+ channels,
35
+ channelBots,
18
36
  defaultChannel: project.default_channel,
19
37
  notificationRouting,
38
+ bot: project.bot,
39
+ toolProfile: project.tool_profile,
40
+ toolProfileAdd: project.profile_add,
41
+ toolProfileRemove: project.profile_remove,
20
42
  };
21
43
  }
22
44
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/config/profiles.ts"],"names":[],"mappings":"AAUA;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,WAAmB,EACnB,YAA0B,EAC1B,gBAAmC;IAEnC,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACnD,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAE/B,2DAA2D;IAC3D,MAAM,mBAAmB,GAAG;QAC1B,GAAG,YAAY,CAAC,oBAAoB;QACpC,GAAG,gBAAgB,EAAE,oBAAoB;KAC1C,CAAC;IAEF,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,OAAO,CAAC,QAAQ;QACzB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,cAAc,EAAE,OAAO,CAAC,eAAe;QACvC,mBAAmB;KACpB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,YAA0B,EAC1B,gBAAmC;IAEnC,OAAO,gBAAgB,EAAE,OAAO,IAAI,YAAY,CAAC,eAAe,CAAC;AACnE,CAAC"}
1
+ {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/config/profiles.ts"],"names":[],"mappings":"AAeA;;GAEG;AACH,SAAS,kBAAkB,CAAC,KAA4C;IACtE,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;AACtD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,WAAmB,EACnB,YAA0B,EAC1B,gBAAmC;IAEnC,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACnD,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAE/B,2DAA2D;IAC3D,MAAM,mBAAmB,GAAG;QAC1B,GAAG,YAAY,CAAC,oBAAoB;QACpC,GAAG,gBAAgB,EAAE,oBAAoB;KAC1C,CAAC;IAEF,kEAAkE;IAClE,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAC5C,MAAM,WAAW,GAA2B,EAAE,CAAC;IAE/C,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9D,QAAQ,CAAC,KAAK,CAAC,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;YAC3C,WAAW,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC;QACjC,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,OAAO,CAAC,QAAQ;QACzB,QAAQ;QACR,WAAW;QACX,cAAc,EAAE,OAAO,CAAC,eAAe;QACvC,mBAAmB;QACnB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,WAAW,EAAE,OAAO,CAAC,YAAY;QACjC,cAAc,EAAE,OAAO,CAAC,WAAW;QACnC,iBAAiB,EAAE,OAAO,CAAC,cAAc;KAC1C,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,YAA0B,EAC1B,gBAAmC;IAEnC,OAAO,gBAAgB,EAAE,OAAO,IAAI,YAAY,CAAC,eAAe,CAAC;AACnE,CAAC"}