@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,229 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import type {
3
+ ProviderConfigEntry,
4
+ SkillConfig,
5
+ SystemSkillEntry,
6
+ SystemSkillsConfigFile,
7
+ } from "@lobu/core";
8
+ import { createLogger } from "@lobu/core";
9
+
10
+ const logger = createLogger("system-skills-service");
11
+
12
+ // Block sensitive env vars from ${env:VAR} substitution in system skills config
13
+ const ENV_SUBSTITUTION_BLOCKLIST = new Set([
14
+ "ENCRYPTION_KEY",
15
+ "ADMIN_PASSWORD",
16
+ "DATABASE_PASSWORD",
17
+ "DATABASE_URL",
18
+ "REDIS_URL",
19
+ "REDIS_PASSWORD",
20
+ "SLACK_CLIENT_SECRET",
21
+ "SLACK_SIGNING_SECRET",
22
+ "GH_TOKEN",
23
+ "GITHUB_TOKEN",
24
+ "AWS_SECRET_ACCESS_KEY",
25
+ "SENTRY_DSN",
26
+ ]);
27
+
28
+ export interface RuntimeSystemSkill {
29
+ id: string;
30
+ repo: string;
31
+ name: string;
32
+ description?: string;
33
+ instructions?: string;
34
+ content: string;
35
+ }
36
+
37
+ export class SystemSkillsService {
38
+ private configUrl?: string;
39
+ private loaded?: SystemSkillsConfigFile;
40
+ private rawLoaded?: SystemSkillsConfigFile;
41
+ private loadAttempted = false;
42
+
43
+ constructor(configUrl?: string, preloadedSkills?: SystemSkillEntry[]) {
44
+ this.configUrl = configUrl;
45
+ if (preloadedSkills) {
46
+ const config: SystemSkillsConfigFile = { skills: preloadedSkills };
47
+ this.loaded = config;
48
+ this.rawLoaded = config;
49
+ logger.info(
50
+ `Loaded ${preloadedSkills.length} system skill(s) (injected)`
51
+ );
52
+ }
53
+ }
54
+
55
+ async getSystemSkills(): Promise<SkillConfig[]> {
56
+ const config = await this.loadConfig();
57
+ if (!config) return [];
58
+ return config.skills.map((entry) => this.toSkillConfig(entry));
59
+ }
60
+
61
+ /**
62
+ * Returns only skills that are discoverable via search.
63
+ * Filters out entries with `hidden: true` (e.g. Owletto which is embedded).
64
+ */
65
+ async getSearchableSkills(): Promise<SkillConfig[]> {
66
+ const config = await this.loadConfig();
67
+ if (!config) return [];
68
+ return config.skills
69
+ .filter((entry) => !entry.hidden)
70
+ .map((entry) => this.toSkillConfig(entry));
71
+ }
72
+
73
+ /**
74
+ * Returns skills with original ${env:*} patterns intact (not substituted).
75
+ * Used by the admin env catalog to discover which env vars are referenced.
76
+ */
77
+ async getRawSystemSkills(): Promise<SkillConfig[]> {
78
+ await this.loadConfig();
79
+ if (!this.rawLoaded) return [];
80
+ return this.rawLoaded.skills.map((entry) => this.toSkillConfig(entry));
81
+ }
82
+
83
+ async getProviderConfigs(): Promise<Record<string, ProviderConfigEntry>> {
84
+ const config = await this.loadConfig();
85
+ if (!config) return {};
86
+ const result: Record<string, ProviderConfigEntry> = {};
87
+ for (const skill of config.skills) {
88
+ if (!skill.providers) continue;
89
+ for (const provider of skill.providers) {
90
+ result[skill.id] = provider;
91
+ }
92
+ }
93
+ return result;
94
+ }
95
+
96
+ async getRuntimeSystemSkills(): Promise<RuntimeSystemSkill[]> {
97
+ const config = await this.loadConfig();
98
+ if (!config) return [];
99
+ return config.skills.map((entry) => this.toRuntimeSystemSkill(entry));
100
+ }
101
+
102
+ private toSkillConfig(entry: SystemSkillEntry): SkillConfig {
103
+ return {
104
+ repo: `system/${entry.id}`,
105
+ name: entry.name,
106
+ description: entry.description,
107
+ instructions: entry.instructions,
108
+ enabled: true,
109
+ system: true,
110
+ mcpServers: entry.mcpServers,
111
+ nixPackages: entry.nixPackages,
112
+ permissions: entry.permissions,
113
+ };
114
+ }
115
+
116
+ private toRuntimeSystemSkill(entry: SystemSkillEntry): RuntimeSystemSkill {
117
+ const repo = `system/${entry.id}`;
118
+ const lines: string[] = [
119
+ `# ${entry.name}`,
120
+ "",
121
+ `System skill ID: \`${repo}\``,
122
+ ];
123
+
124
+ if (entry.instructions?.trim()) {
125
+ lines.push("", `**Instructions:** ${entry.instructions.trim()}`);
126
+ }
127
+
128
+ if (entry.description?.trim()) {
129
+ lines.push("", entry.description.trim());
130
+ }
131
+
132
+ if (entry.mcpServers?.length) {
133
+ lines.push("", "## MCP Servers");
134
+ for (const mcp of entry.mcpServers) {
135
+ const endpoint = mcp.url || mcp.command || "n/a";
136
+ lines.push(`- ${mcp.name || mcp.id} (\`${mcp.id}\`): ${endpoint}`);
137
+ }
138
+ }
139
+
140
+ if (entry.permissions?.length) {
141
+ lines.push(
142
+ "",
143
+ "## Network Permissions",
144
+ `- ${entry.permissions.join(", ")}`
145
+ );
146
+ }
147
+
148
+ if (entry.nixPackages?.length) {
149
+ lines.push("", "## Nix Packages", `- ${entry.nixPackages.join(", ")}`);
150
+ }
151
+
152
+ lines.push(
153
+ "",
154
+ "## Usage",
155
+ "- Use MCP tools for API access (auth handled by Owletto).",
156
+ "- Skills with `permissions` require user-approved network grants.",
157
+ "- Skills with `nixPackages` require user-approved package installs."
158
+ );
159
+
160
+ return {
161
+ id: entry.id,
162
+ repo,
163
+ name: entry.name,
164
+ description: entry.description,
165
+ instructions: entry.instructions,
166
+ content: lines.join("\n"),
167
+ };
168
+ }
169
+
170
+ /**
171
+ * Clear cached config and optionally set a new URL.
172
+ * Next call to getSystemSkills() etc. will re-fetch.
173
+ */
174
+ reload(newUrl?: string): void {
175
+ this.loaded = undefined;
176
+ this.rawLoaded = undefined;
177
+ if (newUrl !== undefined) {
178
+ this.configUrl = newUrl;
179
+ }
180
+ }
181
+
182
+ private async loadConfig(): Promise<SystemSkillsConfigFile | null> {
183
+ if (this.loaded) return this.loaded;
184
+ if (this.loadAttempted || !this.configUrl) return null;
185
+ try {
186
+ let raw: string;
187
+ if (
188
+ this.configUrl.startsWith("http://") ||
189
+ this.configUrl.startsWith("https://")
190
+ ) {
191
+ const response = await fetch(this.configUrl);
192
+ if (!response.ok) {
193
+ logger.error(
194
+ `Failed to fetch system skills config: ${response.status}`
195
+ );
196
+ return null;
197
+ }
198
+ raw = await response.text();
199
+ } else {
200
+ raw = await readFile(this.configUrl, "utf-8");
201
+ }
202
+ this.rawLoaded = JSON.parse(raw) as SystemSkillsConfigFile;
203
+ const substituted = raw.replace(
204
+ /\$\{env:([^}]+)\}/g,
205
+ (_match, varName) => {
206
+ if (ENV_SUBSTITUTION_BLOCKLIST.has(varName)) {
207
+ logger.warn(
208
+ `Blocked env substitution for sensitive var: ${varName}`
209
+ );
210
+ return "";
211
+ }
212
+ return process.env[varName] || "";
213
+ }
214
+ );
215
+ const parsed = JSON.parse(substituted) as SystemSkillsConfigFile;
216
+ if (!Array.isArray(parsed.skills)) {
217
+ logger.error("Invalid system skills config: missing 'skills' array");
218
+ return null;
219
+ }
220
+ this.loaded = parsed;
221
+ logger.info(`Loaded ${parsed.skills.length} system skill(s)`);
222
+ return parsed;
223
+ } catch (error) {
224
+ this.loadAttempted = true;
225
+ logger.debug("System skills config not available", { error });
226
+ return null;
227
+ }
228
+ }
229
+ }