@lobu/gateway 3.0.9 → 3.0.12

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 (210) 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/interactions.d.ts +9 -43
  19. package/dist/interactions.d.ts.map +1 -1
  20. package/dist/interactions.js +10 -52
  21. package/dist/interactions.js.map +1 -1
  22. package/dist/routes/public/agent.d.ts +4 -0
  23. package/dist/routes/public/agent.d.ts.map +1 -1
  24. package/dist/routes/public/agent.js +21 -0
  25. package/dist/routes/public/agent.js.map +1 -1
  26. package/dist/services/core-services.d.ts.map +1 -1
  27. package/dist/services/core-services.js +4 -0
  28. package/dist/services/core-services.js.map +1 -1
  29. package/package.json +9 -9
  30. package/src/__tests__/agent-config-routes.test.ts +0 -254
  31. package/src/__tests__/agent-history-routes.test.ts +0 -72
  32. package/src/__tests__/agent-routes.test.ts +0 -68
  33. package/src/__tests__/agent-schedules-routes.test.ts +0 -59
  34. package/src/__tests__/agent-settings-store.test.ts +0 -323
  35. package/src/__tests__/bedrock-model-catalog.test.ts +0 -40
  36. package/src/__tests__/bedrock-openai-service.test.ts +0 -157
  37. package/src/__tests__/bedrock-provider-module.test.ts +0 -56
  38. package/src/__tests__/chat-instance-manager-slack.test.ts +0 -204
  39. package/src/__tests__/chat-response-bridge.test.ts +0 -131
  40. package/src/__tests__/config-memory-plugins.test.ts +0 -92
  41. package/src/__tests__/config-request-store.test.ts +0 -127
  42. package/src/__tests__/connection-routes.test.ts +0 -144
  43. package/src/__tests__/core-services-store-selection.test.ts +0 -92
  44. package/src/__tests__/docker-deployment.test.ts +0 -1211
  45. package/src/__tests__/embedded-deployment.test.ts +0 -342
  46. package/src/__tests__/grant-store.test.ts +0 -148
  47. package/src/__tests__/http-proxy.test.ts +0 -281
  48. package/src/__tests__/instruction-service.test.ts +0 -37
  49. package/src/__tests__/link-buttons.test.ts +0 -112
  50. package/src/__tests__/lobu.test.ts +0 -32
  51. package/src/__tests__/mcp-config-service.test.ts +0 -347
  52. package/src/__tests__/mcp-proxy.test.ts +0 -694
  53. package/src/__tests__/message-handler-bridge.test.ts +0 -17
  54. package/src/__tests__/model-selection.test.ts +0 -172
  55. package/src/__tests__/oauth-templates.test.ts +0 -39
  56. package/src/__tests__/platform-adapter-slack-send.test.ts +0 -114
  57. package/src/__tests__/platform-helpers-model-resolution.test.ts +0 -253
  58. package/src/__tests__/provider-inheritance.test.ts +0 -212
  59. package/src/__tests__/routes/cli-auth.test.ts +0 -337
  60. package/src/__tests__/routes/interactions.test.ts +0 -121
  61. package/src/__tests__/secret-proxy.test.ts +0 -85
  62. package/src/__tests__/session-manager.test.ts +0 -572
  63. package/src/__tests__/setup.ts +0 -133
  64. package/src/__tests__/skill-and-mcp-registry.test.ts +0 -203
  65. package/src/__tests__/slack-routes.test.ts +0 -161
  66. package/src/__tests__/system-config-resolver.test.ts +0 -75
  67. package/src/__tests__/system-message-limiter.test.ts +0 -89
  68. package/src/__tests__/system-skills-service.test.ts +0 -362
  69. package/src/__tests__/transcription-service.test.ts +0 -222
  70. package/src/__tests__/utils/rate-limiter.test.ts +0 -102
  71. package/src/__tests__/worker-connection-manager.test.ts +0 -497
  72. package/src/__tests__/worker-job-router.test.ts +0 -722
  73. package/src/api/index.ts +0 -1
  74. package/src/api/platform.ts +0 -292
  75. package/src/api/response-renderer.ts +0 -157
  76. package/src/auth/agent-metadata-store.ts +0 -168
  77. package/src/auth/api-auth-middleware.ts +0 -69
  78. package/src/auth/api-key-provider-module.ts +0 -213
  79. package/src/auth/base-provider-module.ts +0 -201
  80. package/src/auth/bedrock/provider-module.ts +0 -110
  81. package/src/auth/chatgpt/chatgpt-oauth-module.ts +0 -185
  82. package/src/auth/chatgpt/device-code-client.ts +0 -218
  83. package/src/auth/chatgpt/index.ts +0 -1
  84. package/src/auth/claude/oauth-module.ts +0 -280
  85. package/src/auth/cli/token-service.ts +0 -249
  86. package/src/auth/external/client.ts +0 -560
  87. package/src/auth/external/device-code-client.ts +0 -235
  88. package/src/auth/mcp/config-service.ts +0 -420
  89. package/src/auth/mcp/proxy.ts +0 -1086
  90. package/src/auth/mcp/string-substitution.ts +0 -17
  91. package/src/auth/mcp/tool-cache.ts +0 -90
  92. package/src/auth/oauth/base-client.ts +0 -267
  93. package/src/auth/oauth/client.ts +0 -153
  94. package/src/auth/oauth/credentials.ts +0 -7
  95. package/src/auth/oauth/providers.ts +0 -69
  96. package/src/auth/oauth/state-store.ts +0 -150
  97. package/src/auth/oauth-templates.ts +0 -179
  98. package/src/auth/provider-catalog.ts +0 -220
  99. package/src/auth/provider-model-options.ts +0 -41
  100. package/src/auth/settings/agent-settings-store.ts +0 -565
  101. package/src/auth/settings/auth-profiles-manager.ts +0 -216
  102. package/src/auth/settings/index.ts +0 -12
  103. package/src/auth/settings/model-preference-store.ts +0 -52
  104. package/src/auth/settings/model-selection.ts +0 -135
  105. package/src/auth/settings/resolved-settings-view.ts +0 -298
  106. package/src/auth/settings/template-utils.ts +0 -44
  107. package/src/auth/settings/token-service.ts +0 -88
  108. package/src/auth/system-env-store.ts +0 -98
  109. package/src/auth/user-agents-store.ts +0 -68
  110. package/src/channels/binding-service.ts +0 -214
  111. package/src/channels/index.ts +0 -4
  112. package/src/cli/gateway.ts +0 -1312
  113. package/src/cli/index.ts +0 -74
  114. package/src/commands/built-in-commands.ts +0 -80
  115. package/src/commands/command-dispatcher.ts +0 -94
  116. package/src/commands/command-reply-adapters.ts +0 -27
  117. package/src/config/file-loader.ts +0 -618
  118. package/src/config/index.ts +0 -588
  119. package/src/config/network-allowlist.ts +0 -71
  120. package/src/connections/chat-instance-manager.ts +0 -1284
  121. package/src/connections/chat-response-bridge.ts +0 -618
  122. package/src/connections/index.ts +0 -7
  123. package/src/connections/interaction-bridge.ts +0 -831
  124. package/src/connections/message-handler-bridge.ts +0 -440
  125. package/src/connections/platform-auth-methods.ts +0 -15
  126. package/src/connections/types.ts +0 -84
  127. package/src/gateway/connection-manager.ts +0 -291
  128. package/src/gateway/index.ts +0 -698
  129. package/src/gateway/job-router.ts +0 -201
  130. package/src/gateway-main.ts +0 -200
  131. package/src/index.ts +0 -41
  132. package/src/infrastructure/queue/index.ts +0 -12
  133. package/src/infrastructure/queue/queue-producer.ts +0 -148
  134. package/src/infrastructure/queue/redis-queue.ts +0 -361
  135. package/src/infrastructure/queue/types.ts +0 -133
  136. package/src/infrastructure/redis/system-message-limiter.ts +0 -94
  137. package/src/interactions/config-request-store.ts +0 -198
  138. package/src/interactions.ts +0 -363
  139. package/src/lobu.ts +0 -311
  140. package/src/metrics/prometheus.ts +0 -159
  141. package/src/modules/module-system.ts +0 -179
  142. package/src/orchestration/base-deployment-manager.ts +0 -900
  143. package/src/orchestration/deployment-utils.ts +0 -98
  144. package/src/orchestration/impl/docker-deployment.ts +0 -620
  145. package/src/orchestration/impl/embedded-deployment.ts +0 -268
  146. package/src/orchestration/impl/index.ts +0 -8
  147. package/src/orchestration/impl/k8s/deployment.ts +0 -1061
  148. package/src/orchestration/impl/k8s/helpers.ts +0 -610
  149. package/src/orchestration/impl/k8s/index.ts +0 -1
  150. package/src/orchestration/index.ts +0 -333
  151. package/src/orchestration/message-consumer.ts +0 -584
  152. package/src/orchestration/scheduled-wakeup.ts +0 -704
  153. package/src/permissions/approval-policy.ts +0 -36
  154. package/src/permissions/grant-store.ts +0 -219
  155. package/src/platform/file-handler.ts +0 -66
  156. package/src/platform/link-buttons.ts +0 -57
  157. package/src/platform/renderer-utils.ts +0 -44
  158. package/src/platform/response-renderer.ts +0 -84
  159. package/src/platform/unified-thread-consumer.ts +0 -194
  160. package/src/platform.ts +0 -318
  161. package/src/proxy/http-proxy.ts +0 -752
  162. package/src/proxy/proxy-manager.ts +0 -81
  163. package/src/proxy/secret-proxy.ts +0 -402
  164. package/src/proxy/token-refresh-job.ts +0 -143
  165. package/src/routes/internal/audio.ts +0 -141
  166. package/src/routes/internal/device-auth.ts +0 -652
  167. package/src/routes/internal/files.ts +0 -226
  168. package/src/routes/internal/history.ts +0 -69
  169. package/src/routes/internal/images.ts +0 -127
  170. package/src/routes/internal/interactions.ts +0 -84
  171. package/src/routes/internal/middleware.ts +0 -23
  172. package/src/routes/internal/schedule.ts +0 -226
  173. package/src/routes/internal/types.ts +0 -22
  174. package/src/routes/openapi-auto.ts +0 -239
  175. package/src/routes/public/agent-access.ts +0 -23
  176. package/src/routes/public/agent-config.ts +0 -675
  177. package/src/routes/public/agent-history.ts +0 -422
  178. package/src/routes/public/agent-schedules.ts +0 -296
  179. package/src/routes/public/agent.ts +0 -1086
  180. package/src/routes/public/agents.ts +0 -373
  181. package/src/routes/public/channels.ts +0 -191
  182. package/src/routes/public/cli-auth.ts +0 -896
  183. package/src/routes/public/connections.ts +0 -574
  184. package/src/routes/public/landing.ts +0 -16
  185. package/src/routes/public/oauth.ts +0 -147
  186. package/src/routes/public/settings-auth.ts +0 -104
  187. package/src/routes/public/slack.ts +0 -173
  188. package/src/routes/shared/agent-ownership.ts +0 -101
  189. package/src/routes/shared/token-verifier.ts +0 -34
  190. package/src/services/bedrock-model-catalog.ts +0 -217
  191. package/src/services/bedrock-openai-service.ts +0 -658
  192. package/src/services/core-services.ts +0 -1072
  193. package/src/services/image-generation-service.ts +0 -257
  194. package/src/services/instruction-service.ts +0 -318
  195. package/src/services/mcp-registry.ts +0 -94
  196. package/src/services/platform-helpers.ts +0 -287
  197. package/src/services/session-manager.ts +0 -262
  198. package/src/services/settings-resolver.ts +0 -74
  199. package/src/services/system-config-resolver.ts +0 -89
  200. package/src/services/system-skills-service.ts +0 -229
  201. package/src/services/transcription-service.ts +0 -684
  202. package/src/session.ts +0 -110
  203. package/src/spaces/index.ts +0 -1
  204. package/src/spaces/space-resolver.ts +0 -17
  205. package/src/stores/in-memory-agent-store.ts +0 -403
  206. package/src/stores/redis-agent-store.ts +0 -279
  207. package/src/utils/public-url.ts +0 -44
  208. package/src/utils/rate-limiter.ts +0 -94
  209. package/tsconfig.json +0 -33
  210. 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";