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