@lobu/gateway 3.0.9 → 3.0.13

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 (212) hide show
  1. package/dist/api/platform.d.ts.map +1 -1
  2. package/dist/api/platform.js +7 -26
  3. package/dist/api/platform.js.map +1 -1
  4. package/dist/auth/mcp/proxy.d.ts +14 -0
  5. package/dist/auth/mcp/proxy.d.ts.map +1 -1
  6. package/dist/auth/mcp/proxy.js +149 -13
  7. package/dist/auth/mcp/proxy.js.map +1 -1
  8. package/dist/cli/gateway.d.ts.map +1 -1
  9. package/dist/cli/gateway.js +29 -0
  10. package/dist/cli/gateway.js.map +1 -1
  11. package/dist/connections/chat-instance-manager.d.ts.map +1 -1
  12. package/dist/connections/chat-instance-manager.js +2 -1
  13. package/dist/connections/chat-instance-manager.js.map +1 -1
  14. package/dist/connections/interaction-bridge.d.ts +9 -2
  15. package/dist/connections/interaction-bridge.d.ts.map +1 -1
  16. package/dist/connections/interaction-bridge.js +121 -261
  17. package/dist/connections/interaction-bridge.js.map +1 -1
  18. package/dist/gateway/index.js +1 -1
  19. package/dist/gateway/index.js.map +1 -1
  20. package/dist/interactions.d.ts +9 -43
  21. package/dist/interactions.d.ts.map +1 -1
  22. package/dist/interactions.js +10 -52
  23. package/dist/interactions.js.map +1 -1
  24. package/dist/routes/public/agent.d.ts +4 -0
  25. package/dist/routes/public/agent.d.ts.map +1 -1
  26. package/dist/routes/public/agent.js +21 -0
  27. package/dist/routes/public/agent.js.map +1 -1
  28. package/dist/services/core-services.d.ts.map +1 -1
  29. package/dist/services/core-services.js +4 -0
  30. package/dist/services/core-services.js.map +1 -1
  31. package/package.json +9 -9
  32. package/src/__tests__/agent-config-routes.test.ts +0 -254
  33. package/src/__tests__/agent-history-routes.test.ts +0 -72
  34. package/src/__tests__/agent-routes.test.ts +0 -68
  35. package/src/__tests__/agent-schedules-routes.test.ts +0 -59
  36. package/src/__tests__/agent-settings-store.test.ts +0 -323
  37. package/src/__tests__/bedrock-model-catalog.test.ts +0 -40
  38. package/src/__tests__/bedrock-openai-service.test.ts +0 -157
  39. package/src/__tests__/bedrock-provider-module.test.ts +0 -56
  40. package/src/__tests__/chat-instance-manager-slack.test.ts +0 -204
  41. package/src/__tests__/chat-response-bridge.test.ts +0 -131
  42. package/src/__tests__/config-memory-plugins.test.ts +0 -92
  43. package/src/__tests__/config-request-store.test.ts +0 -127
  44. package/src/__tests__/connection-routes.test.ts +0 -144
  45. package/src/__tests__/core-services-store-selection.test.ts +0 -92
  46. package/src/__tests__/docker-deployment.test.ts +0 -1211
  47. package/src/__tests__/embedded-deployment.test.ts +0 -342
  48. package/src/__tests__/grant-store.test.ts +0 -148
  49. package/src/__tests__/http-proxy.test.ts +0 -281
  50. package/src/__tests__/instruction-service.test.ts +0 -37
  51. package/src/__tests__/link-buttons.test.ts +0 -112
  52. package/src/__tests__/lobu.test.ts +0 -32
  53. package/src/__tests__/mcp-config-service.test.ts +0 -347
  54. package/src/__tests__/mcp-proxy.test.ts +0 -694
  55. package/src/__tests__/message-handler-bridge.test.ts +0 -17
  56. package/src/__tests__/model-selection.test.ts +0 -172
  57. package/src/__tests__/oauth-templates.test.ts +0 -39
  58. package/src/__tests__/platform-adapter-slack-send.test.ts +0 -114
  59. package/src/__tests__/platform-helpers-model-resolution.test.ts +0 -253
  60. package/src/__tests__/provider-inheritance.test.ts +0 -212
  61. package/src/__tests__/routes/cli-auth.test.ts +0 -337
  62. package/src/__tests__/routes/interactions.test.ts +0 -121
  63. package/src/__tests__/secret-proxy.test.ts +0 -85
  64. package/src/__tests__/session-manager.test.ts +0 -572
  65. package/src/__tests__/setup.ts +0 -133
  66. package/src/__tests__/skill-and-mcp-registry.test.ts +0 -203
  67. package/src/__tests__/slack-routes.test.ts +0 -161
  68. package/src/__tests__/system-config-resolver.test.ts +0 -75
  69. package/src/__tests__/system-message-limiter.test.ts +0 -89
  70. package/src/__tests__/system-skills-service.test.ts +0 -362
  71. package/src/__tests__/transcription-service.test.ts +0 -222
  72. package/src/__tests__/utils/rate-limiter.test.ts +0 -102
  73. package/src/__tests__/worker-connection-manager.test.ts +0 -497
  74. package/src/__tests__/worker-job-router.test.ts +0 -722
  75. package/src/api/index.ts +0 -1
  76. package/src/api/platform.ts +0 -292
  77. package/src/api/response-renderer.ts +0 -157
  78. package/src/auth/agent-metadata-store.ts +0 -168
  79. package/src/auth/api-auth-middleware.ts +0 -69
  80. package/src/auth/api-key-provider-module.ts +0 -213
  81. package/src/auth/base-provider-module.ts +0 -201
  82. package/src/auth/bedrock/provider-module.ts +0 -110
  83. package/src/auth/chatgpt/chatgpt-oauth-module.ts +0 -185
  84. package/src/auth/chatgpt/device-code-client.ts +0 -218
  85. package/src/auth/chatgpt/index.ts +0 -1
  86. package/src/auth/claude/oauth-module.ts +0 -280
  87. package/src/auth/cli/token-service.ts +0 -249
  88. package/src/auth/external/client.ts +0 -560
  89. package/src/auth/external/device-code-client.ts +0 -235
  90. package/src/auth/mcp/config-service.ts +0 -420
  91. package/src/auth/mcp/proxy.ts +0 -1086
  92. package/src/auth/mcp/string-substitution.ts +0 -17
  93. package/src/auth/mcp/tool-cache.ts +0 -90
  94. package/src/auth/oauth/base-client.ts +0 -267
  95. package/src/auth/oauth/client.ts +0 -153
  96. package/src/auth/oauth/credentials.ts +0 -7
  97. package/src/auth/oauth/providers.ts +0 -69
  98. package/src/auth/oauth/state-store.ts +0 -150
  99. package/src/auth/oauth-templates.ts +0 -179
  100. package/src/auth/provider-catalog.ts +0 -220
  101. package/src/auth/provider-model-options.ts +0 -41
  102. package/src/auth/settings/agent-settings-store.ts +0 -565
  103. package/src/auth/settings/auth-profiles-manager.ts +0 -216
  104. package/src/auth/settings/index.ts +0 -12
  105. package/src/auth/settings/model-preference-store.ts +0 -52
  106. package/src/auth/settings/model-selection.ts +0 -135
  107. package/src/auth/settings/resolved-settings-view.ts +0 -298
  108. package/src/auth/settings/template-utils.ts +0 -44
  109. package/src/auth/settings/token-service.ts +0 -88
  110. package/src/auth/system-env-store.ts +0 -98
  111. package/src/auth/user-agents-store.ts +0 -68
  112. package/src/channels/binding-service.ts +0 -214
  113. package/src/channels/index.ts +0 -4
  114. package/src/cli/gateway.ts +0 -1312
  115. package/src/cli/index.ts +0 -74
  116. package/src/commands/built-in-commands.ts +0 -80
  117. package/src/commands/command-dispatcher.ts +0 -94
  118. package/src/commands/command-reply-adapters.ts +0 -27
  119. package/src/config/file-loader.ts +0 -618
  120. package/src/config/index.ts +0 -588
  121. package/src/config/network-allowlist.ts +0 -71
  122. package/src/connections/chat-instance-manager.ts +0 -1284
  123. package/src/connections/chat-response-bridge.ts +0 -618
  124. package/src/connections/index.ts +0 -7
  125. package/src/connections/interaction-bridge.ts +0 -831
  126. package/src/connections/message-handler-bridge.ts +0 -440
  127. package/src/connections/platform-auth-methods.ts +0 -15
  128. package/src/connections/types.ts +0 -84
  129. package/src/gateway/connection-manager.ts +0 -291
  130. package/src/gateway/index.ts +0 -698
  131. package/src/gateway/job-router.ts +0 -201
  132. package/src/gateway-main.ts +0 -200
  133. package/src/index.ts +0 -41
  134. package/src/infrastructure/queue/index.ts +0 -12
  135. package/src/infrastructure/queue/queue-producer.ts +0 -148
  136. package/src/infrastructure/queue/redis-queue.ts +0 -361
  137. package/src/infrastructure/queue/types.ts +0 -133
  138. package/src/infrastructure/redis/system-message-limiter.ts +0 -94
  139. package/src/interactions/config-request-store.ts +0 -198
  140. package/src/interactions.ts +0 -363
  141. package/src/lobu.ts +0 -311
  142. package/src/metrics/prometheus.ts +0 -159
  143. package/src/modules/module-system.ts +0 -179
  144. package/src/orchestration/base-deployment-manager.ts +0 -900
  145. package/src/orchestration/deployment-utils.ts +0 -98
  146. package/src/orchestration/impl/docker-deployment.ts +0 -620
  147. package/src/orchestration/impl/embedded-deployment.ts +0 -268
  148. package/src/orchestration/impl/index.ts +0 -8
  149. package/src/orchestration/impl/k8s/deployment.ts +0 -1061
  150. package/src/orchestration/impl/k8s/helpers.ts +0 -610
  151. package/src/orchestration/impl/k8s/index.ts +0 -1
  152. package/src/orchestration/index.ts +0 -333
  153. package/src/orchestration/message-consumer.ts +0 -584
  154. package/src/orchestration/scheduled-wakeup.ts +0 -704
  155. package/src/permissions/approval-policy.ts +0 -36
  156. package/src/permissions/grant-store.ts +0 -219
  157. package/src/platform/file-handler.ts +0 -66
  158. package/src/platform/link-buttons.ts +0 -57
  159. package/src/platform/renderer-utils.ts +0 -44
  160. package/src/platform/response-renderer.ts +0 -84
  161. package/src/platform/unified-thread-consumer.ts +0 -194
  162. package/src/platform.ts +0 -318
  163. package/src/proxy/http-proxy.ts +0 -752
  164. package/src/proxy/proxy-manager.ts +0 -81
  165. package/src/proxy/secret-proxy.ts +0 -402
  166. package/src/proxy/token-refresh-job.ts +0 -143
  167. package/src/routes/internal/audio.ts +0 -141
  168. package/src/routes/internal/device-auth.ts +0 -652
  169. package/src/routes/internal/files.ts +0 -226
  170. package/src/routes/internal/history.ts +0 -69
  171. package/src/routes/internal/images.ts +0 -127
  172. package/src/routes/internal/interactions.ts +0 -84
  173. package/src/routes/internal/middleware.ts +0 -23
  174. package/src/routes/internal/schedule.ts +0 -226
  175. package/src/routes/internal/types.ts +0 -22
  176. package/src/routes/openapi-auto.ts +0 -239
  177. package/src/routes/public/agent-access.ts +0 -23
  178. package/src/routes/public/agent-config.ts +0 -675
  179. package/src/routes/public/agent-history.ts +0 -422
  180. package/src/routes/public/agent-schedules.ts +0 -296
  181. package/src/routes/public/agent.ts +0 -1086
  182. package/src/routes/public/agents.ts +0 -373
  183. package/src/routes/public/channels.ts +0 -191
  184. package/src/routes/public/cli-auth.ts +0 -896
  185. package/src/routes/public/connections.ts +0 -574
  186. package/src/routes/public/landing.ts +0 -16
  187. package/src/routes/public/oauth.ts +0 -147
  188. package/src/routes/public/settings-auth.ts +0 -104
  189. package/src/routes/public/slack.ts +0 -173
  190. package/src/routes/shared/agent-ownership.ts +0 -101
  191. package/src/routes/shared/token-verifier.ts +0 -34
  192. package/src/services/bedrock-model-catalog.ts +0 -217
  193. package/src/services/bedrock-openai-service.ts +0 -658
  194. package/src/services/core-services.ts +0 -1072
  195. package/src/services/image-generation-service.ts +0 -257
  196. package/src/services/instruction-service.ts +0 -318
  197. package/src/services/mcp-registry.ts +0 -94
  198. package/src/services/platform-helpers.ts +0 -287
  199. package/src/services/session-manager.ts +0 -262
  200. package/src/services/settings-resolver.ts +0 -74
  201. package/src/services/system-config-resolver.ts +0 -89
  202. package/src/services/system-skills-service.ts +0 -229
  203. package/src/services/transcription-service.ts +0 -684
  204. package/src/session.ts +0 -110
  205. package/src/spaces/index.ts +0 -1
  206. package/src/spaces/space-resolver.ts +0 -17
  207. package/src/stores/in-memory-agent-store.ts +0 -403
  208. package/src/stores/redis-agent-store.ts +0 -279
  209. package/src/utils/public-url.ts +0 -44
  210. package/src/utils/rate-limiter.ts +0 -94
  211. package/tsconfig.json +0 -33
  212. package/tsconfig.tsbuildinfo +0 -1
@@ -1,44 +0,0 @@
1
- import type { AgentSettings } from "./index";
2
-
3
- function cloneSettingValue<T>(value: T): T {
4
- return JSON.parse(JSON.stringify(value)) as T;
5
- }
6
-
7
- export function buildDefaultSettingsFromSource(
8
- source: AgentSettings | null
9
- ): Omit<AgentSettings, "updatedAt"> {
10
- if (!source) return {};
11
-
12
- const defaults: Omit<AgentSettings, "updatedAt"> = {};
13
-
14
- if (source.model !== undefined) defaults.model = source.model;
15
- if (source.modelSelection)
16
- defaults.modelSelection = cloneSettingValue(source.modelSelection);
17
- if (source.providerModelPreferences)
18
- defaults.providerModelPreferences = cloneSettingValue(
19
- source.providerModelPreferences
20
- );
21
- if (source.networkConfig)
22
- defaults.networkConfig = cloneSettingValue(source.networkConfig);
23
- if (source.nixConfig)
24
- defaults.nixConfig = cloneSettingValue(source.nixConfig);
25
- if (source.mcpServers)
26
- defaults.mcpServers = cloneSettingValue(source.mcpServers);
27
- if (source.soulMd !== undefined) defaults.soulMd = source.soulMd;
28
- if (source.userMd !== undefined) defaults.userMd = source.userMd;
29
- if (source.identityMd !== undefined) defaults.identityMd = source.identityMd;
30
- if (source.skillsConfig)
31
- defaults.skillsConfig = cloneSettingValue(source.skillsConfig);
32
- if (source.toolsConfig)
33
- defaults.toolsConfig = cloneSettingValue(source.toolsConfig);
34
- if (source.pluginsConfig)
35
- defaults.pluginsConfig = cloneSettingValue(source.pluginsConfig);
36
- if (source.installedProviders) {
37
- defaults.installedProviders = cloneSettingValue(source.installedProviders);
38
- }
39
- if (source.verboseLogging !== undefined) {
40
- defaults.verboseLogging = source.verboseLogging;
41
- }
42
-
43
- return defaults;
44
- }
@@ -1,88 +0,0 @@
1
- /**
2
- * Pre-filled skill configuration for an agent config session
3
- */
4
- export interface PrefillSkill {
5
- /** Skill repository (e.g., "anthropics/skills/pdf") */
6
- repo: string;
7
- /** Display name */
8
- name?: string;
9
- /** Description */
10
- description?: string;
11
- }
12
-
13
- /**
14
- * Pre-filled MCP server configuration for an agent config session
15
- */
16
- export interface PrefillMcpServer {
17
- /** MCP server ID (key in mcpServers record) */
18
- id: string;
19
- /** Display name/description */
20
- name?: string;
21
- /** Server URL (for SSE type) */
22
- url?: string;
23
- /** Server type */
24
- type?: "sse" | "stdio";
25
- /** Command (for stdio type) */
26
- command?: string;
27
- /** Args (for stdio type) */
28
- args?: string[];
29
- /** Environment variables needed (just the keys, user fills values) */
30
- envVars?: string[];
31
- }
32
-
33
- /**
34
- * Source message context where settings link was requested.
35
- * Used to send follow-up notifications back to the same conversation.
36
- */
37
- export interface SettingsSourceContext {
38
- conversationId: string;
39
- channelId: string;
40
- teamId?: string;
41
- platform?: string;
42
- }
43
-
44
- /**
45
- * Unified session payload for config/auth sessions.
46
- *
47
- * OAuth sessions populate email/name/oauthUserId. Claimed chat sessions
48
- * populate platform/channelId/userId directly.
49
- */
50
- export interface SettingsTokenPayload {
51
- /** Agent to configure. Optional when using channel-based entry (user picks agent on page). */
52
- agentId?: string;
53
- userId: string;
54
- platform: string;
55
- exp: number; // Expiration timestamp (ms)
56
- /** Channel that triggered the settings link. Used for agent switching and binding. */
57
- channelId?: string;
58
- /** Team/workspace ID for multi-tenant platforms (Slack). */
59
- teamId?: string;
60
- /** OAuth user email (set for OAuth sessions). */
61
- email?: string;
62
- /** OAuth user display name (set for OAuth sessions). */
63
- name?: string;
64
- /** OAuth provider user ID (set for OAuth sessions). */
65
- oauthUserId?: string;
66
- /** Optional message to display during config/auth flows */
67
- message?: string;
68
- /** Optional provider IDs to associate with the flow */
69
- prefillProviders?: string[];
70
- /** Optional skills to pre-fill (user confirms to enable) */
71
- prefillSkills?: PrefillSkill[];
72
- /** Optional MCP servers to pre-fill (user confirms to enable) */
73
- prefillMcpServers?: PrefillMcpServer[];
74
- /** Optional Nix packages to pre-fill */
75
- prefillNixPackages?: string[];
76
- /** Optional domain patterns to pre-fill as grants */
77
- prefillGrants?: string[];
78
- /** Optional source context for post-install notifications */
79
- sourceContext?: SettingsSourceContext;
80
- /** Settings mode: "admin" has full access, "user" is restricted by allowedScopes */
81
- settingsMode?: "admin" | "user";
82
- /** Scopes the user is allowed to configure (only relevant when settingsMode is "user") */
83
- allowedScopes?: string[];
84
- /** Connection ID that triggered this session */
85
- connectionId?: string;
86
- /** Whether this session has admin access */
87
- isAdmin?: boolean;
88
- }
@@ -1,98 +0,0 @@
1
- import { createLogger, decrypt, encrypt } from "@lobu/core";
2
- import type Redis from "ioredis";
3
-
4
- const logger = createLogger("system-env-store");
5
- const KEY_PREFIX = "system:env:";
6
-
7
- /**
8
- * Redis-backed store for system environment variables.
9
- * Maintains an in-memory cache for synchronous resolution
10
- * (required by the string-substitution system).
11
- */
12
- export class SystemEnvStore {
13
- private redis: Redis;
14
- private cache: Map<string, string> = new Map();
15
-
16
- constructor(redis: Redis) {
17
- this.redis = redis;
18
- }
19
-
20
- async get(key: string): Promise<string | null> {
21
- try {
22
- const raw = await this.redis.get(`${KEY_PREFIX}${key}`);
23
- if (raw === null) return null;
24
- return decrypt(raw);
25
- } catch (error) {
26
- logger.error("Failed to get env var", { key, error });
27
- return null;
28
- }
29
- }
30
-
31
- async set(key: string, value: string): Promise<void> {
32
- try {
33
- await this.redis.set(`${KEY_PREFIX}${key}`, encrypt(value));
34
- this.cache.set(key, value);
35
- logger.info(`Set system env var: ${key}`);
36
- } catch (error) {
37
- logger.error("Failed to set env var", { key, error });
38
- throw error;
39
- }
40
- }
41
-
42
- async delete(key: string): Promise<void> {
43
- try {
44
- await this.redis.del(`${KEY_PREFIX}${key}`);
45
- this.cache.delete(key);
46
- logger.info(`Deleted system env var: ${key}`);
47
- } catch (error) {
48
- logger.error("Failed to delete env var", { key, error });
49
- }
50
- }
51
-
52
- async listAll(): Promise<Record<string, string>> {
53
- const result: Record<string, string> = {};
54
- let cursor = "0";
55
- try {
56
- do {
57
- const [nextCursor, keys] = await this.redis.scan(
58
- cursor,
59
- "MATCH",
60
- `${KEY_PREFIX}*`,
61
- "COUNT",
62
- 100
63
- );
64
- cursor = nextCursor;
65
- for (const key of keys) {
66
- const raw = await this.redis.get(key);
67
- if (raw !== null) {
68
- result[key.slice(KEY_PREFIX.length)] = decrypt(raw);
69
- }
70
- }
71
- } while (cursor !== "0");
72
- } catch (error) {
73
- logger.error("Failed to list env vars", { error });
74
- }
75
- return result;
76
- }
77
-
78
- /**
79
- * Synchronous lookup: cache first, then process.env.
80
- * Used by the string-substitution env resolver.
81
- */
82
- resolve(key: string): string | undefined {
83
- return this.cache.get(key) ?? process.env[key] ?? undefined;
84
- }
85
-
86
- /**
87
- * Load all system:env:* keys from Redis into the in-memory cache.
88
- * Call on startup before registering the resolver.
89
- */
90
- async refreshCache(): Promise<void> {
91
- const all = await this.listAll();
92
- this.cache.clear();
93
- for (const [key, value] of Object.entries(all)) {
94
- this.cache.set(key, value);
95
- }
96
- logger.debug(`Loaded ${this.cache.size} system env vars into cache`);
97
- }
98
- }
@@ -1,68 +0,0 @@
1
- import { createLogger } from "@lobu/core";
2
- import type Redis from "ioredis";
3
-
4
- const logger = createLogger("user-agents-store");
5
-
6
- /**
7
- * Track which agents belong to which users.
8
- * Uses Redis sets for fast membership checks and listing.
9
- *
10
- * Storage pattern:
11
- * - user_agents:{platform}:{userId} -> Set of agentIds
12
- */
13
- export class UserAgentsStore {
14
- private readonly KEY_PREFIX = "user_agents";
15
-
16
- constructor(private redis: Redis) {}
17
-
18
- private buildKey(platform: string, userId: string): string {
19
- return `${this.KEY_PREFIX}:${platform}:${userId}`;
20
- }
21
-
22
- /**
23
- * Add an agent to a user's list
24
- */
25
- async addAgent(
26
- platform: string,
27
- userId: string,
28
- agentId: string
29
- ): Promise<void> {
30
- const key = this.buildKey(platform, userId);
31
- await this.redis.sadd(key, agentId);
32
- logger.info(`Added agent ${agentId} to user ${platform}/${userId}`);
33
- }
34
-
35
- /**
36
- * Remove an agent from a user's list
37
- */
38
- async removeAgent(
39
- platform: string,
40
- userId: string,
41
- agentId: string
42
- ): Promise<void> {
43
- const key = this.buildKey(platform, userId);
44
- await this.redis.srem(key, agentId);
45
- logger.info(`Removed agent ${agentId} from user ${platform}/${userId}`);
46
- }
47
-
48
- /**
49
- * List all agents owned by a user
50
- */
51
- async listAgents(platform: string, userId: string): Promise<string[]> {
52
- const key = this.buildKey(platform, userId);
53
- return this.redis.smembers(key);
54
- }
55
-
56
- /**
57
- * Check if a user owns a specific agent
58
- */
59
- async ownsAgent(
60
- platform: string,
61
- userId: string,
62
- agentId: string
63
- ): Promise<boolean> {
64
- const key = this.buildKey(platform, userId);
65
- const result = await this.redis.sismember(key, agentId);
66
- return result === 1;
67
- }
68
- }
@@ -1,214 +0,0 @@
1
- import { BaseRedisStore, createLogger } from "@lobu/core";
2
- import type Redis from "ioredis";
3
-
4
- const logger = createLogger("channel-binding-service");
5
-
6
- /**
7
- * Channel binding - links a platform channel to a specific agent
8
- */
9
- export interface ChannelBinding {
10
- platform: string; // Platform identifier
11
- channelId: string;
12
- agentId: string;
13
- teamId?: string; // Optional workspace/team ID for multi-tenant platforms
14
- configuredBy?: string; // userId of who configured this binding
15
- configuredAt?: number; // When the binding was configured
16
- wasAdmin?: boolean; // Whether the configurer was an admin at time of configuration
17
- createdAt: number;
18
- }
19
-
20
- /**
21
- * Internal storage format includes reverse lookup info
22
- */
23
- interface StoredBinding extends ChannelBinding {
24
- // Stored at channel_binding:{platform}:{channelId} or channel_binding:{platform}:{teamId}:{channelId}
25
- }
26
-
27
- /**
28
- * Service for managing channel-to-agent bindings
29
- *
30
- * Storage patterns:
31
- * - Forward lookup: channel_binding:{platform}:{channelId} → binding data
32
- * - Forward lookup (Slack): channel_binding:{platform}:{teamId}:{channelId} → binding data
33
- * - Reverse index: channel_binding_index:{agentId} → Set of binding keys
34
- */
35
- export class ChannelBindingService extends BaseRedisStore<StoredBinding> {
36
- private readonly INDEX_PREFIX = "channel_binding_index";
37
-
38
- constructor(redis: Redis) {
39
- super({
40
- redis,
41
- keyPrefix: "channel_binding",
42
- loggerName: "channel-binding-service",
43
- });
44
- }
45
-
46
- /**
47
- * Build the binding key for a channel
48
- * Includes teamId for multi-tenant platforms (e.g., Slack workspaces)
49
- */
50
- private buildBindingKey(
51
- platform: string,
52
- channelId: string,
53
- teamId?: string
54
- ): string {
55
- if (teamId) {
56
- return this.buildKey(platform, teamId, channelId);
57
- }
58
- return this.buildKey(platform, channelId);
59
- }
60
-
61
- /**
62
- * Build the index key for an agent's bindings
63
- */
64
- private buildIndexKey(agentId: string): string {
65
- return `${this.INDEX_PREFIX}:${agentId}`;
66
- }
67
-
68
- /**
69
- * Get binding for a channel
70
- * Returns null if channel is not bound to any agent
71
- */
72
- async getBinding(
73
- platform: string,
74
- channelId: string,
75
- teamId?: string
76
- ): Promise<ChannelBinding | null> {
77
- const key = this.buildBindingKey(platform, channelId, teamId);
78
- const binding = await this.get(key);
79
- if (binding) {
80
- logger.debug(
81
- `Found binding for ${platform}/${channelId}: ${binding.agentId}`
82
- );
83
- }
84
- return binding;
85
- }
86
-
87
- /**
88
- * Create a binding from a channel to an agent
89
- * If the channel was already bound, the old binding is removed
90
- */
91
- async createBinding(
92
- agentId: string,
93
- platform: string,
94
- channelId: string,
95
- teamId?: string,
96
- options?: { configuredBy?: string; wasAdmin?: boolean }
97
- ): Promise<void> {
98
- const key = this.buildBindingKey(platform, channelId, teamId);
99
-
100
- // Check if already bound to a different agent
101
- const existing = await this.get(key);
102
- if (existing && existing.agentId !== agentId) {
103
- // Remove from old agent's index
104
- const oldIndexKey = this.buildIndexKey(existing.agentId);
105
- await this.redis.srem(oldIndexKey, key);
106
- logger.info(
107
- `Removed binding from agent ${existing.agentId} for ${platform}/${channelId}`
108
- );
109
- }
110
-
111
- // Create the binding
112
- const binding: StoredBinding = {
113
- platform,
114
- channelId,
115
- agentId,
116
- teamId,
117
- configuredBy: options?.configuredBy,
118
- configuredAt: Date.now(),
119
- wasAdmin: options?.wasAdmin,
120
- createdAt: Date.now(),
121
- };
122
- await this.set(key, binding);
123
-
124
- // Add to agent's index
125
- const indexKey = this.buildIndexKey(agentId);
126
- await this.redis.sadd(indexKey, key);
127
-
128
- logger.info(`Created binding: ${platform}/${channelId} → ${agentId}`);
129
- }
130
-
131
- /**
132
- * Delete a binding for a channel
133
- */
134
- async deleteBinding(
135
- agentId: string,
136
- platform: string,
137
- channelId: string,
138
- teamId?: string
139
- ): Promise<boolean> {
140
- const key = this.buildBindingKey(platform, channelId, teamId);
141
- const existing = await this.get(key);
142
-
143
- if (!existing) {
144
- logger.warn(`No binding found for ${platform}/${channelId}`);
145
- return false;
146
- }
147
-
148
- if (existing.agentId !== agentId) {
149
- logger.warn(
150
- `Binding for ${platform}/${channelId} belongs to ${existing.agentId}, not ${agentId}`
151
- );
152
- return false;
153
- }
154
-
155
- // Delete the binding
156
- await this.delete(key);
157
-
158
- // Remove from agent's index
159
- const indexKey = this.buildIndexKey(agentId);
160
- await this.redis.srem(indexKey, key);
161
-
162
- logger.info(`Deleted binding: ${platform}/${channelId} from ${agentId}`);
163
- return true;
164
- }
165
-
166
- /**
167
- * List all bindings for an agent
168
- */
169
- async listBindings(agentId: string): Promise<ChannelBinding[]> {
170
- const indexKey = this.buildIndexKey(agentId);
171
- const bindingKeys = await this.redis.smembers(indexKey);
172
-
173
- if (bindingKeys.length === 0) {
174
- return [];
175
- }
176
-
177
- const bindings: ChannelBinding[] = [];
178
- for (const key of bindingKeys) {
179
- const binding = await this.get(key);
180
- if (binding) {
181
- bindings.push(binding);
182
- } else {
183
- // Clean up stale index entry
184
- await this.redis.srem(indexKey, key);
185
- }
186
- }
187
-
188
- return bindings;
189
- }
190
-
191
- /**
192
- * Delete all bindings for an agent
193
- * Used when deleting an agent
194
- */
195
- async deleteAllBindings(agentId: string): Promise<number> {
196
- const bindings = await this.listBindings(agentId);
197
-
198
- for (const binding of bindings) {
199
- const key = this.buildBindingKey(
200
- binding.platform,
201
- binding.channelId,
202
- binding.teamId
203
- );
204
- await this.delete(key);
205
- }
206
-
207
- // Delete the index
208
- const indexKey = this.buildIndexKey(agentId);
209
- await this.redis.del(indexKey);
210
-
211
- logger.info(`Deleted ${bindings.length} bindings for agent ${agentId}`);
212
- return bindings.length;
213
- }
214
- }
@@ -1,4 +0,0 @@
1
- export {
2
- type ChannelBinding,
3
- ChannelBindingService,
4
- } from "./binding-service";