@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.
- package/CHANGELOG.md +29 -0
- package/CONTRIBUTING.md +117 -0
- package/LICENSE +21 -0
- package/README.md +354 -0
- package/SECURITY.md +37 -0
- package/dist/agents/agentPrompt.d.ts +5 -0
- package/dist/agents/agentPrompt.js +30 -0
- package/dist/agents/agentPrompt.js.map +1 -0
- package/dist/ai/aiClient.d.ts +7 -0
- package/dist/ai/aiClient.js +30 -0
- package/dist/ai/aiClient.js.map +1 -0
- package/dist/ai/aiGenerator.d.ts +10 -0
- package/dist/ai/aiGenerator.js +101 -0
- package/dist/ai/aiGenerator.js.map +1 -0
- package/dist/ai/genericInference.d.ts +5 -0
- package/dist/ai/genericInference.js +80 -0
- package/dist/ai/genericInference.js.map +1 -0
- package/dist/ai/index.d.ts +3 -0
- package/dist/ai/index.js +2 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/jsonRepair.d.ts +2 -0
- package/dist/ai/jsonRepair.js +31 -0
- package/dist/ai/jsonRepair.js.map +1 -0
- package/dist/ai/prompts.d.ts +4 -0
- package/dist/ai/prompts.js +81 -0
- package/dist/ai/prompts.js.map +1 -0
- package/dist/behavior/behaviorStore.d.ts +12 -0
- package/dist/behavior/behaviorStore.js +72 -0
- package/dist/behavior/behaviorStore.js.map +1 -0
- package/dist/cache/index.d.ts +9 -0
- package/dist/cache/index.js +34 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cli/errors.d.ts +4 -0
- package/dist/cli/errors.js +9 -0
- package/dist/cli/errors.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +277 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/parser.d.ts +56 -0
- package/dist/cli/parser.js +169 -0
- package/dist/cli/parser.js.map +1 -0
- package/dist/config/localConfig.d.ts +16 -0
- package/dist/config/localConfig.js +58 -0
- package/dist/config/localConfig.js.map +1 -0
- package/dist/config/serverConfig.d.ts +13 -0
- package/dist/config/serverConfig.js +44 -0
- package/dist/config/serverConfig.js.map +1 -0
- package/dist/dashboard/app.js +615 -0
- package/dist/dashboard/dashboard.d.ts +4 -0
- package/dist/dashboard/dashboard.js +20 -0
- package/dist/dashboard/dashboard.js.map +1 -0
- package/dist/dashboard/index.d.ts +1 -0
- package/dist/dashboard/index.html +298 -0
- package/dist/dashboard/index.js +2 -0
- package/dist/dashboard/index.js.map +1 -0
- package/dist/dashboard/styles.css +349 -0
- package/dist/errors/errorEngine.d.ts +3 -0
- package/dist/errors/errorEngine.js +65 -0
- package/dist/errors/errorEngine.js.map +1 -0
- package/dist/errors/index.d.ts +3 -0
- package/dist/errors/index.js +3 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/providerErrors.d.ts +3 -0
- package/dist/errors/providerErrors.js +6 -0
- package/dist/errors/providerErrors.js.map +1 -0
- package/dist/fault/faultLab.d.ts +27 -0
- package/dist/fault/faultLab.js +89 -0
- package/dist/fault/faultLab.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/landing/assets/index-BXR41fAD.css +1 -0
- package/dist/landing/assets/index-D8_ggMRE.js +55 -0
- package/dist/landing/index.html +15 -0
- package/dist/landing/landing.d.ts +3 -0
- package/dist/landing/landing.js +12 -0
- package/dist/landing/landing.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +52 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/providers/discord.d.ts +2 -0
- package/dist/providers/discord.js +11 -0
- package/dist/providers/discord.js.map +1 -0
- package/dist/providers/generic.d.ts +2 -0
- package/dist/providers/generic.js +13 -0
- package/dist/providers/generic.js.map +1 -0
- package/dist/providers/github.d.ts +2 -0
- package/dist/providers/github.js +11 -0
- package/dist/providers/github.js.map +1 -0
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.js +2 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +2 -0
- package/dist/providers/openai.js +15 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/registry.d.ts +12 -0
- package/dist/providers/registry.js +23 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/resend.d.ts +2 -0
- package/dist/providers/resend.js +12 -0
- package/dist/providers/resend.js.map +1 -0
- package/dist/providers/stripe.d.ts +2 -0
- package/dist/providers/stripe.js +15 -0
- package/dist/providers/stripe.js.map +1 -0
- package/dist/providers/twilio.d.ts +2 -0
- package/dist/providers/twilio.js +14 -0
- package/dist/providers/twilio.js.map +1 -0
- package/dist/providers/types.d.ts +13 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/proxy/cacheKey.d.ts +2 -0
- package/dist/proxy/cacheKey.js +37 -0
- package/dist/proxy/cacheKey.js.map +1 -0
- package/dist/proxy/providerDetector.d.ts +11 -0
- package/dist/proxy/providerDetector.js +74 -0
- package/dist/proxy/providerDetector.js.map +1 -0
- package/dist/proxy/proxyHandler.d.ts +3 -0
- package/dist/proxy/proxyHandler.js +114 -0
- package/dist/proxy/proxyHandler.js.map +1 -0
- package/dist/proxy/requestNormalizer.d.ts +11 -0
- package/dist/proxy/requestNormalizer.js +30 -0
- package/dist/proxy/requestNormalizer.js.map +1 -0
- package/dist/report/safetyReport.d.ts +13 -0
- package/dist/report/safetyReport.js +65 -0
- package/dist/report/safetyReport.js.map +1 -0
- package/dist/rules/aiRules.d.ts +7 -0
- package/dist/rules/aiRules.js +31 -0
- package/dist/rules/aiRules.js.map +1 -0
- package/dist/scenarios/scenarioStore.d.ts +37 -0
- package/dist/scenarios/scenarioStore.js +201 -0
- package/dist/scenarios/scenarioStore.js.map +1 -0
- package/dist/security/headerSanitizer.d.ts +3 -0
- package/dist/security/headerSanitizer.js +12 -0
- package/dist/security/headerSanitizer.js.map +1 -0
- package/dist/security/maskSecrets.d.ts +1 -0
- package/dist/security/maskSecrets.js +2 -0
- package/dist/security/maskSecrets.js.map +1 -0
- package/dist/security/secrets.d.ts +4 -0
- package/dist/security/secrets.js +61 -0
- package/dist/security/secrets.js.map +1 -0
- package/dist/server/createServer.d.ts +8 -0
- package/dist/server/createServer.js +42 -0
- package/dist/server/createServer.js.map +1 -0
- package/dist/server/eventsStore.d.ts +17 -0
- package/dist/server/eventsStore.js +31 -0
- package/dist/server/eventsStore.js.map +1 -0
- package/dist/server/routes.d.ts +3 -0
- package/dist/server/routes.js +189 -0
- package/dist/server/routes.js.map +1 -0
- package/dist/server/sse.d.ts +5 -0
- package/dist/server/sse.js +32 -0
- package/dist/server/sse.js.map +1 -0
- package/dist/setup/sdkDetector.d.ts +9 -0
- package/dist/setup/sdkDetector.js +31 -0
- package/dist/setup/sdkDetector.js.map +1 -0
- package/dist/setup/setupGenerator.d.ts +28 -0
- package/dist/setup/setupGenerator.js +146 -0
- package/dist/setup/setupGenerator.js.map +1 -0
- package/dist/state/index.d.ts +1 -0
- package/dist/state/index.js +2 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/stateExtractor.d.ts +3 -0
- package/dist/state/stateExtractor.js +36 -0
- package/dist/state/stateExtractor.js.map +1 -0
- package/dist/state/stateResolver.d.ts +8 -0
- package/dist/state/stateResolver.js +95 -0
- package/dist/state/stateResolver.js.map +1 -0
- package/dist/state/stateStore.d.ts +4 -0
- package/dist/state/stateStore.js +72 -0
- package/dist/state/stateStore.js.map +1 -0
- package/dist/tests/testGenerator.d.ts +6 -0
- package/dist/tests/testGenerator.js +33 -0
- package/dist/tests/testGenerator.js.map +1 -0
- package/dist/utils/json.d.ts +2 -0
- package/dist/utils/json.js +10 -0
- package/dist/utils/json.js.map +1 -0
- package/docs/release-checklist.md +71 -0
- package/examples/agent-instructions/README.md +39 -0
- package/examples/generic-rest/README.md +27 -0
- package/examples/github-fetch/README.md +35 -0
- package/examples/resend-node/README.md +22 -0
- package/examples/stripe-node/README.md +29 -0
- package/examples/twilio-node/README.md +21 -0
- 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,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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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,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,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,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,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 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/providers/types.ts"],"names":[],"mappings":""}
|
|
@@ -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,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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"}
|