@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,11 @@
|
|
|
1
|
+
import type { Request } from "express";
|
|
2
|
+
export type NormalizedRequest = {
|
|
3
|
+
method: string;
|
|
4
|
+
path: string;
|
|
5
|
+
query: Record<string, unknown>;
|
|
6
|
+
headers: Record<string, string | string[]>;
|
|
7
|
+
body: unknown;
|
|
8
|
+
rawBody?: string;
|
|
9
|
+
receivedAt: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function normalizeRequest(request: Request): NormalizedRequest;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { sanitizeHeaders } from "../security/headerSanitizer.js";
|
|
2
|
+
import { sanitizeSecrets } from "../security/secrets.js";
|
|
3
|
+
export function normalizeRequest(request) {
|
|
4
|
+
const normalized = {
|
|
5
|
+
method: request.method,
|
|
6
|
+
path: request.path,
|
|
7
|
+
query: sanitizeRecord(request.query),
|
|
8
|
+
headers: sanitizeHeaders(request.headers),
|
|
9
|
+
body: sanitizeSecrets(getSafeBody(request)),
|
|
10
|
+
receivedAt: new Date().toISOString()
|
|
11
|
+
};
|
|
12
|
+
if (request.rawBody !== undefined) {
|
|
13
|
+
normalized.rawBody = request.rawBody;
|
|
14
|
+
}
|
|
15
|
+
return normalized;
|
|
16
|
+
}
|
|
17
|
+
function getSafeBody(request) {
|
|
18
|
+
if (Buffer.isBuffer(request.body)) {
|
|
19
|
+
return `[Binary Data: ${request.body.length} bytes]`;
|
|
20
|
+
}
|
|
21
|
+
return request.body;
|
|
22
|
+
}
|
|
23
|
+
function sanitizeRecord(value) {
|
|
24
|
+
const sanitized = sanitizeSecrets(value);
|
|
25
|
+
if (sanitized === null || typeof sanitized !== "object" || Array.isArray(sanitized)) {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
return sanitized;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=requestNormalizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestNormalizer.js","sourceRoot":"","sources":["../../src/proxy/requestNormalizer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAYzD,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,MAAM,UAAU,GAAsB;QACpC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC;QACpC,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC;QACzC,IAAI,EAAE,eAAe,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC3C,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACrC,CAAC;IAEF,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAClC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACvC,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,WAAW,CAAC,OAAgB;IACnC,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,iBAAiB,OAAO,CAAC,IAAI,CAAC,MAAM,SAAS,CAAC;IACvD,CAAC;IACD,OAAO,OAAO,CAAC,IAAI,CAAC;AACtB,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,MAAM,SAAS,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IAEzC,IAAI,SAAS,KAAK,IAAI,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACpF,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,SAAoC,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type DetectedSdk } from "../setup/sdkDetector.js";
|
|
2
|
+
export type SafetyFinding = {
|
|
3
|
+
severity: "high" | "medium" | "low";
|
|
4
|
+
message: string;
|
|
5
|
+
file?: string;
|
|
6
|
+
};
|
|
7
|
+
export type SafetyReport = {
|
|
8
|
+
projectRoot: string;
|
|
9
|
+
detected: DetectedSdk[];
|
|
10
|
+
findings: SafetyFinding[];
|
|
11
|
+
recommendations: string[];
|
|
12
|
+
};
|
|
13
|
+
export declare function generateSafetyReport(projectRoot?: string): Promise<SafetyReport>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { join, relative } from "node:path";
|
|
3
|
+
import { detectSdks, readPackageJson } from "../setup/sdkDetector.js";
|
|
4
|
+
const SKIP_DIRS = new Set(["node_modules", "dist", ".git", ".ghostapi", "coverage"]);
|
|
5
|
+
const SOURCE_EXTENSIONS = new Set([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".env", ".json"]);
|
|
6
|
+
export async function generateSafetyReport(projectRoot = process.cwd()) {
|
|
7
|
+
const packageJson = await readPackageJson(projectRoot);
|
|
8
|
+
const detected = detectSdks(packageJson);
|
|
9
|
+
const findings = [];
|
|
10
|
+
const files = await collectFiles(projectRoot, projectRoot, 250);
|
|
11
|
+
for (const file of files) {
|
|
12
|
+
const content = await readFile(join(projectRoot, file), "utf8");
|
|
13
|
+
if (/api\.stripe\.com|api\.openai\.com|api\.twilio\.com/i.test(content)) {
|
|
14
|
+
findings.push({ severity: "high", message: "Production provider host appears in source.", file });
|
|
15
|
+
}
|
|
16
|
+
if (/sk_live_[A-Za-z0-9]+/.test(content)) {
|
|
17
|
+
findings.push({ severity: "high", message: "Live-looking Stripe key appears in source.", file });
|
|
18
|
+
}
|
|
19
|
+
if (/STRIPE_SECRET_KEY|OPENAI_API_KEY|TWILIO_AUTH_TOKEN/.test(content) && !/GHOSTAPI_|apiBase|baseURL/.test(content)) {
|
|
20
|
+
findings.push({ severity: "medium", message: "Provider secret is referenced without an obvious GhostAPI local override nearby.", file });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
projectRoot,
|
|
25
|
+
detected,
|
|
26
|
+
findings,
|
|
27
|
+
recommendations: buildRecommendations(detected, findings)
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async function collectFiles(root, dir, limit, acc = []) {
|
|
31
|
+
if (acc.length >= limit)
|
|
32
|
+
return acc;
|
|
33
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
34
|
+
if (acc.length >= limit)
|
|
35
|
+
break;
|
|
36
|
+
const fullPath = join(dir, entry.name);
|
|
37
|
+
if (entry.isDirectory()) {
|
|
38
|
+
if (!SKIP_DIRS.has(entry.name))
|
|
39
|
+
await collectFiles(root, fullPath, limit, acc);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (!entry.isFile())
|
|
43
|
+
continue;
|
|
44
|
+
const dot = entry.name.lastIndexOf(".");
|
|
45
|
+
const ext = dot >= 0 ? entry.name.slice(dot) : entry.name;
|
|
46
|
+
if (!SOURCE_EXTENSIONS.has(ext))
|
|
47
|
+
continue;
|
|
48
|
+
const info = await stat(fullPath);
|
|
49
|
+
if (info.size > 300_000)
|
|
50
|
+
continue;
|
|
51
|
+
acc.push(relative(root, fullPath).replace(/\\/g, "/"));
|
|
52
|
+
}
|
|
53
|
+
return acc;
|
|
54
|
+
}
|
|
55
|
+
function buildRecommendations(detected, findings) {
|
|
56
|
+
const recommendations = ["Run ghostapi setup --write to generate agent rules and MCP snippets.", "Use ghostapi start --open during local integration work."];
|
|
57
|
+
if (detected.includes("stripe"))
|
|
58
|
+
recommendations.push("Configure Stripe with apiBase: process.env.GHOSTAPI_STRIPE_BASE_URL ?? 'http://127.0.0.1:8080'.");
|
|
59
|
+
if (detected.includes("openai"))
|
|
60
|
+
recommendations.push("Configure OpenAI with baseURL: process.env.GHOSTAPI_OPENAI_BASE_URL ?? 'http://127.0.0.1:8080/v1'.");
|
|
61
|
+
if (findings.some((finding) => finding.severity === "high"))
|
|
62
|
+
recommendations.push("Remove live-looking keys and production provider hosts before committing.");
|
|
63
|
+
return recommendations;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=safetyReport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safetyReport.js","sourceRoot":"","sources":["../../src/report/safetyReport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { detectSdks, readPackageJson } from "../setup/sdkDetector.js";
|
|
2
|
+
export async function generateAiRules(projectRoot = process.cwd()) {
|
|
3
|
+
const packageJson = await readPackageJson(projectRoot);
|
|
4
|
+
const detected = detectSdks(packageJson);
|
|
5
|
+
return {
|
|
6
|
+
filename: ".cursorrules",
|
|
7
|
+
projectRoot,
|
|
8
|
+
detected,
|
|
9
|
+
content: buildCursorRules(detected)
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function buildCursorRules(detected) {
|
|
13
|
+
const lines = [
|
|
14
|
+
"# GhostAPI Agent Rules",
|
|
15
|
+
"",
|
|
16
|
+
"You are working in a project that should use GhostAPI for local API simulations.",
|
|
17
|
+
"Never call paid or production third-party APIs during local tests unless the user explicitly asks.",
|
|
18
|
+
"Route supported SDKs and raw HTTP calls through http://127.0.0.1:8080.",
|
|
19
|
+
"Use GhostAPI MCP tools when available to inspect state, configure deterministic API behavior, read traffic logs, and toggle Chaos Mode.",
|
|
20
|
+
""
|
|
21
|
+
];
|
|
22
|
+
if (detected.includes("stripe")) {
|
|
23
|
+
lines.push("## Stripe", "", "This project depends on `stripe`. Configure the Stripe client to use GhostAPI in local development:", "", "```ts", "import Stripe from \"stripe\";", "", "export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY ?? \"sk_test_ghostapi\", {", " apiBase: process.env.GHOSTAPI_STRIPE_BASE_URL ?? \"http://127.0.0.1:8080\"", "});", "```", "", "Prefer `GHOSTAPI_STRIPE_BASE_URL=http://127.0.0.1:8080` in local env files instead of hard-coding production URLs.", "When writing tests, assert behavior against GhostAPI responses and avoid using live Stripe credentials.", "");
|
|
24
|
+
}
|
|
25
|
+
if (detected.length === 0) {
|
|
26
|
+
lines.push("## No Known SDKs Detected", "", "No first-class provider SDK was found in `package.json`. For raw HTTP clients, set the API base URL to `http://127.0.0.1:8080` during local development.", "");
|
|
27
|
+
}
|
|
28
|
+
lines.push("## Chaos Testing", "", "When testing retry logic, ask GhostAPI to enable Chaos Mode. Expect occasional provider-shaped `429` and `503` responses or 2-5 second artificial latency.", "");
|
|
29
|
+
return lines.join("\n");
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=aiRules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aiRules.js","sourceRoot":"","sources":["../../src/rules/aiRules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAStE,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE;IAC/D,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,WAAW,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACzC,OAAO;QACL,QAAQ,EAAE,cAAc;QACxB,WAAW;QACX,QAAQ;QACR,OAAO,EAAE,gBAAgB,CAAC,QAAQ,CAAC;KACpC,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAkB;IAC1C,MAAM,KAAK,GAAG;QACZ,wBAAwB;QACxB,EAAE;QACF,kFAAkF;QAClF,oGAAoG;QACpG,wEAAwE;QACxE,yIAAyI;QACzI,EAAE;KACH,CAAC;IAEF,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CACR,WAAW,EACX,EAAE,EACF,qGAAqG,EACrG,EAAE,EACF,OAAO,EACP,gCAAgC,EAChC,EAAE,EACF,2FAA2F,EAC3F,8EAA8E,EAC9E,KAAK,EACL,KAAK,EACL,EAAE,EACF,oHAAoH,EACpH,yGAAyG,EACzG,EAAE,CACH,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CACR,2BAA2B,EAC3B,EAAE,EACF,0JAA0J,EAC1J,EAAE,CACH,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI,CACR,kBAAkB,EAClB,EAAE,EACF,4JAA4J,EAC5J,EAAE,CACH,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ProxyEvent } from "../server/eventsStore.js";
|
|
2
|
+
export type ScenarioStep = {
|
|
3
|
+
name: string;
|
|
4
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
5
|
+
path: string;
|
|
6
|
+
status: number;
|
|
7
|
+
body: unknown;
|
|
8
|
+
headers?: Record<string, string>;
|
|
9
|
+
};
|
|
10
|
+
export type ScenarioPreset = {
|
|
11
|
+
id: string;
|
|
12
|
+
title: string;
|
|
13
|
+
provider: string;
|
|
14
|
+
description: string;
|
|
15
|
+
steps: ScenarioStep[];
|
|
16
|
+
};
|
|
17
|
+
export type ScenarioReplayResult = {
|
|
18
|
+
scenario: ScenarioPreset;
|
|
19
|
+
applied: Array<{
|
|
20
|
+
method: string;
|
|
21
|
+
path: string;
|
|
22
|
+
status: number;
|
|
23
|
+
}>;
|
|
24
|
+
};
|
|
25
|
+
export declare function listScenarioPresets(): Promise<ScenarioPreset[]>;
|
|
26
|
+
export declare function getScenarioPreset(id: string): Promise<ScenarioPreset | null>;
|
|
27
|
+
export declare function replayScenario(id: string): Promise<ScenarioReplayResult>;
|
|
28
|
+
export declare function exportScenario(id: string): Promise<ScenarioPreset>;
|
|
29
|
+
export declare function shareScenario(id: string): Promise<{
|
|
30
|
+
scenario: ScenarioPreset;
|
|
31
|
+
shareText: string;
|
|
32
|
+
}>;
|
|
33
|
+
export declare function importScenario(input: unknown): Promise<ScenarioPreset>;
|
|
34
|
+
export declare function saveEventsAsScenario(events: ProxyEvent[], input?: {
|
|
35
|
+
title?: string;
|
|
36
|
+
id?: string;
|
|
37
|
+
}): Promise<ScenarioPreset>;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { setApiBehavior } from "../behavior/behaviorStore.js";
|
|
4
|
+
const SCENARIOS = [
|
|
5
|
+
{
|
|
6
|
+
id: "stripe-customer-create",
|
|
7
|
+
title: "Stripe customer create",
|
|
8
|
+
provider: "stripe",
|
|
9
|
+
description: "Returns a stable customer object for local onboarding and billing flows.",
|
|
10
|
+
steps: [
|
|
11
|
+
{
|
|
12
|
+
name: "Create customer",
|
|
13
|
+
method: "POST",
|
|
14
|
+
path: "/v1/customers",
|
|
15
|
+
status: 200,
|
|
16
|
+
body: { id: "cus_ghostapi_123", object: "customer", email: "founder@example.com", livemode: false, metadata: { scenario: "stripe-customer-create" } }
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: "stripe-payment-intent-fail",
|
|
22
|
+
title: "Payment intent fail",
|
|
23
|
+
provider: "stripe",
|
|
24
|
+
description: "Forces a card-decline shaped Stripe error so agents must implement failure handling.",
|
|
25
|
+
steps: [
|
|
26
|
+
{
|
|
27
|
+
name: "Payment intent card declined",
|
|
28
|
+
method: "POST",
|
|
29
|
+
path: "/v1/payment_intents",
|
|
30
|
+
status: 402,
|
|
31
|
+
body: { error: { type: "card_error", code: "card_declined", decline_code: "generic_decline", message: "Your card was declined.", param: "payment_method" } }
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: "resend-email-send",
|
|
37
|
+
title: "Resend email send",
|
|
38
|
+
provider: "resend",
|
|
39
|
+
description: "Returns a delivered email ID for transactional email flows.",
|
|
40
|
+
steps: [
|
|
41
|
+
{
|
|
42
|
+
name: "Send email",
|
|
43
|
+
method: "POST",
|
|
44
|
+
path: "/emails",
|
|
45
|
+
status: 200,
|
|
46
|
+
body: { id: "email_ghostapi_123", object: "email", status: "sent" }
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: "github-issue-create",
|
|
52
|
+
title: "GitHub issue create",
|
|
53
|
+
provider: "github",
|
|
54
|
+
description: "Returns a deterministic issue payload for GitHub automation tests.",
|
|
55
|
+
steps: [
|
|
56
|
+
{
|
|
57
|
+
name: "Create issue",
|
|
58
|
+
method: "POST",
|
|
59
|
+
path: "/repos/ghostapi/demo/issues",
|
|
60
|
+
status: 201,
|
|
61
|
+
body: { id: 10001, number: 42, title: "GhostAPI local issue", state: "open", html_url: "https://github.local/ghostapi/demo/issues/42" }
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
];
|
|
66
|
+
const CUSTOM_SCENARIOS_DIR = join(".ghostapi", "scenarios");
|
|
67
|
+
export async function listScenarioPresets() {
|
|
68
|
+
return [...structuredClone(SCENARIOS), ...(await listCustomScenarios())];
|
|
69
|
+
}
|
|
70
|
+
export async function getScenarioPreset(id) {
|
|
71
|
+
const builtIn = SCENARIOS.find((scenario) => scenario.id === id);
|
|
72
|
+
if (builtIn !== undefined)
|
|
73
|
+
return structuredClone(builtIn);
|
|
74
|
+
return await readCustomScenario(id);
|
|
75
|
+
}
|
|
76
|
+
export async function replayScenario(id) {
|
|
77
|
+
const scenario = await getScenarioPreset(id);
|
|
78
|
+
if (scenario === null)
|
|
79
|
+
throw new Error(`Unknown scenario: ${id}`);
|
|
80
|
+
const applied = [];
|
|
81
|
+
for (const step of scenario.steps) {
|
|
82
|
+
await setApiBehavior({ method: step.method, path: step.path, status: step.status, body: step.body, headers: step.headers });
|
|
83
|
+
applied.push({ method: step.method, path: step.path, status: step.status });
|
|
84
|
+
}
|
|
85
|
+
return { scenario, applied };
|
|
86
|
+
}
|
|
87
|
+
export async function exportScenario(id) {
|
|
88
|
+
const scenario = await getScenarioPreset(id);
|
|
89
|
+
if (scenario === null)
|
|
90
|
+
throw new Error(`Unknown scenario: ${id}`);
|
|
91
|
+
return scenario;
|
|
92
|
+
}
|
|
93
|
+
export async function shareScenario(id) {
|
|
94
|
+
const scenario = await exportScenario(id);
|
|
95
|
+
const encoded = Buffer.from(JSON.stringify(scenario), "utf8").toString("base64url");
|
|
96
|
+
return {
|
|
97
|
+
scenario,
|
|
98
|
+
shareText: `ghostapi://scenario/${encoded}`
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
export async function importScenario(input) {
|
|
102
|
+
const scenario = normalizeScenario(input);
|
|
103
|
+
await writeScenario(scenario);
|
|
104
|
+
return scenario;
|
|
105
|
+
}
|
|
106
|
+
export async function saveEventsAsScenario(events, input = {}) {
|
|
107
|
+
const title = input.title?.trim() || `Traffic scenario ${new Date().toISOString()}`;
|
|
108
|
+
const id = input.id?.trim() || slugify(title);
|
|
109
|
+
const scenario = normalizeScenario({
|
|
110
|
+
id,
|
|
111
|
+
title,
|
|
112
|
+
provider: "traffic",
|
|
113
|
+
description: "Saved from recent GhostAPI traffic.",
|
|
114
|
+
steps: events.filter((event) => event.source !== "fault").slice(-20).map((event, index) => ({
|
|
115
|
+
name: `${index + 1}. ${event.method} ${event.path}`,
|
|
116
|
+
method: event.method,
|
|
117
|
+
path: event.path,
|
|
118
|
+
status: event.statusCode,
|
|
119
|
+
body: event.response
|
|
120
|
+
}))
|
|
121
|
+
});
|
|
122
|
+
await writeScenario(scenario);
|
|
123
|
+
return scenario;
|
|
124
|
+
}
|
|
125
|
+
async function listCustomScenarios() {
|
|
126
|
+
try {
|
|
127
|
+
const entries = await readdir(CUSTOM_SCENARIOS_DIR, { withFileTypes: true });
|
|
128
|
+
const scenarios = [];
|
|
129
|
+
for (const entry of entries) {
|
|
130
|
+
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
131
|
+
scenarios.push(normalizeScenario(JSON.parse(await readFile(join(CUSTOM_SCENARIOS_DIR, entry.name), "utf8"))));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return scenarios;
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
138
|
+
return [];
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async function readCustomScenario(id) {
|
|
143
|
+
try {
|
|
144
|
+
return normalizeScenario(JSON.parse(await readFile(join(CUSTOM_SCENARIOS_DIR, `${slugify(id)}.json`), "utf8")));
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
148
|
+
return null;
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function writeScenario(scenario) {
|
|
153
|
+
await mkdir(CUSTOM_SCENARIOS_DIR, { recursive: true });
|
|
154
|
+
await writeFile(join(CUSTOM_SCENARIOS_DIR, `${scenario.id}.json`), JSON.stringify(scenario, null, 2), "utf8");
|
|
155
|
+
}
|
|
156
|
+
function normalizeScenario(input) {
|
|
157
|
+
if (input === null || typeof input !== "object" || Array.isArray(input))
|
|
158
|
+
throw new Error("Scenario must be an object.");
|
|
159
|
+
const record = input;
|
|
160
|
+
if (typeof record.title !== "string" || record.title.trim() === "")
|
|
161
|
+
throw new Error("Scenario title is required.");
|
|
162
|
+
if (!Array.isArray(record.steps) || record.steps.length === 0)
|
|
163
|
+
throw new Error("Scenario steps are required.");
|
|
164
|
+
const id = typeof record.id === "string" && record.id.trim() !== "" ? slugify(record.id) : slugify(record.title);
|
|
165
|
+
return {
|
|
166
|
+
id,
|
|
167
|
+
title: record.title.trim(),
|
|
168
|
+
provider: typeof record.provider === "string" ? record.provider : "custom",
|
|
169
|
+
description: typeof record.description === "string" ? record.description : "Custom GhostAPI scenario.",
|
|
170
|
+
steps: record.steps.map(normalizeStep)
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function normalizeStep(input) {
|
|
174
|
+
if (input === null || typeof input !== "object" || Array.isArray(input))
|
|
175
|
+
throw new Error("Scenario step must be an object.");
|
|
176
|
+
const record = input;
|
|
177
|
+
const method = String(record.method ?? "GET").toUpperCase();
|
|
178
|
+
if (!isScenarioMethod(method))
|
|
179
|
+
throw new Error("Scenario step method is invalid.");
|
|
180
|
+
const path = String(record.path ?? "");
|
|
181
|
+
if (!path.startsWith("/"))
|
|
182
|
+
throw new Error("Scenario step path must start with '/'.");
|
|
183
|
+
const status = Number(record.status);
|
|
184
|
+
if (!Number.isInteger(status) || status < 100 || status > 599)
|
|
185
|
+
throw new Error("Scenario step status must be 100-599.");
|
|
186
|
+
return { name: typeof record.name === "string" ? record.name : `${method} ${path}`, method, path, status, body: record.body ?? {}, headers: readHeaders(record.headers) };
|
|
187
|
+
}
|
|
188
|
+
function readHeaders(value) {
|
|
189
|
+
if (value === undefined)
|
|
190
|
+
return undefined;
|
|
191
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
192
|
+
return undefined;
|
|
193
|
+
return Object.fromEntries(Object.entries(value).map(([key, headerValue]) => [key, String(headerValue)]));
|
|
194
|
+
}
|
|
195
|
+
function isScenarioMethod(value) {
|
|
196
|
+
return value === "GET" || value === "POST" || value === "PUT" || value === "PATCH" || value === "DELETE";
|
|
197
|
+
}
|
|
198
|
+
function slugify(value) {
|
|
199
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 80) || "custom-scenario";
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=scenarioStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenarioStore.js","sourceRoot":"","sources":["../../src/scenarios/scenarioStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { sanitizeSecrets } from "./secrets.js";
|
|
2
|
+
export function sanitizeHeaders(headers) {
|
|
3
|
+
const sanitized = sanitizeSecrets(headers);
|
|
4
|
+
if (sanitized === null || typeof sanitized !== "object" || Array.isArray(sanitized)) {
|
|
5
|
+
return {};
|
|
6
|
+
}
|
|
7
|
+
return Object.fromEntries(Object.entries(sanitized).filter((entry) => typeof entry[1] === "string" || isStringArray(entry[1])));
|
|
8
|
+
}
|
|
9
|
+
function isStringArray(value) {
|
|
10
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=headerSanitizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headerSanitizer.js","sourceRoot":"","sources":["../../src/security/headerSanitizer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAI/C,MAAM,UAAU,eAAe,CAAC,OAA4B;IAC1D,MAAM,SAAS,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAE3C,IAAI,SAAS,KAAK,IAAI,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACpF,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,SAAoC,CAAC,CAAC,MAAM,CACzD,CAAC,KAAK,EAAwC,EAAE,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CACzG,CACF,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC;AACnF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { sanitizeSecrets as maskSecrets } from "./secrets.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maskSecrets.js","sourceRoot":"","sources":["../../src/security/maskSecrets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,IAAI,WAAW,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const SECRET_FIELD_NAMES = [
|
|
2
|
+
"authorization",
|
|
3
|
+
"api_key",
|
|
4
|
+
"apikey",
|
|
5
|
+
"x-api-key",
|
|
6
|
+
"access_token",
|
|
7
|
+
"refresh_token",
|
|
8
|
+
"client_secret",
|
|
9
|
+
"password",
|
|
10
|
+
"secret",
|
|
11
|
+
"token",
|
|
12
|
+
"key"
|
|
13
|
+
];
|
|
14
|
+
const TOKEN_PATTERNS = [
|
|
15
|
+
/sk_live_[A-Za-z0-9_\-]+/g,
|
|
16
|
+
/sk_test_[A-Za-z0-9_\-]+/g,
|
|
17
|
+
/rk_live_[A-Za-z0-9_\-]+/g,
|
|
18
|
+
/ghp_[A-Za-z0-9_]+/g,
|
|
19
|
+
/github_pat_[A-Za-z0-9_]+/g,
|
|
20
|
+
/xoxb-[A-Za-z0-9\-]+/g,
|
|
21
|
+
/SG\.[A-Za-z0-9_\-\.]+/g
|
|
22
|
+
];
|
|
23
|
+
export const MASK = "***";
|
|
24
|
+
export function sanitizeSecrets(value) {
|
|
25
|
+
return sanitizeValue(value);
|
|
26
|
+
}
|
|
27
|
+
export function sanitizeSecretString(value) {
|
|
28
|
+
return TOKEN_PATTERNS.reduce((result, pattern) => result.replace(pattern, MASK), value.replace(/Bearer\s+\S+/gi, "Bearer ***"));
|
|
29
|
+
}
|
|
30
|
+
export function isSecretFieldName(name) {
|
|
31
|
+
const normalizedName = name.toLowerCase();
|
|
32
|
+
return SECRET_FIELD_NAMES.some((secretName) => normalizedName.includes(secretName));
|
|
33
|
+
}
|
|
34
|
+
function sanitizeValue(value) {
|
|
35
|
+
if (Array.isArray(value)) {
|
|
36
|
+
return value.map(sanitizeValue);
|
|
37
|
+
}
|
|
38
|
+
if (value !== null && typeof value === "object") {
|
|
39
|
+
return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [
|
|
40
|
+
key,
|
|
41
|
+
isSecretFieldName(key) ? maskByType(entryValue) : sanitizeValue(entryValue)
|
|
42
|
+
]));
|
|
43
|
+
}
|
|
44
|
+
if (typeof value === "string") {
|
|
45
|
+
return sanitizeSecretString(value);
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
function maskByType(value) {
|
|
50
|
+
if (Array.isArray(value)) {
|
|
51
|
+
return value.map(maskByType);
|
|
52
|
+
}
|
|
53
|
+
if (value !== null && typeof value === "object") {
|
|
54
|
+
return Object.fromEntries(Object.keys(value).map((key) => [key, MASK]));
|
|
55
|
+
}
|
|
56
|
+
if (typeof value === "string" && value.trim().toLowerCase().startsWith("bearer ")) {
|
|
57
|
+
return "Bearer ***";
|
|
58
|
+
}
|
|
59
|
+
return MASK;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/security/secrets.ts"],"names":[],"mappings":"AAAA,MAAM,kBAAkB,GAAG;IACzB,eAAe;IACf,SAAS;IACT,QAAQ;IACR,WAAW;IACX,cAAc;IACd,eAAe;IACf,eAAe;IACf,UAAU;IACV,QAAQ;IACR,OAAO;IACP,KAAK;CACN,CAAC;AAEF,MAAM,cAAc,GAAG;IACrB,0BAA0B;IAC1B,0BAA0B;IAC1B,0BAA0B;IAC1B,oBAAoB;IACpB,2BAA2B;IAC3B,sBAAsB;IACtB,wBAAwB;CACzB,CAAC;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,CAAC;AAE1B,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;AAClI,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAC1C,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,cAAc,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;AACtF,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC;YAC1E,GAAG;YACH,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC;SAC5E,CAAC,CACH,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IAChC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,KAAgC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACrG,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAClF,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Express } from "express";
|
|
2
|
+
import type { ServerConfig } from "../config/serverConfig.js";
|
|
3
|
+
declare module "express-serve-static-core" {
|
|
4
|
+
interface Request {
|
|
5
|
+
rawBody?: string;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export declare function createServer(config: ServerConfig): Promise<Express>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { initializeCacheDir } from "../cache/index.js";
|
|
3
|
+
import { initializeStateStore } from "../state/stateStore.js";
|
|
4
|
+
import { registerRoutes } from "./routes.js";
|
|
5
|
+
function captureRawBody(request, _response, buffer) {
|
|
6
|
+
if (buffer.length > 0) {
|
|
7
|
+
request.rawBody = buffer.toString("utf8");
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export async function createServer(config) {
|
|
11
|
+
await initializeStateStore();
|
|
12
|
+
await initializeCacheDir();
|
|
13
|
+
const app = express();
|
|
14
|
+
app.disable("x-powered-by");
|
|
15
|
+
app.use((req, res, next) => {
|
|
16
|
+
const origin = req.header("origin");
|
|
17
|
+
if (req.path.startsWith("/api/") && origin !== undefined && !isLocalOrigin(origin)) {
|
|
18
|
+
res.status(403).json({ error: { code: "forbidden_origin", message: "GhostAPI dashboard APIs only accept local origins." } });
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
res.header("Access-Control-Allow-Origin", origin && isLocalOrigin(origin) ? origin : "*");
|
|
22
|
+
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS");
|
|
23
|
+
res.header("Access-Control-Allow-Headers", "*");
|
|
24
|
+
res.header("Access-Control-Expose-Headers", "*");
|
|
25
|
+
next();
|
|
26
|
+
});
|
|
27
|
+
app.use(express.json({ limit: "5mb", verify: captureRawBody }));
|
|
28
|
+
app.use(express.urlencoded({ limit: "5mb", extended: true, verify: captureRawBody }));
|
|
29
|
+
app.use(express.raw({ limit: "15mb", type: () => true, verify: captureRawBody }));
|
|
30
|
+
registerRoutes(app, config);
|
|
31
|
+
return app;
|
|
32
|
+
}
|
|
33
|
+
function isLocalOrigin(origin) {
|
|
34
|
+
try {
|
|
35
|
+
const { hostname } = new URL(origin);
|
|
36
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=createServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createServer.js","sourceRoot":"","sources":["../../src/server/createServer.ts"],"names":[],"mappings":"AAAA,OAAO,OAAyB,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAS7C,SAAS,cAAc,CAAC,OAAwB,EAAE,SAA2B,EAAE,MAAc;IAC3F,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAoB;IACrD,MAAM,oBAAoB,EAAE,CAAC;IAC7B,MAAM,kBAAkB,EAAE,CAAC;IAE3B,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IAEtB,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAE5B,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;YACnF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,oDAAoD,EAAE,EAAE,CAAC,CAAC;YAC7H,OAAO;QACT,CAAC;QAED,GAAG,CAAC,MAAM,CAAC,6BAA6B,EAAE,MAAM,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC1F,GAAG,CAAC,MAAM,CAAC,8BAA8B,EAAE,8CAA8C,CAAC,CAAC;QAC3F,GAAG,CAAC,MAAM,CAAC,8BAA8B,EAAE,GAAG,CAAC,CAAC;QAChD,GAAG,CAAC,MAAM,CAAC,+BAA+B,EAAE,GAAG,CAAC,CAAC;QACjD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;IAChE,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;IACtF,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;IAElF,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAE5B,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,MAAc;IACnC,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QACrC,OAAO,QAAQ,KAAK,WAAW,IAAI,QAAQ,KAAK,WAAW,IAAI,QAAQ,KAAK,KAAK,CAAC;IACpF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type EventSource = "state" | "cache" | "ai" | "error" | "fallback" | "stream" | "fault" | "behavior";
|
|
2
|
+
export type ProxyEvent = {
|
|
3
|
+
id: string;
|
|
4
|
+
timestamp: string;
|
|
5
|
+
provider: string;
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
statusCode: number;
|
|
9
|
+
source: EventSource;
|
|
10
|
+
durationMs: number;
|
|
11
|
+
request: unknown;
|
|
12
|
+
response: unknown;
|
|
13
|
+
};
|
|
14
|
+
export declare function addEvent(event: ProxyEvent): Promise<void>;
|
|
15
|
+
export declare function getEventsHistory(): ProxyEvent[];
|
|
16
|
+
export declare function clearEventsHistoryForTests(): void;
|
|
17
|
+
export declare function clearEvents(): Promise<void>;
|