@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,147 +0,0 @@
1
- /**
2
- * OAuth Utility Routes
3
- *
4
- * OAuth code exchange and redirect handling.
5
- */
6
-
7
- import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
8
- import type { ProviderOAuthStateStore } from "../../auth/oauth/state-store";
9
- import { verifySettingsSessionOrToken } from "./settings-auth";
10
-
11
- const TAG = "Auth";
12
- const SuccessResponse = z.object({ success: z.boolean() });
13
- const ErrorResponse = z.object({ error: z.string() });
14
-
15
- export interface ProviderCredentialStore {
16
- hasCredentials(agentId: string): Promise<boolean>;
17
- deleteCredentials(agentId: string): Promise<void>;
18
- setCredentials(agentId: string, credentials: unknown): Promise<void>;
19
- }
20
-
21
- export interface ProviderOAuthClient {
22
- generateCodeVerifier(): string;
23
- buildAuthUrl(state: string, codeVerifier: string): string;
24
- exchangeCodeForToken(
25
- code: string,
26
- codeVerifier: string,
27
- redirectUri?: string,
28
- state?: string
29
- ): Promise<unknown>;
30
- }
31
-
32
- const codeExchangeRoute = createRoute({
33
- method: "post",
34
- path: "/{provider}/code",
35
- tags: [TAG],
36
- summary: "Exchange OAuth code for token",
37
- request: {
38
- query: z.object({ token: z.string().optional() }),
39
- params: z.object({ provider: z.string() }),
40
- body: {
41
- content: {
42
- "application/json": {
43
- schema: z.object({ code: z.string() }),
44
- },
45
- },
46
- },
47
- },
48
- responses: {
49
- 200: {
50
- description: "Exchanged",
51
- content: { "application/json": { schema: SuccessResponse } },
52
- },
53
- 400: {
54
- description: "Invalid",
55
- content: { "application/json": { schema: ErrorResponse } },
56
- },
57
- 401: {
58
- description: "Unauthorized",
59
- content: { "application/json": { schema: ErrorResponse } },
60
- },
61
- },
62
- });
63
-
64
- export interface OAuthRoutesConfig {
65
- providerStores?: Record<string, ProviderCredentialStore>;
66
- oauthClients?: Record<string, ProviderOAuthClient>;
67
- oauthStateStore?: ProviderOAuthStateStore;
68
- }
69
-
70
- export function createOAuthRoutes(config: OAuthRoutesConfig): OpenAPIHono {
71
- const app = new OpenAPIHono();
72
-
73
- // --- Provider login redirect (excluded from docs) ---
74
- app.get("/:provider/login", async (c) => {
75
- const session = verifySettingsSessionOrToken(c);
76
- const agentId = session?.agentId || c.req.query("agentId");
77
-
78
- if (!agentId) return c.json({ error: "Missing agentId" }, 400);
79
-
80
- if (!session) {
81
- // No session — redirect through settings OAuth to establish one, then return here
82
- const returnUrl = `${c.req.path}?agentId=${encodeURIComponent(agentId)}`;
83
- return c.redirect(
84
- `/connect/oauth/login?returnUrl=${encodeURIComponent(returnUrl)}`
85
- );
86
- }
87
-
88
- const oauthClient = config.oauthClients?.[c.req.param("provider")];
89
- if (!oauthClient) return c.json({ error: "Unknown provider" }, 404);
90
- if (!config.oauthStateStore)
91
- return c.json({ error: "Not configured" }, 500);
92
-
93
- const codeVerifier = oauthClient.generateCodeVerifier();
94
- const state = await config.oauthStateStore.create({
95
- userId: session.userId,
96
- agentId,
97
- codeVerifier,
98
- context: { platform: session.platform, channelId: agentId },
99
- });
100
-
101
- return c.redirect(oauthClient.buildAuthUrl(state, codeVerifier));
102
- });
103
-
104
- // --- Provider code exchange ---
105
- app.openapi(codeExchangeRoute, async (c): Promise<any> => {
106
- const session = verifySettingsSessionOrToken(c);
107
- const agentId = session?.agentId;
108
- if (!session || !agentId) return c.json({ error: "Unauthorized" }, 401);
109
-
110
- const { provider } = c.req.valid("param");
111
- const oauthClient = config.oauthClients?.[provider];
112
- const credentialStore = config.providerStores?.[provider];
113
- if (!oauthClient || !credentialStore)
114
- return c.json({ error: "Unknown provider" }, 404);
115
- if (!config.oauthStateStore)
116
- return c.json({ error: "Not configured" }, 500);
117
-
118
- const { code: input } = c.req.valid("json");
119
- const parts = input.split("#");
120
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
121
- return c.json({ error: "Invalid format" }, 400);
122
- }
123
-
124
- const authCode = parts[0].trim();
125
- const state = parts[1].trim();
126
- const stateData = await config.oauthStateStore.consume(state);
127
- if (!stateData) return c.json({ error: "Invalid state" }, 400);
128
-
129
- try {
130
- const credentials = await oauthClient.exchangeCodeForToken(
131
- authCode,
132
- stateData.codeVerifier,
133
- "https://console.anthropic.com/oauth/code/callback",
134
- state
135
- );
136
- await credentialStore.setCredentials(agentId, credentials);
137
- return c.json({ success: true });
138
- } catch (e) {
139
- return c.json(
140
- { error: e instanceof Error ? e.message : "Exchange failed" },
141
- 400
142
- );
143
- }
144
- });
145
-
146
- return app;
147
- }
@@ -1,104 +0,0 @@
1
- import { decrypt, encrypt } from "@lobu/core";
2
- import type { Context } from "hono";
3
- import { deleteCookie, getCookie, setCookie } from "hono/cookie";
4
- import type { SettingsTokenPayload } from "../../auth/settings/token-service";
5
-
6
- export type AuthProvider = (c: Context) => SettingsTokenPayload | null;
7
-
8
- export const SETTINGS_SESSION_COOKIE_NAME = "lobu_settings_session";
9
-
10
- let _authProvider: AuthProvider | null = null;
11
-
12
- /**
13
- * Set a custom auth provider for embedded mode.
14
- * When set, verifySettingsSession delegates to this provider first,
15
- * falling back to cookie auth only if it returns null.
16
- */
17
- export function setAuthProvider(provider: AuthProvider | null): void {
18
- _authProvider = provider;
19
- }
20
-
21
- function decodeSettingsPayload(
22
- token: string | null | undefined
23
- ): SettingsTokenPayload | null {
24
- if (!token || token.trim().length === 0) return null;
25
-
26
- try {
27
- const decrypted = decrypt(token);
28
- const payload = JSON.parse(decrypted) as SettingsTokenPayload;
29
-
30
- if (!payload.userId || !payload.exp) return null;
31
- if (Date.now() > payload.exp) return null;
32
-
33
- return payload;
34
- } catch {
35
- return null;
36
- }
37
- }
38
-
39
- function isSecureRequest(c: Context): boolean {
40
- const forwardedProto = c.req.header("x-forwarded-proto");
41
- if (forwardedProto) {
42
- return forwardedProto.split(",")[0]?.trim().toLowerCase() === "https";
43
- }
44
- return new URL(c.req.url).protocol === "https:";
45
- }
46
-
47
- /**
48
- * Verify settings session.
49
- * Checks injected auth provider first (for embedded mode),
50
- * then falls back to cookie-based session auth.
51
- */
52
- export function verifySettingsSession(c: Context): SettingsTokenPayload | null {
53
- if (_authProvider) {
54
- const result = _authProvider(c);
55
- if (result) return result;
56
- }
57
-
58
- const token = getCookie(c, SETTINGS_SESSION_COOKIE_NAME);
59
- return decodeSettingsPayload(token);
60
- }
61
-
62
- export function verifySettingsToken(
63
- token: string | null | undefined
64
- ): SettingsTokenPayload | null {
65
- if (!token) return null;
66
- return decodeSettingsPayload(token);
67
- }
68
-
69
- /**
70
- * Resolve settings auth from an injected auth provider, cookie session,
71
- * or a direct encrypted query token.
72
- */
73
- export function verifySettingsSessionOrToken(
74
- c: Context,
75
- queryKey = "token"
76
- ): SettingsTokenPayload | null {
77
- return verifySettingsSession(c) ?? verifySettingsToken(c.req.query(queryKey));
78
- }
79
-
80
- /**
81
- * Set a settings session cookie from a SettingsTokenPayload.
82
- */
83
- export function setSettingsSessionCookie(
84
- c: Context,
85
- session: SettingsTokenPayload
86
- ): void {
87
- const token = encrypt(JSON.stringify(session));
88
- const maxAgeSeconds = Math.max(
89
- 1,
90
- Math.floor((session.exp - Date.now()) / 1000)
91
- );
92
-
93
- setCookie(c, SETTINGS_SESSION_COOKIE_NAME, token, {
94
- path: "/",
95
- httpOnly: true,
96
- sameSite: "Lax",
97
- secure: isSecureRequest(c),
98
- maxAge: maxAgeSeconds,
99
- });
100
- }
101
-
102
- export function clearSettingsSessionCookie(c: Context): void {
103
- deleteCookie(c, SETTINGS_SESSION_COOKIE_NAME, { path: "/" });
104
- }
@@ -1,173 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { createLogger } from "@lobu/core";
3
- import { Hono } from "hono";
4
- import { createSlackInstallStateStore } from "../../auth/oauth/state-store";
5
- import {
6
- renderOAuthErrorPage,
7
- renderOAuthSuccessPage,
8
- } from "../../auth/oauth-templates";
9
- import type { ChatInstanceManager } from "../../connections/chat-instance-manager";
10
- import { resolvePublicUrl } from "../../utils/public-url";
11
-
12
- const logger = createLogger("slack-routes");
13
-
14
- const DEFAULT_SLACK_BOT_SCOPES = [
15
- "app_mentions:read",
16
- "assistant:write",
17
- "channels:history",
18
- "channels:read",
19
- "chat:write",
20
- "chat:write.public",
21
- "commands",
22
- "files:read",
23
- "files:write",
24
- "groups:history",
25
- "groups:read",
26
- "im:history",
27
- "im:read",
28
- "im:write",
29
- "mpim:read",
30
- "reactions:read",
31
- "reactions:write",
32
- "users:read",
33
- ];
34
- type SlackManifest = {
35
- oauth_config?: {
36
- scopes?: {
37
- bot?: string[];
38
- };
39
- };
40
- };
41
-
42
- function splitScopes(scopes: string): string[] {
43
- return scopes
44
- .split(",")
45
- .map((scope) => scope.trim())
46
- .filter(Boolean);
47
- }
48
-
49
- async function loadSlackBotScopes(): Promise<string[]> {
50
- const envScopes =
51
- process.env.SLACK_OAUTH_SCOPES || process.env.SLACK_BOT_SCOPES;
52
- if (envScopes) {
53
- return splitScopes(envScopes);
54
- }
55
-
56
- const manifestPath =
57
- process.env.SLACK_MANIFEST_PATH ||
58
- "config/slack-app-manifest.community.json";
59
-
60
- try {
61
- const raw = await readFile(manifestPath, "utf8");
62
- const manifest = JSON.parse(raw) as SlackManifest;
63
- const scopes = manifest.oauth_config?.scopes?.bot;
64
- if (Array.isArray(scopes) && scopes.length > 0) {
65
- return scopes;
66
- }
67
- } catch (error) {
68
- logger.warn(
69
- { manifestPath, error: String(error) },
70
- "Failed to load Slack scopes from manifest, using defaults"
71
- );
72
- }
73
-
74
- return DEFAULT_SLACK_BOT_SCOPES;
75
- }
76
-
77
- export function createSlackRoutes(manager: ChatInstanceManager): Hono {
78
- const router = new Hono();
79
-
80
- router.get("/slack/install", async (c) => {
81
- const clientId = process.env.SLACK_CLIENT_ID;
82
- if (!clientId) {
83
- return c.html(
84
- renderOAuthErrorPage(
85
- "slack_not_configured",
86
- "Slack OAuth is not configured on this gateway. Set SLACK_CLIENT_ID and try again."
87
- ),
88
- 503
89
- );
90
- }
91
-
92
- const redis = manager.getServices().getQueue().getRedisClient();
93
- const stateStore = createSlackInstallStateStore(redis);
94
- const redirectUri = resolvePublicUrl("/slack/oauth_callback", {
95
- configuredUrl: manager.getServices().getPublicGatewayUrl?.(),
96
- requestUrl: c.req.url,
97
- });
98
- const scopes = await loadSlackBotScopes();
99
- const state = await stateStore.create({ redirectUri });
100
-
101
- const oauthUrl = new URL("https://slack.com/oauth/v2/authorize");
102
- oauthUrl.searchParams.set("client_id", clientId);
103
- oauthUrl.searchParams.set("scope", scopes.join(","));
104
- oauthUrl.searchParams.set("redirect_uri", redirectUri);
105
- oauthUrl.searchParams.set("state", state);
106
-
107
- return c.redirect(oauthUrl.toString(), 302);
108
- });
109
-
110
- router.get("/slack/oauth_callback", async (c) => {
111
- const state = c.req.query("state");
112
- const code = c.req.query("code");
113
- if (!state || !code) {
114
- return c.html(
115
- renderOAuthErrorPage(
116
- "invalid_request",
117
- "The Slack OAuth callback is missing the required state or code parameter."
118
- ),
119
- 400
120
- );
121
- }
122
-
123
- const redis = manager.getServices().getQueue().getRedisClient();
124
- const stateStore = createSlackInstallStateStore(redis);
125
- const oauthState = await stateStore.consume(state);
126
-
127
- if (!oauthState) {
128
- return c.html(
129
- renderOAuthErrorPage(
130
- "invalid_state",
131
- "This Slack install link is invalid or has expired."
132
- ),
133
- 400
134
- );
135
- }
136
-
137
- try {
138
- const result = await manager.completeSlackOAuthInstall(
139
- c.req.raw,
140
- oauthState.redirectUri
141
- );
142
- return c.html(
143
- renderOAuthSuccessPage(result.teamName || result.teamId, undefined, {
144
- title: "Slack installed",
145
- description: "Workspace connected to Lobu:",
146
- details: `Connection ID: ${result.connectionId}`,
147
- })
148
- );
149
- } catch (error) {
150
- logger.error({ error: String(error) }, "Slack OAuth callback failed");
151
- return c.html(
152
- renderOAuthErrorPage(
153
- "slack_install_failed",
154
- error instanceof Error
155
- ? error.message
156
- : "Slack OAuth callback failed."
157
- ),
158
- 500
159
- );
160
- }
161
- });
162
-
163
- router.post("/slack/events", async (c) => {
164
- try {
165
- return await manager.handleSlackAppWebhook(c.req.raw);
166
- } catch (error) {
167
- logger.error({ error: String(error) }, "Slack event handling failed");
168
- return c.text("Slack webhook processing failed", 500);
169
- }
170
- });
171
-
172
- return router;
173
- }
@@ -1,101 +0,0 @@
1
- import type { AgentConfigStore } from "@lobu/core";
2
- import type { SettingsTokenPayload } from "../../auth/settings/token-service";
3
- import type { UserAgentsStore } from "../../auth/user-agents-store";
4
- import { getAuthMethod } from "../../connections/platform-auth-methods";
5
-
6
- export interface AgentOwnershipConfig {
7
- userAgentsStore?: UserAgentsStore;
8
- agentMetadataStore?: Pick<AgentConfigStore, "getMetadata">;
9
- }
10
-
11
- export interface AgentOwnershipResult {
12
- authorized: boolean;
13
- ownerPlatform?: string;
14
- ownerUserId?: string;
15
- }
16
-
17
- export function resolveSettingsLookupUserId(
18
- session: SettingsTokenPayload
19
- ): string {
20
- if (session.platform === "external") {
21
- return session.oauthUserId || session.userId;
22
- }
23
-
24
- const isDeterministic = getAuthMethod(session.platform).type !== "oauth";
25
- return isDeterministic
26
- ? session.userId
27
- : session.oauthUserId || session.userId;
28
- }
29
-
30
- function sessionMatchesMetadataOwner(
31
- session: SettingsTokenPayload,
32
- ownerPlatform: string,
33
- ownerUserId: string
34
- ): boolean {
35
- const lookupUserId = resolveSettingsLookupUserId(session);
36
- if (!lookupUserId || ownerUserId !== lookupUserId) {
37
- return false;
38
- }
39
-
40
- return ownerPlatform === session.platform || session.platform === "external";
41
- }
42
-
43
- export async function verifyOwnedAgentAccess(
44
- session: SettingsTokenPayload,
45
- agentId: string,
46
- config: AgentOwnershipConfig
47
- ): Promise<AgentOwnershipResult> {
48
- if (session.isAdmin) {
49
- return { authorized: true };
50
- }
51
-
52
- if (session.agentId) {
53
- return { authorized: session.agentId === agentId };
54
- }
55
-
56
- const lookupUserId = resolveSettingsLookupUserId(session);
57
- if (config.userAgentsStore) {
58
- const owns = await config.userAgentsStore.ownsAgent(
59
- session.platform,
60
- lookupUserId,
61
- agentId
62
- );
63
- if (owns) {
64
- return {
65
- authorized: true,
66
- ownerPlatform: session.platform,
67
- ownerUserId: lookupUserId,
68
- };
69
- }
70
- }
71
-
72
- if (!config.agentMetadataStore) {
73
- return { authorized: false };
74
- }
75
-
76
- const metadata = await config.agentMetadataStore.getMetadata(agentId);
77
- if (
78
- !metadata?.owner ||
79
- !sessionMatchesMetadataOwner(
80
- session,
81
- metadata.owner.platform,
82
- metadata.owner.userId
83
- )
84
- ) {
85
- return { authorized: false };
86
- }
87
-
88
- if (config.userAgentsStore) {
89
- config.userAgentsStore
90
- .addAgent(session.platform, lookupUserId, agentId)
91
- .catch(() => {
92
- /* best-effort reconciliation */
93
- });
94
- }
95
-
96
- return {
97
- authorized: true,
98
- ownerPlatform: metadata.owner.platform,
99
- ownerUserId: metadata.owner.userId,
100
- };
101
- }
@@ -1,34 +0,0 @@
1
- /**
2
- * Shared token verification utility for public routes.
3
- *
4
- * Verifies a settings token against an agentId by checking direct agentId match,
5
- * user ownership via UserAgentsStore, or canonical metadata owner fallback.
6
- */
7
-
8
- import type { AgentConfigStore } from "@lobu/core";
9
- import type { SettingsTokenPayload } from "../../auth/settings/token-service";
10
- import type { UserAgentsStore } from "../../auth/user-agents-store";
11
- import { verifyOwnedAgentAccess } from "./agent-ownership";
12
-
13
- export interface TokenVerifierConfig {
14
- userAgentsStore?: UserAgentsStore;
15
- agentMetadataStore?: Pick<AgentConfigStore, "getMetadata">;
16
- }
17
-
18
- /**
19
- * Create a token verifier function scoped to a given config.
20
- *
21
- * The returned async function accepts a decoded settings token payload and an
22
- * agentId, then returns the payload if the caller is authorised, or null.
23
- */
24
- export function createTokenVerifier(config: TokenVerifierConfig) {
25
- return async (
26
- payload: SettingsTokenPayload | null,
27
- agentId: string
28
- ): Promise<SettingsTokenPayload | null> => {
29
- if (!payload) return null;
30
-
31
- const result = await verifyOwnedAgentAccess(payload, agentId, config);
32
- return result.authorized ? payload : null;
33
- };
34
- }