opalserve 0.1.2 → 3.0.0

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 (310) hide show
  1. package/README.md +108 -115
  2. package/assets/logo.svg +51 -0
  3. package/dist/auth/api-keys.d.ts +7 -0
  4. package/dist/auth/api-keys.d.ts.map +1 -0
  5. package/dist/auth/api-keys.js +12 -0
  6. package/dist/auth/api-keys.js.map +1 -0
  7. package/dist/auth/index.d.ts +4 -0
  8. package/dist/auth/index.d.ts.map +1 -0
  9. package/dist/auth/index.js +4 -0
  10. package/dist/auth/index.js.map +1 -0
  11. package/dist/auth/middleware.d.ts +11 -0
  12. package/dist/auth/middleware.d.ts.map +1 -0
  13. package/dist/auth/middleware.js +46 -0
  14. package/dist/auth/middleware.js.map +1 -0
  15. package/dist/auth/passwords.d.ts +3 -0
  16. package/dist/auth/passwords.d.ts.map +1 -0
  17. package/dist/auth/passwords.js +33 -0
  18. package/dist/auth/passwords.js.map +1 -0
  19. package/dist/cli/commands/admin.d.ts +13 -0
  20. package/dist/cli/commands/admin.d.ts.map +1 -0
  21. package/dist/cli/commands/admin.js +261 -0
  22. package/dist/cli/commands/admin.js.map +1 -0
  23. package/dist/cli/commands/auth.d.ts +4 -0
  24. package/dist/cli/commands/auth.d.ts.map +1 -0
  25. package/dist/cli/commands/auth.js +77 -0
  26. package/dist/cli/commands/auth.js.map +1 -0
  27. package/dist/cli/commands/context.d.ts +5 -0
  28. package/dist/cli/commands/context.d.ts.map +1 -0
  29. package/dist/cli/commands/context.js +190 -0
  30. package/dist/cli/commands/context.js.map +1 -0
  31. package/dist/cli/commands/health.d.ts +4 -0
  32. package/dist/cli/commands/health.d.ts.map +1 -0
  33. package/dist/cli/commands/health.js +36 -0
  34. package/dist/cli/commands/health.js.map +1 -0
  35. package/dist/cli/commands/init.d.ts +2 -0
  36. package/dist/cli/commands/init.d.ts.map +1 -0
  37. package/dist/cli/commands/init.js +81 -0
  38. package/dist/cli/commands/init.js.map +1 -0
  39. package/dist/cli/commands/server.d.ts +10 -0
  40. package/dist/cli/commands/server.d.ts.map +1 -0
  41. package/dist/cli/commands/server.js +108 -0
  42. package/dist/cli/commands/server.js.map +1 -0
  43. package/dist/cli/commands/start.d.ts +6 -0
  44. package/dist/cli/commands/start.d.ts.map +1 -0
  45. package/dist/cli/commands/start.js +63 -0
  46. package/dist/cli/commands/start.js.map +1 -0
  47. package/dist/cli/commands/status.d.ts +2 -0
  48. package/dist/cli/commands/status.d.ts.map +1 -0
  49. package/dist/cli/commands/status.js +21 -0
  50. package/dist/cli/commands/status.js.map +1 -0
  51. package/dist/cli/commands/sync.d.ts +2 -0
  52. package/dist/cli/commands/sync.d.ts.map +1 -0
  53. package/dist/cli/commands/sync.js +58 -0
  54. package/dist/cli/commands/sync.js.map +1 -0
  55. package/dist/cli/commands/tools.d.ts +8 -0
  56. package/dist/cli/commands/tools.d.ts.map +1 -0
  57. package/dist/cli/commands/tools.js +28 -0
  58. package/dist/cli/commands/tools.js.map +1 -0
  59. package/dist/cli/index.d.ts +1 -1
  60. package/dist/cli/index.d.ts.map +1 -1
  61. package/dist/cli/index.js +156 -30
  62. package/dist/cli/index.js.map +1 -1
  63. package/dist/cli/ui/banner.d.ts +8 -0
  64. package/dist/cli/ui/banner.d.ts.map +1 -0
  65. package/dist/cli/ui/banner.js +54 -0
  66. package/dist/cli/ui/banner.js.map +1 -0
  67. package/dist/cli/ui/prompts.d.ts +11 -0
  68. package/dist/cli/ui/prompts.d.ts.map +1 -0
  69. package/dist/cli/ui/prompts.js +74 -0
  70. package/dist/cli/ui/prompts.js.map +1 -0
  71. package/dist/cli/ui/table.d.ts +5 -0
  72. package/dist/cli/ui/table.d.ts.map +1 -0
  73. package/dist/cli/ui/table.js +103 -0
  74. package/dist/cli/ui/table.js.map +1 -0
  75. package/dist/config/credentials.d.ts +10 -0
  76. package/dist/config/credentials.d.ts.map +1 -0
  77. package/dist/config/credentials.js +33 -0
  78. package/dist/config/credentials.js.map +1 -0
  79. package/dist/config/defaults.d.ts +5 -0
  80. package/dist/config/defaults.d.ts.map +1 -0
  81. package/dist/config/defaults.js +20 -0
  82. package/dist/config/defaults.js.map +1 -0
  83. package/dist/config/loader.d.ts +4 -0
  84. package/dist/config/loader.d.ts.map +1 -0
  85. package/dist/config/loader.js +57 -0
  86. package/dist/config/loader.js.map +1 -0
  87. package/dist/config/schema.d.ts +2 -0
  88. package/dist/config/schema.d.ts.map +1 -0
  89. package/dist/config/schema.js +2 -0
  90. package/dist/config/schema.js.map +1 -0
  91. package/dist/constants.d.ts +5 -0
  92. package/dist/constants.d.ts.map +1 -0
  93. package/dist/constants.js +5 -0
  94. package/dist/constants.js.map +1 -0
  95. package/dist/context/chunker.d.ts +2 -0
  96. package/dist/context/chunker.d.ts.map +1 -0
  97. package/dist/context/chunker.js +81 -0
  98. package/dist/context/chunker.js.map +1 -0
  99. package/dist/context/index.d.ts +26 -0
  100. package/dist/context/index.d.ts.map +1 -0
  101. package/dist/context/index.js +97 -0
  102. package/dist/context/index.js.map +1 -0
  103. package/dist/core/discovery.d.ts +17 -0
  104. package/dist/core/discovery.d.ts.map +1 -0
  105. package/dist/core/discovery.js +31 -0
  106. package/dist/core/discovery.js.map +1 -0
  107. package/dist/core/proxy.d.ts +14 -0
  108. package/dist/core/proxy.d.ts.map +1 -0
  109. package/dist/core/proxy.js +36 -0
  110. package/dist/core/proxy.js.map +1 -0
  111. package/dist/core/registry.d.ts +30 -31
  112. package/dist/core/registry.d.ts.map +1 -1
  113. package/dist/core/registry.js +92 -234
  114. package/dist/core/registry.js.map +1 -1
  115. package/dist/core/secrets.d.ts +4 -0
  116. package/dist/core/secrets.d.ts.map +1 -0
  117. package/dist/core/secrets.js +40 -0
  118. package/dist/core/secrets.js.map +1 -0
  119. package/dist/core/server-manager.d.ts +18 -0
  120. package/dist/core/server-manager.d.ts.map +1 -0
  121. package/dist/core/server-manager.js +120 -0
  122. package/dist/core/server-manager.js.map +1 -0
  123. package/dist/dashboard/assets/index-BNOtcUPs.js +257 -0
  124. package/dist/dashboard/assets/index-Duwp34GW.css +1 -0
  125. package/dist/dashboard/index.html +14 -0
  126. package/dist/index.d.ts +22 -12
  127. package/dist/index.d.ts.map +1 -1
  128. package/dist/index.js +31 -9
  129. package/dist/index.js.map +1 -1
  130. package/dist/integrations/github.d.ts +5 -0
  131. package/dist/integrations/github.d.ts.map +1 -0
  132. package/dist/integrations/github.js +63 -0
  133. package/dist/integrations/github.js.map +1 -0
  134. package/dist/integrations/slack.d.ts +21 -0
  135. package/dist/integrations/slack.d.ts.map +1 -0
  136. package/dist/integrations/slack.js +61 -0
  137. package/dist/integrations/slack.js.map +1 -0
  138. package/dist/monitoring/tracker.d.ts +31 -0
  139. package/dist/monitoring/tracker.d.ts.map +1 -0
  140. package/dist/monitoring/tracker.js +86 -0
  141. package/dist/monitoring/tracker.js.map +1 -0
  142. package/dist/server/app.d.ts +8 -0
  143. package/dist/server/app.d.ts.map +1 -0
  144. package/dist/server/app.js +81 -0
  145. package/dist/server/app.js.map +1 -0
  146. package/dist/server/mcp-gateway.d.ts +12 -0
  147. package/dist/server/mcp-gateway.d.ts.map +1 -0
  148. package/dist/server/mcp-gateway.js +91 -0
  149. package/dist/server/mcp-gateway.js.map +1 -0
  150. package/dist/server/routes/auth.d.ts +4 -0
  151. package/dist/server/routes/auth.d.ts.map +1 -0
  152. package/dist/server/routes/auth.js +117 -0
  153. package/dist/server/routes/auth.js.map +1 -0
  154. package/dist/server/routes/context.d.ts +4 -0
  155. package/dist/server/routes/context.d.ts.map +1 -0
  156. package/dist/server/routes/context.js +107 -0
  157. package/dist/server/routes/context.js.map +1 -0
  158. package/dist/server/routes/health.d.ts +4 -0
  159. package/dist/server/routes/health.d.ts.map +1 -0
  160. package/dist/server/routes/health.js +22 -0
  161. package/dist/server/routes/health.js.map +1 -0
  162. package/dist/server/routes/servers.d.ts +4 -0
  163. package/dist/server/routes/servers.d.ts.map +1 -0
  164. package/dist/server/routes/servers.js +47 -0
  165. package/dist/server/routes/servers.js.map +1 -0
  166. package/dist/server/routes/stats.d.ts +4 -0
  167. package/dist/server/routes/stats.d.ts.map +1 -0
  168. package/dist/server/routes/stats.js +97 -0
  169. package/dist/server/routes/stats.js.map +1 -0
  170. package/dist/server/routes/team-servers.d.ts +4 -0
  171. package/dist/server/routes/team-servers.d.ts.map +1 -0
  172. package/dist/server/routes/team-servers.js +108 -0
  173. package/dist/server/routes/team-servers.js.map +1 -0
  174. package/dist/server/routes/tools.d.ts +4 -0
  175. package/dist/server/routes/tools.d.ts.map +1 -0
  176. package/dist/server/routes/tools.js +44 -0
  177. package/dist/server/routes/tools.js.map +1 -0
  178. package/dist/server/routes/webhooks.d.ts +4 -0
  179. package/dist/server/routes/webhooks.d.ts.map +1 -0
  180. package/dist/server/routes/webhooks.js +77 -0
  181. package/dist/server/routes/webhooks.js.map +1 -0
  182. package/dist/storage/database.d.ts +48 -0
  183. package/dist/storage/database.d.ts.map +1 -0
  184. package/dist/storage/database.js +336 -0
  185. package/dist/storage/database.js.map +1 -0
  186. package/dist/storage/repositories/server-repo.d.ts +2 -0
  187. package/dist/storage/repositories/server-repo.d.ts.map +1 -0
  188. package/dist/storage/repositories/server-repo.js +3 -0
  189. package/dist/storage/repositories/server-repo.js.map +1 -0
  190. package/dist/storage/repositories/tool-repo.d.ts +2 -0
  191. package/dist/storage/repositories/tool-repo.d.ts.map +1 -0
  192. package/dist/storage/repositories/tool-repo.js +3 -0
  193. package/dist/storage/repositories/tool-repo.js.map +1 -0
  194. package/dist/types/index.d.ts +482 -587
  195. package/dist/types/index.d.ts.map +1 -1
  196. package/dist/types/index.js +52 -69
  197. package/dist/types/index.js.map +1 -1
  198. package/dist/utils/logger.d.ts +7 -0
  199. package/dist/utils/logger.d.ts.map +1 -0
  200. package/dist/utils/logger.js +23 -0
  201. package/dist/utils/logger.js.map +1 -0
  202. package/package.json +67 -56
  203. package/.env.example +0 -19
  204. package/config/servers.example.yaml +0 -67
  205. package/config/servers.yaml +0 -2
  206. package/dist/cli/discover.d.ts +0 -3
  207. package/dist/cli/discover.d.ts.map +0 -1
  208. package/dist/cli/discover.js +0 -160
  209. package/dist/cli/discover.js.map +0 -1
  210. package/dist/connectors/base.d.ts +0 -49
  211. package/dist/connectors/base.d.ts.map +0 -1
  212. package/dist/connectors/base.js +0 -45
  213. package/dist/connectors/base.js.map +0 -1
  214. package/dist/connectors/custom.d.ts +0 -19
  215. package/dist/connectors/custom.d.ts.map +0 -1
  216. package/dist/connectors/custom.js +0 -129
  217. package/dist/connectors/custom.js.map +0 -1
  218. package/dist/connectors/github.d.ts +0 -18
  219. package/dist/connectors/github.d.ts.map +0 -1
  220. package/dist/connectors/github.js +0 -188
  221. package/dist/connectors/github.js.map +0 -1
  222. package/dist/connectors/google-drive.d.ts +0 -18
  223. package/dist/connectors/google-drive.d.ts.map +0 -1
  224. package/dist/connectors/google-drive.js +0 -209
  225. package/dist/connectors/google-drive.js.map +0 -1
  226. package/dist/connectors/index.d.ts +0 -11
  227. package/dist/connectors/index.d.ts.map +0 -1
  228. package/dist/connectors/index.js +0 -76
  229. package/dist/connectors/index.js.map +0 -1
  230. package/dist/connectors/postgres.d.ts +0 -18
  231. package/dist/connectors/postgres.d.ts.map +0 -1
  232. package/dist/connectors/postgres.js +0 -140
  233. package/dist/connectors/postgres.js.map +0 -1
  234. package/dist/connectors/slack.d.ts +0 -18
  235. package/dist/connectors/slack.d.ts.map +0 -1
  236. package/dist/connectors/slack.js +0 -181
  237. package/dist/connectors/slack.js.map +0 -1
  238. package/dist/core/auth.d.ts +0 -26
  239. package/dist/core/auth.d.ts.map +0 -1
  240. package/dist/core/auth.js +0 -81
  241. package/dist/core/auth.js.map +0 -1
  242. package/dist/core/tokenizer.d.ts +0 -16
  243. package/dist/core/tokenizer.d.ts.map +0 -1
  244. package/dist/core/tokenizer.js +0 -29
  245. package/dist/core/tokenizer.js.map +0 -1
  246. package/dist/governance/audit.d.ts +0 -27
  247. package/dist/governance/audit.d.ts.map +0 -1
  248. package/dist/governance/audit.js +0 -149
  249. package/dist/governance/audit.js.map +0 -1
  250. package/dist/governance/index.d.ts +0 -5
  251. package/dist/governance/index.d.ts.map +0 -1
  252. package/dist/governance/index.js +0 -5
  253. package/dist/governance/index.js.map +0 -1
  254. package/dist/governance/policy.d.ts +0 -20
  255. package/dist/governance/policy.d.ts.map +0 -1
  256. package/dist/governance/policy.js +0 -162
  257. package/dist/governance/policy.js.map +0 -1
  258. package/dist/governance/rate-limiter.d.ts +0 -20
  259. package/dist/governance/rate-limiter.d.ts.map +0 -1
  260. package/dist/governance/rate-limiter.js +0 -73
  261. package/dist/governance/rate-limiter.js.map +0 -1
  262. package/dist/governance/types.d.ts +0 -246
  263. package/dist/governance/types.d.ts.map +0 -1
  264. package/dist/governance/types.js +0 -72
  265. package/dist/governance/types.js.map +0 -1
  266. package/dist/identity/access-control.d.ts +0 -15
  267. package/dist/identity/access-control.d.ts.map +0 -1
  268. package/dist/identity/access-control.js +0 -81
  269. package/dist/identity/access-control.js.map +0 -1
  270. package/dist/identity/index.d.ts +0 -4
  271. package/dist/identity/index.d.ts.map +0 -1
  272. package/dist/identity/index.js +0 -4
  273. package/dist/identity/index.js.map +0 -1
  274. package/dist/identity/manager.d.ts +0 -29
  275. package/dist/identity/manager.d.ts.map +0 -1
  276. package/dist/identity/manager.js +0 -167
  277. package/dist/identity/manager.js.map +0 -1
  278. package/dist/identity/types.d.ts +0 -237
  279. package/dist/identity/types.d.ts.map +0 -1
  280. package/dist/identity/types.js +0 -80
  281. package/dist/identity/types.js.map +0 -1
  282. package/dist/registry/server.d.ts +0 -14
  283. package/dist/registry/server.d.ts.map +0 -1
  284. package/dist/registry/server.js +0 -177
  285. package/dist/registry/server.js.map +0 -1
  286. package/dist/utils/config.d.ts +0 -29
  287. package/dist/utils/config.d.ts.map +0 -1
  288. package/dist/utils/config.js +0 -47
  289. package/dist/utils/config.js.map +0 -1
  290. package/dist/utils/index.d.ts +0 -7
  291. package/dist/utils/index.d.ts.map +0 -1
  292. package/dist/utils/index.js +0 -44
  293. package/dist/utils/index.js.map +0 -1
  294. package/dist/workflow/engine.d.ts +0 -18
  295. package/dist/workflow/engine.d.ts.map +0 -1
  296. package/dist/workflow/engine.js +0 -155
  297. package/dist/workflow/engine.js.map +0 -1
  298. package/dist/workflow/index.d.ts +0 -4
  299. package/dist/workflow/index.d.ts.map +0 -1
  300. package/dist/workflow/index.js +0 -4
  301. package/dist/workflow/index.js.map +0 -1
  302. package/dist/workflow/templates.d.ts +0 -4
  303. package/dist/workflow/templates.d.ts.map +0 -1
  304. package/dist/workflow/templates.js +0 -218
  305. package/dist/workflow/templates.js.map +0 -1
  306. package/dist/workflow/types.d.ts +0 -255
  307. package/dist/workflow/types.d.ts.map +0 -1
  308. package/dist/workflow/types.js +0 -48
  309. package/dist/workflow/types.js.map +0 -1
  310. package/eslint.config.js +0 -25
@@ -0,0 +1,91 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { z } from 'zod';
4
+ import { createLogger } from '../utils/logger.js';
5
+ const log = createLogger('mcp-gateway');
6
+ export class McpGateway {
7
+ registry;
8
+ mcpServer;
9
+ constructor(registry) {
10
+ this.registry = registry;
11
+ this.mcpServer = new McpServer({ name: 'opalserve', version: '3.0.0' }, { capabilities: { tools: {} } });
12
+ this.registerMetaTools();
13
+ this.registerProxyTools();
14
+ }
15
+ registerMetaTools() {
16
+ // Meta-tool: search across all servers
17
+ this.mcpServer.tool('opalserve_search', 'Search for tools across all registered MCP servers', { query: z.string().describe('Search query for finding tools') }, async ({ query }) => {
18
+ const results = this.registry.searchTools(query, { limit: 10 });
19
+ const formatted = results.map(r => `${r.tool.id} — ${r.tool.description || r.tool.name}`).join('\n');
20
+ return {
21
+ content: [{
22
+ type: 'text',
23
+ text: results.length > 0
24
+ ? `Found ${results.length} tools:\n\n${formatted}`
25
+ : `No tools found matching "${query}"`,
26
+ }],
27
+ };
28
+ });
29
+ // Meta-tool: list all servers and their status
30
+ this.mcpServer.tool('opalserve_servers', 'List all registered MCP servers and their connection status', {}, async () => {
31
+ const servers = this.registry.listServers();
32
+ const formatted = servers.map(s => `${s.name} [${s.status}] — ${s.toolCount} tools (${s.transport})`).join('\n');
33
+ return {
34
+ content: [{
35
+ type: 'text',
36
+ text: servers.length > 0
37
+ ? `${servers.length} servers:\n\n${formatted}`
38
+ : 'No servers registered',
39
+ }],
40
+ };
41
+ });
42
+ }
43
+ registerProxyTools() {
44
+ // Register all discovered tools as proxy-through tools
45
+ const tools = this.registry.listTools();
46
+ for (const tool of tools) {
47
+ const toolId = tool.id;
48
+ const inputSchema = (tool.inputSchema && typeof tool.inputSchema === 'object')
49
+ ? tool.inputSchema
50
+ : {};
51
+ // Build a Zod schema from the JSON Schema properties
52
+ const properties = (inputSchema.properties || {});
53
+ const required = (inputSchema.required || []);
54
+ const zodShape = {};
55
+ for (const [key, _prop] of Object.entries(properties)) {
56
+ // Use permissive schema — actual validation happens on the backend server
57
+ zodShape[key] = required.includes(key)
58
+ ? z.any()
59
+ : z.any().optional();
60
+ }
61
+ try {
62
+ this.mcpServer.tool(`${tool.serverName}__${tool.name}`, tool.description || tool.name, zodShape, async (args) => {
63
+ try {
64
+ const result = await this.registry.callTool(toolId, args);
65
+ return result;
66
+ }
67
+ catch (error) {
68
+ const message = error instanceof Error ? error.message : String(error);
69
+ return {
70
+ content: [{ type: 'text', text: `Error: ${message}` }],
71
+ isError: true,
72
+ };
73
+ }
74
+ });
75
+ }
76
+ catch (error) {
77
+ log.warn({ tool: toolId, error }, 'Failed to register proxy tool');
78
+ }
79
+ }
80
+ log.info({ count: tools.length }, 'Proxy tools registered');
81
+ }
82
+ async connectStdio() {
83
+ const transport = new StdioServerTransport();
84
+ await this.mcpServer.connect(transport);
85
+ log.info('MCP gateway connected via stdio');
86
+ }
87
+ getServer() {
88
+ return this.mcpServer;
89
+ }
90
+ }
91
+ //# sourceMappingURL=mcp-gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-gateway.js","sourceRoot":"","sources":["../../src/server/mcp-gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { OpalServeRegistry } from '../../core/registry.js';
3
+ export declare function registerAuthRoutes(app: FastifyInstance, registry: OpalServeRegistry): void;
4
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/server/routes/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAKhE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CA+J1F"}
@@ -0,0 +1,117 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { hashPassword, verifyPassword } from '../../auth/passwords.js';
3
+ import { generateApiKey } from '../../auth/api-keys.js';
4
+ import { createAuthMiddleware } from '../../auth/middleware.js';
5
+ export function registerAuthRoutes(app, registry) {
6
+ const db = registry.getDatabase();
7
+ const requireAuth = createAuthMiddleware(db);
8
+ // --- Register ---
9
+ app.post('/api/v1/auth/register', async (request, reply) => {
10
+ const { email, password, displayName } = request.body;
11
+ if (!email || !password) {
12
+ return reply.status(400).send({ ok: false, error: 'Email and password are required' });
13
+ }
14
+ // Check if user already exists
15
+ const existing = db.get('SELECT id FROM users WHERE email = ?', [email]);
16
+ if (existing) {
17
+ return reply.status(409).send({ ok: false, error: 'User with this email already exists' });
18
+ }
19
+ const now = new Date().toISOString();
20
+ const userId = randomUUID();
21
+ const passwordHash = await hashPassword(password);
22
+ db.run('INSERT INTO users (id, email, password_hash, display_name, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)', [userId, email, passwordHash, displayName ?? null, now, now]);
23
+ // Generate API key
24
+ const { key, keyHash, keyPrefix } = generateApiKey();
25
+ const keyId = randomUUID();
26
+ db.run('INSERT INTO api_keys (id, user_id, key_hash, key_prefix, name, created_at) VALUES (?, ?, ?, ?, ?, ?)', [keyId, userId, keyHash, keyPrefix, 'default', now]);
27
+ return reply.status(201).send({
28
+ ok: true,
29
+ data: {
30
+ user: {
31
+ id: userId,
32
+ email,
33
+ displayName: displayName ?? null,
34
+ createdAt: now,
35
+ updatedAt: now,
36
+ },
37
+ apiKey: key,
38
+ },
39
+ });
40
+ });
41
+ // --- Login ---
42
+ app.post('/api/v1/auth/login', async (request, reply) => {
43
+ const { email, password } = request.body;
44
+ if (!email || !password) {
45
+ return reply.status(400).send({ ok: false, error: 'Email and password are required' });
46
+ }
47
+ const row = db.get('SELECT id, email, password_hash, display_name, created_at, updated_at FROM users WHERE email = ?', [email]);
48
+ if (!row) {
49
+ return reply.status(401).send({ ok: false, error: 'Invalid email or password' });
50
+ }
51
+ const valid = await verifyPassword(password, row.password_hash);
52
+ if (!valid) {
53
+ return reply.status(401).send({ ok: false, error: 'Invalid email or password' });
54
+ }
55
+ // Generate new API key
56
+ const now = new Date().toISOString();
57
+ const { key, keyHash, keyPrefix } = generateApiKey();
58
+ const keyId = randomUUID();
59
+ db.run('INSERT INTO api_keys (id, user_id, key_hash, key_prefix, name, created_at) VALUES (?, ?, ?, ?, ?, ?)', [keyId, row.id, keyHash, keyPrefix, 'login', now]);
60
+ return reply.send({
61
+ ok: true,
62
+ data: {
63
+ user: {
64
+ id: row.id,
65
+ email: row.email,
66
+ displayName: row.display_name,
67
+ createdAt: row.created_at,
68
+ updatedAt: row.updated_at,
69
+ },
70
+ apiKey: key,
71
+ },
72
+ });
73
+ });
74
+ // --- Me ---
75
+ app.get('/api/v1/auth/me', { preHandler: requireAuth }, async (request, reply) => {
76
+ if (!request.user) {
77
+ return reply.status(401).send({ ok: false, error: 'Not authenticated' });
78
+ }
79
+ return reply.send({ ok: true, data: request.user });
80
+ });
81
+ // --- Create API Key ---
82
+ app.post('/api/v1/auth/keys', { preHandler: requireAuth }, async (request, reply) => {
83
+ if (!request.user) {
84
+ return reply.status(401).send({ ok: false, error: 'Not authenticated' });
85
+ }
86
+ const { name } = request.body;
87
+ if (!name) {
88
+ return reply.status(400).send({ ok: false, error: 'Key name is required' });
89
+ }
90
+ const now = new Date().toISOString();
91
+ const { key, keyHash, keyPrefix } = generateApiKey();
92
+ const keyId = randomUUID();
93
+ db.run('INSERT INTO api_keys (id, user_id, key_hash, key_prefix, name, created_at) VALUES (?, ?, ?, ?, ?, ?)', [keyId, request.user.id, keyHash, keyPrefix, name, now]);
94
+ return reply.status(201).send({
95
+ ok: true,
96
+ data: { key, keyPrefix },
97
+ });
98
+ });
99
+ // --- Delete API Key ---
100
+ app.delete('/api/v1/auth/keys/:id', { preHandler: requireAuth }, async (request, reply) => {
101
+ if (!request.user) {
102
+ return reply.status(401).send({ ok: false, error: 'Not authenticated' });
103
+ }
104
+ const { id } = request.params;
105
+ // Verify the key belongs to the current user
106
+ const existing = db.get('SELECT user_id FROM api_keys WHERE id = ?', [id]);
107
+ if (!existing) {
108
+ return reply.status(404).send({ ok: false, error: 'API key not found' });
109
+ }
110
+ if (existing.user_id !== request.user.id) {
111
+ return reply.status(403).send({ ok: false, error: 'Cannot delete another user\'s API key' });
112
+ }
113
+ db.run('DELETE FROM api_keys WHERE id = ?', [id]);
114
+ return reply.send({ ok: true });
115
+ });
116
+ }
117
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/server/routes/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { OpalServeRegistry } from '../../core/registry.js';
3
+ export declare function registerContextRoutes(app: FastifyInstance, registry: OpalServeRegistry): void;
4
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/server/routes/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGhE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CA4J7F"}
@@ -0,0 +1,107 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { createAuthMiddleware } from '../../auth/middleware.js';
3
+ export function registerContextRoutes(app, registry) {
4
+ const db = registry.getDatabase();
5
+ const requireAuth = createAuthMiddleware(db);
6
+ // --- Create Document ---
7
+ app.post('/api/v1/context/documents', { preHandler: requireAuth }, async (request, reply) => {
8
+ const { title, content, contentType, source, tags } = request.body;
9
+ if (!title || !content) {
10
+ return reply.status(400).send({ ok: false, error: 'Title and content are required' });
11
+ }
12
+ const now = new Date().toISOString();
13
+ const id = randomUUID();
14
+ const userId = request.user?.id ?? null;
15
+ // Resolve team_id from user membership
16
+ let teamId = null;
17
+ if (userId) {
18
+ const membership = db.get('SELECT team_id FROM team_members WHERE user_id = ?', [userId]);
19
+ teamId = membership?.team_id ?? null;
20
+ }
21
+ db.run(`INSERT INTO context_documents (id, team_id, title, content, content_type, source, tags, uploaded_by, created_at, updated_at)
22
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, teamId, title, content, contentType ?? 'text/markdown', source ?? null, JSON.stringify(tags ?? []), userId, now, now]);
23
+ const doc = {
24
+ id,
25
+ teamId,
26
+ title,
27
+ content,
28
+ contentType: contentType ?? 'text/markdown',
29
+ source: source ?? null,
30
+ tags: tags ?? [],
31
+ uploadedBy: userId,
32
+ createdAt: now,
33
+ updatedAt: now,
34
+ };
35
+ return reply.status(201).send({ ok: true, data: doc });
36
+ });
37
+ // --- List Documents ---
38
+ app.get('/api/v1/context/documents', { preHandler: requireAuth }, async (_request, reply) => {
39
+ const rows = db.all('SELECT * FROM context_documents ORDER BY created_at DESC');
40
+ const data = rows.map(r => ({
41
+ id: r.id,
42
+ teamId: r.team_id,
43
+ title: r.title,
44
+ content: r.content,
45
+ contentType: r.content_type,
46
+ source: r.source,
47
+ tags: JSON.parse(r.tags || '[]'),
48
+ uploadedBy: r.uploaded_by,
49
+ createdAt: r.created_at,
50
+ updatedAt: r.updated_at,
51
+ }));
52
+ return reply.send({ ok: true, data });
53
+ });
54
+ // --- Search Documents ---
55
+ app.get('/api/v1/context/search', { preHandler: requireAuth }, async (request, reply) => {
56
+ const { q } = request.query;
57
+ if (!q || !q.trim()) {
58
+ return reply.status(400).send({ ok: false, error: 'Query parameter "q" is required' });
59
+ }
60
+ const terms = q.toLowerCase().split(/\s+/);
61
+ const rows = db.all('SELECT * FROM context_documents ORDER BY created_at DESC');
62
+ const results = rows
63
+ .map(r => {
64
+ let score = 0;
65
+ const titleL = r.title.toLowerCase();
66
+ const contentL = r.content.toLowerCase();
67
+ const tagsL = (r.tags || '[]').toLowerCase();
68
+ for (const term of terms) {
69
+ if (titleL.includes(term))
70
+ score += 5;
71
+ if (contentL.includes(term))
72
+ score += 3;
73
+ if (tagsL.includes(term))
74
+ score += 2;
75
+ }
76
+ return {
77
+ doc: {
78
+ id: r.id,
79
+ teamId: r.team_id,
80
+ title: r.title,
81
+ content: r.content,
82
+ contentType: r.content_type,
83
+ source: r.source,
84
+ tags: JSON.parse(r.tags || '[]'),
85
+ uploadedBy: r.uploaded_by,
86
+ createdAt: r.created_at,
87
+ updatedAt: r.updated_at,
88
+ },
89
+ score,
90
+ };
91
+ })
92
+ .filter(r => r.score > 0)
93
+ .sort((a, b) => b.score - a.score);
94
+ return reply.send({ ok: true, data: results });
95
+ });
96
+ // --- Delete Document ---
97
+ app.delete('/api/v1/context/documents/:id', { preHandler: requireAuth }, async (request, reply) => {
98
+ const { id } = request.params;
99
+ const existing = db.get('SELECT id FROM context_documents WHERE id = ?', [id]);
100
+ if (!existing) {
101
+ return reply.status(404).send({ ok: false, error: 'Document not found' });
102
+ }
103
+ db.run('DELETE FROM context_documents WHERE id = ?', [id]);
104
+ return reply.send({ ok: true });
105
+ });
106
+ }
107
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/server/routes/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { OpalServeRegistry } from '../../core/registry.js';
3
+ export declare function registerHealthRoutes(app: FastifyInstance, registry: OpalServeRegistry): void;
4
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../../src/server/routes/health.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAqB5F"}
@@ -0,0 +1,22 @@
1
+ export function registerHealthRoutes(app, registry) {
2
+ app.get('/api/v1/health', async (_request, reply) => {
3
+ const servers = registry.listServers();
4
+ const tools = registry.listTools();
5
+ return reply.send({
6
+ ok: true,
7
+ data: {
8
+ status: 'running',
9
+ version: '3.0.0',
10
+ uptime: process.uptime(),
11
+ servers: {
12
+ total: servers.length,
13
+ connected: servers.filter(s => s.status === 'connected').length,
14
+ },
15
+ tools: {
16
+ total: tools.length,
17
+ },
18
+ },
19
+ });
20
+ });
21
+ }
22
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../../src/server/routes/health.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,oBAAoB,CAAC,GAAoB,EAAE,QAA2B;IACpF,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;QAClD,MAAM,OAAO,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC;QAEnC,OAAO,KAAK,CAAC,IAAI,CAAC;YAChB,EAAE,EAAE,IAAI;YACR,IAAI,EAAE;gBACJ,MAAM,EAAE,SAAS;gBACjB,OAAO,EAAE,OAAO;gBAChB,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;gBACxB,OAAO,EAAE;oBACP,KAAK,EAAE,OAAO,CAAC,MAAM;oBACrB,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,MAAM;iBAChE;gBACD,KAAK,EAAE;oBACL,KAAK,EAAE,KAAK,CAAC,MAAM;iBACpB;aACF;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { OpalServeRegistry } from '../../core/registry.js';
3
+ export declare function registerServerRoutes(app: FastifyInstance, registry: OpalServeRegistry): void;
4
+ //# sourceMappingURL=servers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"servers.d.ts","sourceRoot":"","sources":["../../../src/server/routes/servers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAiD5F"}
@@ -0,0 +1,47 @@
1
+ import { McpServerConfigSchema } from '../../types/index.js';
2
+ export function registerServerRoutes(app, registry) {
3
+ app.get('/api/v1/servers', async (_request, reply) => {
4
+ const servers = registry.listServers();
5
+ return reply.send({ ok: true, data: servers });
6
+ });
7
+ app.post('/api/v1/servers', async (request, reply) => {
8
+ const parsed = McpServerConfigSchema.safeParse(request.body);
9
+ if (!parsed.success) {
10
+ return reply.status(400).send({
11
+ ok: false,
12
+ error: 'Invalid server configuration',
13
+ details: parsed.error.flatten(),
14
+ });
15
+ }
16
+ try {
17
+ await registry.addServer(parsed.data);
18
+ const servers = registry.listServers();
19
+ const added = servers.find(s => s.name === parsed.data.name);
20
+ return reply.status(201).send({ ok: true, data: added });
21
+ }
22
+ catch (error) {
23
+ const message = error instanceof Error ? error.message : String(error);
24
+ return reply.status(500).send({ ok: false, error: message });
25
+ }
26
+ });
27
+ app.delete('/api/v1/servers/:name', async (request, reply) => {
28
+ const { name } = request.params;
29
+ try {
30
+ await registry.removeServer(name);
31
+ return reply.send({ ok: true });
32
+ }
33
+ catch (error) {
34
+ const message = error instanceof Error ? error.message : String(error);
35
+ return reply.status(404).send({ ok: false, error: message });
36
+ }
37
+ });
38
+ app.get('/api/v1/servers/:name/health', async (request, reply) => {
39
+ const { name } = request.params;
40
+ const results = await registry.healthCheck(name);
41
+ if (results.length === 0) {
42
+ return reply.status(404).send({ ok: false, error: `Server "${name}" not found` });
43
+ }
44
+ return reply.send({ ok: true, data: results[0] });
45
+ });
46
+ }
47
+ //# sourceMappingURL=servers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"servers.js","sourceRoot":"","sources":["../../../src/server/routes/servers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAG7D,MAAM,UAAU,oBAAoB,CAAC,GAAoB,EAAE,QAA2B;IACpF,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;QACnD,MAAM,OAAO,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACnD,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC5B,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,8BAA8B;gBACrC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE;aAChC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACtC,MAAM,OAAO,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;YACvC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7D,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,MAAM,CAAC,uBAAuB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAC3D,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAA0B,CAAC;QAEpD,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAClC,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,8BAA8B,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAC/D,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAA0B,CAAC;QACpD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAEjD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,IAAI,aAAa,EAAE,CAAC,CAAC;QACpF,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { OpalServeRegistry } from '../../core/registry.js';
3
+ export declare function registerStatsRoutes(app: FastifyInstance, registry: OpalServeRegistry): void;
4
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../../src/server/routes/stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAmBhE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAqI3F"}
@@ -0,0 +1,97 @@
1
+ import { createAuthMiddleware } from '../../auth/middleware.js';
2
+ function parsePeriod(period) {
3
+ const match = period.match(/^(\d+)(h|d|w|m)$/);
4
+ if (!match)
5
+ return "datetime('now', '-24 hours')";
6
+ const [, value, unit] = match;
7
+ const num = parseInt(value, 10);
8
+ switch (unit) {
9
+ case 'h': return `datetime('now', '-${num} hours')`;
10
+ case 'd': return `datetime('now', '-${num} days')`;
11
+ case 'w': return `datetime('now', '-${num * 7} days')`;
12
+ case 'm': return `datetime('now', '-${num * 30} days')`;
13
+ default: return "datetime('now', '-24 hours')";
14
+ }
15
+ }
16
+ export function registerStatsRoutes(app, registry) {
17
+ const db = registry.getDatabase();
18
+ const requireAuth = createAuthMiddleware(db);
19
+ // --- Overview Stats ---
20
+ app.get('/api/v1/stats/overview', { preHandler: requireAuth }, async (request, reply) => {
21
+ const { period } = request.query;
22
+ const since = parsePeriod(period ?? '24h');
23
+ const totalRow = db.get(`SELECT COUNT(*) as count FROM usage_events WHERE created_at >= ${since}`);
24
+ const totalCalls = totalRow?.count ?? 0;
25
+ const activeUsersRow = db.get(`SELECT COUNT(DISTINCT user_id) as count FROM usage_events WHERE created_at >= ${since} AND user_id IS NOT NULL`);
26
+ const activeUsers = activeUsersRow?.count ?? 0;
27
+ const topTools = db.all(`SELECT tool_name as name, COUNT(*) as count FROM usage_events
28
+ WHERE created_at >= ${since} AND tool_name IS NOT NULL
29
+ GROUP BY tool_name ORDER BY count DESC LIMIT 10`);
30
+ const errorRow = db.get(`SELECT COUNT(*) as count FROM usage_events WHERE created_at >= ${since} AND success = 0`);
31
+ const errorCount = errorRow?.count ?? 0;
32
+ const errorRate = totalCalls > 0 ? errorCount / totalCalls : 0;
33
+ return reply.send({
34
+ ok: true,
35
+ data: {
36
+ totalCalls,
37
+ activeUsers,
38
+ topTools,
39
+ errorRate: Math.round(errorRate * 10000) / 10000,
40
+ period: period ?? '24h',
41
+ },
42
+ });
43
+ });
44
+ // --- Per-User Stats ---
45
+ app.get('/api/v1/stats/users/:id', { preHandler: requireAuth }, async (request, reply) => {
46
+ const { id } = request.params;
47
+ const { period } = request.query;
48
+ const since = parsePeriod(period ?? '7d');
49
+ const user = db.get('SELECT id, email, display_name FROM users WHERE id = ?', [id]);
50
+ if (!user) {
51
+ return reply.status(404).send({ ok: false, error: 'User not found' });
52
+ }
53
+ const totalRow = db.get(`SELECT COUNT(*) as count FROM usage_events WHERE user_id = ? AND created_at >= ${since}`, [id]);
54
+ const topTools = db.all(`SELECT tool_name as name, COUNT(*) as count FROM usage_events
55
+ WHERE user_id = ? AND created_at >= ${since} AND tool_name IS NOT NULL
56
+ GROUP BY tool_name ORDER BY count DESC LIMIT 10`, [id]);
57
+ const errorRow = db.get(`SELECT COUNT(*) as count FROM usage_events WHERE user_id = ? AND created_at >= ${since} AND success = 0`, [id]);
58
+ const totalCalls = totalRow?.count ?? 0;
59
+ const errorCount = errorRow?.count ?? 0;
60
+ return reply.send({
61
+ ok: true,
62
+ data: {
63
+ user: { id: user.id, email: user.email, displayName: user.display_name },
64
+ totalCalls,
65
+ topTools,
66
+ errorRate: totalCalls > 0 ? Math.round((errorCount / totalCalls) * 10000) / 10000 : 0,
67
+ period: period ?? '7d',
68
+ },
69
+ });
70
+ });
71
+ // --- Per-Server Stats ---
72
+ app.get('/api/v1/stats/servers/:name', { preHandler: requireAuth }, async (request, reply) => {
73
+ const { name } = request.params;
74
+ const { period } = request.query;
75
+ const since = parsePeriod(period ?? '7d');
76
+ const totalRow = db.get(`SELECT COUNT(*) as count FROM usage_events WHERE server_name = ? AND created_at >= ${since}`, [name]);
77
+ const topTools = db.all(`SELECT tool_name as name, COUNT(*) as count FROM usage_events
78
+ WHERE server_name = ? AND created_at >= ${since} AND tool_name IS NOT NULL
79
+ GROUP BY tool_name ORDER BY count DESC LIMIT 10`, [name]);
80
+ const errorRow = db.get(`SELECT COUNT(*) as count FROM usage_events WHERE server_name = ? AND created_at >= ${since} AND success = 0`, [name]);
81
+ const avgLatencyRow = db.get(`SELECT AVG(latency_ms) as avg_latency FROM usage_events WHERE server_name = ? AND created_at >= ${since} AND latency_ms IS NOT NULL`, [name]);
82
+ const totalCalls = totalRow?.count ?? 0;
83
+ const errorCount = errorRow?.count ?? 0;
84
+ return reply.send({
85
+ ok: true,
86
+ data: {
87
+ serverName: name,
88
+ totalCalls,
89
+ topTools,
90
+ errorRate: totalCalls > 0 ? Math.round((errorCount / totalCalls) * 10000) / 10000 : 0,
91
+ avgLatencyMs: avgLatencyRow?.avg_latency ? Math.round(avgLatencyRow.avg_latency) : null,
92
+ period: period ?? '7d',
93
+ },
94
+ });
95
+ });
96
+ }
97
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../../src/server/routes/stats.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { OpalServeRegistry } from '../../core/registry.js';
3
+ export declare function registerTeamServerRoutes(app: FastifyInstance, registry: OpalServeRegistry): void;
4
+ //# sourceMappingURL=team-servers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-servers.d.ts","sourceRoot":"","sources":["../../../src/server/routes/team-servers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAG/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGhE,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAyJhG"}