@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,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
- }