@lobu/gateway 2.8.0 → 3.0.6

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 (175) hide show
  1. package/package.json +2 -2
  2. package/src/__tests__/agent-config-routes.test.ts +254 -0
  3. package/src/__tests__/agent-history-routes.test.ts +72 -0
  4. package/src/__tests__/agent-routes.test.ts +68 -0
  5. package/src/__tests__/agent-schedules-routes.test.ts +59 -0
  6. package/src/__tests__/agent-settings-store.test.ts +323 -0
  7. package/src/__tests__/chat-instance-manager-slack.test.ts +204 -0
  8. package/src/__tests__/chat-response-bridge.test.ts +131 -0
  9. package/src/__tests__/config-memory-plugins.test.ts +92 -0
  10. package/src/__tests__/config-request-store.test.ts +127 -0
  11. package/src/__tests__/connection-routes.test.ts +144 -0
  12. package/src/__tests__/core-services-store-selection.test.ts +92 -0
  13. package/src/__tests__/docker-deployment.test.ts +1211 -0
  14. package/src/__tests__/embedded-deployment.test.ts +342 -0
  15. package/src/__tests__/grant-store.test.ts +148 -0
  16. package/src/__tests__/http-proxy.test.ts +281 -0
  17. package/src/__tests__/instruction-service.test.ts +37 -0
  18. package/src/__tests__/link-buttons.test.ts +112 -0
  19. package/src/__tests__/lobu.test.ts +32 -0
  20. package/src/__tests__/mcp-config-service.test.ts +347 -0
  21. package/src/__tests__/mcp-proxy.test.ts +696 -0
  22. package/src/__tests__/message-handler-bridge.test.ts +17 -0
  23. package/src/__tests__/model-selection.test.ts +172 -0
  24. package/src/__tests__/oauth-templates.test.ts +39 -0
  25. package/src/__tests__/platform-adapter-slack-send.test.ts +114 -0
  26. package/src/__tests__/platform-helpers-model-resolution.test.ts +253 -0
  27. package/src/__tests__/provider-inheritance.test.ts +212 -0
  28. package/src/__tests__/routes/cli-auth.test.ts +337 -0
  29. package/src/__tests__/routes/interactions.test.ts +121 -0
  30. package/src/__tests__/secret-proxy.test.ts +85 -0
  31. package/src/__tests__/session-manager.test.ts +572 -0
  32. package/src/__tests__/setup.ts +133 -0
  33. package/src/__tests__/skill-and-mcp-registry.test.ts +203 -0
  34. package/src/__tests__/slack-routes.test.ts +161 -0
  35. package/src/__tests__/system-config-resolver.test.ts +75 -0
  36. package/src/__tests__/system-message-limiter.test.ts +89 -0
  37. package/src/__tests__/system-skills-service.test.ts +362 -0
  38. package/src/__tests__/transcription-service.test.ts +222 -0
  39. package/src/__tests__/utils/rate-limiter.test.ts +102 -0
  40. package/src/__tests__/worker-connection-manager.test.ts +497 -0
  41. package/src/__tests__/worker-job-router.test.ts +722 -0
  42. package/src/api/index.ts +1 -0
  43. package/src/api/platform.ts +292 -0
  44. package/src/api/response-renderer.ts +157 -0
  45. package/src/auth/agent-metadata-store.ts +168 -0
  46. package/src/auth/api-auth-middleware.ts +69 -0
  47. package/src/auth/api-key-provider-module.ts +213 -0
  48. package/src/auth/base-provider-module.ts +201 -0
  49. package/src/auth/chatgpt/chatgpt-oauth-module.ts +185 -0
  50. package/src/auth/chatgpt/device-code-client.ts +218 -0
  51. package/src/auth/chatgpt/index.ts +1 -0
  52. package/src/auth/claude/oauth-module.ts +280 -0
  53. package/src/auth/cli/token-service.ts +249 -0
  54. package/src/auth/external/client.ts +560 -0
  55. package/src/auth/external/device-code-client.ts +225 -0
  56. package/src/auth/mcp/config-service.ts +392 -0
  57. package/src/auth/mcp/proxy.ts +1088 -0
  58. package/src/auth/mcp/string-substitution.ts +17 -0
  59. package/src/auth/mcp/tool-cache.ts +90 -0
  60. package/src/auth/oauth/base-client.ts +267 -0
  61. package/src/auth/oauth/client.ts +153 -0
  62. package/src/auth/oauth/credentials.ts +7 -0
  63. package/src/auth/oauth/providers.ts +69 -0
  64. package/src/auth/oauth/state-store.ts +150 -0
  65. package/src/auth/oauth-templates.ts +179 -0
  66. package/src/auth/provider-catalog.ts +220 -0
  67. package/src/auth/provider-model-options.ts +41 -0
  68. package/src/auth/settings/agent-settings-store.ts +565 -0
  69. package/src/auth/settings/auth-profiles-manager.ts +216 -0
  70. package/src/auth/settings/index.ts +12 -0
  71. package/src/auth/settings/model-preference-store.ts +52 -0
  72. package/src/auth/settings/model-selection.ts +135 -0
  73. package/src/auth/settings/resolved-settings-view.ts +298 -0
  74. package/src/auth/settings/template-utils.ts +44 -0
  75. package/src/auth/settings/token-service.ts +88 -0
  76. package/src/auth/system-env-store.ts +98 -0
  77. package/src/auth/user-agents-store.ts +68 -0
  78. package/src/channels/binding-service.ts +214 -0
  79. package/src/channels/index.ts +4 -0
  80. package/src/cli/gateway.ts +1304 -0
  81. package/src/cli/index.ts +74 -0
  82. package/src/commands/built-in-commands.ts +80 -0
  83. package/src/commands/command-dispatcher.ts +94 -0
  84. package/src/commands/command-reply-adapters.ts +27 -0
  85. package/src/config/file-loader.ts +618 -0
  86. package/src/config/index.ts +588 -0
  87. package/src/config/network-allowlist.ts +71 -0
  88. package/src/connections/chat-instance-manager.ts +1284 -0
  89. package/src/connections/chat-response-bridge.ts +618 -0
  90. package/src/connections/index.ts +7 -0
  91. package/src/connections/interaction-bridge.ts +831 -0
  92. package/src/connections/message-handler-bridge.ts +415 -0
  93. package/src/connections/platform-auth-methods.ts +15 -0
  94. package/src/connections/types.ts +84 -0
  95. package/src/gateway/connection-manager.ts +291 -0
  96. package/src/gateway/index.ts +700 -0
  97. package/src/gateway/job-router.ts +201 -0
  98. package/src/gateway-main.ts +200 -0
  99. package/src/index.ts +41 -0
  100. package/src/infrastructure/queue/index.ts +12 -0
  101. package/src/infrastructure/queue/queue-producer.ts +148 -0
  102. package/src/infrastructure/queue/redis-queue.ts +361 -0
  103. package/src/infrastructure/queue/types.ts +133 -0
  104. package/src/infrastructure/redis/system-message-limiter.ts +94 -0
  105. package/src/interactions/config-request-store.ts +198 -0
  106. package/src/interactions.ts +363 -0
  107. package/src/lobu.ts +311 -0
  108. package/src/metrics/prometheus.ts +159 -0
  109. package/src/modules/module-system.ts +179 -0
  110. package/src/orchestration/base-deployment-manager.ts +900 -0
  111. package/src/orchestration/deployment-utils.ts +98 -0
  112. package/src/orchestration/impl/docker-deployment.ts +620 -0
  113. package/src/orchestration/impl/embedded-deployment.ts +268 -0
  114. package/src/orchestration/impl/index.ts +8 -0
  115. package/src/orchestration/impl/k8s/deployment.ts +1061 -0
  116. package/src/orchestration/impl/k8s/helpers.ts +610 -0
  117. package/src/orchestration/impl/k8s/index.ts +1 -0
  118. package/src/orchestration/index.ts +333 -0
  119. package/src/orchestration/message-consumer.ts +584 -0
  120. package/src/orchestration/scheduled-wakeup.ts +704 -0
  121. package/src/permissions/approval-policy.ts +36 -0
  122. package/src/permissions/grant-store.ts +219 -0
  123. package/src/platform/file-handler.ts +66 -0
  124. package/src/platform/link-buttons.ts +57 -0
  125. package/src/platform/renderer-utils.ts +44 -0
  126. package/src/platform/response-renderer.ts +84 -0
  127. package/src/platform/unified-thread-consumer.ts +187 -0
  128. package/src/platform.ts +318 -0
  129. package/src/proxy/http-proxy.ts +752 -0
  130. package/src/proxy/proxy-manager.ts +81 -0
  131. package/src/proxy/secret-proxy.ts +402 -0
  132. package/src/proxy/token-refresh-job.ts +143 -0
  133. package/src/routes/internal/audio.ts +141 -0
  134. package/src/routes/internal/device-auth.ts +566 -0
  135. package/src/routes/internal/files.ts +226 -0
  136. package/src/routes/internal/history.ts +69 -0
  137. package/src/routes/internal/images.ts +127 -0
  138. package/src/routes/internal/interactions.ts +84 -0
  139. package/src/routes/internal/middleware.ts +23 -0
  140. package/src/routes/internal/schedule.ts +226 -0
  141. package/src/routes/internal/types.ts +22 -0
  142. package/src/routes/openapi-auto.ts +239 -0
  143. package/src/routes/public/agent-access.ts +23 -0
  144. package/src/routes/public/agent-config.ts +675 -0
  145. package/src/routes/public/agent-history.ts +422 -0
  146. package/src/routes/public/agent-schedules.ts +296 -0
  147. package/src/routes/public/agent.ts +1086 -0
  148. package/src/routes/public/agents.ts +373 -0
  149. package/src/routes/public/channels.ts +191 -0
  150. package/src/routes/public/cli-auth.ts +883 -0
  151. package/src/routes/public/connections.ts +574 -0
  152. package/src/routes/public/landing.ts +16 -0
  153. package/src/routes/public/oauth.ts +147 -0
  154. package/src/routes/public/settings-auth.ts +104 -0
  155. package/src/routes/public/slack.ts +173 -0
  156. package/src/routes/shared/agent-ownership.ts +101 -0
  157. package/src/routes/shared/token-verifier.ts +34 -0
  158. package/src/services/core-services.ts +1053 -0
  159. package/src/services/image-generation-service.ts +257 -0
  160. package/src/services/instruction-service.ts +318 -0
  161. package/src/services/mcp-registry.ts +94 -0
  162. package/src/services/platform-helpers.ts +287 -0
  163. package/src/services/session-manager.ts +262 -0
  164. package/src/services/settings-resolver.ts +74 -0
  165. package/src/services/system-config-resolver.ts +90 -0
  166. package/src/services/system-skills-service.ts +229 -0
  167. package/src/services/transcription-service.ts +684 -0
  168. package/src/session.ts +110 -0
  169. package/src/spaces/index.ts +1 -0
  170. package/src/spaces/space-resolver.ts +17 -0
  171. package/src/stores/in-memory-agent-store.ts +403 -0
  172. package/src/stores/redis-agent-store.ts +279 -0
  173. package/src/utils/public-url.ts +44 -0
  174. package/src/utils/rate-limiter.ts +94 -0
  175. package/tsconfig.json +33 -0
@@ -0,0 +1,92 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
+ import { buildMemoryPlugins } from "../config";
3
+
4
+ const originalMemoryUrl = process.env.MEMORY_URL;
5
+ const originalDispatcherServiceName = process.env.DISPATCHER_SERVICE_NAME;
6
+ const originalKubernetesNamespace = process.env.KUBERNETES_NAMESPACE;
7
+
8
+ afterEach(() => {
9
+ if (originalMemoryUrl === undefined) {
10
+ delete process.env.MEMORY_URL;
11
+ } else {
12
+ process.env.MEMORY_URL = originalMemoryUrl;
13
+ }
14
+
15
+ if (originalDispatcherServiceName === undefined) {
16
+ delete process.env.DISPATCHER_SERVICE_NAME;
17
+ } else {
18
+ process.env.DISPATCHER_SERVICE_NAME = originalDispatcherServiceName;
19
+ }
20
+
21
+ if (originalKubernetesNamespace === undefined) {
22
+ delete process.env.KUBERNETES_NAMESPACE;
23
+ } else {
24
+ process.env.KUBERNETES_NAMESPACE = originalKubernetesNamespace;
25
+ }
26
+ });
27
+
28
+ describe("buildMemoryPlugins", () => {
29
+ test("returns native memory when MEMORY_URL is unset and plugin exists", () => {
30
+ delete process.env.MEMORY_URL;
31
+
32
+ expect(buildMemoryPlugins({ hasNativeMemoryPlugin: true })).toEqual([
33
+ {
34
+ source: "@openclaw/native-memory",
35
+ slot: "memory",
36
+ enabled: true,
37
+ },
38
+ ]);
39
+ });
40
+
41
+ test("returns no plugin when MEMORY_URL is unset and native memory is unavailable", () => {
42
+ delete process.env.MEMORY_URL;
43
+
44
+ expect(buildMemoryPlugins({ hasNativeMemoryPlugin: false })).toEqual([]);
45
+ });
46
+
47
+ test("falls back to native memory when Owletto plugin is unavailable", () => {
48
+ process.env.MEMORY_URL = "https://memory.example.com";
49
+
50
+ expect(
51
+ buildMemoryPlugins({
52
+ hasOwlettoPlugin: false,
53
+ hasNativeMemoryPlugin: true,
54
+ })
55
+ ).toEqual([
56
+ {
57
+ source: "@openclaw/native-memory",
58
+ slot: "memory",
59
+ enabled: true,
60
+ },
61
+ ]);
62
+ });
63
+
64
+ test("returns no plugin when neither Owletto nor native memory plugin exists", () => {
65
+ process.env.MEMORY_URL = "https://memory.example.com";
66
+
67
+ expect(
68
+ buildMemoryPlugins({
69
+ hasOwlettoPlugin: false,
70
+ hasNativeMemoryPlugin: false,
71
+ })
72
+ ).toEqual([]);
73
+ });
74
+
75
+ test("uses Owletto plugin when installed and MEMORY_URL is set", () => {
76
+ process.env.MEMORY_URL = "https://memory.example.com";
77
+ process.env.DISPATCHER_SERVICE_NAME = "lobu-gateway";
78
+ process.env.KUBERNETES_NAMESPACE = "lobu";
79
+
80
+ expect(buildMemoryPlugins({ hasOwlettoPlugin: true })).toEqual([
81
+ {
82
+ source: "@lobu/owletto-openclaw",
83
+ slot: "memory",
84
+ enabled: true,
85
+ config: {
86
+ mcpUrl: "http://lobu-gateway.lobu.svc.cluster.local:8080/mcp/owletto",
87
+ gatewayAuthUrl: "http://lobu-gateway.lobu.svc.cluster.local:8080",
88
+ },
89
+ },
90
+ ]);
91
+ });
92
+ });
@@ -0,0 +1,127 @@
1
+ import { describe, expect, mock, test } from "bun:test";
2
+ import {
3
+ applyPendingConfigRequest,
4
+ buildConfigRequestText,
5
+ } from "../interactions/config-request-store";
6
+
7
+ describe("config-request-store", () => {
8
+ test("applies skills, mcps, nix packages, and grants", async () => {
9
+ const updateSettings = mock(() => Promise.resolve());
10
+ const grant = mock(() => Promise.resolve());
11
+ const agentSettingsStore = {
12
+ getSettings: mock(() =>
13
+ Promise.resolve({
14
+ nixConfig: { packages: ["git"] },
15
+ skillsConfig: {
16
+ skills: [
17
+ {
18
+ repo: "existing-skill",
19
+ name: "Existing Skill",
20
+ description: "Existing",
21
+ enabled: false,
22
+ },
23
+ ],
24
+ },
25
+ mcpServers: {
26
+ existing: { enabled: true, url: "https://existing.example.com" },
27
+ },
28
+ })
29
+ ),
30
+ updateSettings,
31
+ };
32
+ const grantStore = { grant };
33
+
34
+ await applyPendingConfigRequest(
35
+ agentSettingsStore as any,
36
+ grantStore as any,
37
+ {
38
+ agentId: "agent-1",
39
+ reason: "Install requested skill",
40
+ skills: [
41
+ {
42
+ repo: "existing-skill",
43
+ name: "Existing Skill",
44
+ description: "Updated",
45
+ },
46
+ {
47
+ repo: "new-skill",
48
+ name: "New Skill",
49
+ description: "New",
50
+ },
51
+ ],
52
+ mcpServers: [
53
+ {
54
+ id: "new-mcp",
55
+ name: "New MCP",
56
+ url: "https://mcp.example.com",
57
+ type: "sse",
58
+ },
59
+ ],
60
+ nixPackages: ["git", "ffmpeg"],
61
+ grants: ["api.example.com"],
62
+ }
63
+ );
64
+
65
+ expect(updateSettings).toHaveBeenCalledTimes(1);
66
+ const [, updates] = updateSettings.mock.calls[0]!;
67
+ expect(updates.skillsConfig.skills).toHaveLength(2);
68
+ expect(
69
+ updates.skillsConfig.skills.find(
70
+ (skill: any) => skill.repo === "existing-skill"
71
+ ).enabled
72
+ ).toBe(true);
73
+ expect(
74
+ updates.skillsConfig.skills.find(
75
+ (skill: any) => skill.repo === "new-skill"
76
+ ).name
77
+ ).toBe("New Skill");
78
+ expect(updates.mcpServers["new-mcp"]).toMatchObject({
79
+ enabled: true,
80
+ url: "https://mcp.example.com",
81
+ type: "sse",
82
+ });
83
+ expect(updates.nixConfig.packages).toEqual(["git", "ffmpeg"]);
84
+ expect(grant).toHaveBeenCalledWith("agent-1", "api.example.com", null);
85
+ });
86
+
87
+ test("skips settings writes when a request only grants permissions", async () => {
88
+ const updateSettings = mock(() => Promise.resolve());
89
+ const grant = mock(() => Promise.resolve());
90
+ const agentSettingsStore = {
91
+ getSettings: mock(() => Promise.resolve({})),
92
+ updateSettings,
93
+ };
94
+ const grantStore = { grant };
95
+
96
+ await applyPendingConfigRequest(
97
+ agentSettingsStore as any,
98
+ grantStore as any,
99
+ {
100
+ agentId: "agent-1",
101
+ reason: "Allow API access",
102
+ grants: ["api.example.com"],
103
+ }
104
+ );
105
+
106
+ expect(updateSettings).not.toHaveBeenCalled();
107
+ expect(grant).toHaveBeenCalledWith("agent-1", "api.example.com", null);
108
+ });
109
+
110
+ test("builds readable config request text", () => {
111
+ const text = buildConfigRequestText({
112
+ agentId: "agent-1",
113
+ reason: "Install skill",
114
+ message: "Needed for triage",
115
+ skills: [{ repo: "ops-triage", name: "Ops Triage" }],
116
+ nixPackages: ["ffmpeg"],
117
+ grants: ["api.example.com"],
118
+ providers: ["openai"],
119
+ });
120
+
121
+ expect(text).toContain("Configuration Change Request");
122
+ expect(text).toContain("Ops Triage");
123
+ expect(text).toContain("ffmpeg");
124
+ expect(text).toContain("api.example.com");
125
+ expect(text).toContain("openai");
126
+ });
127
+ });
@@ -0,0 +1,144 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { MockRedisClient } from "@lobu/core/testing";
3
+ import { AgentMetadataStore } from "../auth/agent-metadata-store";
4
+ import { UserAgentsStore } from "../auth/user-agents-store";
5
+ import { createConnectionCrudRoutes } from "../routes/public/connections";
6
+ import { setAuthProvider } from "../routes/public/settings-auth";
7
+
8
+ describe("connection routes", () => {
9
+ let redis: MockRedisClient;
10
+ let agentMetadataStore: AgentMetadataStore;
11
+ let userAgentsStore: UserAgentsStore;
12
+
13
+ beforeEach(async () => {
14
+ redis = new MockRedisClient();
15
+ agentMetadataStore = new AgentMetadataStore(redis as any);
16
+ userAgentsStore = new UserAgentsStore(redis as any);
17
+
18
+ await agentMetadataStore.createAgent(
19
+ "agent-1",
20
+ "Agent 1",
21
+ "telegram",
22
+ "u1"
23
+ );
24
+ await agentMetadataStore.createAgent(
25
+ "sandbox-1",
26
+ "Sandbox 1",
27
+ "telegram",
28
+ "u1",
29
+ {
30
+ parentConnectionId: "conn-1",
31
+ }
32
+ );
33
+ await userAgentsStore.addAgent("telegram", "u1", "agent-1");
34
+ });
35
+
36
+ afterEach(() => {
37
+ setAuthProvider(null);
38
+ });
39
+
40
+ function buildApp() {
41
+ return createConnectionCrudRoutes(
42
+ {
43
+ async listConnections(filters?: any) {
44
+ const connection = {
45
+ id: "conn-1",
46
+ platform: "telegram",
47
+ templateAgentId: "agent-1",
48
+ config: { platform: "telegram" },
49
+ settings: {},
50
+ metadata: {},
51
+ status: "active",
52
+ createdAt: 1,
53
+ updatedAt: 1,
54
+ };
55
+ if (
56
+ filters?.templateAgentId &&
57
+ filters.templateAgentId !== "agent-1"
58
+ ) {
59
+ return [];
60
+ }
61
+ return [connection];
62
+ },
63
+ async getConnection(id: string) {
64
+ if (id !== "conn-1") return null;
65
+ return {
66
+ id: "conn-1",
67
+ platform: "telegram",
68
+ templateAgentId: "agent-1",
69
+ config: { platform: "telegram" },
70
+ settings: {},
71
+ metadata: {},
72
+ status: "active",
73
+ createdAt: 1,
74
+ updatedAt: 1,
75
+ };
76
+ },
77
+ has() {
78
+ return true;
79
+ },
80
+ getServices() {
81
+ return {
82
+ getQueue() {
83
+ return {
84
+ getRedisClient() {
85
+ return redis;
86
+ },
87
+ };
88
+ },
89
+ };
90
+ },
91
+ } as any,
92
+ {
93
+ userAgentsStore,
94
+ agentMetadataStore: {
95
+ getMetadata: (agentId: string) =>
96
+ agentMetadataStore.getMetadata(agentId),
97
+ listSandboxes: (connectionId: string) =>
98
+ agentMetadataStore.listSandboxes(connectionId),
99
+ },
100
+ }
101
+ );
102
+ }
103
+
104
+ test("forbids non-admin sessions from listing all connections", async () => {
105
+ setAuthProvider(() => ({
106
+ userId: "u1",
107
+ platform: "telegram",
108
+ exp: Date.now() + 60_000,
109
+ }));
110
+
111
+ const response = await buildApp().request("/api/v1/connections");
112
+ expect(response.status).toBe(403);
113
+ });
114
+
115
+ test("allows external owner sessions to list connections for their agent", async () => {
116
+ setAuthProvider(() => ({
117
+ userId: "u1",
118
+ oauthUserId: "u1",
119
+ platform: "external",
120
+ exp: Date.now() + 60_000,
121
+ }));
122
+
123
+ const response = await buildApp().request(
124
+ "/api/v1/connections?templateAgentId=agent-1"
125
+ );
126
+ expect(response.status).toBe(200);
127
+ const data = (await response.json()) as any;
128
+ expect(data.connections).toHaveLength(1);
129
+ expect(data.connections[0]?.id).toBe("conn-1");
130
+ });
131
+
132
+ test("forbids sandbox listing when session cannot access the connection template agent", async () => {
133
+ setAuthProvider(() => ({
134
+ userId: "u2",
135
+ platform: "telegram",
136
+ exp: Date.now() + 60_000,
137
+ }));
138
+
139
+ const response = await buildApp().request(
140
+ "/api/v1/connections/conn-1/sandboxes"
141
+ );
142
+ expect(response.status).toBe(403);
143
+ });
144
+ });
@@ -0,0 +1,92 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
+ import type { GatewayConfig } from "../config";
3
+ import { CoreServices } from "../services/core-services";
4
+ import { MockMessageQueue } from "./setup";
5
+ import {
6
+ RedisAgentAccessStore,
7
+ RedisAgentConfigStore,
8
+ RedisAgentConnectionStore,
9
+ } from "../stores/redis-agent-store";
10
+
11
+ function createGatewayConfig(
12
+ overrides?: Partial<GatewayConfig>
13
+ ): GatewayConfig {
14
+ return {
15
+ agentDefaults: {},
16
+ sessionTimeoutMinutes: 5,
17
+ logLevel: "INFO",
18
+ queues: {
19
+ connectionString: "redis://test",
20
+ directMessage: "direct_message",
21
+ messageQueue: "message_queue",
22
+ retryLimit: 3,
23
+ retryDelay: 1,
24
+ expireInHours: 24,
25
+ },
26
+ anthropicProxy: {
27
+ enabled: true,
28
+ },
29
+ orchestration: {
30
+ deploymentMode: "docker",
31
+ queues: {
32
+ connectionString: "redis://test",
33
+ retryLimit: 3,
34
+ retryDelay: 1,
35
+ expireInSeconds: 3600,
36
+ },
37
+ worker: {
38
+ image: {
39
+ repository: "lobu-worker",
40
+ tag: "latest",
41
+ digest: "",
42
+ pullPolicy: "Always",
43
+ },
44
+ imagePullSecrets: [],
45
+ serviceAccountName: "lobu-worker",
46
+ runtimeClassName: "",
47
+ startupTimeoutSeconds: 90,
48
+ resources: {
49
+ requests: { cpu: "100m", memory: "256Mi" },
50
+ limits: { cpu: "1000m", memory: "2Gi" },
51
+ },
52
+ idleCleanupMinutes: 60,
53
+ maxDeployments: 100,
54
+ },
55
+ kubernetes: { namespace: "lobu" },
56
+ cleanup: {
57
+ initialDelayMs: 1000,
58
+ intervalMs: 60000,
59
+ veryOldDays: 7,
60
+ },
61
+ },
62
+ mcp: {
63
+ publicGatewayUrl: "http://localhost:8080",
64
+ internalGatewayUrl: "http://gateway:8080",
65
+ },
66
+ health: {
67
+ checkIntervalMs: 1000,
68
+ staleThresholdMs: 2000,
69
+ protectActiveWorkers: true,
70
+ },
71
+ ...overrides,
72
+ };
73
+ }
74
+
75
+ afterEach(() => {
76
+ delete process.env.LOBU_WORKSPACE_ROOT;
77
+ });
78
+
79
+ describe("CoreServices store selection", () => {
80
+ test("uses Redis-backed stores by default when no file-first config is present", async () => {
81
+ const coreServices = new CoreServices(createGatewayConfig());
82
+ (coreServices as any).queue = new MockMessageQueue();
83
+
84
+ await (coreServices as any).initializeSessionServices();
85
+
86
+ expect(coreServices.getConfigStore()).toBeInstanceOf(RedisAgentConfigStore);
87
+ expect(coreServices.getConnectionStore()).toBeInstanceOf(
88
+ RedisAgentConnectionStore
89
+ );
90
+ expect(coreServices.getAccessStore()).toBeInstanceOf(RedisAgentAccessStore);
91
+ });
92
+ });