untrap-mcp 0.1.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 (205) hide show
  1. package/dist/auth/api-keys.d.ts +53 -0
  2. package/dist/auth/api-keys.d.ts.map +1 -0
  3. package/dist/auth/api-keys.js +115 -0
  4. package/dist/auth/api-keys.js.map +1 -0
  5. package/dist/auth/rate-limiter.d.ts +6 -0
  6. package/dist/auth/rate-limiter.d.ts.map +1 -0
  7. package/dist/auth/rate-limiter.js +77 -0
  8. package/dist/auth/rate-limiter.js.map +1 -0
  9. package/dist/config.d.ts +42 -0
  10. package/dist/config.d.ts.map +1 -0
  11. package/dist/config.js +62 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/db/__tests__/assert-msp-scoped.test.d.ts +2 -0
  14. package/dist/db/__tests__/assert-msp-scoped.test.d.ts.map +1 -0
  15. package/dist/db/__tests__/assert-msp-scoped.test.js +93 -0
  16. package/dist/db/__tests__/assert-msp-scoped.test.js.map +1 -0
  17. package/dist/db/azure-openai.d.ts +24 -0
  18. package/dist/db/azure-openai.d.ts.map +1 -0
  19. package/dist/db/azure-openai.js +45 -0
  20. package/dist/db/azure-openai.js.map +1 -0
  21. package/dist/db/gateway-client.d.ts +13 -0
  22. package/dist/db/gateway-client.d.ts.map +1 -0
  23. package/dist/db/gateway-client.js +210 -0
  24. package/dist/db/gateway-client.js.map +1 -0
  25. package/dist/index.d.ts +3 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +46 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/middleware/logging.d.ts +16 -0
  30. package/dist/middleware/logging.d.ts.map +1 -0
  31. package/dist/middleware/logging.js +52 -0
  32. package/dist/middleware/logging.js.map +1 -0
  33. package/dist/server.d.ts +3 -0
  34. package/dist/server.d.ts.map +1 -0
  35. package/dist/server.js +466 -0
  36. package/dist/server.js.map +1 -0
  37. package/dist/tools/ai/explain-budget.d.ts +10 -0
  38. package/dist/tools/ai/explain-budget.d.ts.map +1 -0
  39. package/dist/tools/ai/explain-budget.js +61 -0
  40. package/dist/tools/ai/explain-budget.js.map +1 -0
  41. package/dist/tools/ai/explain-experience.d.ts +6 -0
  42. package/dist/tools/ai/explain-experience.d.ts.map +1 -0
  43. package/dist/tools/ai/explain-experience.js +62 -0
  44. package/dist/tools/ai/explain-experience.js.map +1 -0
  45. package/dist/tools/ai/explain-sla.d.ts +9 -0
  46. package/dist/tools/ai/explain-sla.d.ts.map +1 -0
  47. package/dist/tools/ai/explain-sla.js +57 -0
  48. package/dist/tools/ai/explain-sla.js.map +1 -0
  49. package/dist/tools/ai/kb-search.d.ts +17 -0
  50. package/dist/tools/ai/kb-search.d.ts.map +1 -0
  51. package/dist/tools/ai/kb-search.js +28 -0
  52. package/dist/tools/ai/kb-search.js.map +1 -0
  53. package/dist/tools/ai/root-causes.d.ts +21 -0
  54. package/dist/tools/ai/root-causes.d.ts.map +1 -0
  55. package/dist/tools/ai/root-causes.js +54 -0
  56. package/dist/tools/ai/root-causes.js.map +1 -0
  57. package/dist/tools/ai/similar-tickets.d.ts +21 -0
  58. package/dist/tools/ai/similar-tickets.d.ts.map +1 -0
  59. package/dist/tools/ai/similar-tickets.js +47 -0
  60. package/dist/tools/ai/similar-tickets.js.map +1 -0
  61. package/dist/tools/ai/suggest-improvement.d.ts +6 -0
  62. package/dist/tools/ai/suggest-improvement.d.ts.map +1 -0
  63. package/dist/tools/ai/suggest-improvement.js +75 -0
  64. package/dist/tools/ai/suggest-improvement.js.map +1 -0
  65. package/dist/tools/analytics/backlog.d.ts +15 -0
  66. package/dist/tools/analytics/backlog.d.ts.map +1 -0
  67. package/dist/tools/analytics/backlog.js +41 -0
  68. package/dist/tools/analytics/backlog.js.map +1 -0
  69. package/dist/tools/analytics/billing.d.ts +15 -0
  70. package/dist/tools/analytics/billing.d.ts.map +1 -0
  71. package/dist/tools/analytics/billing.js +24 -0
  72. package/dist/tools/analytics/billing.js.map +1 -0
  73. package/dist/tools/analytics/client-experience.d.ts +32 -0
  74. package/dist/tools/analytics/client-experience.d.ts.map +1 -0
  75. package/dist/tools/analytics/client-experience.js +34 -0
  76. package/dist/tools/analytics/client-experience.js.map +1 -0
  77. package/dist/tools/analytics/client-health.d.ts +16 -0
  78. package/dist/tools/analytics/client-health.d.ts.map +1 -0
  79. package/dist/tools/analytics/client-health.js +21 -0
  80. package/dist/tools/analytics/client-health.js.map +1 -0
  81. package/dist/tools/analytics/disengaged.d.ts +5 -0
  82. package/dist/tools/analytics/disengaged.d.ts.map +1 -0
  83. package/dist/tools/analytics/disengaged.js +63 -0
  84. package/dist/tools/analytics/disengaged.js.map +1 -0
  85. package/dist/tools/analytics/patterns.d.ts +9 -0
  86. package/dist/tools/analytics/patterns.d.ts.map +1 -0
  87. package/dist/tools/analytics/patterns.js +11 -0
  88. package/dist/tools/analytics/patterns.js.map +1 -0
  89. package/dist/tools/analytics/qc-violations.d.ts +21 -0
  90. package/dist/tools/analytics/qc-violations.d.ts.map +1 -0
  91. package/dist/tools/analytics/qc-violations.js +55 -0
  92. package/dist/tools/analytics/qc-violations.js.map +1 -0
  93. package/dist/tools/analytics/sla-breach-tickets.d.ts +21 -0
  94. package/dist/tools/analytics/sla-breach-tickets.d.ts.map +1 -0
  95. package/dist/tools/analytics/sla-breach-tickets.js +72 -0
  96. package/dist/tools/analytics/sla-breach-tickets.js.map +1 -0
  97. package/dist/tools/analytics/sla-breaches.d.ts +17 -0
  98. package/dist/tools/analytics/sla-breaches.d.ts.map +1 -0
  99. package/dist/tools/analytics/sla-breaches.js +60 -0
  100. package/dist/tools/analytics/sla-breaches.js.map +1 -0
  101. package/dist/tools/analytics/technician-perf.d.ts +10 -0
  102. package/dist/tools/analytics/technician-perf.d.ts.map +1 -0
  103. package/dist/tools/analytics/technician-perf.js +14 -0
  104. package/dist/tools/analytics/technician-perf.js.map +1 -0
  105. package/dist/tools/cfo/agreement-analysis.d.ts +12 -0
  106. package/dist/tools/cfo/agreement-analysis.d.ts.map +1 -0
  107. package/dist/tools/cfo/agreement-analysis.js +59 -0
  108. package/dist/tools/cfo/agreement-analysis.js.map +1 -0
  109. package/dist/tools/cfo/client-profitability.d.ts +25 -0
  110. package/dist/tools/cfo/client-profitability.d.ts.map +1 -0
  111. package/dist/tools/cfo/client-profitability.js +73 -0
  112. package/dist/tools/cfo/client-profitability.js.map +1 -0
  113. package/dist/tools/cfo/financial-overview.d.ts +34 -0
  114. package/dist/tools/cfo/financial-overview.d.ts.map +1 -0
  115. package/dist/tools/cfo/financial-overview.js +67 -0
  116. package/dist/tools/cfo/financial-overview.js.map +1 -0
  117. package/dist/tools/cfo/labor-cost-trend.d.ts +14 -0
  118. package/dist/tools/cfo/labor-cost-trend.d.ts.map +1 -0
  119. package/dist/tools/cfo/labor-cost-trend.js +65 -0
  120. package/dist/tools/cfo/labor-cost-trend.js.map +1 -0
  121. package/dist/tools/cfo/revenue-leakage.d.ts +22 -0
  122. package/dist/tools/cfo/revenue-leakage.d.ts.map +1 -0
  123. package/dist/tools/cfo/revenue-leakage.js +67 -0
  124. package/dist/tools/cfo/revenue-leakage.js.map +1 -0
  125. package/dist/tools/cfo/service-cost-analysis.d.ts +15 -0
  126. package/dist/tools/cfo/service-cost-analysis.d.ts.map +1 -0
  127. package/dist/tools/cfo/service-cost-analysis.js +66 -0
  128. package/dist/tools/cfo/service-cost-analysis.js.map +1 -0
  129. package/dist/tools/cfo/technician-utilization.d.ts +26 -0
  130. package/dist/tools/cfo/technician-utilization.d.ts.map +1 -0
  131. package/dist/tools/cfo/technician-utilization.js +67 -0
  132. package/dist/tools/cfo/technician-utilization.js.map +1 -0
  133. package/dist/tools/composite/client-360.d.ts +28 -0
  134. package/dist/tools/composite/client-360.d.ts.map +1 -0
  135. package/dist/tools/composite/client-360.js +67 -0
  136. package/dist/tools/composite/client-360.js.map +1 -0
  137. package/dist/tools/composite/compare-periods.d.ts +31 -0
  138. package/dist/tools/composite/compare-periods.d.ts.map +1 -0
  139. package/dist/tools/composite/compare-periods.js +94 -0
  140. package/dist/tools/composite/compare-periods.js.map +1 -0
  141. package/dist/tools/composite/draft-email.d.ts +7 -0
  142. package/dist/tools/composite/draft-email.d.ts.map +1 -0
  143. package/dist/tools/composite/draft-email.js +54 -0
  144. package/dist/tools/composite/draft-email.js.map +1 -0
  145. package/dist/tools/composite/morning-briefing.d.ts +15 -0
  146. package/dist/tools/composite/morning-briefing.d.ts.map +1 -0
  147. package/dist/tools/composite/morning-briefing.js +119 -0
  148. package/dist/tools/composite/morning-briefing.js.map +1 -0
  149. package/dist/tools/composite/technician-360.d.ts +22 -0
  150. package/dist/tools/composite/technician-360.d.ts.map +1 -0
  151. package/dist/tools/composite/technician-360.js +55 -0
  152. package/dist/tools/composite/technician-360.js.map +1 -0
  153. package/dist/tools/composite/ticket-deep-dive.d.ts +24 -0
  154. package/dist/tools/composite/ticket-deep-dive.d.ts.map +1 -0
  155. package/dist/tools/composite/ticket-deep-dive.js +49 -0
  156. package/dist/tools/composite/ticket-deep-dive.js.map +1 -0
  157. package/dist/tools/config/msp-context.d.ts +26 -0
  158. package/dist/tools/config/msp-context.d.ts.map +1 -0
  159. package/dist/tools/config/msp-context.js +41 -0
  160. package/dist/tools/config/msp-context.js.map +1 -0
  161. package/dist/tools/qbr/client-detail.d.ts +26 -0
  162. package/dist/tools/qbr/client-detail.d.ts.map +1 -0
  163. package/dist/tools/qbr/client-detail.js +72 -0
  164. package/dist/tools/qbr/client-detail.js.map +1 -0
  165. package/dist/tools/qbr/client-health.d.ts +11 -0
  166. package/dist/tools/qbr/client-health.d.ts.map +1 -0
  167. package/dist/tools/qbr/client-health.js +48 -0
  168. package/dist/tools/qbr/client-health.js.map +1 -0
  169. package/dist/tools/qbr/executive-summary.d.ts +8 -0
  170. package/dist/tools/qbr/executive-summary.d.ts.map +1 -0
  171. package/dist/tools/qbr/executive-summary.js +83 -0
  172. package/dist/tools/qbr/executive-summary.js.map +1 -0
  173. package/dist/tools/qbr/financial-summary.d.ts +19 -0
  174. package/dist/tools/qbr/financial-summary.d.ts.map +1 -0
  175. package/dist/tools/qbr/financial-summary.js +64 -0
  176. package/dist/tools/qbr/financial-summary.js.map +1 -0
  177. package/dist/tools/qbr/recommendations.d.ts +7 -0
  178. package/dist/tools/qbr/recommendations.d.ts.map +1 -0
  179. package/dist/tools/qbr/recommendations.js +80 -0
  180. package/dist/tools/qbr/recommendations.js.map +1 -0
  181. package/dist/tools/qbr/sla-performance.d.ts +12 -0
  182. package/dist/tools/qbr/sla-performance.d.ts.map +1 -0
  183. package/dist/tools/qbr/sla-performance.js +68 -0
  184. package/dist/tools/qbr/sla-performance.js.map +1 -0
  185. package/dist/tools/qbr/technician-scorecard.d.ts +12 -0
  186. package/dist/tools/qbr/technician-scorecard.d.ts.map +1 -0
  187. package/dist/tools/qbr/technician-scorecard.js +69 -0
  188. package/dist/tools/qbr/technician-scorecard.js.map +1 -0
  189. package/dist/tools/qbr/ticket-trends.d.ts +14 -0
  190. package/dist/tools/qbr/ticket-trends.d.ts.map +1 -0
  191. package/dist/tools/qbr/ticket-trends.js +57 -0
  192. package/dist/tools/qbr/ticket-trends.js.map +1 -0
  193. package/dist/tools/qbr/top-issues.d.ts +12 -0
  194. package/dist/tools/qbr/top-issues.d.ts.map +1 -0
  195. package/dist/tools/qbr/top-issues.js +39 -0
  196. package/dist/tools/qbr/top-issues.js.map +1 -0
  197. package/dist/tools/query/data-query.d.ts +24 -0
  198. package/dist/tools/query/data-query.d.ts.map +1 -0
  199. package/dist/tools/query/data-query.js +160 -0
  200. package/dist/tools/query/data-query.js.map +1 -0
  201. package/dist/types/database.d.ts +137 -0
  202. package/dist/types/database.d.ts.map +1 -0
  203. package/dist/types/database.js +3 -0
  204. package/dist/types/database.js.map +1 -0
  205. package/package.json +41 -0
@@ -0,0 +1,53 @@
1
+ interface ApiKeyValidation {
2
+ valid: boolean;
3
+ mspId?: string;
4
+ keyId?: string;
5
+ name?: string;
6
+ scopes?: string[];
7
+ error?: string;
8
+ }
9
+ /**
10
+ * Generate a new API key.
11
+ * Returns the raw key (shown once to user) and the hash (stored in DB).
12
+ */
13
+ export declare function generateApiKey(): {
14
+ rawKey: string;
15
+ keyHash: string;
16
+ };
17
+ /**
18
+ * Validate an API key and return the associated MSP ID.
19
+ * This is the core auth function — the key determines which MSP's data you can access.
20
+ *
21
+ * Uses a SECURITY DEFINER function (lookup_api_key_by_hash) to bypass RLS
22
+ * for this single bootstrap lookup. We don't know the mspId yet — that's
23
+ * what we're resolving — so normal RLS can't work here.
24
+ */
25
+ export declare function validateApiKey(rawKey: string): Promise<ApiKeyValidation>;
26
+ /**
27
+ * Create a new API key for an MSP. Called from the dashboard API route.
28
+ * Returns the raw key (shown once) — it cannot be retrieved again.
29
+ */
30
+ export declare function createApiKeyForMsp(mspId: string, name: string, createdBy: string, scopes?: string[], expiresInDays?: number): Promise<{
31
+ rawKey: string;
32
+ keyId: string;
33
+ }>;
34
+ /**
35
+ * Revoke an API key. Called from the dashboard API route.
36
+ */
37
+ export declare function revokeApiKey(mspId: string, keyId: string): Promise<boolean>;
38
+ /**
39
+ * List API keys for an MSP (without hashes). Called from the dashboard.
40
+ */
41
+ export declare function listApiKeys(mspId: string): Promise<{
42
+ id: string;
43
+ name: string;
44
+ key_prefix: string;
45
+ scopes: string[];
46
+ is_active: boolean;
47
+ last_used_at: string | null;
48
+ expires_at: string | null;
49
+ created_by: string;
50
+ created_at: string;
51
+ }[]>;
52
+ export {};
53
+ //# sourceMappingURL=api-keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-keys.d.ts","sourceRoot":"","sources":["../../src/auth/api-keys.ts"],"names":[],"mappings":"AAkBA,UAAU,gBAAgB;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAUD;;;GAGG;AACH,wBAAgB,cAAc,IAAI;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAKpE;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA0D9E;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,MAAM,EAAY,EAC1B,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAwB5C;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM;QAEvC,MAAM;UACJ,MAAM;gBACA,MAAM;YACV,MAAM,EAAE;eACL,OAAO;kBACJ,MAAM,GAAG,IAAI;gBACf,MAAM,GAAG,IAAI;gBACb,MAAM;gBACN,MAAM;KAWrB"}
@@ -0,0 +1,115 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import { queryRLS, queryWrite } from "../db/gateway-client.js";
3
+ import { getServerMode } from "../config.js";
4
+ const KEY_PREFIX = "untrap_sk_";
5
+ /**
6
+ * Hash an API key using SHA-256.
7
+ * We never store the raw key — only the hash.
8
+ */
9
+ function hashKey(rawKey) {
10
+ return createHash("sha256").update(rawKey).digest("hex");
11
+ }
12
+ /**
13
+ * Generate a new API key.
14
+ * Returns the raw key (shown once to user) and the hash (stored in DB).
15
+ */
16
+ export function generateApiKey() {
17
+ const random = randomBytes(32).toString("base64url");
18
+ const rawKey = `${KEY_PREFIX}${random}`;
19
+ const keyHash = hashKey(rawKey);
20
+ return { rawKey, keyHash };
21
+ }
22
+ /**
23
+ * Validate an API key and return the associated MSP ID.
24
+ * This is the core auth function — the key determines which MSP's data you can access.
25
+ *
26
+ * Uses a SECURITY DEFINER function (lookup_api_key_by_hash) to bypass RLS
27
+ * for this single bootstrap lookup. We don't know the mspId yet — that's
28
+ * what we're resolving — so normal RLS can't work here.
29
+ */
30
+ export async function validateApiKey(rawKey) {
31
+ // Basic format check
32
+ if (!rawKey.startsWith(KEY_PREFIX)) {
33
+ return { valid: false, error: "Invalid key format" };
34
+ }
35
+ const keyHash = hashKey(rawKey);
36
+ try {
37
+ // Use SECURITY DEFINER function — bypasses RLS for this narrow lookup.
38
+ // The function only accepts a key_hash and returns the minimum fields
39
+ // needed for auth. It cannot enumerate or scan the table.
40
+ const rows = await queryWrite(`SELECT id, msp_id, name, scopes, is_active, expires_at
41
+ FROM lookup_api_key_by_hash($1)`, [keyHash],
42
+ // The mspId here is only needed for the Gateway Lambda's SET LOCAL.
43
+ // The SECURITY DEFINER function ignores RLS, so this value doesn't matter.
44
+ "00000000-0000-0000-0000-000000000000");
45
+ if (rows.length === 0) {
46
+ return { valid: false, error: "Invalid API key" };
47
+ }
48
+ const key = rows[0];
49
+ // Check if key is active
50
+ if (!key.is_active) {
51
+ return { valid: false, error: "API key has been revoked" };
52
+ }
53
+ // Check expiration
54
+ if (key.expires_at && new Date(key.expires_at) < new Date()) {
55
+ return { valid: false, error: "API key has expired" };
56
+ }
57
+ // Update last_used_at (fire and forget — only in modes with direct DB write access)
58
+ if (getServerMode() !== "production") {
59
+ queryWrite(`UPDATE mcp_api_keys SET last_used_at = NOW() WHERE id = $1 AND msp_id = $2`, [key.id, key.msp_id], key.msp_id).catch(() => {
60
+ // Non-critical, don't fail the request
61
+ });
62
+ }
63
+ return {
64
+ valid: true,
65
+ mspId: key.msp_id,
66
+ keyId: key.id,
67
+ name: key.name,
68
+ scopes: key.scopes,
69
+ };
70
+ }
71
+ catch {
72
+ return { valid: false, error: "Authentication service unavailable" };
73
+ }
74
+ }
75
+ /**
76
+ * Create a new API key for an MSP. Called from the dashboard API route.
77
+ * Returns the raw key (shown once) — it cannot be retrieved again.
78
+ */
79
+ export async function createApiKeyForMsp(mspId, name, createdBy, scopes = ["all"], expiresInDays) {
80
+ const { rawKey, keyHash } = generateApiKey();
81
+ const expiresAt = expiresInDays
82
+ ? new Date(Date.now() + expiresInDays * 86400000).toISOString()
83
+ : null;
84
+ const rows = await queryWrite(`INSERT INTO mcp_api_keys (msp_id, key_hash, key_prefix, name, scopes, created_by, expires_at)
85
+ VALUES ($1, $2, $3, $4, $5::text[], $6, $7::timestamptz)
86
+ RETURNING id`, [
87
+ mspId,
88
+ keyHash,
89
+ rawKey.substring(0, 15) + "...", // Store prefix for identification
90
+ name,
91
+ scopes,
92
+ createdBy,
93
+ expiresAt,
94
+ ], mspId);
95
+ return { rawKey, keyId: rows[0].id };
96
+ }
97
+ /**
98
+ * Revoke an API key. Called from the dashboard API route.
99
+ */
100
+ export async function revokeApiKey(mspId, keyId) {
101
+ const rows = await queryWrite(`UPDATE mcp_api_keys SET is_active = false WHERE id = $1 AND msp_id = $2 RETURNING id`, [keyId, mspId], mspId);
102
+ return rows.length > 0;
103
+ }
104
+ /**
105
+ * List API keys for an MSP (without hashes). Called from the dashboard.
106
+ */
107
+ export async function listApiKeys(mspId) {
108
+ const rows = await queryRLS(`SELECT id, name, key_prefix, scopes, is_active, last_used_at::text,
109
+ expires_at::text, created_by, created_at::text
110
+ FROM mcp_api_keys
111
+ WHERE msp_id = $1
112
+ ORDER BY created_at DESC`, [mspId], mspId);
113
+ return rows;
114
+ }
115
+ //# sourceMappingURL=api-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-keys.js","sourceRoot":"","sources":["../../src/auth/api-keys.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare function checkRateLimit(mspId: string, toolName: string): Promise<{
2
+ allowed: boolean;
3
+ remaining: number;
4
+ limit: number;
5
+ }>;
6
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/auth/rate-limiter.ts"],"names":[],"mappings":"AAgFA,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBjE"}
@@ -0,0 +1,77 @@
1
+ import { config } from "../config.js";
2
+ const TOOL_TIERS = {
3
+ // Tier 1: Analytics (read-only)
4
+ get_client_health_scores: "tier1",
5
+ get_disengaged_clients: "tier1",
6
+ get_backlog_pressure: "tier1",
7
+ get_technician_performance: "tier1",
8
+ get_pattern_clusters: "tier1",
9
+ get_billing_opportunities: "tier1",
10
+ get_qc_violations: "tier1",
11
+ get_client_experience: "tier1",
12
+ get_sla_breaches_by_technician: "tier1",
13
+ get_sla_breach_tickets: "tier1",
14
+ // Tier 2: AI-powered
15
+ explain_budget_overage: "tier2",
16
+ explain_experience_score: "tier2",
17
+ explain_sla_variance: "tier2",
18
+ suggest_technician_improvement: "tier2",
19
+ find_similar_tickets: "tier2",
20
+ analyze_root_causes: "tier2",
21
+ search_knowledge_base: "tier2",
22
+ // Tier 3: QBR
23
+ qbr_executive_summary: "tier3",
24
+ qbr_ticket_trends: "tier3",
25
+ qbr_client_health_overview: "tier3",
26
+ qbr_client_detail: "tier3",
27
+ qbr_sla_performance: "tier3",
28
+ qbr_technician_scorecard: "tier3",
29
+ qbr_top_issues: "tier3",
30
+ qbr_financial_summary: "tier3",
31
+ qbr_recommendations: "tier3",
32
+ // Tier 4: NL Query
33
+ query_data: "tier4",
34
+ // Tier 5: Composite
35
+ what_needs_attention_now: "tier5",
36
+ get_ticket_deep_dive: "tier5",
37
+ get_client_360: "tier5",
38
+ get_technician_360: "tier5",
39
+ compare_periods: "tier5",
40
+ draft_client_email: "tier5",
41
+ // Tier 6: Config
42
+ get_msp_context: "tier6",
43
+ get_configured_boards: "tier6",
44
+ get_configured_agreements: "tier6",
45
+ get_configured_technicians: "tier6",
46
+ get_agent_statuses: "tier6",
47
+ // CFO tools
48
+ cfo_financial_overview: "tier1",
49
+ cfo_client_profitability: "tier1",
50
+ cfo_technician_utilization: "tier1",
51
+ cfo_revenue_leakage: "tier1",
52
+ cfo_labor_cost_trend: "tier1",
53
+ cfo_agreement_analysis: "tier1",
54
+ cfo_service_cost_analysis: "tier2",
55
+ };
56
+ const buckets = new Map();
57
+ function today() {
58
+ return new Date().toISOString().split("T")[0];
59
+ }
60
+ export async function checkRateLimit(mspId, toolName) {
61
+ const tier = TOOL_TIERS[toolName] ?? "tier1";
62
+ const limit = config.rateLimits[tier];
63
+ const key = `${mspId}:${toolName}`;
64
+ const now = today();
65
+ let bucket = buckets.get(key);
66
+ // Reset if new day or first call
67
+ if (!bucket || bucket.resetDate !== now) {
68
+ bucket = { count: 0, resetDate: now };
69
+ buckets.set(key, bucket);
70
+ }
71
+ if (bucket.count >= limit) {
72
+ return { allowed: false, remaining: 0, limit };
73
+ }
74
+ bucket.count++;
75
+ return { allowed: true, remaining: limit - bucket.count, limit };
76
+ }
77
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/auth/rate-limiter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ export declare const config: {
2
+ readonly gatewayApiUrl: string;
3
+ readonly gatewayApiUrlRead: string;
4
+ readonly gatewayApiKey: string;
5
+ readonly azureOpenAiEndpoint: string;
6
+ readonly azureOpenAiApiKey: string;
7
+ readonly azureOpenAiDeployment: string;
8
+ readonly azureOpenAiApiVersion: string;
9
+ readonly untrapApiUrl: string;
10
+ readonly apiKey: string;
11
+ readonly devMspId: string;
12
+ readonly rateLimits: {
13
+ readonly tier1: 500;
14
+ readonly tier2: 50;
15
+ readonly tier3: 10;
16
+ readonly tier4: 100;
17
+ readonly tier5: 200;
18
+ readonly tier6: 500;
19
+ };
20
+ readonly maxConcurrentRequests: 2;
21
+ readonly requestDelayMs: 300;
22
+ readonly requestTimeoutMs: 45000;
23
+ readonly maxRetries: 3;
24
+ };
25
+ /**
26
+ * Three operating modes:
27
+ *
28
+ * 1. DEV MODE: MSP_ID + GATEWAY_API_KEY
29
+ * → Hardcoded msp_id, queries Gateway directly. Local testing only.
30
+ *
31
+ * 2. GATEWAY AUTH MODE: UNTRAP_API_KEY + GATEWAY_API_KEY (no MSP_ID)
32
+ * → API key resolves msp_id, queries Gateway directly.
33
+ * → For internal testing — doesn't need proxy deployed.
34
+ *
35
+ * 3. PRODUCTION MODE: UNTRAP_API_KEY only (no GATEWAY_API_KEY)
36
+ * → API key auth + queries go through Untrap API proxy.
37
+ * → Gateway key never leaves the server. Shipped to MSPs.
38
+ */
39
+ export type ServerMode = "dev" | "gateway_auth" | "production";
40
+ export declare function getServerMode(): ServerMode;
41
+ export declare function validateConfig(): string[];
42
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;CAsCT,CAAC;AAEX;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,cAAc,GAAG,YAAY,CAAC;AAE/D,wBAAgB,aAAa,IAAI,UAAU,CAI1C;AAED,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAoBzC"}
package/dist/config.js ADDED
@@ -0,0 +1,62 @@
1
+ export const config = {
2
+ // Gateway API (RDS access with RLS)
3
+ gatewayApiUrl: process.env.GATEWAY_API_URL || "",
4
+ gatewayApiUrlRead: process.env.GATEWAY_API_URL_READ || "",
5
+ gatewayApiKey: process.env.GATEWAY_API_KEY || "",
6
+ // Azure OpenAI (for AI-powered tools)
7
+ azureOpenAiEndpoint: process.env.AZURE_OPENAI_ENDPOINT || "",
8
+ azureOpenAiApiKey: process.env.AZURE_OPENAI_KEY || process.env.AZURE_OPENAI_API_KEY || "",
9
+ azureOpenAiDeployment: process.env.AZURE_OPENAI_DEPLOYMENT_NAME || "",
10
+ azureOpenAiApiVersion: process.env.AZURE_OPENAI_API_VERSION || "2024-12-01-preview",
11
+ // Untrap API URL (production proxy endpoint)
12
+ untrapApiUrl: process.env.UNTRAP_API_URL || "https://app.getuntrap.com",
13
+ // Auth — API key determines which MSP's data is accessible
14
+ // In stdio mode, the key is passed via env var at startup
15
+ apiKey: process.env.UNTRAP_API_KEY || "",
16
+ // Dev mode — bypass API key auth and use MSP_ID directly
17
+ // ONLY for local testing. Never use in production.
18
+ devMspId: process.env.MSP_ID || "",
19
+ // Rate limiting defaults
20
+ rateLimits: {
21
+ tier1: 500, // Analytics (read-only)
22
+ tier2: 50, // AI-powered
23
+ tier3: 10, // QBR
24
+ tier4: 100, // NL Query
25
+ tier5: 200, // Composite
26
+ tier6: 500, // Config
27
+ },
28
+ // Request queue settings
29
+ maxConcurrentRequests: 2,
30
+ requestDelayMs: 300,
31
+ requestTimeoutMs: 45_000,
32
+ maxRetries: 3,
33
+ };
34
+ export function getServerMode() {
35
+ if (config.devMspId && config.gatewayApiKey)
36
+ return "dev";
37
+ if (config.apiKey && config.gatewayApiKey)
38
+ return "gateway_auth";
39
+ return "production";
40
+ }
41
+ export function validateConfig() {
42
+ const errors = [];
43
+ const mode = getServerMode();
44
+ if (mode === "dev") {
45
+ if (!config.gatewayApiUrl) {
46
+ errors.push("GATEWAY_API_URL is required in dev mode");
47
+ }
48
+ }
49
+ else if (mode === "gateway_auth") {
50
+ if (!config.gatewayApiUrl) {
51
+ errors.push("GATEWAY_API_URL is required in gateway auth mode");
52
+ }
53
+ }
54
+ else {
55
+ // Production: needs Untrap API key (proxy handles Gateway access)
56
+ if (!config.apiKey) {
57
+ errors.push("UNTRAP_API_KEY is required (or set MSP_ID + GATEWAY_API_KEY for dev mode)");
58
+ }
59
+ }
60
+ return errors;
61
+ }
62
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,oCAAoC;IACpC,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE;IAChD,iBAAiB,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,EAAE;IACzD,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE;IAEhD,sCAAsC;IACtC,mBAAmB,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,EAAE;IAC5D,iBAAiB,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,EAAE;IACzF,qBAAqB,EAAE,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,EAAE;IACrE,qBAAqB,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,oBAAoB;IAEnF,6CAA6C;IAC7C,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,2BAA2B;IAEvE,2DAA2D;IAC3D,0DAA0D;IAC1D,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE;IAExC,yDAAyD;IACzD,mDAAmD;IACnD,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE;IAElC,yBAAyB;IACzB,UAAU,EAAE;QACV,KAAK,EAAE,GAAG,EAAE,wBAAwB;QACpC,KAAK,EAAE,EAAE,EAAE,aAAa;QACxB,KAAK,EAAE,EAAE,EAAE,MAAM;QACjB,KAAK,EAAE,GAAG,EAAE,WAAW;QACvB,KAAK,EAAE,GAAG,EAAE,YAAY;QACxB,KAAK,EAAE,GAAG,EAAE,SAAS;KACtB;IAED,yBAAyB;IACzB,qBAAqB,EAAE,CAAC;IACxB,cAAc,EAAE,GAAG;IACnB,gBAAgB,EAAE,MAAM;IACxB,UAAU,EAAE,CAAC;CACL,CAAC;AAkBX,MAAM,UAAU,aAAa;IAC3B,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,aAAa;QAAE,OAAO,KAAK,CAAC;IAC1D,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,aAAa;QAAE,OAAO,cAAc,CAAC;IACjE,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAE7B,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;SAAM,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;SAAM,CAAC;QACN,kEAAkE;QAClE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,CAAC,IAAI,CAAC,2EAA2E,CAAC,CAAC;QAC3F,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=assert-msp-scoped.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert-msp-scoped.test.d.ts","sourceRoot":"","sources":["../../../src/db/__tests__/assert-msp-scoped.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Regression tests for assertMspScoped() — the tenant isolation guard.
3
+ *
4
+ * These tests verify that queries without proper msp_id predicates are
5
+ * rejected at runtime, preventing cross-MSP data leaks when RLS is
6
+ * bypassed by the rds_superuser gateway role.
7
+ *
8
+ * Run: npx vitest run src/db/__tests__/assert-msp-scoped.test.ts
9
+ */
10
+ import { describe, it, expect } from "vitest";
11
+ // We can't import assertMspScoped directly (it's not exported), so we
12
+ // replicate the exact logic here for isolated testing.
13
+ const MSP_FILTER_EXEMPT = [
14
+ /lookup_api_key_by_hash/i,
15
+ ];
16
+ const TENANT_PREDICATE_PATTERNS = [
17
+ /\b(WHERE|AND)\s+\w*\.?msp_id\s*(?:::[\w]+)?\s*=\s*\$\d+/i,
18
+ /\bON\s+[^)]*\bmsp_id\b[^)]*=/i,
19
+ /INSERT\s+INTO\s+\w+\s*\([^)]*\bmsp_id\b/i,
20
+ /\(\s*SELECT[^)]+\b(WHERE|AND)\s+\w*\.?msp_id\s*(?:::[\w]+)?\s*=\s*\$\d+/i,
21
+ ];
22
+ function assertMspScoped(text, params, mspId) {
23
+ for (const pattern of MSP_FILTER_EXEMPT) {
24
+ if (pattern.test(text))
25
+ return;
26
+ }
27
+ if (!params.includes(mspId)) {
28
+ throw new Error(`[TENANT ISOLATION] mspId not found in query params.`);
29
+ }
30
+ const hasPredicate = TENANT_PREDICATE_PATTERNS.some((p) => p.test(text));
31
+ if (!hasPredicate) {
32
+ throw new Error(`[TENANT ISOLATION] No msp_id predicate found in SQL.`);
33
+ }
34
+ }
35
+ const MSP = "5f54271b-54d5-40b3-8ad7-38cfdde8db85";
36
+ describe("assertMspScoped", () => {
37
+ // ═══════════════════════════════════════════
38
+ // SHOULD PASS — properly scoped queries
39
+ // ═══════════════════════════════════════════
40
+ it("passes: WHERE msp_id = $1", () => {
41
+ expect(() => assertMspScoped("SELECT * FROM ticket_lifecycle WHERE msp_id = $1 LIMIT 100", [MSP], MSP)).not.toThrow();
42
+ });
43
+ it("passes: WHERE msp_id::uuid = $1::uuid", () => {
44
+ expect(() => assertMspScoped("SELECT * FROM billing_classifications WHERE msp_id::uuid = $1::uuid", [MSP], MSP)).not.toThrow();
45
+ });
46
+ it("passes: AND msp_id = $1 (compound WHERE)", () => {
47
+ expect(() => assertMspScoped("SELECT * FROM ticket_lifecycle WHERE is_closed = true AND msp_id = $1", [MSP], MSP)).not.toThrow();
48
+ });
49
+ it("passes: table-qualified tl.msp_id = $1", () => {
50
+ expect(() => assertMspScoped("SELECT * FROM ticket_lifecycle tl WHERE tl.msp_id = $1 LIMIT 100", [MSP], MSP)).not.toThrow();
51
+ });
52
+ it("passes: INSERT with msp_id in columns", () => {
53
+ expect(() => assertMspScoped("INSERT INTO mcp_api_keys (msp_id, key_hash, name) VALUES ($1, $2, $3)", [MSP, "abc", "test"], MSP)).not.toThrow();
54
+ });
55
+ it("passes: CTE with msp_id predicate", () => {
56
+ expect(() => assertMspScoped(`WITH active AS (SELECT * FROM ticket_lifecycle WHERE msp_id = $1 AND is_closed = false)
57
+ SELECT * FROM active LIMIT 100`, [MSP], MSP)).not.toThrow();
58
+ });
59
+ it("passes: JOIN with msp_id", () => {
60
+ expect(() => assertMspScoped(`SELECT tl.*, te.actual_hours
61
+ FROM ticket_lifecycle tl
62
+ JOIN time_entries te ON te.msp_id = tl.msp_id AND te.ticket_id = tl.ticket_id
63
+ WHERE tl.msp_id = $1`, [MSP], MSP)).not.toThrow();
64
+ });
65
+ it("passes: bootstrap key lookup (exempt)", () => {
66
+ expect(() => assertMspScoped("SELECT * FROM lookup_api_key_by_hash($1)", ["somehash"], "00000000-0000-0000-0000-000000000000")).not.toThrow();
67
+ });
68
+ it("passes: UPDATE with AND msp_id = $2", () => {
69
+ expect(() => assertMspScoped("UPDATE mcp_api_keys SET last_used_at = NOW() WHERE id = $1 AND msp_id = $2", ["some-id", MSP], MSP)).not.toThrow();
70
+ });
71
+ // ═══════════════════════════════════════════
72
+ // SHOULD FAIL — cross-MSP leak risks
73
+ // ═══════════════════════════════════════════
74
+ it("REJECTS: SELECT msp_id without filtering (GPT's example)", () => {
75
+ expect(() => assertMspScoped("SELECT msp_id, company_name FROM companies LIMIT 10", [MSP], MSP)).toThrow("[TENANT ISOLATION]");
76
+ });
77
+ it("REJECTS: no msp_id at all", () => {
78
+ expect(() => assertMspScoped("SELECT * FROM ticket_lifecycle LIMIT 100", [], MSP)).toThrow("[TENANT ISOLATION]");
79
+ });
80
+ it("REJECTS: msp_id in SELECT list but not WHERE", () => {
81
+ expect(() => assertMspScoped("SELECT msp_id, COUNT(*) FROM ticket_lifecycle GROUP BY msp_id", [MSP], MSP)).toThrow("[TENANT ISOLATION]");
82
+ });
83
+ it("REJECTS: mspId not in params even if SQL looks right", () => {
84
+ expect(() => assertMspScoped("SELECT * FROM ticket_lifecycle WHERE msp_id = $1", ["wrong-id"], MSP)).toThrow("[TENANT ISOLATION]");
85
+ });
86
+ it("REJECTS: msp_id only in ORDER BY", () => {
87
+ expect(() => assertMspScoped("SELECT * FROM companies ORDER BY msp_id LIMIT 10", [MSP], MSP)).toThrow("[TENANT ISOLATION]");
88
+ });
89
+ it("REJECTS: msp_id only as a column alias", () => {
90
+ expect(() => assertMspScoped("SELECT id AS msp_id FROM companies LIMIT 10", [MSP], MSP)).toThrow("[TENANT ISOLATION]");
91
+ });
92
+ });
93
+ //# sourceMappingURL=assert-msp-scoped.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert-msp-scoped.test.js","sourceRoot":"","sources":["../../../src/db/__tests__/assert-msp-scoped.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ interface ChatMessage {
2
+ role: "system" | "user" | "assistant";
3
+ content: string;
4
+ }
5
+ interface CompletionOptions {
6
+ messages: ChatMessage[];
7
+ maxTokens?: number;
8
+ temperature?: number;
9
+ }
10
+ interface CompletionResult {
11
+ content: string;
12
+ usage: {
13
+ prompt_tokens: number;
14
+ completion_tokens: number;
15
+ total_tokens: number;
16
+ };
17
+ }
18
+ /**
19
+ * Call Azure OpenAI chat completions API.
20
+ * Uses raw fetch (same pattern as all existing API routes in the Next.js app).
21
+ */
22
+ export declare function chatCompletion(options: CompletionOptions): Promise<CompletionResult>;
23
+ export {};
24
+ //# sourceMappingURL=azure-openai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"azure-openai.d.ts","sourceRoot":"","sources":["../../src/db/azure-openai.ts"],"names":[],"mappings":"AAEA,UAAU,WAAW;IACnB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,iBAAiB;IACzB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,gBAAgB;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE;QACL,aAAa,EAAE,MAAM,CAAC;QACtB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CA6C3B"}
@@ -0,0 +1,45 @@
1
+ import { config } from "../config.js";
2
+ /**
3
+ * Call Azure OpenAI chat completions API.
4
+ * Uses raw fetch (same pattern as all existing API routes in the Next.js app).
5
+ */
6
+ export async function chatCompletion(options) {
7
+ const { azureOpenAiEndpoint, azureOpenAiApiKey, azureOpenAiDeployment, azureOpenAiApiVersion } = config;
8
+ if (!azureOpenAiApiKey || !azureOpenAiEndpoint || !azureOpenAiDeployment) {
9
+ throw new Error("Azure OpenAI environment variables are not set (AZURE_OPENAI_KEY, AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_DEPLOYMENT_NAME)");
10
+ }
11
+ const url = `${azureOpenAiEndpoint}/openai/deployments/${azureOpenAiDeployment}/chat/completions?api-version=${azureOpenAiApiVersion}`;
12
+ const res = await fetch(url, {
13
+ method: "POST",
14
+ headers: {
15
+ "Content-Type": "application/json",
16
+ "api-key": azureOpenAiApiKey,
17
+ },
18
+ body: JSON.stringify({
19
+ messages: options.messages,
20
+ max_completion_tokens: options.maxTokens ?? 500,
21
+ temperature: options.temperature ?? 0.7,
22
+ model: azureOpenAiDeployment,
23
+ }),
24
+ });
25
+ const rawBody = await res.text();
26
+ if (!res.ok) {
27
+ throw new Error(`Azure OpenAI API error (${res.status}): ${rawBody.substring(0, 200)}`);
28
+ }
29
+ let data;
30
+ try {
31
+ data = JSON.parse(rawBody);
32
+ }
33
+ catch {
34
+ throw new Error("Azure OpenAI returned non-JSON response");
35
+ }
36
+ const choices = data.choices;
37
+ const content = choices?.[0]?.message?.content ?? "";
38
+ const usage = data.usage ?? {
39
+ prompt_tokens: 0,
40
+ completion_tokens: 0,
41
+ total_tokens: 0,
42
+ };
43
+ return { content, usage };
44
+ }
45
+ //# sourceMappingURL=azure-openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"azure-openai.js","sourceRoot":"","sources":["../../src/db/azure-openai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAsBtC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAA0B;IAE1B,MAAM,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,GAAG,MAAM,CAAC;IAExG,IAAI,CAAC,iBAAiB,IAAI,CAAC,mBAAmB,IAAI,CAAC,qBAAqB,EAAE,CAAC;QACzE,MAAM,IAAI,KAAK,CAAC,wHAAwH,CAAC,CAAC;IAC5I,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,mBAAmB,uBAAuB,qBAAqB,iCAAiC,qBAAqB,EAAE,CAAC;IAEvI,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC3B,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,SAAS,EAAE,iBAAiB;SAC7B;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,qBAAqB,EAAE,OAAO,CAAC,SAAS,IAAI,GAAG;YAC/C,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,GAAG;YACvC,KAAK,EAAE,qBAAqB;SAC7B,CAAC;KACH,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAEjC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,MAAM,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAC1F,CAAC;IAED,IAAI,IAA6B,CAAC;IAClC,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAA8D,CAAC;IACpF,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC;IACrD,MAAM,KAAK,GAAI,IAAI,CAAC,KAAmC,IAAI;QACzD,aAAa,EAAE,CAAC;QAChB,iBAAiB,EAAE,CAAC;QACpB,YAAY,EAAE,CAAC;KAChB,CAAC;IAEF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Execute a query. Routes based on server mode:
3
+ * - dev / gateway_auth → Gateway Lambda directly
4
+ * - production → Untrap API proxy
5
+ */
6
+ export declare function queryRLS<T = Record<string, unknown>>(text: string, params: unknown[], mspId: string): Promise<T[]>;
7
+ /**
8
+ * Execute a write query. Routes based on server mode:
9
+ * - dev / gateway_auth → Gateway Lambda directly
10
+ * - production → Untrap API proxy
11
+ */
12
+ export declare function queryWrite<T = Record<string, unknown>>(text: string, params: unknown[], mspId: string): Promise<T[]>;
13
+ //# sourceMappingURL=gateway-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway-client.d.ts","sourceRoot":"","sources":["../../src/db/gateway-client.ts"],"names":[],"mappings":"AA8KA;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxD,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,EAAE,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,CAAC,EAAE,CAAC,CAWd;AA8DD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1D,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,EAAE,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,CAAC,EAAE,CAAC,CAiBd"}