@wener/mcps 1.0.1

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 (141) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.mjs +15 -0
  3. package/dist/mcps-cli.mjs +174727 -0
  4. package/lib/chat/agent.js +187 -0
  5. package/lib/chat/agent.js.map +1 -0
  6. package/lib/chat/audit.js +238 -0
  7. package/lib/chat/audit.js.map +1 -0
  8. package/lib/chat/converters.js +467 -0
  9. package/lib/chat/converters.js.map +1 -0
  10. package/lib/chat/handler.js +1068 -0
  11. package/lib/chat/handler.js.map +1 -0
  12. package/lib/chat/index.js +12 -0
  13. package/lib/chat/index.js.map +1 -0
  14. package/lib/chat/types.js +35 -0
  15. package/lib/chat/types.js.map +1 -0
  16. package/lib/contracts/AuditContract.js +85 -0
  17. package/lib/contracts/AuditContract.js.map +1 -0
  18. package/lib/contracts/McpsContract.js +113 -0
  19. package/lib/contracts/McpsContract.js.map +1 -0
  20. package/lib/contracts/index.js +3 -0
  21. package/lib/contracts/index.js.map +1 -0
  22. package/lib/dev.server.js +7 -0
  23. package/lib/dev.server.js.map +1 -0
  24. package/lib/entities/ChatRequestEntity.js +318 -0
  25. package/lib/entities/ChatRequestEntity.js.map +1 -0
  26. package/lib/entities/McpRequestEntity.js +271 -0
  27. package/lib/entities/McpRequestEntity.js.map +1 -0
  28. package/lib/entities/RequestLogEntity.js +177 -0
  29. package/lib/entities/RequestLogEntity.js.map +1 -0
  30. package/lib/entities/ResponseEntity.js +150 -0
  31. package/lib/entities/ResponseEntity.js.map +1 -0
  32. package/lib/entities/index.js +11 -0
  33. package/lib/entities/index.js.map +1 -0
  34. package/lib/entities/types.js +11 -0
  35. package/lib/entities/types.js.map +1 -0
  36. package/lib/index.js +3 -0
  37. package/lib/index.js.map +1 -0
  38. package/lib/mcps-cli.js +44 -0
  39. package/lib/mcps-cli.js.map +1 -0
  40. package/lib/providers/McpServerHandlerDef.js +40 -0
  41. package/lib/providers/McpServerHandlerDef.js.map +1 -0
  42. package/lib/providers/findMcpServerDef.js +26 -0
  43. package/lib/providers/findMcpServerDef.js.map +1 -0
  44. package/lib/providers/prometheus/def.js +24 -0
  45. package/lib/providers/prometheus/def.js.map +1 -0
  46. package/lib/providers/prometheus/index.js +2 -0
  47. package/lib/providers/prometheus/index.js.map +1 -0
  48. package/lib/providers/relay/def.js +32 -0
  49. package/lib/providers/relay/def.js.map +1 -0
  50. package/lib/providers/relay/index.js +2 -0
  51. package/lib/providers/relay/index.js.map +1 -0
  52. package/lib/providers/sql/def.js +31 -0
  53. package/lib/providers/sql/def.js.map +1 -0
  54. package/lib/providers/sql/index.js +2 -0
  55. package/lib/providers/sql/index.js.map +1 -0
  56. package/lib/providers/tencent-cls/def.js +44 -0
  57. package/lib/providers/tencent-cls/def.js.map +1 -0
  58. package/lib/providers/tencent-cls/index.js +2 -0
  59. package/lib/providers/tencent-cls/index.js.map +1 -0
  60. package/lib/scripts/bundle.js +90 -0
  61. package/lib/scripts/bundle.js.map +1 -0
  62. package/lib/server/api-routes.js +96 -0
  63. package/lib/server/api-routes.js.map +1 -0
  64. package/lib/server/audit.js +274 -0
  65. package/lib/server/audit.js.map +1 -0
  66. package/lib/server/chat-routes.js +82 -0
  67. package/lib/server/chat-routes.js.map +1 -0
  68. package/lib/server/config.js +223 -0
  69. package/lib/server/config.js.map +1 -0
  70. package/lib/server/db.js +97 -0
  71. package/lib/server/db.js.map +1 -0
  72. package/lib/server/index.js +2 -0
  73. package/lib/server/index.js.map +1 -0
  74. package/lib/server/mcp-handler.js +167 -0
  75. package/lib/server/mcp-handler.js.map +1 -0
  76. package/lib/server/mcp-routes.js +112 -0
  77. package/lib/server/mcp-routes.js.map +1 -0
  78. package/lib/server/mcps-router.js +119 -0
  79. package/lib/server/mcps-router.js.map +1 -0
  80. package/lib/server/schema.js +129 -0
  81. package/lib/server/schema.js.map +1 -0
  82. package/lib/server/server.js +166 -0
  83. package/lib/server/server.js.map +1 -0
  84. package/lib/web/ChatPage.js +827 -0
  85. package/lib/web/ChatPage.js.map +1 -0
  86. package/lib/web/McpInspectorPage.js +214 -0
  87. package/lib/web/McpInspectorPage.js.map +1 -0
  88. package/lib/web/ServersPage.js +93 -0
  89. package/lib/web/ServersPage.js.map +1 -0
  90. package/lib/web/main.js +541 -0
  91. package/lib/web/main.js.map +1 -0
  92. package/package.json +83 -0
  93. package/src/chat/agent.ts +240 -0
  94. package/src/chat/audit.ts +377 -0
  95. package/src/chat/converters.test.ts +325 -0
  96. package/src/chat/converters.ts +459 -0
  97. package/src/chat/handler.test.ts +137 -0
  98. package/src/chat/handler.ts +1233 -0
  99. package/src/chat/index.ts +16 -0
  100. package/src/chat/types.ts +72 -0
  101. package/src/contracts/AuditContract.ts +93 -0
  102. package/src/contracts/McpsContract.ts +141 -0
  103. package/src/contracts/index.ts +18 -0
  104. package/src/dev.server.ts +7 -0
  105. package/src/entities/ChatRequestEntity.ts +157 -0
  106. package/src/entities/McpRequestEntity.ts +149 -0
  107. package/src/entities/RequestLogEntity.ts +78 -0
  108. package/src/entities/ResponseEntity.ts +75 -0
  109. package/src/entities/index.ts +12 -0
  110. package/src/entities/types.ts +188 -0
  111. package/src/index.ts +1 -0
  112. package/src/mcps-cli.ts +59 -0
  113. package/src/providers/McpServerHandlerDef.ts +105 -0
  114. package/src/providers/findMcpServerDef.ts +31 -0
  115. package/src/providers/prometheus/def.ts +21 -0
  116. package/src/providers/prometheus/index.ts +1 -0
  117. package/src/providers/relay/def.ts +31 -0
  118. package/src/providers/relay/index.ts +1 -0
  119. package/src/providers/relay/relay.test.ts +47 -0
  120. package/src/providers/sql/def.ts +33 -0
  121. package/src/providers/sql/index.ts +1 -0
  122. package/src/providers/tencent-cls/def.ts +38 -0
  123. package/src/providers/tencent-cls/index.ts +1 -0
  124. package/src/scripts/bundle.ts +82 -0
  125. package/src/server/api-routes.ts +98 -0
  126. package/src/server/audit.ts +310 -0
  127. package/src/server/chat-routes.ts +95 -0
  128. package/src/server/config.test.ts +162 -0
  129. package/src/server/config.ts +198 -0
  130. package/src/server/db.ts +115 -0
  131. package/src/server/index.ts +1 -0
  132. package/src/server/mcp-handler.ts +209 -0
  133. package/src/server/mcp-routes.ts +133 -0
  134. package/src/server/mcps-router.ts +133 -0
  135. package/src/server/schema.ts +175 -0
  136. package/src/server/server.ts +163 -0
  137. package/src/web/ChatPage.tsx +1005 -0
  138. package/src/web/McpInspectorPage.tsx +254 -0
  139. package/src/web/ServersPage.tsx +139 -0
  140. package/src/web/main.tsx +600 -0
  141. package/src/web/styles.css +15 -0
@@ -0,0 +1,31 @@
1
+ import { SqlMcpServerDef } from "@wener/ai/mcp/sql";
2
+ import { HeaderNames } from "../../server/schema.js";
3
+ import { defineMcpServerHandler, registerMcpServerHandler } from "../McpServerHandlerDef.js";
4
+ export const SqlMcpServerHandlerDef = defineMcpServerHandler(SqlMcpServerDef, {
5
+ headerMappings: [
6
+ {
7
+ header: HeaderNames.DB_URL,
8
+ property: "url"
9
+ },
10
+ {
11
+ header: HeaderNames.DB_READ_URL,
12
+ property: "readUrl"
13
+ },
14
+ {
15
+ header: HeaderNames.DB_WRITE_URL,
16
+ property: "writeUrl"
17
+ }
18
+ ],
19
+ resolveConfig(config, headers) {
20
+ const url = config.dbUrl || config.dbReadUrl || config.dbWriteUrl || headers?.get(HeaderNames.DB_URL) || headers?.get(HeaderNames.DB_READ_URL) || headers?.get(HeaderNames.DB_WRITE_URL) || config.headers?.[HeaderNames.DB_URL] || config.headers?.[HeaderNames.DB_READ_URL] || config.headers?.[HeaderNames.DB_WRITE_URL];
21
+ if (!url)
22
+ return null;
23
+ return {
24
+ url
25
+ };
26
+ }
27
+ });
28
+ registerMcpServerHandler(SqlMcpServerHandlerDef);
29
+ // backward compatibility
30
+ export { SqlMcpServerHandlerDef as SqlMcpServerDef };
31
+ //# sourceMappingURL=def.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/providers/sql/def.ts"],"sourcesContent":["import { SqlMcpServerDef, type CreateSqlMcpServerOptions } from '@wener/ai/mcp/sql';\nimport { HeaderNames, type SqlConfig } from '../../server/schema';\nimport { defineMcpServerHandler, registerMcpServerHandler } from '../McpServerHandlerDef';\n\nexport const SqlMcpServerHandlerDef = defineMcpServerHandler<CreateSqlMcpServerOptions, SqlConfig>(SqlMcpServerDef, {\n\theaderMappings: [\n\t\t{ header: HeaderNames.DB_URL, property: 'url' },\n\t\t{ header: HeaderNames.DB_READ_URL, property: 'readUrl' },\n\t\t{ header: HeaderNames.DB_WRITE_URL, property: 'writeUrl' },\n\t],\n\n\tresolveConfig(config, headers) {\n\t\tconst url =\n\t\t\tconfig.dbUrl ||\n\t\t\tconfig.dbReadUrl ||\n\t\t\tconfig.dbWriteUrl ||\n\t\t\theaders?.get(HeaderNames.DB_URL) ||\n\t\t\theaders?.get(HeaderNames.DB_READ_URL) ||\n\t\t\theaders?.get(HeaderNames.DB_WRITE_URL) ||\n\t\t\tconfig.headers?.[HeaderNames.DB_URL] ||\n\t\t\tconfig.headers?.[HeaderNames.DB_READ_URL] ||\n\t\t\tconfig.headers?.[HeaderNames.DB_WRITE_URL];\n\n\t\tif (!url) return null;\n\n\t\treturn { url };\n\t},\n});\n\nregisterMcpServerHandler(SqlMcpServerHandlerDef);\n\n// backward compatibility\nexport { SqlMcpServerHandlerDef as SqlMcpServerDef };\n"],"names":["SqlMcpServerDef","HeaderNames","defineMcpServerHandler","registerMcpServerHandler","SqlMcpServerHandlerDef","headerMappings","header","DB_URL","property","DB_READ_URL","DB_WRITE_URL","resolveConfig","config","headers","url","dbUrl","dbReadUrl","dbWriteUrl","get"],"mappings":"AAAA,SAASA,eAAe,QAAwC,oBAAoB;AACpF,SAASC,WAAW,QAAwB,sBAAsB;AAClE,SAASC,sBAAsB,EAAEC,wBAAwB,QAAQ,yBAAyB;AAE1F,OAAO,MAAMC,yBAAyBF,uBAA6DF,iBAAiB;IACnHK,gBAAgB;QACf;YAAEC,QAAQL,YAAYM,MAAM;YAAEC,UAAU;QAAM;QAC9C;YAAEF,QAAQL,YAAYQ,WAAW;YAAED,UAAU;QAAU;QACvD;YAAEF,QAAQL,YAAYS,YAAY;YAAEF,UAAU;QAAW;KACzD;IAEDG,eAAcC,MAAM,EAAEC,OAAO;QAC5B,MAAMC,MACLF,OAAOG,KAAK,IACZH,OAAOI,SAAS,IAChBJ,OAAOK,UAAU,IACjBJ,SAASK,IAAIjB,YAAYM,MAAM,KAC/BM,SAASK,IAAIjB,YAAYQ,WAAW,KACpCI,SAASK,IAAIjB,YAAYS,YAAY,KACrCE,OAAOC,OAAO,EAAE,CAACZ,YAAYM,MAAM,CAAC,IACpCK,OAAOC,OAAO,EAAE,CAACZ,YAAYQ,WAAW,CAAC,IACzCG,OAAOC,OAAO,EAAE,CAACZ,YAAYS,YAAY,CAAC;QAE3C,IAAI,CAACI,KAAK,OAAO;QAEjB,OAAO;YAAEA;QAAI;IACd;AACD,GAAG;AAEHX,yBAAyBC;AAEzB,yBAAyB;AACzB,SAASA,0BAA0BJ,eAAe,GAAG"}
@@ -0,0 +1,2 @@
1
+ export { SqlMcpServerDef, SqlMcpServerHandlerDef } from "./def.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/providers/sql/index.ts"],"sourcesContent":["export { SqlMcpServerDef, SqlMcpServerHandlerDef } from './def';\n"],"names":["SqlMcpServerDef","SqlMcpServerHandlerDef"],"mappings":"AAAA,SAASA,eAAe,EAAEC,sBAAsB,QAAQ,QAAQ"}
@@ -0,0 +1,44 @@
1
+ import { TencentClsMcpServerDef } from "@wener/ai/mcp/tencent-cls";
2
+ import { HeaderNames } from "../../server/schema.js";
3
+ import { defineMcpServerHandler, registerMcpServerHandler } from "../McpServerHandlerDef.js";
4
+ export const TencentClsMcpServerHandlerDef = defineMcpServerHandler(TencentClsMcpServerDef, {
5
+ headerMappings: [
6
+ {
7
+ header: HeaderNames.CLS_SECRET_ID,
8
+ property: "clientId",
9
+ required: true
10
+ },
11
+ {
12
+ header: HeaderNames.CLS_SECRET_KEY,
13
+ property: "clientSecret",
14
+ required: true
15
+ },
16
+ {
17
+ header: HeaderNames.CLS_REGION,
18
+ property: "region",
19
+ default: "ap-shanghai"
20
+ },
21
+ {
22
+ header: HeaderNames.CLS_ENDPOINT,
23
+ property: "endpoint"
24
+ }
25
+ ],
26
+ resolveConfig(config, headers) {
27
+ const clientId = config.clientId || headers?.get(HeaderNames.CLS_SECRET_ID) || config.headers?.[HeaderNames.CLS_SECRET_ID];
28
+ const clientSecret = config.clientSecret || headers?.get(HeaderNames.CLS_SECRET_KEY) || config.headers?.[HeaderNames.CLS_SECRET_KEY];
29
+ const region = config.region || headers?.get(HeaderNames.CLS_REGION) || config.headers?.[HeaderNames.CLS_REGION] || "ap-shanghai";
30
+ const endpoint = config.endpoint || headers?.get(HeaderNames.CLS_ENDPOINT) || config.headers?.[HeaderNames.CLS_ENDPOINT];
31
+ if (!clientId || !clientSecret)
32
+ return null;
33
+ return {
34
+ clientId,
35
+ clientSecret,
36
+ region,
37
+ endpoint
38
+ };
39
+ }
40
+ });
41
+ registerMcpServerHandler(TencentClsMcpServerHandlerDef);
42
+ // backward compatibility
43
+ export { TencentClsMcpServerHandlerDef as TencentClsMcpServerDef };
44
+ //# sourceMappingURL=def.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/providers/tencent-cls/def.ts"],"sourcesContent":["import { TencentClsMcpServerDef, type CreateTencentClsMcpServerOptions } from '@wener/ai/mcp/tencent-cls';\nimport { HeaderNames, type TencentClsConfig } from '../../server/schema';\nimport { defineMcpServerHandler, registerMcpServerHandler } from '../McpServerHandlerDef';\n\nexport const TencentClsMcpServerHandlerDef = defineMcpServerHandler<\n\tCreateTencentClsMcpServerOptions,\n\tTencentClsConfig\n>(TencentClsMcpServerDef, {\n\theaderMappings: [\n\t\t{ header: HeaderNames.CLS_SECRET_ID, property: 'clientId', required: true },\n\t\t{ header: HeaderNames.CLS_SECRET_KEY, property: 'clientSecret', required: true },\n\t\t{ header: HeaderNames.CLS_REGION, property: 'region', default: 'ap-shanghai' },\n\t\t{ header: HeaderNames.CLS_ENDPOINT, property: 'endpoint' },\n\t],\n\n\tresolveConfig(config, headers) {\n\t\tconst clientId =\n\t\t\tconfig.clientId || headers?.get(HeaderNames.CLS_SECRET_ID) || config.headers?.[HeaderNames.CLS_SECRET_ID];\n\t\tconst clientSecret =\n\t\t\tconfig.clientSecret || headers?.get(HeaderNames.CLS_SECRET_KEY) || config.headers?.[HeaderNames.CLS_SECRET_KEY];\n\t\tconst region =\n\t\t\tconfig.region ||\n\t\t\theaders?.get(HeaderNames.CLS_REGION) ||\n\t\t\tconfig.headers?.[HeaderNames.CLS_REGION] ||\n\t\t\t'ap-shanghai';\n\t\tconst endpoint =\n\t\t\tconfig.endpoint || headers?.get(HeaderNames.CLS_ENDPOINT) || config.headers?.[HeaderNames.CLS_ENDPOINT];\n\n\t\tif (!clientId || !clientSecret) return null;\n\n\t\treturn { clientId, clientSecret, region, endpoint };\n\t},\n});\n\nregisterMcpServerHandler(TencentClsMcpServerHandlerDef);\n\n// backward compatibility\nexport { TencentClsMcpServerHandlerDef as TencentClsMcpServerDef };\n"],"names":["TencentClsMcpServerDef","HeaderNames","defineMcpServerHandler","registerMcpServerHandler","TencentClsMcpServerHandlerDef","headerMappings","header","CLS_SECRET_ID","property","required","CLS_SECRET_KEY","CLS_REGION","default","CLS_ENDPOINT","resolveConfig","config","headers","clientId","get","clientSecret","region","endpoint"],"mappings":"AAAA,SAASA,sBAAsB,QAA+C,4BAA4B;AAC1G,SAASC,WAAW,QAA+B,sBAAsB;AACzE,SAASC,sBAAsB,EAAEC,wBAAwB,QAAQ,yBAAyB;AAE1F,OAAO,MAAMC,gCAAgCF,uBAG3CF,wBAAwB;IACzBK,gBAAgB;QACf;YAAEC,QAAQL,YAAYM,aAAa;YAAEC,UAAU;YAAYC,UAAU;QAAK;QAC1E;YAAEH,QAAQL,YAAYS,cAAc;YAAEF,UAAU;YAAgBC,UAAU;QAAK;QAC/E;YAAEH,QAAQL,YAAYU,UAAU;YAAEH,UAAU;YAAUI,SAAS;QAAc;QAC7E;YAAEN,QAAQL,YAAYY,YAAY;YAAEL,UAAU;QAAW;KACzD;IAEDM,eAAcC,MAAM,EAAEC,OAAO;QAC5B,MAAMC,WACLF,OAAOE,QAAQ,IAAID,SAASE,IAAIjB,YAAYM,aAAa,KAAKQ,OAAOC,OAAO,EAAE,CAACf,YAAYM,aAAa,CAAC;QAC1G,MAAMY,eACLJ,OAAOI,YAAY,IAAIH,SAASE,IAAIjB,YAAYS,cAAc,KAAKK,OAAOC,OAAO,EAAE,CAACf,YAAYS,cAAc,CAAC;QAChH,MAAMU,SACLL,OAAOK,MAAM,IACbJ,SAASE,IAAIjB,YAAYU,UAAU,KACnCI,OAAOC,OAAO,EAAE,CAACf,YAAYU,UAAU,CAAC,IACxC;QACD,MAAMU,WACLN,OAAOM,QAAQ,IAAIL,SAASE,IAAIjB,YAAYY,YAAY,KAAKE,OAAOC,OAAO,EAAE,CAACf,YAAYY,YAAY,CAAC;QAExG,IAAI,CAACI,YAAY,CAACE,cAAc,OAAO;QAEvC,OAAO;YAAEF;YAAUE;YAAcC;YAAQC;QAAS;IACnD;AACD,GAAG;AAEHlB,yBAAyBC;AAEzB,yBAAyB;AACzB,SAASA,iCAAiCJ,sBAAsB,GAAG"}
@@ -0,0 +1,2 @@
1
+ export { TencentClsMcpServerDef, TencentClsMcpServerHandlerDef } from "./def.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/providers/tencent-cls/index.ts"],"sourcesContent":["export { TencentClsMcpServerDef, TencentClsMcpServerHandlerDef } from './def';\n"],"names":["TencentClsMcpServerDef","TencentClsMcpServerHandlerDef"],"mappings":"AAAA,SAASA,sBAAsB,EAAEC,6BAA6B,QAAQ,QAAQ"}
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import * as esbuild from 'esbuild';
4
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
5
+ const banner = `// Bundled with esbuild
6
+ // ${pkg.name}@${pkg.version}
7
+
8
+ var require,__filename,__dirname;
9
+ {
10
+ const {createRequire} = await import('node:module');
11
+ require ||= createRequire(import.meta.url);
12
+ }
13
+ {
14
+ const {fileURLToPath} = await import('node:url');
15
+ const {dirname} = await import('node:path');
16
+ __filename ||= fileURLToPath(import.meta.url);
17
+ __dirname ||= dirname(__filename)
18
+ };
19
+ `;
20
+ const commonOptions = {
21
+ bundle: true,
22
+ logLevel: 'info',
23
+ banner: {
24
+ js: banner
25
+ },
26
+ define: {
27
+ NODE_ENV: JSON.stringify('production'),
28
+ __DEV__: JSON.stringify(false),
29
+ 'process.env.NODE_ENV': JSON.stringify('production')
30
+ },
31
+ keepNames: true,
32
+ treeShaking: true,
33
+ minifySyntax: true,
34
+ format: 'esm',
35
+ platform: 'node',
36
+ charset: 'utf8',
37
+ target: 'node18',
38
+ sourcemap: false,
39
+ legalComments: 'none',
40
+ // External native modules
41
+ external: [
42
+ 'better-sqlite3',
43
+ 'oracledb',
44
+ 'mariadb/callback',
45
+ 'mysql'
46
+ ]
47
+ };
48
+ // Ensure dist directory exists
49
+ fs.mkdirSync('dist', {
50
+ recursive: true
51
+ });
52
+ console.log('Building MCPS...');
53
+ // Build library entry (index.ts)
54
+ const libResult = await esbuild.build({
55
+ ...commonOptions,
56
+ entryPoints: [
57
+ 'src/index.ts'
58
+ ],
59
+ outfile: 'dist/index.mjs'
60
+ });
61
+ // Build CLI entry (mcps-cli.ts)
62
+ const cliResult = await esbuild.build({
63
+ ...commonOptions,
64
+ entryPoints: [
65
+ 'src/mcps-cli.ts'
66
+ ],
67
+ outfile: 'dist/mcps-cli.mjs'
68
+ });
69
+ if (libResult.errors.length === 0 && cliResult.errors.length === 0) {
70
+ // Process CLI output - add shebang
71
+ const cliOutfile = 'dist/mcps-cli.mjs';
72
+ let content = fs.readFileSync(cliOutfile, 'utf-8');
73
+ content = content.replace(/^#!.*\n/gm, '');
74
+ fs.writeFileSync(cliOutfile, `#!/usr/bin/env node\n${content}`);
75
+ fs.chmodSync(cliOutfile, 493);
76
+ const libStats = fs.statSync('dist/index.mjs');
77
+ const cliStats = fs.statSync(cliOutfile);
78
+ console.log(`✅ Build successful!`);
79
+ console.log(`📦 Library: dist/index.mjs (${(libStats.size / 1024).toFixed(1)}KB)`);
80
+ console.log(`📦 CLI: ${cliOutfile} (${(cliStats.size / 1024).toFixed(1)}KB)`);
81
+ console.log(`🚀 Ready for npm publish and npx usage`);
82
+ } else {
83
+ console.error('❌ Build failed:', [
84
+ ...libResult.errors,
85
+ ...cliResult.errors
86
+ ]);
87
+ process.exit(1);
88
+ }
89
+
90
+ //# sourceMappingURL=bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/scripts/bundle.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'node:fs';\nimport * as esbuild from 'esbuild';\n\nconst pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));\n\nconst banner = `// Bundled with esbuild\n// ${pkg.name}@${pkg.version}\n\nvar require,__filename,__dirname;\n{\n const {createRequire} = await import('node:module');\n require ||= createRequire(import.meta.url);\n}\n{\n const {fileURLToPath} = await import('node:url');\n const {dirname} = await import('node:path');\n __filename ||= fileURLToPath(import.meta.url);\n __dirname ||= dirname(__filename)\n};\n`;\n\nconst commonOptions: esbuild.BuildOptions = {\n\tbundle: true,\n\tlogLevel: 'info',\n\tbanner: { js: banner },\n\tdefine: {\n\t\tNODE_ENV: JSON.stringify('production'),\n\t\t__DEV__: JSON.stringify(false),\n\t\t'process.env.NODE_ENV': JSON.stringify('production'),\n\t},\n\tkeepNames: true,\n\ttreeShaking: true,\n\tminifySyntax: true,\n\tformat: 'esm',\n\tplatform: 'node',\n\tcharset: 'utf8',\n\ttarget: 'node18',\n\tsourcemap: false,\n\tlegalComments: 'none',\n\t// External native modules\n\texternal: ['better-sqlite3', 'oracledb', 'mariadb/callback', 'mysql'],\n};\n\n// Ensure dist directory exists\nfs.mkdirSync('dist', { recursive: true });\n\nconsole.log('Building MCPS...');\n\n// Build library entry (index.ts)\nconst libResult = await esbuild.build({\n\t...commonOptions,\n\tentryPoints: ['src/index.ts'],\n\toutfile: 'dist/index.mjs',\n});\n\n// Build CLI entry (mcps-cli.ts)\nconst cliResult = await esbuild.build({\n\t...commonOptions,\n\tentryPoints: ['src/mcps-cli.ts'],\n\toutfile: 'dist/mcps-cli.mjs',\n});\n\nif (libResult.errors.length === 0 && cliResult.errors.length === 0) {\n\t// Process CLI output - add shebang\n\tconst cliOutfile = 'dist/mcps-cli.mjs';\n\tlet content = fs.readFileSync(cliOutfile, 'utf-8');\n\tcontent = content.replace(/^#!.*\\n/gm, '');\n\tfs.writeFileSync(cliOutfile, `#!/usr/bin/env node\\n${content}`);\n\tfs.chmodSync(cliOutfile, 0o755);\n\n\tconst libStats = fs.statSync('dist/index.mjs');\n\tconst cliStats = fs.statSync(cliOutfile);\n\n\tconsole.log(`✅ Build successful!`);\n\tconsole.log(`📦 Library: dist/index.mjs (${(libStats.size / 1024).toFixed(1)}KB)`);\n\tconsole.log(`📦 CLI: ${cliOutfile} (${(cliStats.size / 1024).toFixed(1)}KB)`);\n\tconsole.log(`🚀 Ready for npm publish and npx usage`);\n} else {\n\tconsole.error('❌ Build failed:', [...libResult.errors, ...cliResult.errors]);\n\tprocess.exit(1);\n}\n"],"names":["fs","esbuild","pkg","JSON","parse","readFileSync","banner","name","version","commonOptions","bundle","logLevel","js","define","NODE_ENV","stringify","__DEV__","keepNames","treeShaking","minifySyntax","format","platform","charset","target","sourcemap","legalComments","external","mkdirSync","recursive","console","log","libResult","build","entryPoints","outfile","cliResult","errors","length","cliOutfile","content","replace","writeFileSync","chmodSync","libStats","statSync","cliStats","size","toFixed","error","process","exit"],"mappings":";AACA,OAAOA,QAAQ,UAAU;AACzB,YAAYC,aAAa,UAAU;AAEnC,MAAMC,MAAMC,KAAKC,KAAK,CAACJ,GAAGK,YAAY,CAAC,gBAAgB;AAEvD,MAAMC,SAAS,CAAC;GACb,EAAEJ,IAAIK,IAAI,CAAC,CAAC,EAAEL,IAAIM,OAAO,CAAC;;;;;;;;;;;;;AAa7B,CAAC;AAED,MAAMC,gBAAsC;IAC3CC,QAAQ;IACRC,UAAU;IACVL,QAAQ;QAAEM,IAAIN;IAAO;IACrBO,QAAQ;QACPC,UAAUX,KAAKY,SAAS,CAAC;QACzBC,SAASb,KAAKY,SAAS,CAAC;QACxB,wBAAwBZ,KAAKY,SAAS,CAAC;IACxC;IACAE,WAAW;IACXC,aAAa;IACbC,cAAc;IACdC,QAAQ;IACRC,UAAU;IACVC,SAAS;IACTC,QAAQ;IACRC,WAAW;IACXC,eAAe;IACf,0BAA0B;IAC1BC,UAAU;QAAC;QAAkB;QAAY;QAAoB;KAAQ;AACtE;AAEA,+BAA+B;AAC/B1B,GAAG2B,SAAS,CAAC,QAAQ;IAAEC,WAAW;AAAK;AAEvCC,QAAQC,GAAG,CAAC;AAEZ,iCAAiC;AACjC,MAAMC,YAAY,MAAM9B,QAAQ+B,KAAK,CAAC;IACrC,GAAGvB,aAAa;IAChBwB,aAAa;QAAC;KAAe;IAC7BC,SAAS;AACV;AAEA,gCAAgC;AAChC,MAAMC,YAAY,MAAMlC,QAAQ+B,KAAK,CAAC;IACrC,GAAGvB,aAAa;IAChBwB,aAAa;QAAC;KAAkB;IAChCC,SAAS;AACV;AAEA,IAAIH,UAAUK,MAAM,CAACC,MAAM,KAAK,KAAKF,UAAUC,MAAM,CAACC,MAAM,KAAK,GAAG;IACnE,mCAAmC;IACnC,MAAMC,aAAa;IACnB,IAAIC,UAAUvC,GAAGK,YAAY,CAACiC,YAAY;IAC1CC,UAAUA,QAAQC,OAAO,CAAC,aAAa;IACvCxC,GAAGyC,aAAa,CAACH,YAAY,CAAC,qBAAqB,EAAEC,SAAS;IAC9DvC,GAAG0C,SAAS,CAACJ,YAAY;IAEzB,MAAMK,WAAW3C,GAAG4C,QAAQ,CAAC;IAC7B,MAAMC,WAAW7C,GAAG4C,QAAQ,CAACN;IAE7BT,QAAQC,GAAG,CAAC,CAAC,mBAAmB,CAAC;IACjCD,QAAQC,GAAG,CAAC,CAAC,4BAA4B,EAAE,AAACa,CAAAA,SAASG,IAAI,GAAG,IAAG,EAAGC,OAAO,CAAC,GAAG,GAAG,CAAC;IACjFlB,QAAQC,GAAG,CAAC,CAAC,QAAQ,EAAEQ,WAAW,EAAE,EAAE,AAACO,CAAAA,SAASC,IAAI,GAAG,IAAG,EAAGC,OAAO,CAAC,GAAG,GAAG,CAAC;IAC5ElB,QAAQC,GAAG,CAAC,CAAC,sCAAsC,CAAC;AACrD,OAAO;IACND,QAAQmB,KAAK,CAAC,mBAAmB;WAAIjB,UAAUK,MAAM;WAAKD,UAAUC,MAAM;KAAC;IAC3Ea,QAAQC,IAAI,CAAC;AACd"}
@@ -0,0 +1,96 @@
1
+ import { SmartCoercionPlugin } from "@orpc/json-schema";
2
+ import { OpenAPIGenerator } from "@orpc/openapi";
3
+ import { OpenAPIHandler } from "@orpc/openapi/fetch";
4
+ import { RPCHandler } from "@orpc/server/fetch";
5
+ import { CORSPlugin } from "@orpc/server/plugins";
6
+ import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
7
+ import { html } from "hono/html";
8
+ import { AuditRouter } from "./audit.js";
9
+ import { createMcpsRouter } from "./mcps-router.js";
10
+ /**
11
+ * Register oRPC API routes for audit and MCPS
12
+ */ export function registerApiRoutes({ app, config }) {
13
+ // Create MCPS router with config context
14
+ const McpsRouter = createMcpsRouter({
15
+ config
16
+ });
17
+ // Combined router for all APIs
18
+ const combinedRouter = {
19
+ audit: AuditRouter,
20
+ mcps: McpsRouter
21
+ };
22
+ const handleByRpc = new RPCHandler(combinedRouter);
23
+ const handleByOpenAPI = new OpenAPIHandler(combinedRouter, {
24
+ plugins: [
25
+ new SmartCoercionPlugin({
26
+ schemaConverters: [
27
+ new ZodToJsonSchemaConverter()
28
+ ]
29
+ }),
30
+ new CORSPlugin({
31
+ exposeHeaders: [
32
+ "Content-Disposition"
33
+ ]
34
+ })
35
+ ]
36
+ });
37
+ app.use("/api/rpc/*", async (c, next) => {
38
+ const { matched, response } = await handleByRpc.handle(c.req.raw, {
39
+ prefix: "/api/rpc",
40
+ context: {}
41
+ });
42
+ if (matched) {
43
+ return c.newResponse(response.body, response);
44
+ }
45
+ return next();
46
+ });
47
+ app.use("/api/*", async (c, next) => {
48
+ const { matched, response } = await handleByOpenAPI.handle(c.req.raw, {
49
+ prefix: "/api",
50
+ context: {}
51
+ });
52
+ if (matched) {
53
+ return c.newResponse(response.body, response);
54
+ }
55
+ return next();
56
+ });
57
+ // OpenAPI spec
58
+ const openAPIGenerator = new OpenAPIGenerator({
59
+ schemaConverters: [
60
+ new ZodToJsonSchemaConverter()
61
+ ]
62
+ });
63
+ let specCache;
64
+ app.get("/api/spec.json", async (c) => {
65
+ if (!specCache) {
66
+ specCache = await openAPIGenerator.generate(combinedRouter, {
67
+ info: {
68
+ title: "MCPS API",
69
+ version: "1.0.0"
70
+ },
71
+ servers: [
72
+ {
73
+ url: "/api"
74
+ }
75
+ ]
76
+ });
77
+ }
78
+ return c.json(specCache);
79
+ });
80
+ // Swagger UI
81
+ app.get("/api/docs", (c) => {
82
+ return c.html(html `<!doctype html>
83
+ <html lang="en">
84
+ <head>
85
+ <title>MCPS API</title>
86
+ <meta charset="utf-8" />
87
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
88
+ </head>
89
+ <body>
90
+ <script id="api-reference" data-url="/api/spec.json"></script>
91
+ <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
92
+ </body>
93
+ </html>`);
94
+ });
95
+ }
96
+ //# sourceMappingURL=api-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/api-routes.ts"],"sourcesContent":["import { SmartCoercionPlugin } from '@orpc/json-schema';\nimport { OpenAPIGenerator } from '@orpc/openapi';\nimport { OpenAPIHandler } from '@orpc/openapi/fetch';\nimport { RPCHandler } from '@orpc/server/fetch';\nimport { CORSPlugin } from '@orpc/server/plugins';\nimport { ZodToJsonSchemaConverter } from '@orpc/zod/zod4';\nimport type { Hono } from 'hono';\nimport { html } from 'hono/html';\nimport { AuditRouter } from './audit';\nimport { createMcpsRouter } from './mcps-router';\nimport type { McpsConfig } from './schema';\n\nexport interface RegisterApiRoutesOptions {\n\tapp: Hono;\n\tconfig: McpsConfig;\n}\n\n/**\n * Register oRPC API routes for audit and MCPS\n */\nexport function registerApiRoutes({ app, config }: RegisterApiRoutesOptions) {\n\t// Create MCPS router with config context\n\tconst McpsRouter = createMcpsRouter({ config });\n\n\t// Combined router for all APIs\n\tconst combinedRouter = {\n\t\taudit: AuditRouter,\n\t\tmcps: McpsRouter,\n\t};\n\n\tconst handleByRpc = new RPCHandler(combinedRouter);\n\tconst handleByOpenAPI = new OpenAPIHandler(combinedRouter, {\n\t\tplugins: [\n\t\t\tnew SmartCoercionPlugin({\n\t\t\t\tschemaConverters: [new ZodToJsonSchemaConverter()],\n\t\t\t}),\n\t\t\tnew CORSPlugin({\n\t\t\t\texposeHeaders: ['Content-Disposition'],\n\t\t\t}),\n\t\t],\n\t});\n\n\tapp.use('/api/rpc/*', async (c, next) => {\n\t\tconst { matched, response } = await handleByRpc.handle(c.req.raw, {\n\t\t\tprefix: '/api/rpc',\n\t\t\tcontext: {},\n\t\t});\n\t\tif (matched) {\n\t\t\treturn c.newResponse(response.body, response);\n\t\t}\n\t\treturn next();\n\t});\n\n\tapp.use('/api/*', async (c, next) => {\n\t\tconst { matched, response } = await handleByOpenAPI.handle(c.req.raw, {\n\t\t\tprefix: '/api',\n\t\t\tcontext: {},\n\t\t});\n\t\tif (matched) {\n\t\t\treturn c.newResponse(response.body, response);\n\t\t}\n\t\treturn next();\n\t});\n\n\t// OpenAPI spec\n\tconst openAPIGenerator = new OpenAPIGenerator({\n\t\tschemaConverters: [new ZodToJsonSchemaConverter()],\n\t});\n\tlet specCache: unknown;\n\n\tapp.get('/api/spec.json', async (c) => {\n\t\tif (!specCache) {\n\t\t\tspecCache = await openAPIGenerator.generate(combinedRouter, {\n\t\t\t\tinfo: { title: 'MCPS API', version: '1.0.0' },\n\t\t\t\tservers: [{ url: '/api' }],\n\t\t\t});\n\t\t}\n\t\treturn c.json(specCache);\n\t});\n\n\t// Swagger UI\n\tapp.get('/api/docs', (c) => {\n\t\treturn c.html(\n\t\t\thtml`<!doctype html>\n\t\t\t\t<html lang=\"en\">\n\t\t\t\t\t<head>\n\t\t\t\t\t\t<title>MCPS API</title>\n\t\t\t\t\t\t<meta charset=\"utf-8\" />\n\t\t\t\t\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t\t\t\t</head>\n\t\t\t\t\t<body>\n\t\t\t\t\t\t<script id=\"api-reference\" data-url=\"/api/spec.json\"></script>\n\t\t\t\t\t\t<script src=\"https://cdn.jsdelivr.net/npm/@scalar/api-reference\"></script>\n\t\t\t\t\t</body>\n\t\t\t\t</html>`,\n\t\t);\n\t});\n}\n"],"names":["SmartCoercionPlugin","OpenAPIGenerator","OpenAPIHandler","RPCHandler","CORSPlugin","ZodToJsonSchemaConverter","html","AuditRouter","createMcpsRouter","registerApiRoutes","app","config","McpsRouter","combinedRouter","audit","mcps","handleByRpc","handleByOpenAPI","plugins","schemaConverters","exposeHeaders","use","c","next","matched","response","handle","req","raw","prefix","context","newResponse","body","openAPIGenerator","specCache","get","generate","info","title","version","servers","url","json"],"mappings":"AAAA,SAASA,mBAAmB,QAAQ,oBAAoB;AACxD,SAASC,gBAAgB,QAAQ,gBAAgB;AACjD,SAASC,cAAc,QAAQ,sBAAsB;AACrD,SAASC,UAAU,QAAQ,qBAAqB;AAChD,SAASC,UAAU,QAAQ,uBAAuB;AAClD,SAASC,wBAAwB,QAAQ,iBAAiB;AAE1D,SAASC,IAAI,QAAQ,YAAY;AACjC,SAASC,WAAW,QAAQ,UAAU;AACtC,SAASC,gBAAgB,QAAQ,gBAAgB;AAQjD;;CAEC,GACD,OAAO,SAASC,kBAAkB,EAAEC,GAAG,EAAEC,MAAM,EAA4B;IAC1E,yCAAyC;IACzC,MAAMC,aAAaJ,iBAAiB;QAAEG;IAAO;IAE7C,+BAA+B;IAC/B,MAAME,iBAAiB;QACtBC,OAAOP;QACPQ,MAAMH;IACP;IAEA,MAAMI,cAAc,IAAIb,WAAWU;IACnC,MAAMI,kBAAkB,IAAIf,eAAeW,gBAAgB;QAC1DK,SAAS;YACR,IAAIlB,oBAAoB;gBACvBmB,kBAAkB;oBAAC,IAAId;iBAA2B;YACnD;YACA,IAAID,WAAW;gBACdgB,eAAe;oBAAC;iBAAsB;YACvC;SACA;IACF;IAEAV,IAAIW,GAAG,CAAC,cAAc,OAAOC,GAAGC;QAC/B,MAAM,EAAEC,OAAO,EAAEC,QAAQ,EAAE,GAAG,MAAMT,YAAYU,MAAM,CAACJ,EAAEK,GAAG,CAACC,GAAG,EAAE;YACjEC,QAAQ;YACRC,SAAS,CAAC;QACX;QACA,IAAIN,SAAS;YACZ,OAAOF,EAAES,WAAW,CAACN,SAASO,IAAI,EAAEP;QACrC;QACA,OAAOF;IACR;IAEAb,IAAIW,GAAG,CAAC,UAAU,OAAOC,GAAGC;QAC3B,MAAM,EAAEC,OAAO,EAAEC,QAAQ,EAAE,GAAG,MAAMR,gBAAgBS,MAAM,CAACJ,EAAEK,GAAG,CAACC,GAAG,EAAE;YACrEC,QAAQ;YACRC,SAAS,CAAC;QACX;QACA,IAAIN,SAAS;YACZ,OAAOF,EAAES,WAAW,CAACN,SAASO,IAAI,EAAEP;QACrC;QACA,OAAOF;IACR;IAEA,eAAe;IACf,MAAMU,mBAAmB,IAAIhC,iBAAiB;QAC7CkB,kBAAkB;YAAC,IAAId;SAA2B;IACnD;IACA,IAAI6B;IAEJxB,IAAIyB,GAAG,CAAC,kBAAkB,OAAOb;QAChC,IAAI,CAACY,WAAW;YACfA,YAAY,MAAMD,iBAAiBG,QAAQ,CAACvB,gBAAgB;gBAC3DwB,MAAM;oBAAEC,OAAO;oBAAYC,SAAS;gBAAQ;gBAC5CC,SAAS;oBAAC;wBAAEC,KAAK;oBAAO;iBAAE;YAC3B;QACD;QACA,OAAOnB,EAAEoB,IAAI,CAACR;IACf;IAEA,aAAa;IACbxB,IAAIyB,GAAG,CAAC,aAAa,CAACb;QACrB,OAAOA,EAAEhB,IAAI,CACZA,IAAI,CAAC;;;;;;;;;;;WAWG,CAAC;IAEX;AACD"}
@@ -0,0 +1,274 @@
1
+ import { implement } from "@orpc/server";
2
+ import { LRUCache } from "lru-cache";
3
+ import { AuditContract } from "../contracts/index.js";
4
+ import { RequestLogEntity } from "../entities/index.js";
5
+ import { ensureDbInitialized, configureDb } from "./db.js";
6
+ /**
7
+ * Convert Headers to a plain record
8
+ */ function headersToRecord(headers) {
9
+ const record = {};
10
+ headers.forEach((value, key) => {
11
+ record[key] = value;
12
+ });
13
+ return record;
14
+ }
15
+ // In-memory audit store using LRU cache
16
+ const auditStore = new LRUCache({
17
+ max: 10000,
18
+ ttl: 1000 * 60 * 60 * 24
19
+ });
20
+ // Counter for IDs
21
+ let eventCounter = 0;
22
+ // Audit configuration state
23
+ let auditEnabled = true; // default to enabled
24
+ let dbConfigured = false;
25
+ /**
26
+ * Configure audit module with settings
27
+ * Call this before using audit features
28
+ *
29
+ * @param auditConfig - Audit config section
30
+ * @param fallbackDbConfig - Fallback db config from root config
31
+ */ export function configureAudit(auditConfig, fallbackDbConfig) {
32
+ // Determine if audit is enabled (default: true)
33
+ auditEnabled = auditConfig?.enabled !== false;
34
+ if (auditEnabled) {
35
+ // Use audit.db config if present, otherwise fallback to root db config
36
+ const dbConfig = auditConfig?.db ?? fallbackDbConfig;
37
+ configureDb(dbConfig);
38
+ dbConfigured = true;
39
+ }
40
+ }
41
+ /**
42
+ * Check if audit is enabled
43
+ */ export function isAuditEnabled() {
44
+ return auditEnabled;
45
+ }
46
+ /**
47
+ * Persist audit event to database (lazy init)
48
+ */ async function persistToDb(event, id) {
49
+ if (!auditEnabled || !dbConfigured) {
50
+ return;
51
+ }
52
+ try {
53
+ // Lazy initialize DB on first persist
54
+ const orm = await ensureDbInitialized();
55
+ const em = orm.em.fork();
56
+ const logEntry = new RequestLogEntity();
57
+ logEntry.requestId = id;
58
+ logEntry.timestamp = new Date(event.timestamp);
59
+ logEntry.method = event.method;
60
+ logEntry.path = event.path;
61
+ logEntry.serverName = event.serverName ?? undefined;
62
+ logEntry.serverType = event.serverType ?? undefined;
63
+ logEntry.status = event.status ?? undefined;
64
+ logEntry.durationMs = event.durationMs ?? undefined;
65
+ logEntry.error = event.error ?? undefined;
66
+ logEntry.requestHeaders = event.requestHeaders ?? undefined;
67
+ // Determine request type
68
+ if (event.path.startsWith("/mcp/")) {
69
+ logEntry.requestType = "mcp";
70
+ }
71
+ else if (event.path.startsWith("/v1/")) {
72
+ logEntry.requestType = "chat";
73
+ }
74
+ else {
75
+ logEntry.requestType = "api";
76
+ }
77
+ em.persist(logEntry);
78
+ await em.flush();
79
+ }
80
+ catch (e) {
81
+ // Log persistence errors but don't throw - in-memory store is the primary
82
+ console.error("Failed to persist audit log:", e);
83
+ }
84
+ }
85
+ /**
86
+ * Add an audit event
87
+ */ export function addAuditEvent(event) {
88
+ const id = `${Date.now()}-${++eventCounter}`;
89
+ const fullEvent = {
90
+ ...event,
91
+ id
92
+ };
93
+ auditStore.set(id, fullEvent);
94
+ // Persist to database asynchronously (lazy init)
95
+ persistToDb(fullEvent, id).catch(() => {
96
+ // Already logged in persistToDb
97
+ });
98
+ return fullEvent;
99
+ }
100
+ /**
101
+ * Query audit events
102
+ */ export function queryAuditEvents(options) {
103
+ const { limit = 50, offset = 0, serverName, serverType, method, from, to } = options;
104
+ // Get all events as array
105
+ let events = [];
106
+ for (const [, event] of auditStore.entries()) {
107
+ events.push(event);
108
+ }
109
+ // Sort by timestamp desc
110
+ events.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
111
+ // Apply filters
112
+ if (serverName) {
113
+ events = events.filter((e) => e.serverName === serverName);
114
+ }
115
+ if (serverType) {
116
+ events = events.filter((e) => e.serverType === serverType);
117
+ }
118
+ if (method) {
119
+ events = events.filter((e) => e.method === method);
120
+ }
121
+ if (from) {
122
+ const fromTime = new Date(from).getTime();
123
+ events = events.filter((e) => new Date(e.timestamp).getTime() >= fromTime);
124
+ }
125
+ if (to) {
126
+ const toTime = new Date(to).getTime();
127
+ events = events.filter((e) => new Date(e.timestamp).getTime() <= toTime);
128
+ }
129
+ const total = events.length;
130
+ // Paginate
131
+ events = events.slice(offset, offset + limit);
132
+ return {
133
+ events,
134
+ total
135
+ };
136
+ }
137
+ /**
138
+ * Get audit statistics
139
+ */ export function getAuditStats(options) {
140
+ let events = [];
141
+ for (const [, event] of auditStore.entries()) {
142
+ events.push(event);
143
+ }
144
+ // Apply time filters
145
+ if (options.from) {
146
+ const fromTime = new Date(options.from).getTime();
147
+ events = events.filter((e) => new Date(e.timestamp).getTime() >= fromTime);
148
+ }
149
+ if (options.to) {
150
+ const toTime = new Date(options.to).getTime();
151
+ events = events.filter((e) => new Date(e.timestamp).getTime() <= toTime);
152
+ }
153
+ // Calculate stats
154
+ const totalRequests = events.length;
155
+ const totalErrors = events.filter((e) => e.error || e.status && e.status >= 400).length;
156
+ const durations = events.map((e) => e.durationMs).filter((d) => d != null);
157
+ const avgDurationMs = durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0;
158
+ // Group by server
159
+ const serverCounts = new Map();
160
+ for (const event of events) {
161
+ const name = event.serverName || "unknown";
162
+ serverCounts.set(name, (serverCounts.get(name) || 0) + 1);
163
+ }
164
+ const byServer = Array.from(serverCounts.entries()).map(([name, count]) => ({
165
+ name,
166
+ count
167
+ })).sort((a, b) => b.count - a.count);
168
+ // Group by method
169
+ const methodCounts = new Map();
170
+ for (const event of events) {
171
+ const method = event.method || "unknown";
172
+ methodCounts.set(method, (methodCounts.get(method) || 0) + 1);
173
+ }
174
+ const byMethod = Array.from(methodCounts.entries()).map(([method, count]) => ({
175
+ method,
176
+ count
177
+ })).sort((a, b) => b.count - a.count);
178
+ return {
179
+ totalRequests,
180
+ totalErrors,
181
+ avgDurationMs,
182
+ byServer,
183
+ byMethod
184
+ };
185
+ }
186
+ /**
187
+ * Clear audit events before a timestamp
188
+ */ export function clearAuditEvents(before) {
189
+ const beforeTime = new Date(before).getTime();
190
+ let deleted = 0;
191
+ for (const [id, event] of auditStore.entries()) {
192
+ if (new Date(event.timestamp).getTime() < beforeTime) {
193
+ auditStore.delete(id);
194
+ deleted++;
195
+ }
196
+ }
197
+ return deleted;
198
+ }
199
+ /**
200
+ * Audit Router implementation
201
+ */ export const AuditRouter = implement(AuditContract).router({
202
+ list: implement(AuditContract.list).handler(async ({ input }) => {
203
+ return queryAuditEvents(input);
204
+ }),
205
+ get: implement(AuditContract.get).handler(async ({ input }) => {
206
+ return auditStore.get(input.id) ?? null;
207
+ }),
208
+ stats: implement(AuditContract.stats).handler(async ({ input }) => {
209
+ return getAuditStats(input);
210
+ }),
211
+ clear: implement(AuditContract.clear).handler(async ({ input }) => {
212
+ const deleted = clearAuditEvents(input.before);
213
+ return {
214
+ deleted
215
+ };
216
+ })
217
+ });
218
+ /**
219
+ * Hono middleware for audit logging
220
+ */ export function auditMiddleware() {
221
+ return async (c, next) => {
222
+ const startTime = Date.now();
223
+ const path = c.req.path;
224
+ // Extract server info from path
225
+ let serverName;
226
+ let serverType;
227
+ const mcpMatch = path.match(/^\/mcp\/([^/]+)/);
228
+ if (mcpMatch) {
229
+ serverName = mcpMatch[1];
230
+ // Infer type from well-known paths
231
+ if (serverName === "tencent-cls")
232
+ serverType = "tencent-cls";
233
+ else if (serverName === "sql")
234
+ serverType = "sql";
235
+ else if (serverName === "prometheus")
236
+ serverType = "prometheus";
237
+ else if (serverName === "relay")
238
+ serverType = "relay";
239
+ else
240
+ serverType = "custom";
241
+ }
242
+ // Extract model info from chat requests
243
+ if (path.startsWith("/v1/")) {
244
+ serverType = "chat";
245
+ }
246
+ let error;
247
+ try {
248
+ await next();
249
+ }
250
+ catch (e) {
251
+ error = e instanceof Error ? e.message : String(e);
252
+ throw e;
253
+ }
254
+ finally {
255
+ const durationMs = Date.now() - startTime;
256
+ // Audit MCP requests, Chat API requests, and other API requests
257
+ const shouldAudit = path.startsWith("/mcp/") || path.startsWith("/v1/") || path.startsWith("/api/") && c.req.method !== "GET";
258
+ if (shouldAudit) {
259
+ addAuditEvent({
260
+ timestamp: new Date().toISOString(),
261
+ method: c.req.method,
262
+ path,
263
+ serverName,
264
+ serverType,
265
+ status: c.res.status,
266
+ durationMs,
267
+ error,
268
+ requestHeaders: headersToRecord(c.req.raw.headers)
269
+ });
270
+ }
271
+ }
272
+ };
273
+ }
274
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/audit.ts"],"sourcesContent":["import { implement } from '@orpc/server';\nimport type { Context, Next } from 'hono';\nimport { LRUCache } from 'lru-cache';\nimport { AuditContract, type AuditEvent } from '../contracts';\nimport { RequestLogEntity } from '../entities';\nimport { ensureDbInitialized, configureDb } from './db';\nimport type { AuditConfig, DbConfig } from './schema';\n\n/**\n * Convert Headers to a plain record\n */\nfunction headersToRecord(headers: Headers): Record<string, string> {\n\tconst record: Record<string, string> = {};\n\theaders.forEach((value, key) => {\n\t\trecord[key] = value;\n\t});\n\treturn record;\n}\n\n// In-memory audit store using LRU cache\nconst auditStore = new LRUCache<string, AuditEvent>({\n\tmax: 10000, // Keep last 10k events\n\tttl: 1000 * 60 * 60 * 24, // 24 hours\n});\n\n// Counter for IDs\nlet eventCounter = 0;\n\n// Audit configuration state\nlet auditEnabled = true; // default to enabled\nlet dbConfigured = false;\n\n/**\n * Configure audit module with settings\n * Call this before using audit features\n *\n * @param auditConfig - Audit config section\n * @param fallbackDbConfig - Fallback db config from root config\n */\nexport function configureAudit(auditConfig?: AuditConfig, fallbackDbConfig?: DbConfig): void {\n\t// Determine if audit is enabled (default: true)\n\tauditEnabled = auditConfig?.enabled !== false;\n\n\tif (auditEnabled) {\n\t\t// Use audit.db config if present, otherwise fallback to root db config\n\t\tconst dbConfig = auditConfig?.db ?? fallbackDbConfig;\n\t\tconfigureDb(dbConfig);\n\t\tdbConfigured = true;\n\t}\n}\n\n/**\n * Check if audit is enabled\n */\nexport function isAuditEnabled(): boolean {\n\treturn auditEnabled;\n}\n\n/**\n * Persist audit event to database (lazy init)\n */\nasync function persistToDb(event: AuditEvent, id: string): Promise<void> {\n\tif (!auditEnabled || !dbConfigured) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\t// Lazy initialize DB on first persist\n\t\tconst orm = await ensureDbInitialized();\n\t\tconst em = orm.em.fork();\n\n\t\tconst logEntry = new RequestLogEntity();\n\t\tlogEntry.requestId = id;\n\t\tlogEntry.timestamp = new Date(event.timestamp);\n\t\tlogEntry.method = event.method;\n\t\tlogEntry.path = event.path;\n\t\tlogEntry.serverName = event.serverName ?? undefined;\n\t\tlogEntry.serverType = event.serverType ?? undefined;\n\t\tlogEntry.status = event.status ?? undefined;\n\t\tlogEntry.durationMs = event.durationMs ?? undefined;\n\t\tlogEntry.error = event.error ?? undefined;\n\t\tlogEntry.requestHeaders = event.requestHeaders ?? undefined;\n\t\t// Determine request type\n\t\tif (event.path.startsWith('/mcp/')) {\n\t\t\tlogEntry.requestType = 'mcp';\n\t\t} else if (event.path.startsWith('/v1/')) {\n\t\t\tlogEntry.requestType = 'chat';\n\t\t} else {\n\t\t\tlogEntry.requestType = 'api';\n\t\t}\n\t\tem.persist(logEntry);\n\t\tawait em.flush();\n\t} catch (e) {\n\t\t// Log persistence errors but don't throw - in-memory store is the primary\n\t\tconsole.error('Failed to persist audit log:', e);\n\t}\n}\n\n/**\n * Add an audit event\n */\nexport function addAuditEvent(event: Omit<AuditEvent, 'id'>): AuditEvent {\n\tconst id = `${Date.now()}-${++eventCounter}`;\n\tconst fullEvent: AuditEvent = { ...event, id };\n\tauditStore.set(id, fullEvent);\n\n\t// Persist to database asynchronously (lazy init)\n\tpersistToDb(fullEvent, id).catch(() => {\n\t\t// Already logged in persistToDb\n\t});\n\n\treturn fullEvent;\n}\n\n/**\n * Query audit events\n */\nexport function queryAuditEvents(options: {\n\tlimit?: number;\n\toffset?: number;\n\tserverName?: string | null;\n\tserverType?: string | null;\n\tmethod?: string | null;\n\tfrom?: string | null;\n\tto?: string | null;\n}): { events: AuditEvent[]; total: number } {\n\tconst { limit = 50, offset = 0, serverName, serverType, method, from, to } = options;\n\n\t// Get all events as array\n\tlet events: AuditEvent[] = [];\n\tfor (const [, event] of auditStore.entries()) {\n\t\tevents.push(event);\n\t}\n\n\t// Sort by timestamp desc\n\tevents.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());\n\n\t// Apply filters\n\tif (serverName) {\n\t\tevents = events.filter((e) => e.serverName === serverName);\n\t}\n\tif (serverType) {\n\t\tevents = events.filter((e) => e.serverType === serverType);\n\t}\n\tif (method) {\n\t\tevents = events.filter((e) => e.method === method);\n\t}\n\tif (from) {\n\t\tconst fromTime = new Date(from).getTime();\n\t\tevents = events.filter((e) => new Date(e.timestamp).getTime() >= fromTime);\n\t}\n\tif (to) {\n\t\tconst toTime = new Date(to).getTime();\n\t\tevents = events.filter((e) => new Date(e.timestamp).getTime() <= toTime);\n\t}\n\n\tconst total = events.length;\n\n\t// Paginate\n\tevents = events.slice(offset, offset + limit);\n\n\treturn { events, total };\n}\n\n/**\n * Get audit statistics\n */\nexport function getAuditStats(options: { from?: string | null; to?: string | null }) {\n\tlet events: AuditEvent[] = [];\n\tfor (const [, event] of auditStore.entries()) {\n\t\tevents.push(event);\n\t}\n\n\t// Apply time filters\n\tif (options.from) {\n\t\tconst fromTime = new Date(options.from).getTime();\n\t\tevents = events.filter((e) => new Date(e.timestamp).getTime() >= fromTime);\n\t}\n\tif (options.to) {\n\t\tconst toTime = new Date(options.to).getTime();\n\t\tevents = events.filter((e) => new Date(e.timestamp).getTime() <= toTime);\n\t}\n\n\t// Calculate stats\n\tconst totalRequests = events.length;\n\tconst totalErrors = events.filter((e) => e.error || (e.status && e.status >= 400)).length;\n\n\tconst durations = events.map((e) => e.durationMs).filter((d): d is number => d != null);\n\tconst avgDurationMs = durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0;\n\n\t// Group by server\n\tconst serverCounts = new Map<string, number>();\n\tfor (const event of events) {\n\t\tconst name = event.serverName || 'unknown';\n\t\tserverCounts.set(name, (serverCounts.get(name) || 0) + 1);\n\t}\n\tconst byServer = Array.from(serverCounts.entries())\n\t\t.map(([name, count]) => ({ name, count }))\n\t\t.sort((a, b) => b.count - a.count);\n\n\t// Group by method\n\tconst methodCounts = new Map<string, number>();\n\tfor (const event of events) {\n\t\tconst method = event.method || 'unknown';\n\t\tmethodCounts.set(method, (methodCounts.get(method) || 0) + 1);\n\t}\n\tconst byMethod = Array.from(methodCounts.entries())\n\t\t.map(([method, count]) => ({ method, count }))\n\t\t.sort((a, b) => b.count - a.count);\n\n\treturn { totalRequests, totalErrors, avgDurationMs, byServer, byMethod };\n}\n\n/**\n * Clear audit events before a timestamp\n */\nexport function clearAuditEvents(before: string): number {\n\tconst beforeTime = new Date(before).getTime();\n\tlet deleted = 0;\n\n\tfor (const [id, event] of auditStore.entries()) {\n\t\tif (new Date(event.timestamp).getTime() < beforeTime) {\n\t\t\tauditStore.delete(id);\n\t\t\tdeleted++;\n\t\t}\n\t}\n\n\treturn deleted;\n}\n\n/**\n * Audit Router implementation\n */\nexport const AuditRouter = implement(AuditContract).router({\n\tlist: implement(AuditContract.list).handler(async ({ input }) => {\n\t\treturn queryAuditEvents(input);\n\t}),\n\n\tget: implement(AuditContract.get).handler(async ({ input }) => {\n\t\treturn auditStore.get(input.id) ?? null;\n\t}),\n\n\tstats: implement(AuditContract.stats).handler(async ({ input }) => {\n\t\treturn getAuditStats(input);\n\t}),\n\n\tclear: implement(AuditContract.clear).handler(async ({ input }) => {\n\t\tconst deleted = clearAuditEvents(input.before);\n\t\treturn { deleted };\n\t}),\n});\n\n/**\n * Hono middleware for audit logging\n */\nexport function auditMiddleware() {\n\treturn async (c: Context, next: Next) => {\n\t\tconst startTime = Date.now();\n\t\tconst path = c.req.path;\n\n\t\t// Extract server info from path\n\t\tlet serverName: string | undefined;\n\t\tlet serverType: string | undefined;\n\n\t\tconst mcpMatch = path.match(/^\\/mcp\\/([^/]+)/);\n\t\tif (mcpMatch) {\n\t\t\tserverName = mcpMatch[1];\n\t\t\t// Infer type from well-known paths\n\t\t\tif (serverName === 'tencent-cls') serverType = 'tencent-cls';\n\t\t\telse if (serverName === 'sql') serverType = 'sql';\n\t\t\telse if (serverName === 'prometheus') serverType = 'prometheus';\n\t\t\telse if (serverName === 'relay') serverType = 'relay';\n\t\t\telse serverType = 'custom';\n\t\t}\n\n\t\t// Extract model info from chat requests\n\t\tif (path.startsWith('/v1/')) {\n\t\t\tserverType = 'chat';\n\t\t}\n\n\t\tlet error: string | undefined;\n\n\t\ttry {\n\t\t\tawait next();\n\t\t} catch (e) {\n\t\t\terror = e instanceof Error ? e.message : String(e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconst durationMs = Date.now() - startTime;\n\n\t\t\t// Audit MCP requests, Chat API requests, and other API requests\n\t\t\tconst shouldAudit =\n\t\t\t\tpath.startsWith('/mcp/') || path.startsWith('/v1/') || (path.startsWith('/api/') && c.req.method !== 'GET');\n\n\t\t\tif (shouldAudit) {\n\t\t\t\taddAuditEvent({\n\t\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\t\tmethod: c.req.method,\n\t\t\t\t\tpath,\n\t\t\t\t\tserverName,\n\t\t\t\t\tserverType,\n\t\t\t\t\tstatus: c.res.status,\n\t\t\t\t\tdurationMs,\n\t\t\t\t\terror,\n\t\t\t\t\trequestHeaders: headersToRecord(c.req.raw.headers),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t};\n}\n"],"names":["implement","LRUCache","AuditContract","RequestLogEntity","ensureDbInitialized","configureDb","headersToRecord","headers","record","forEach","value","key","auditStore","max","ttl","eventCounter","auditEnabled","dbConfigured","configureAudit","auditConfig","fallbackDbConfig","enabled","dbConfig","db","isAuditEnabled","persistToDb","event","id","orm","em","fork","logEntry","requestId","timestamp","Date","method","path","serverName","undefined","serverType","status","durationMs","error","requestHeaders","startsWith","requestType","persist","flush","e","console","addAuditEvent","now","fullEvent","set","catch","queryAuditEvents","options","limit","offset","from","to","events","entries","push","sort","a","b","getTime","filter","fromTime","toTime","total","length","slice","getAuditStats","totalRequests","totalErrors","durations","map","d","avgDurationMs","reduce","serverCounts","Map","name","get","byServer","Array","count","methodCounts","byMethod","clearAuditEvents","before","beforeTime","deleted","delete","AuditRouter","router","list","handler","input","stats","clear","auditMiddleware","c","next","startTime","req","mcpMatch","match","Error","message","String","shouldAudit","toISOString","res","raw"],"mappings":"AAAA,SAASA,SAAS,QAAQ,eAAe;AAEzC,SAASC,QAAQ,QAAQ,YAAY;AACrC,SAASC,aAAa,QAAyB,eAAe;AAC9D,SAASC,gBAAgB,QAAQ,cAAc;AAC/C,SAASC,mBAAmB,EAAEC,WAAW,QAAQ,OAAO;AAGxD;;CAEC,GACD,SAASC,gBAAgBC,OAAgB;IACxC,MAAMC,SAAiC,CAAC;IACxCD,QAAQE,OAAO,CAAC,CAACC,OAAOC;QACvBH,MAAM,CAACG,IAAI,GAAGD;IACf;IACA,OAAOF;AACR;AAEA,wCAAwC;AACxC,MAAMI,aAAa,IAAIX,SAA6B;IACnDY,KAAK;IACLC,KAAK,OAAO,KAAK,KAAK;AACvB;AAEA,kBAAkB;AAClB,IAAIC,eAAe;AAEnB,4BAA4B;AAC5B,IAAIC,eAAe,MAAM,qBAAqB;AAC9C,IAAIC,eAAe;AAEnB;;;;;;CAMC,GACD,OAAO,SAASC,eAAeC,WAAyB,EAAEC,gBAA2B;IACpF,gDAAgD;IAChDJ,eAAeG,aAAaE,YAAY;IAExC,IAAIL,cAAc;QACjB,uEAAuE;QACvE,MAAMM,WAAWH,aAAaI,MAAMH;QACpCf,YAAYiB;QACZL,eAAe;IAChB;AACD;AAEA;;CAEC,GACD,OAAO,SAASO;IACf,OAAOR;AACR;AAEA;;CAEC,GACD,eAAeS,YAAYC,KAAiB,EAAEC,EAAU;IACvD,IAAI,CAACX,gBAAgB,CAACC,cAAc;QACnC;IACD;IAEA,IAAI;QACH,sCAAsC;QACtC,MAAMW,MAAM,MAAMxB;QAClB,MAAMyB,KAAKD,IAAIC,EAAE,CAACC,IAAI;QAEtB,MAAMC,WAAW,IAAI5B;QACrB4B,SAASC,SAAS,GAAGL;QACrBI,SAASE,SAAS,GAAG,IAAIC,KAAKR,MAAMO,SAAS;QAC7CF,SAASI,MAAM,GAAGT,MAAMS,MAAM;QAC9BJ,SAASK,IAAI,GAAGV,MAAMU,IAAI;QAC1BL,SAASM,UAAU,GAAGX,MAAMW,UAAU,IAAIC;QAC1CP,SAASQ,UAAU,GAAGb,MAAMa,UAAU,IAAID;QAC1CP,SAASS,MAAM,GAAGd,MAAMc,MAAM,IAAIF;QAClCP,SAASU,UAAU,GAAGf,MAAMe,UAAU,IAAIH;QAC1CP,SAASW,KAAK,GAAGhB,MAAMgB,KAAK,IAAIJ;QAChCP,SAASY,cAAc,GAAGjB,MAAMiB,cAAc,IAAIL;QAClD,yBAAyB;QACzB,IAAIZ,MAAMU,IAAI,CAACQ,UAAU,CAAC,UAAU;YACnCb,SAASc,WAAW,GAAG;QACxB,OAAO,IAAInB,MAAMU,IAAI,CAACQ,UAAU,CAAC,SAAS;YACzCb,SAASc,WAAW,GAAG;QACxB,OAAO;YACNd,SAASc,WAAW,GAAG;QACxB;QACAhB,GAAGiB,OAAO,CAACf;QACX,MAAMF,GAAGkB,KAAK;IACf,EAAE,OAAOC,GAAG;QACX,0EAA0E;QAC1EC,QAAQP,KAAK,CAAC,gCAAgCM;IAC/C;AACD;AAEA;;CAEC,GACD,OAAO,SAASE,cAAcxB,KAA6B;IAC1D,MAAMC,KAAK,GAAGO,KAAKiB,GAAG,GAAG,CAAC,EAAE,EAAEpC,cAAc;IAC5C,MAAMqC,YAAwB;QAAE,GAAG1B,KAAK;QAAEC;IAAG;IAC7Cf,WAAWyC,GAAG,CAAC1B,IAAIyB;IAEnB,iDAAiD;IACjD3B,YAAY2B,WAAWzB,IAAI2B,KAAK,CAAC;IAChC,gCAAgC;IACjC;IAEA,OAAOF;AACR;AAEA;;CAEC,GACD,OAAO,SAASG,iBAAiBC,OAQhC;IACA,MAAM,EAAEC,QAAQ,EAAE,EAAEC,SAAS,CAAC,EAAErB,UAAU,EAAEE,UAAU,EAAEJ,MAAM,EAAEwB,IAAI,EAAEC,EAAE,EAAE,GAAGJ;IAE7E,0BAA0B;IAC1B,IAAIK,SAAuB,EAAE;IAC7B,KAAK,MAAM,GAAGnC,MAAM,IAAId,WAAWkD,OAAO,GAAI;QAC7CD,OAAOE,IAAI,CAACrC;IACb;IAEA,yBAAyB;IACzBmC,OAAOG,IAAI,CAAC,CAACC,GAAGC,IAAM,IAAIhC,KAAKgC,EAAEjC,SAAS,EAAEkC,OAAO,KAAK,IAAIjC,KAAK+B,EAAEhC,SAAS,EAAEkC,OAAO;IAErF,gBAAgB;IAChB,IAAI9B,YAAY;QACfwB,SAASA,OAAOO,MAAM,CAAC,CAACpB,IAAMA,EAAEX,UAAU,KAAKA;IAChD;IACA,IAAIE,YAAY;QACfsB,SAASA,OAAOO,MAAM,CAAC,CAACpB,IAAMA,EAAET,UAAU,KAAKA;IAChD;IACA,IAAIJ,QAAQ;QACX0B,SAASA,OAAOO,MAAM,CAAC,CAACpB,IAAMA,EAAEb,MAAM,KAAKA;IAC5C;IACA,IAAIwB,MAAM;QACT,MAAMU,WAAW,IAAInC,KAAKyB,MAAMQ,OAAO;QACvCN,SAASA,OAAOO,MAAM,CAAC,CAACpB,IAAM,IAAId,KAAKc,EAAEf,SAAS,EAAEkC,OAAO,MAAME;IAClE;IACA,IAAIT,IAAI;QACP,MAAMU,SAAS,IAAIpC,KAAK0B,IAAIO,OAAO;QACnCN,SAASA,OAAOO,MAAM,CAAC,CAACpB,IAAM,IAAId,KAAKc,EAAEf,SAAS,EAAEkC,OAAO,MAAMG;IAClE;IAEA,MAAMC,QAAQV,OAAOW,MAAM;IAE3B,WAAW;IACXX,SAASA,OAAOY,KAAK,CAACf,QAAQA,SAASD;IAEvC,OAAO;QAAEI;QAAQU;IAAM;AACxB;AAEA;;CAEC,GACD,OAAO,SAASG,cAAclB,OAAqD;IAClF,IAAIK,SAAuB,EAAE;IAC7B,KAAK,MAAM,GAAGnC,MAAM,IAAId,WAAWkD,OAAO,GAAI;QAC7CD,OAAOE,IAAI,CAACrC;IACb;IAEA,qBAAqB;IACrB,IAAI8B,QAAQG,IAAI,EAAE;QACjB,MAAMU,WAAW,IAAInC,KAAKsB,QAAQG,IAAI,EAAEQ,OAAO;QAC/CN,SAASA,OAAOO,MAAM,CAAC,CAACpB,IAAM,IAAId,KAAKc,EAAEf,SAAS,EAAEkC,OAAO,MAAME;IAClE;IACA,IAAIb,QAAQI,EAAE,EAAE;QACf,MAAMU,SAAS,IAAIpC,KAAKsB,QAAQI,EAAE,EAAEO,OAAO;QAC3CN,SAASA,OAAOO,MAAM,CAAC,CAACpB,IAAM,IAAId,KAAKc,EAAEf,SAAS,EAAEkC,OAAO,MAAMG;IAClE;IAEA,kBAAkB;IAClB,MAAMK,gBAAgBd,OAAOW,MAAM;IACnC,MAAMI,cAAcf,OAAOO,MAAM,CAAC,CAACpB,IAAMA,EAAEN,KAAK,IAAKM,EAAER,MAAM,IAAIQ,EAAER,MAAM,IAAI,KAAMgC,MAAM;IAEzF,MAAMK,YAAYhB,OAAOiB,GAAG,CAAC,CAAC9B,IAAMA,EAAEP,UAAU,EAAE2B,MAAM,CAAC,CAACW,IAAmBA,KAAK;IAClF,MAAMC,gBAAgBH,UAAUL,MAAM,GAAG,IAAIK,UAAUI,MAAM,CAAC,CAAChB,GAAGC,IAAMD,IAAIC,GAAG,KAAKW,UAAUL,MAAM,GAAG;IAEvG,kBAAkB;IAClB,MAAMU,eAAe,IAAIC;IACzB,KAAK,MAAMzD,SAASmC,OAAQ;QAC3B,MAAMuB,OAAO1D,MAAMW,UAAU,IAAI;QACjC6C,aAAa7B,GAAG,CAAC+B,MAAM,AAACF,CAAAA,aAAaG,GAAG,CAACD,SAAS,CAAA,IAAK;IACxD;IACA,MAAME,WAAWC,MAAM5B,IAAI,CAACuB,aAAapB,OAAO,IAC9CgB,GAAG,CAAC,CAAC,CAACM,MAAMI,MAAM,GAAM,CAAA;YAAEJ;YAAMI;QAAM,CAAA,GACtCxB,IAAI,CAAC,CAACC,GAAGC,IAAMA,EAAEsB,KAAK,GAAGvB,EAAEuB,KAAK;IAElC,kBAAkB;IAClB,MAAMC,eAAe,IAAIN;IACzB,KAAK,MAAMzD,SAASmC,OAAQ;QAC3B,MAAM1B,SAAST,MAAMS,MAAM,IAAI;QAC/BsD,aAAapC,GAAG,CAAClB,QAAQ,AAACsD,CAAAA,aAAaJ,GAAG,CAAClD,WAAW,CAAA,IAAK;IAC5D;IACA,MAAMuD,WAAWH,MAAM5B,IAAI,CAAC8B,aAAa3B,OAAO,IAC9CgB,GAAG,CAAC,CAAC,CAAC3C,QAAQqD,MAAM,GAAM,CAAA;YAAErD;YAAQqD;QAAM,CAAA,GAC1CxB,IAAI,CAAC,CAACC,GAAGC,IAAMA,EAAEsB,KAAK,GAAGvB,EAAEuB,KAAK;IAElC,OAAO;QAAEb;QAAeC;QAAaI;QAAeM;QAAUI;IAAS;AACxE;AAEA;;CAEC,GACD,OAAO,SAASC,iBAAiBC,MAAc;IAC9C,MAAMC,aAAa,IAAI3D,KAAK0D,QAAQzB,OAAO;IAC3C,IAAI2B,UAAU;IAEd,KAAK,MAAM,CAACnE,IAAID,MAAM,IAAId,WAAWkD,OAAO,GAAI;QAC/C,IAAI,IAAI5B,KAAKR,MAAMO,SAAS,EAAEkC,OAAO,KAAK0B,YAAY;YACrDjF,WAAWmF,MAAM,CAACpE;YAClBmE;QACD;IACD;IAEA,OAAOA;AACR;AAEA;;CAEC,GACD,OAAO,MAAME,cAAchG,UAAUE,eAAe+F,MAAM,CAAC;IAC1DC,MAAMlG,UAAUE,cAAcgG,IAAI,EAAEC,OAAO,CAAC,OAAO,EAAEC,KAAK,EAAE;QAC3D,OAAO7C,iBAAiB6C;IACzB;IAEAf,KAAKrF,UAAUE,cAAcmF,GAAG,EAAEc,OAAO,CAAC,OAAO,EAAEC,KAAK,EAAE;QACzD,OAAOxF,WAAWyE,GAAG,CAACe,MAAMzE,EAAE,KAAK;IACpC;IAEA0E,OAAOrG,UAAUE,cAAcmG,KAAK,EAAEF,OAAO,CAAC,OAAO,EAAEC,KAAK,EAAE;QAC7D,OAAO1B,cAAc0B;IACtB;IAEAE,OAAOtG,UAAUE,cAAcoG,KAAK,EAAEH,OAAO,CAAC,OAAO,EAAEC,KAAK,EAAE;QAC7D,MAAMN,UAAUH,iBAAiBS,MAAMR,MAAM;QAC7C,OAAO;YAAEE;QAAQ;IAClB;AACD,GAAG;AAEH;;CAEC,GACD,OAAO,SAASS;IACf,OAAO,OAAOC,GAAYC;QACzB,MAAMC,YAAYxE,KAAKiB,GAAG;QAC1B,MAAMf,OAAOoE,EAAEG,GAAG,CAACvE,IAAI;QAEvB,gCAAgC;QAChC,IAAIC;QACJ,IAAIE;QAEJ,MAAMqE,WAAWxE,KAAKyE,KAAK,CAAC;QAC5B,IAAID,UAAU;YACbvE,aAAauE,QAAQ,CAAC,EAAE;YACxB,mCAAmC;YACnC,IAAIvE,eAAe,eAAeE,aAAa;iBAC1C,IAAIF,eAAe,OAAOE,aAAa;iBACvC,IAAIF,eAAe,cAAcE,aAAa;iBAC9C,IAAIF,eAAe,SAASE,aAAa;iBACzCA,aAAa;QACnB;QAEA,wCAAwC;QACxC,IAAIH,KAAKQ,UAAU,CAAC,SAAS;YAC5BL,aAAa;QACd;QAEA,IAAIG;QAEJ,IAAI;YACH,MAAM+D;QACP,EAAE,OAAOzD,GAAG;YACXN,QAAQM,aAAa8D,QAAQ9D,EAAE+D,OAAO,GAAGC,OAAOhE;YAChD,MAAMA;QACP,SAAU;YACT,MAAMP,aAAaP,KAAKiB,GAAG,KAAKuD;YAEhC,gEAAgE;YAChE,MAAMO,cACL7E,KAAKQ,UAAU,CAAC,YAAYR,KAAKQ,UAAU,CAAC,WAAYR,KAAKQ,UAAU,CAAC,YAAY4D,EAAEG,GAAG,CAACxE,MAAM,KAAK;YAEtG,IAAI8E,aAAa;gBAChB/D,cAAc;oBACbjB,WAAW,IAAIC,OAAOgF,WAAW;oBACjC/E,QAAQqE,EAAEG,GAAG,CAACxE,MAAM;oBACpBC;oBACAC;oBACAE;oBACAC,QAAQgE,EAAEW,GAAG,CAAC3E,MAAM;oBACpBC;oBACAC;oBACAC,gBAAgBrC,gBAAgBkG,EAAEG,GAAG,CAACS,GAAG,CAAC7G,OAAO;gBAClD;YACD;QACD;IACD;AACD"}