@yiaany/ghostapi 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 (184) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/CONTRIBUTING.md +117 -0
  3. package/LICENSE +21 -0
  4. package/README.md +354 -0
  5. package/SECURITY.md +37 -0
  6. package/dist/agents/agentPrompt.d.ts +5 -0
  7. package/dist/agents/agentPrompt.js +30 -0
  8. package/dist/agents/agentPrompt.js.map +1 -0
  9. package/dist/ai/aiClient.d.ts +7 -0
  10. package/dist/ai/aiClient.js +30 -0
  11. package/dist/ai/aiClient.js.map +1 -0
  12. package/dist/ai/aiGenerator.d.ts +10 -0
  13. package/dist/ai/aiGenerator.js +101 -0
  14. package/dist/ai/aiGenerator.js.map +1 -0
  15. package/dist/ai/genericInference.d.ts +5 -0
  16. package/dist/ai/genericInference.js +80 -0
  17. package/dist/ai/genericInference.js.map +1 -0
  18. package/dist/ai/index.d.ts +3 -0
  19. package/dist/ai/index.js +2 -0
  20. package/dist/ai/index.js.map +1 -0
  21. package/dist/ai/jsonRepair.d.ts +2 -0
  22. package/dist/ai/jsonRepair.js +31 -0
  23. package/dist/ai/jsonRepair.js.map +1 -0
  24. package/dist/ai/prompts.d.ts +4 -0
  25. package/dist/ai/prompts.js +81 -0
  26. package/dist/ai/prompts.js.map +1 -0
  27. package/dist/behavior/behaviorStore.d.ts +12 -0
  28. package/dist/behavior/behaviorStore.js +72 -0
  29. package/dist/behavior/behaviorStore.js.map +1 -0
  30. package/dist/cache/index.d.ts +9 -0
  31. package/dist/cache/index.js +34 -0
  32. package/dist/cache/index.js.map +1 -0
  33. package/dist/cli/errors.d.ts +4 -0
  34. package/dist/cli/errors.js +9 -0
  35. package/dist/cli/errors.js.map +1 -0
  36. package/dist/cli/index.d.ts +2 -0
  37. package/dist/cli/index.js +277 -0
  38. package/dist/cli/index.js.map +1 -0
  39. package/dist/cli/parser.d.ts +56 -0
  40. package/dist/cli/parser.js +169 -0
  41. package/dist/cli/parser.js.map +1 -0
  42. package/dist/config/localConfig.d.ts +16 -0
  43. package/dist/config/localConfig.js +58 -0
  44. package/dist/config/localConfig.js.map +1 -0
  45. package/dist/config/serverConfig.d.ts +13 -0
  46. package/dist/config/serverConfig.js +44 -0
  47. package/dist/config/serverConfig.js.map +1 -0
  48. package/dist/dashboard/app.js +615 -0
  49. package/dist/dashboard/dashboard.d.ts +4 -0
  50. package/dist/dashboard/dashboard.js +20 -0
  51. package/dist/dashboard/dashboard.js.map +1 -0
  52. package/dist/dashboard/index.d.ts +1 -0
  53. package/dist/dashboard/index.html +298 -0
  54. package/dist/dashboard/index.js +2 -0
  55. package/dist/dashboard/index.js.map +1 -0
  56. package/dist/dashboard/styles.css +349 -0
  57. package/dist/errors/errorEngine.d.ts +3 -0
  58. package/dist/errors/errorEngine.js +65 -0
  59. package/dist/errors/errorEngine.js.map +1 -0
  60. package/dist/errors/index.d.ts +3 -0
  61. package/dist/errors/index.js +3 -0
  62. package/dist/errors/index.js.map +1 -0
  63. package/dist/errors/providerErrors.d.ts +3 -0
  64. package/dist/errors/providerErrors.js +6 -0
  65. package/dist/errors/providerErrors.js.map +1 -0
  66. package/dist/fault/faultLab.d.ts +27 -0
  67. package/dist/fault/faultLab.js +89 -0
  68. package/dist/fault/faultLab.js.map +1 -0
  69. package/dist/index.d.ts +2 -0
  70. package/dist/index.js +2 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/landing/assets/index-BXR41fAD.css +1 -0
  73. package/dist/landing/assets/index-D8_ggMRE.js +55 -0
  74. package/dist/landing/index.html +15 -0
  75. package/dist/landing/landing.d.ts +3 -0
  76. package/dist/landing/landing.js +12 -0
  77. package/dist/landing/landing.js.map +1 -0
  78. package/dist/mcp/server.d.ts +1 -0
  79. package/dist/mcp/server.js +52 -0
  80. package/dist/mcp/server.js.map +1 -0
  81. package/dist/providers/discord.d.ts +2 -0
  82. package/dist/providers/discord.js +11 -0
  83. package/dist/providers/discord.js.map +1 -0
  84. package/dist/providers/generic.d.ts +2 -0
  85. package/dist/providers/generic.js +13 -0
  86. package/dist/providers/generic.js.map +1 -0
  87. package/dist/providers/github.d.ts +2 -0
  88. package/dist/providers/github.js +11 -0
  89. package/dist/providers/github.js.map +1 -0
  90. package/dist/providers/index.d.ts +2 -0
  91. package/dist/providers/index.js +2 -0
  92. package/dist/providers/index.js.map +1 -0
  93. package/dist/providers/openai.d.ts +2 -0
  94. package/dist/providers/openai.js +15 -0
  95. package/dist/providers/openai.js.map +1 -0
  96. package/dist/providers/registry.d.ts +12 -0
  97. package/dist/providers/registry.js +23 -0
  98. package/dist/providers/registry.js.map +1 -0
  99. package/dist/providers/resend.d.ts +2 -0
  100. package/dist/providers/resend.js +12 -0
  101. package/dist/providers/resend.js.map +1 -0
  102. package/dist/providers/stripe.d.ts +2 -0
  103. package/dist/providers/stripe.js +15 -0
  104. package/dist/providers/stripe.js.map +1 -0
  105. package/dist/providers/twilio.d.ts +2 -0
  106. package/dist/providers/twilio.js +14 -0
  107. package/dist/providers/twilio.js.map +1 -0
  108. package/dist/providers/types.d.ts +13 -0
  109. package/dist/providers/types.js +2 -0
  110. package/dist/providers/types.js.map +1 -0
  111. package/dist/proxy/cacheKey.d.ts +2 -0
  112. package/dist/proxy/cacheKey.js +37 -0
  113. package/dist/proxy/cacheKey.js.map +1 -0
  114. package/dist/proxy/providerDetector.d.ts +11 -0
  115. package/dist/proxy/providerDetector.js +74 -0
  116. package/dist/proxy/providerDetector.js.map +1 -0
  117. package/dist/proxy/proxyHandler.d.ts +3 -0
  118. package/dist/proxy/proxyHandler.js +114 -0
  119. package/dist/proxy/proxyHandler.js.map +1 -0
  120. package/dist/proxy/requestNormalizer.d.ts +11 -0
  121. package/dist/proxy/requestNormalizer.js +30 -0
  122. package/dist/proxy/requestNormalizer.js.map +1 -0
  123. package/dist/report/safetyReport.d.ts +13 -0
  124. package/dist/report/safetyReport.js +65 -0
  125. package/dist/report/safetyReport.js.map +1 -0
  126. package/dist/rules/aiRules.d.ts +7 -0
  127. package/dist/rules/aiRules.js +31 -0
  128. package/dist/rules/aiRules.js.map +1 -0
  129. package/dist/scenarios/scenarioStore.d.ts +37 -0
  130. package/dist/scenarios/scenarioStore.js +201 -0
  131. package/dist/scenarios/scenarioStore.js.map +1 -0
  132. package/dist/security/headerSanitizer.d.ts +3 -0
  133. package/dist/security/headerSanitizer.js +12 -0
  134. package/dist/security/headerSanitizer.js.map +1 -0
  135. package/dist/security/maskSecrets.d.ts +1 -0
  136. package/dist/security/maskSecrets.js +2 -0
  137. package/dist/security/maskSecrets.js.map +1 -0
  138. package/dist/security/secrets.d.ts +4 -0
  139. package/dist/security/secrets.js +61 -0
  140. package/dist/security/secrets.js.map +1 -0
  141. package/dist/server/createServer.d.ts +8 -0
  142. package/dist/server/createServer.js +42 -0
  143. package/dist/server/createServer.js.map +1 -0
  144. package/dist/server/eventsStore.d.ts +17 -0
  145. package/dist/server/eventsStore.js +31 -0
  146. package/dist/server/eventsStore.js.map +1 -0
  147. package/dist/server/routes.d.ts +3 -0
  148. package/dist/server/routes.js +189 -0
  149. package/dist/server/routes.js.map +1 -0
  150. package/dist/server/sse.d.ts +5 -0
  151. package/dist/server/sse.js +32 -0
  152. package/dist/server/sse.js.map +1 -0
  153. package/dist/setup/sdkDetector.d.ts +9 -0
  154. package/dist/setup/sdkDetector.js +31 -0
  155. package/dist/setup/sdkDetector.js.map +1 -0
  156. package/dist/setup/setupGenerator.d.ts +28 -0
  157. package/dist/setup/setupGenerator.js +146 -0
  158. package/dist/setup/setupGenerator.js.map +1 -0
  159. package/dist/state/index.d.ts +1 -0
  160. package/dist/state/index.js +2 -0
  161. package/dist/state/index.js.map +1 -0
  162. package/dist/state/stateExtractor.d.ts +3 -0
  163. package/dist/state/stateExtractor.js +36 -0
  164. package/dist/state/stateExtractor.js.map +1 -0
  165. package/dist/state/stateResolver.d.ts +8 -0
  166. package/dist/state/stateResolver.js +95 -0
  167. package/dist/state/stateResolver.js.map +1 -0
  168. package/dist/state/stateStore.d.ts +4 -0
  169. package/dist/state/stateStore.js +72 -0
  170. package/dist/state/stateStore.js.map +1 -0
  171. package/dist/tests/testGenerator.d.ts +6 -0
  172. package/dist/tests/testGenerator.js +33 -0
  173. package/dist/tests/testGenerator.js.map +1 -0
  174. package/dist/utils/json.d.ts +2 -0
  175. package/dist/utils/json.js +10 -0
  176. package/dist/utils/json.js.map +1 -0
  177. package/docs/release-checklist.md +71 -0
  178. package/examples/agent-instructions/README.md +39 -0
  179. package/examples/generic-rest/README.md +27 -0
  180. package/examples/github-fetch/README.md +35 -0
  181. package/examples/resend-node/README.md +22 -0
  182. package/examples/stripe-node/README.md +29 -0
  183. package/examples/twilio-node/README.md +21 -0
  184. package/package.json +67 -0
@@ -0,0 +1,15 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta name="description" content="GhostAPI is the local internet for AI coding agents." />
7
+ <title>GhostAPI - The local internet for AI coding agents</title>
8
+ <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='22' fill='%23121212'/%3E%3Cpath d='M26 36c0-12 10-22 24-22s24 10 24 22v36c0 7-8 10-13 5l-4-4-5 5-5-5-5 5-5-5-4 4c-5 5-13 2-13-5V36Z' fill='%233ecf8e'/%3E%3Ccircle cx='40' cy='42' r='5' fill='%23121212'/%3E%3Ccircle cx='60' cy='42' r='5' fill='%23121212'/%3E%3C/svg%3E" />
9
+ <script type="module" crossorigin src="/landing/assets/index-D8_ggMRE.js"></script>
10
+ <link rel="stylesheet" crossorigin href="/landing/assets/index-BXR41fAD.css">
11
+ </head>
12
+ <body>
13
+ <div id="root"></div>
14
+ </body>
15
+ </html>
@@ -0,0 +1,3 @@
1
+ import type { Request, Response } from "express";
2
+ export declare const landingAssetsHandler: import("serve-static").RequestHandler<Response<any, Record<string, any>>>;
3
+ export declare function landingHandler(_request: Request, response: Response): Promise<void>;
@@ -0,0 +1,12 @@
1
+ import express from "express";
2
+ import { readFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ export const landingAssetsHandler = express.static(join(__dirname, "assets"), { index: false });
7
+ export async function landingHandler(_request, response) {
8
+ response.setHeader("Content-Type", "text/html");
9
+ const html = await readFile(join(__dirname, "index.html"), "utf8");
10
+ response.status(200).send(html);
11
+ }
12
+ //# sourceMappingURL=landing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"landing.js","sourceRoot":"","sources":["../../src/landing/landing.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D,MAAM,CAAC,MAAM,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAEhG,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAiB,EAAE,QAAkB;IACxE,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC;IACnE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function startMcpServer(): Promise<void>;
@@ -0,0 +1,52 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { z } from "zod";
4
+ import { getStateStore } from "../state/stateStore.js";
5
+ import { getEventsHistory } from "../server/eventsStore.js";
6
+ import { setApiBehavior } from "../behavior/behaviorStore.js";
7
+ import { getFaultLabConfig, updateFaultLabConfig } from "../fault/faultLab.js";
8
+ export async function startMcpServer() {
9
+ const server = new McpServer({ name: "ghostapi", version: "0.1.0" });
10
+ server.registerTool("inspect_state", {
11
+ title: "Inspect GhostAPI State",
12
+ description: "Return the current local object state from .ghostapi/state.json."
13
+ }, async () => jsonResult(await getStateStore()));
14
+ server.registerTool("set_api_behavior", {
15
+ title: "Set API Behavior",
16
+ description: "Configure a deterministic local response for a method/path pair.",
17
+ inputSchema: {
18
+ path: z.string().min(1),
19
+ method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]),
20
+ status: z.number().int().min(100).max(599).default(200),
21
+ body: z.unknown().default({ ok: true }),
22
+ headers: z.record(z.string(), z.string()).optional()
23
+ }
24
+ }, async (args) => jsonResult({ behavior: await setApiBehavior(args) }));
25
+ server.registerTool("get_traffic_logs", {
26
+ title: "Get Traffic Logs",
27
+ description: "Return the latest 50 captured HTTP requests from the in-memory dashboard buffer."
28
+ }, async () => jsonResult({ events: getEventsHistory().slice(-50).reverse() }));
29
+ server.registerTool("toggle_chaos_mode", {
30
+ title: "Toggle Fault Lab",
31
+ description: "Enable or disable local latency/errors for resilience testing.",
32
+ inputSchema: {
33
+ enabled: z.boolean(),
34
+ latencyMs: z.number().int().min(0).max(10_000).optional(),
35
+ errorRate: z.number().int().min(0).max(100).optional(),
36
+ statusCode: z.union([z.literal(429), z.literal(500), z.literal(502), z.literal(503)]).optional(),
37
+ retryAfterSeconds: z.number().int().min(0).max(120).optional()
38
+ }
39
+ }, async (args) => jsonResult({ faultLab: updateFaultLabConfig({ ...getFaultLabConfig(), ...args }) }));
40
+ await server.connect(new StdioServerTransport());
41
+ }
42
+ function jsonResult(value) {
43
+ return {
44
+ content: [
45
+ {
46
+ type: "text",
47
+ text: JSON.stringify(value, null, 2)
48
+ }
49
+ ]
50
+ };
51
+ }
52
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { ProviderAdapter } from "./types.js";
2
+ export declare const discordAdapter: ProviderAdapter;
@@ -0,0 +1,11 @@
1
+ export const discordAdapter = {
2
+ name: "discord",
3
+ displayName: "Discord",
4
+ formatError(details) {
5
+ return {
6
+ code: typeof details.code === "number" ? details.code : 50035,
7
+ message: details.message
8
+ };
9
+ }
10
+ };
11
+ //# sourceMappingURL=discord.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discord.js","sourceRoot":"","sources":["../../src/providers/discord.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAoB;IAC7C,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,SAAS;IACtB,WAAW,CAAC,OAA6B;QACvC,OAAO;YACL,IAAI,EAAE,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK;YAC7D,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ProviderAdapter } from "./types.js";
2
+ export declare const genericAdapter: ProviderAdapter;
@@ -0,0 +1,13 @@
1
+ export const genericAdapter = {
2
+ name: "generic",
3
+ displayName: "Generic REST API",
4
+ formatError(details) {
5
+ return {
6
+ error: {
7
+ status: details.status,
8
+ message: details.message
9
+ }
10
+ };
11
+ }
12
+ };
13
+ //# sourceMappingURL=generic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generic.js","sourceRoot":"","sources":["../../src/providers/generic.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAoB;IAC7C,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,kBAAkB;IAC/B,WAAW,CAAC,OAA6B;QACvC,OAAO;YACL,KAAK,EAAE;gBACL,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,OAAO,CAAC,OAAO;aACzB;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ProviderAdapter } from "./types.js";
2
+ export declare const githubAdapter: ProviderAdapter;
@@ -0,0 +1,11 @@
1
+ export const githubAdapter = {
2
+ name: "github",
3
+ displayName: "GitHub",
4
+ formatError(details) {
5
+ return {
6
+ message: details.message,
7
+ documentation_url: "https://docs.github.com/rest"
8
+ };
9
+ }
10
+ };
11
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/providers/github.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAoB;IAC5C,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,QAAQ;IACrB,WAAW,CAAC,OAA6B;QACvC,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,iBAAiB,EAAE,8BAA8B;SAClD,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { getProviderAdapter, isRegisteredProvider, providerRegistry } from "./registry.js";
2
+ export type { ProviderAdapter, ProviderName } from "./types.js";
@@ -0,0 +1,2 @@
1
+ export { getProviderAdapter, isRegisteredProvider, providerRegistry } from "./registry.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ProviderAdapter } from "./types.js";
2
+ export declare const openaiAdapter: ProviderAdapter;
@@ -0,0 +1,15 @@
1
+ export const openaiAdapter = {
2
+ name: "openai",
3
+ displayName: "OpenAI",
4
+ formatError(details) {
5
+ return {
6
+ error: {
7
+ message: details.message,
8
+ type: details.type ?? "invalid_request_error",
9
+ param: details.param ?? null,
10
+ code: details.code ?? null
11
+ }
12
+ };
13
+ }
14
+ };
15
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAoB;IAC5C,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,QAAQ;IACrB,WAAW,CAAC,OAA6B;QACvC,OAAO;YACL,KAAK,EAAE;gBACL,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,uBAAuB;gBAC7C,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI;gBAC5B,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,IAAI;aAC3B;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { ProviderAdapter, ProviderName } from "./types.js";
2
+ export declare const providerRegistry: {
3
+ stripe: ProviderAdapter;
4
+ twilio: ProviderAdapter;
5
+ resend: ProviderAdapter;
6
+ github: ProviderAdapter;
7
+ discord: ProviderAdapter;
8
+ openai: ProviderAdapter;
9
+ generic: ProviderAdapter;
10
+ };
11
+ export declare function getProviderAdapter(provider: ProviderName): ProviderAdapter;
12
+ export declare function isRegisteredProvider(value: string): value is ProviderName;
@@ -0,0 +1,23 @@
1
+ import { discordAdapter } from "./discord.js";
2
+ import { genericAdapter } from "./generic.js";
3
+ import { githubAdapter } from "./github.js";
4
+ import { openaiAdapter } from "./openai.js";
5
+ import { resendAdapter } from "./resend.js";
6
+ import { stripeAdapter } from "./stripe.js";
7
+ import { twilioAdapter } from "./twilio.js";
8
+ export const providerRegistry = {
9
+ stripe: stripeAdapter,
10
+ twilio: twilioAdapter,
11
+ resend: resendAdapter,
12
+ github: githubAdapter,
13
+ discord: discordAdapter,
14
+ openai: openaiAdapter,
15
+ generic: genericAdapter
16
+ };
17
+ export function getProviderAdapter(provider) {
18
+ return providerRegistry[provider];
19
+ }
20
+ export function isRegisteredProvider(value) {
21
+ return Object.hasOwn(providerRegistry, value);
22
+ }
23
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,MAAM,EAAE,aAAa;IACrB,MAAM,EAAE,aAAa;IACrB,MAAM,EAAE,aAAa;IACrB,MAAM,EAAE,aAAa;IACrB,OAAO,EAAE,cAAc;IACvB,MAAM,EAAE,aAAa;IACrB,OAAO,EAAE,cAAc;CACwB,CAAC;AAElD,MAAM,UAAU,kBAAkB,CAAC,QAAsB;IACvD,OAAO,gBAAgB,CAAC,QAAQ,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,OAAO,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ProviderAdapter } from "./types.js";
2
+ export declare const resendAdapter: ProviderAdapter;
@@ -0,0 +1,12 @@
1
+ export const resendAdapter = {
2
+ name: "resend",
3
+ displayName: "Resend",
4
+ formatError(details) {
5
+ return {
6
+ statusCode: details.status,
7
+ name: details.type ?? "validation_error",
8
+ message: details.message
9
+ };
10
+ }
11
+ };
12
+ //# sourceMappingURL=resend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resend.js","sourceRoot":"","sources":["../../src/providers/resend.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAoB;IAC5C,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,QAAQ;IACrB,WAAW,CAAC,OAA6B;QACvC,OAAO;YACL,UAAU,EAAE,OAAO,CAAC,MAAM;YAC1B,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,kBAAkB;YACxC,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ProviderAdapter } from "./types.js";
2
+ export declare const stripeAdapter: ProviderAdapter;
@@ -0,0 +1,15 @@
1
+ export const stripeAdapter = {
2
+ name: "stripe",
3
+ displayName: "Stripe",
4
+ formatError(details) {
5
+ return {
6
+ error: {
7
+ type: details.type ?? "invalid_request_error",
8
+ message: details.message,
9
+ code: typeof details.code === "string" ? details.code : "parameter_missing",
10
+ param: details.param ?? "unknown"
11
+ }
12
+ };
13
+ }
14
+ };
15
+ //# sourceMappingURL=stripe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe.js","sourceRoot":"","sources":["../../src/providers/stripe.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAoB;IAC5C,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,QAAQ;IACrB,WAAW,CAAC,OAA6B;QACvC,OAAO;YACL,KAAK,EAAE;gBACL,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,uBAAuB;gBAC7C,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,mBAAmB;gBAC3E,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS;aAClC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ProviderAdapter } from "./types.js";
2
+ export declare const twilioAdapter: ProviderAdapter;
@@ -0,0 +1,14 @@
1
+ export const twilioAdapter = {
2
+ name: "twilio",
3
+ displayName: "Twilio",
4
+ formatError(details) {
5
+ const code = typeof details.code === "number" ? details.code : 21604;
6
+ return {
7
+ code,
8
+ message: details.message,
9
+ more_info: `https://www.twilio.com/docs/errors/${code}`,
10
+ status: details.status
11
+ };
12
+ }
13
+ };
14
+ //# sourceMappingURL=twilio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twilio.js","sourceRoot":"","sources":["../../src/providers/twilio.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAoB;IAC5C,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,QAAQ;IACrB,WAAW,CAAC,OAA6B;QACvC,MAAM,IAAI,GAAG,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;QACrE,OAAO;YACL,IAAI;YACJ,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,SAAS,EAAE,sCAAsC,IAAI,EAAE;YACvD,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,13 @@
1
+ export type ProviderName = "stripe" | "twilio" | "resend" | "github" | "discord" | "openai" | "generic";
2
+ export type ProviderErrorDetails = {
3
+ status: number;
4
+ message: string;
5
+ code?: string | number;
6
+ param?: string;
7
+ type?: string;
8
+ };
9
+ export type ProviderAdapter = {
10
+ name: ProviderName;
11
+ displayName: string;
12
+ formatError: (details: ProviderErrorDetails) => unknown;
13
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/providers/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ import type { NormalizedRequest } from "./requestNormalizer.js";
2
+ export declare function createCacheKey(normalizedRequest: NormalizedRequest, provider: string): string;
@@ -0,0 +1,37 @@
1
+ import { createHash } from "node:crypto";
2
+ export function createCacheKey(normalizedRequest, provider) {
3
+ const input = {
4
+ provider,
5
+ method: normalizedRequest.method,
6
+ path: normalizedRequest.path,
7
+ query: sortObject(normalizedRequest.query),
8
+ body: normalizedRequest.body,
9
+ importantHeaders: extractImportantHeaders(normalizedRequest.headers)
10
+ };
11
+ return createHash("sha256").update(stableStringify(input)).digest("hex").slice(0, 10);
12
+ }
13
+ function extractImportantHeaders(headers) {
14
+ const importantKeys = ["content-type", "accept", "stripe-version", "x-github-api-version", "twilio-version"];
15
+ const result = {};
16
+ for (const key of importantKeys) {
17
+ if (headers[key] !== undefined) {
18
+ result[key] = headers[key];
19
+ }
20
+ }
21
+ return sortObject(result);
22
+ }
23
+ function sortObject(obj) {
24
+ return Object.fromEntries(Object.entries(obj).sort(([left], [right]) => left.localeCompare(right)));
25
+ }
26
+ function stableStringify(value) {
27
+ if (Array.isArray(value)) {
28
+ const stringifiedArr = value.map(stableStringify).sort();
29
+ return `[${stringifiedArr.join(",")}]`;
30
+ }
31
+ if (value !== null && typeof value === "object") {
32
+ const entries = Object.entries(value).sort(([left], [right]) => left.localeCompare(right));
33
+ return `{${entries.map(([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue)}`).join(",")}}`;
34
+ }
35
+ return JSON.stringify(value);
36
+ }
37
+ //# sourceMappingURL=cacheKey.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cacheKey.js","sourceRoot":"","sources":["../../src/proxy/cacheKey.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAYzC,MAAM,UAAU,cAAc,CAAC,iBAAoC,EAAE,QAAgB;IACnF,MAAM,KAAK,GAAe;QACxB,QAAQ;QACR,MAAM,EAAE,iBAAiB,CAAC,MAAM;QAChC,IAAI,EAAE,iBAAiB,CAAC,IAAI;QAC5B,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,KAAK,CAAC;QAC1C,IAAI,EAAE,iBAAiB,CAAC,IAAI;QAC5B,gBAAgB,EAAE,uBAAuB,CAAC,iBAAiB,CAAC,OAAO,CAAC;KACrE,CAAC;IAEF,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACxF,CAAC;AAED,SAAS,uBAAuB,CAAC,OAAqC;IACpE,MAAM,aAAa,GAAG,CAAC,cAAc,EAAE,QAAQ,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,gBAAgB,CAAC,CAAC;IAC7G,MAAM,MAAM,GAAsC,EAAE,CAAC;IAErD,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;QAChC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,UAAU,CAAoC,GAAM;IAC3D,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAM,CAAC;AAC3G,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACrC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,cAAc,GAAG,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,EAAE,CAAC;QACzD,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IACzC,CAAC;IAED,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;QACtH,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IACtH,CAAC;IAED,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { IncomingHttpHeaders } from "node:http";
2
+ import { type ProviderName } from "../providers/index.js";
3
+ export type ProviderDetectionInput = {
4
+ path: string;
5
+ headers?: IncomingHttpHeaders | Record<string, string | string[]>;
6
+ query?: Record<string, unknown>;
7
+ body?: unknown;
8
+ };
9
+ export type Provider = ProviderName;
10
+ export declare function detectProvider(input: ProviderDetectionInput): Provider;
11
+ export declare function detectProvider(path: string, headers?: IncomingHttpHeaders): Provider;
@@ -0,0 +1,74 @@
1
+ import { isRegisteredProvider } from "../providers/index.js";
2
+ export function detectProvider(inputOrPath, headers = {}) {
3
+ const input = typeof inputOrPath === "string" ? { path: inputOrPath, headers } : inputOrPath;
4
+ const normalizedPath = input.path.toLowerCase();
5
+ const normalizedHeaders = normalizeHeaders(input.headers ?? {});
6
+ const query = input.query ?? {};
7
+ const body = input.body;
8
+ const override = getProviderOverride(normalizedHeaders, query);
9
+ if (override !== undefined) {
10
+ return override;
11
+ }
12
+ if (isStripePath(normalizedPath) || normalizedHeaders["stripe-version"] !== undefined || hasStripeAuthorization(normalizedHeaders)) {
13
+ return "stripe";
14
+ }
15
+ if (normalizedPath.includes("/2010-04-01/") || normalizedPath.includes("/accounts/")) {
16
+ return "twilio";
17
+ }
18
+ if (normalizedPath === "/emails" || normalizedPath.startsWith("/emails/")) {
19
+ return "resend";
20
+ }
21
+ if (normalizedPath.startsWith("/repos/") || normalizedPath.startsWith("/user/") || normalizedPath === "/user" || normalizedHeaders["x-github-api-version"] !== undefined) {
22
+ return "github";
23
+ }
24
+ if (isOpenAiPath(normalizedPath) || normalizedHeaders["openai-organization"] !== undefined || normalizedHeaders["openai-project"] !== undefined) {
25
+ return "openai";
26
+ }
27
+ if (/^\/api\/v\d+\/(?:channels|guilds|webhooks|interactions|applications)(?:\/|$)/.test(normalizedPath) ||
28
+ normalizedPath.includes("/channels/") ||
29
+ normalizedPath.includes("/webhooks/") ||
30
+ hasKey(query, "guild_id") ||
31
+ hasKey(query, "channel_id") ||
32
+ hasKey(body, "guild_id") ||
33
+ hasKey(body, "channel_id")) {
34
+ return "discord";
35
+ }
36
+ return "generic";
37
+ }
38
+ function isStripePath(path) {
39
+ return /^\/v1\/(?:customers|charges|payment_intents|setup_intents|checkout|prices|products|subscriptions|invoices|payment_methods|refunds|accounts|balance|payouts|transfers|events|webhook_endpoints)(?:\/|$)/.test(path);
40
+ }
41
+ function isOpenAiPath(path) {
42
+ return /^\/v1\/(?:chat\/completions|responses|embeddings|models|images|audio|files|fine_tuning|assistants|threads|vector_stores)(?:\/|$)/.test(path);
43
+ }
44
+ function getProviderOverride(headers, query) {
45
+ const headerOverride = firstValue(headers["x-ghostapi-provider"]);
46
+ const queryOverride = typeof query.ghost_provider === "string" ? query.ghost_provider : undefined;
47
+ const override = (headerOverride ?? queryOverride)?.toLowerCase();
48
+ return override !== undefined && isRegisteredProvider(override) ? override : undefined;
49
+ }
50
+ function normalizeHeaders(headers) {
51
+ return Object.fromEntries(Object.entries(headers)
52
+ .filter((entry) => typeof entry[1] === "string" || isStringArray(entry[1]))
53
+ .map(([key, value]) => [key.toLowerCase(), value]));
54
+ }
55
+ function hasStripeAuthorization(headers) {
56
+ const authorization = firstValue(headers.authorization);
57
+ return authorization !== undefined && /(?:bearer\s+)?[sr]k_(?:live|test)_/i.test(authorization);
58
+ }
59
+ function firstValue(value) {
60
+ return Array.isArray(value) ? value[0] : value;
61
+ }
62
+ function hasKey(value, key) {
63
+ if (Array.isArray(value)) {
64
+ return value.some((entry) => hasKey(entry, key));
65
+ }
66
+ if (value !== null && typeof value === "object") {
67
+ return Object.entries(value).some(([entryKey, entryValue]) => entryKey === key || hasKey(entryValue, key));
68
+ }
69
+ return false;
70
+ }
71
+ function isStringArray(value) {
72
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
73
+ }
74
+ //# sourceMappingURL=providerDetector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providerDetector.js","sourceRoot":"","sources":["../../src/proxy/providerDetector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Request, Response } from "express";
2
+ import type { ServerConfig } from "../config/serverConfig.js";
3
+ export declare function proxyHandler(request: Request, response: Response, config: ServerConfig): Promise<void>;
@@ -0,0 +1,114 @@
1
+ import { generateAiMock } from "../ai/aiGenerator.js";
2
+ import { getCachedResponse, setCachedResponse } from "../cache/index.js";
3
+ import { createCacheKey } from "./cacheKey.js";
4
+ import { detectProvider } from "./providerDetector.js";
5
+ import { normalizeRequest } from "./requestNormalizer.js";
6
+ import { extractIdFromResponse } from "../state/stateExtractor.js";
7
+ import { resolveState } from "../state/stateResolver.js";
8
+ import { saveToStateStore } from "../state/stateStore.js";
9
+ import { validateRequest, createProviderError } from "../errors/index.js";
10
+ import { addEvent } from "../server/eventsStore.js";
11
+ import { broadcastEvent } from "../server/sse.js";
12
+ import { inferGenericService } from "../ai/genericInference.js";
13
+ import { decideFault, waitForFault } from "../fault/faultLab.js";
14
+ import { findApiBehavior } from "../behavior/behaviorStore.js";
15
+ export async function proxyHandler(request, response, config) {
16
+ const t0 = performance.now();
17
+ const provider = detectProvider({ path: request.path, headers: request.headers, query: request.query, body: request.body });
18
+ const normalizedRequest = normalizeRequest(request);
19
+ const providerLabel = provider === "generic" ? inferGenericService(normalizedRequest.path, normalizedRequest.query, normalizedRequest.body) : provider;
20
+ async function completeRequest(source, status, body) {
21
+ const durationMs = Math.round(performance.now() - t0);
22
+ const event = {
23
+ id: `evt_${Date.now().toString(36)}`,
24
+ timestamp: new Date().toISOString(),
25
+ provider: providerLabel,
26
+ method: normalizedRequest.method,
27
+ path: normalizedRequest.path,
28
+ statusCode: status,
29
+ source,
30
+ durationMs,
31
+ request: {
32
+ query: normalizedRequest.query,
33
+ headers: normalizedRequest.headers,
34
+ body: normalizedRequest.body
35
+ },
36
+ response: body
37
+ };
38
+ await addEvent(event);
39
+ broadcastEvent(event);
40
+ }
41
+ const behavior = await findApiBehavior(normalizedRequest);
42
+ if (behavior !== null) {
43
+ for (const [key, value] of Object.entries(behavior.headers ?? {})) {
44
+ response.setHeader(key, value);
45
+ }
46
+ response.setHeader("x-ghostapi-behavior", "HIT");
47
+ response.status(behavior.status).json(behavior.body);
48
+ await completeRequest("behavior", behavior.status, behavior.body);
49
+ return;
50
+ }
51
+ const fault = decideFault(provider);
52
+ if (fault.type === "error") {
53
+ await waitForFault(fault.latencyMs);
54
+ response.setHeader("x-ghostapi-fault-lab", "error");
55
+ response.setHeader("retry-after", String(fault.retryAfterSeconds));
56
+ response.status(fault.statusCode).json(fault.body);
57
+ await completeRequest("fault", fault.statusCode, fault.body);
58
+ return;
59
+ }
60
+ if (fault.type === "delay") {
61
+ response.setHeader("x-ghostapi-fault-lab", "delay");
62
+ await waitForFault(fault.latencyMs);
63
+ }
64
+ const validationError = validateRequest(normalizedRequest, provider);
65
+ if (validationError !== null) {
66
+ const formattedError = createProviderError(provider, validationError);
67
+ response.status(validationError.status).json(formattedError);
68
+ await completeRequest("error", validationError.status, formattedError);
69
+ return;
70
+ }
71
+ const stateResolution = await resolveState(normalizedRequest, provider);
72
+ if (stateResolution !== null) {
73
+ for (const [key, value] of Object.entries(stateResolution.headers)) {
74
+ response.setHeader(key, value);
75
+ }
76
+ response.status(stateResolution.status).json(stateResolution.body);
77
+ await completeRequest("state", stateResolution.status, stateResolution.body);
78
+ return;
79
+ }
80
+ const cacheKey = createCacheKey(normalizedRequest, provider);
81
+ const cachedResponse = await getCachedResponse(provider, cacheKey);
82
+ if (cachedResponse !== null) {
83
+ for (const [key, value] of Object.entries(cachedResponse.headers)) {
84
+ response.setHeader(key, value);
85
+ }
86
+ response.setHeader("x-ghostapi-cache", "HIT");
87
+ response.status(cachedResponse.status).json(cachedResponse.body);
88
+ await completeRequest("cache", cachedResponse.status, cachedResponse.body);
89
+ return;
90
+ }
91
+ const generatedOption = await generateAiMock(normalizedRequest, provider, response, config);
92
+ if (generatedOption === "streamed") {
93
+ await completeRequest("stream", 200, { streamed: true });
94
+ return;
95
+ }
96
+ await setCachedResponse(provider, cacheKey, {
97
+ status: generatedOption.status,
98
+ headers: generatedOption.headers,
99
+ body: generatedOption.body
100
+ });
101
+ if (["POST", "PUT", "PATCH"].includes(normalizedRequest.method)) {
102
+ const extractedId = extractIdFromResponse(generatedOption.body);
103
+ if (extractedId !== undefined) {
104
+ await saveToStateStore(`${provider}:${extractedId}`, generatedOption.body);
105
+ }
106
+ }
107
+ for (const [key, value] of Object.entries(generatedOption.headers)) {
108
+ response.setHeader(key, value);
109
+ }
110
+ response.setHeader("x-ghostapi-cache", "MISS");
111
+ response.status(generatedOption.status).json(generatedOption.body);
112
+ await completeRequest(generatedOption.status >= 400 ? "error" : (String(generatedOption.body)?.includes("mock_response") ? "fallback" : "ai"), generatedOption.status, generatedOption.body);
113
+ }
114
+ //# sourceMappingURL=proxyHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxyHandler.js","sourceRoot":"","sources":["../../src/proxy/proxyHandler.ts"],"names":[],"mappings":"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"}