@congzhen/changewayguard 6.8.12
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/LICENSE +21 -0
- package/README.md +270 -0
- package/dashboard-dist/api/104.index.js +1420 -0
- package/dashboard-dist/api/104.index.js.map +1 -0
- package/dashboard-dist/api/113.index.js +496 -0
- package/dashboard-dist/api/113.index.js.map +1 -0
- package/dashboard-dist/api/18.index.js +67 -0
- package/dashboard-dist/api/18.index.js.map +1 -0
- package/dashboard-dist/api/217.index.js +44 -0
- package/dashboard-dist/api/217.index.js.map +1 -0
- package/dashboard-dist/api/222.index.js +90 -0
- package/dashboard-dist/api/222.index.js.map +1 -0
- package/dashboard-dist/api/25.index.js +3562 -0
- package/dashboard-dist/api/25.index.js.map +1 -0
- package/dashboard-dist/api/280.index.js +206 -0
- package/dashboard-dist/api/280.index.js.map +1 -0
- package/dashboard-dist/api/369.index.js +115 -0
- package/dashboard-dist/api/369.index.js.map +1 -0
- package/dashboard-dist/api/377.index.js +1176 -0
- package/dashboard-dist/api/377.index.js.map +1 -0
- package/dashboard-dist/api/411.index.js +4250 -0
- package/dashboard-dist/api/411.index.js.map +1 -0
- package/dashboard-dist/api/424.index.js +135 -0
- package/dashboard-dist/api/424.index.js.map +1 -0
- package/dashboard-dist/api/573.index.js +806 -0
- package/dashboard-dist/api/573.index.js.map +1 -0
- package/dashboard-dist/api/598.index.js +328 -0
- package/dashboard-dist/api/598.index.js.map +1 -0
- package/dashboard-dist/api/62.index.js +4151 -0
- package/dashboard-dist/api/62.index.js.map +1 -0
- package/dashboard-dist/api/67.index.js +23383 -0
- package/dashboard-dist/api/67.index.js.map +1 -0
- package/dashboard-dist/api/678.index.js +2734 -0
- package/dashboard-dist/api/678.index.js.map +1 -0
- package/dashboard-dist/api/698.index.js +1896 -0
- package/dashboard-dist/api/698.index.js.map +1 -0
- package/dashboard-dist/api/720.index.js +98 -0
- package/dashboard-dist/api/720.index.js.map +1 -0
- package/dashboard-dist/api/830.index.js +95 -0
- package/dashboard-dist/api/830.index.js.map +1 -0
- package/dashboard-dist/api/831.index.js +99 -0
- package/dashboard-dist/api/831.index.js.map +1 -0
- package/dashboard-dist/api/84.index.js +64 -0
- package/dashboard-dist/api/84.index.js.map +1 -0
- package/dashboard-dist/api/900.index.js +65 -0
- package/dashboard-dist/api/900.index.js.map +1 -0
- package/dashboard-dist/api/917.index.js +88 -0
- package/dashboard-dist/api/917.index.js.map +1 -0
- package/dashboard-dist/api/948.index.js +64 -0
- package/dashboard-dist/api/948.index.js.map +1 -0
- package/dashboard-dist/api/953.index.js +67 -0
- package/dashboard-dist/api/953.index.js.map +1 -0
- package/dashboard-dist/api/975.index.js +374 -0
- package/dashboard-dist/api/975.index.js.map +1 -0
- package/dashboard-dist/api/drizzle/sqlite/0000_short_captain_stacy.sql +70 -0
- package/dashboard-dist/api/drizzle/sqlite/0001_closed_magus.sql +10 -0
- package/dashboard-dist/api/drizzle/sqlite/0002_agent_capability_observation.sql +38 -0
- package/dashboard-dist/api/drizzle/sqlite/0003_auth_magic_link.sql +28 -0
- package/dashboard-dist/api/drizzle/sqlite/0004_static_scan_fields.sql +8 -0
- package/dashboard-dist/api/drizzle/sqlite/0005_gateway_activity.sql +24 -0
- package/dashboard-dist/api/drizzle/sqlite/0006_sour_marauders.sql +41 -0
- package/dashboard-dist/api/drizzle/sqlite/meta/0000_snapshot.json +460 -0
- package/dashboard-dist/api/drizzle/sqlite/meta/0001_snapshot.json +536 -0
- package/dashboard-dist/api/drizzle/sqlite/meta/0006_snapshot.json +1249 -0
- package/dashboard-dist/api/drizzle/sqlite/meta/_journal.json +55 -0
- package/dashboard-dist/api/index.js +27340 -0
- package/dashboard-dist/api/index.js.map +1 -0
- package/dashboard-dist/api/package.json +16 -0
- package/dashboard-dist/api/sourcemap-register.cjs +1 -0
- package/dashboard-dist/web/assets/index-CqWIeBTD.js +158 -0
- package/dashboard-dist/web/assets/index-Dw7--9q4.css +1 -0
- package/dashboard-dist/web/changeway-logo.png +0 -0
- package/dashboard-dist/web/favicon.svg +29 -0
- package/dashboard-dist/web/index.html +14 -0
- package/dashboard-dist/web/logo.svg +16 -0
- package/dist/agent/auth.d.ts +37 -0
- package/dist/agent/auth.d.ts.map +1 -0
- package/dist/agent/auth.js +151 -0
- package/dist/agent/auth.js.map +1 -0
- package/dist/agent/behavior-detector.d.ts +150 -0
- package/dist/agent/behavior-detector.d.ts.map +1 -0
- package/dist/agent/behavior-detector.js +573 -0
- package/dist/agent/behavior-detector.js.map +1 -0
- package/dist/agent/business-reporter.d.ts +114 -0
- package/dist/agent/business-reporter.d.ts.map +1 -0
- package/dist/agent/business-reporter.js +359 -0
- package/dist/agent/business-reporter.js.map +1 -0
- package/dist/agent/config-sync.d.ts +70 -0
- package/dist/agent/config-sync.d.ts.map +1 -0
- package/dist/agent/config-sync.js +133 -0
- package/dist/agent/config-sync.js.map +1 -0
- package/dist/agent/config.d.ts +97 -0
- package/dist/agent/config.d.ts.map +1 -0
- package/dist/agent/config.js +359 -0
- package/dist/agent/config.js.map +1 -0
- package/dist/agent/content-injection-scanner.d.ts +35 -0
- package/dist/agent/content-injection-scanner.d.ts.map +1 -0
- package/dist/agent/content-injection-scanner.js +270 -0
- package/dist/agent/content-injection-scanner.js.map +1 -0
- package/dist/agent/engine-log-writer.d.ts +6 -0
- package/dist/agent/engine-log-writer.d.ts.map +1 -0
- package/dist/agent/engine-log-writer.js +18 -0
- package/dist/agent/engine-log-writer.js.map +1 -0
- package/dist/agent/env.d.ts +19 -0
- package/dist/agent/env.d.ts.map +1 -0
- package/dist/agent/env.js +43 -0
- package/dist/agent/env.js.map +1 -0
- package/dist/agent/event-reporter.d.ts +87 -0
- package/dist/agent/event-reporter.d.ts.map +1 -0
- package/dist/agent/event-reporter.js +315 -0
- package/dist/agent/event-reporter.js.map +1 -0
- package/dist/agent/file-watcher.d.ts +50 -0
- package/dist/agent/file-watcher.d.ts.map +1 -0
- package/dist/agent/file-watcher.js +135 -0
- package/dist/agent/file-watcher.js.map +1 -0
- package/dist/agent/fs-utils.d.ts +22 -0
- package/dist/agent/fs-utils.d.ts.map +1 -0
- package/dist/agent/fs-utils.js +41 -0
- package/dist/agent/fs-utils.js.map +1 -0
- package/dist/agent/gateway-manager.d.ts +59 -0
- package/dist/agent/gateway-manager.d.ts.map +1 -0
- package/dist/agent/gateway-manager.js +583 -0
- package/dist/agent/gateway-manager.js.map +1 -0
- package/dist/agent/hook-types.d.ts +276 -0
- package/dist/agent/hook-types.d.ts.map +1 -0
- package/dist/agent/hook-types.js +51 -0
- package/dist/agent/hook-types.js.map +1 -0
- package/dist/agent/index.d.ts +8 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +8 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/prompt-gate.d.ts +13 -0
- package/dist/agent/prompt-gate.d.ts.map +1 -0
- package/dist/agent/prompt-gate.js +28 -0
- package/dist/agent/prompt-gate.js.map +1 -0
- package/dist/agent/prompt-input.d.ts +9 -0
- package/dist/agent/prompt-input.d.ts.map +1 -0
- package/dist/agent/prompt-input.js +158 -0
- package/dist/agent/prompt-input.js.map +1 -0
- package/dist/agent/prompt-output.d.ts +4 -0
- package/dist/agent/prompt-output.d.ts.map +1 -0
- package/dist/agent/prompt-output.js +19 -0
- package/dist/agent/prompt-output.js.map +1 -0
- package/dist/agent/runner.d.ts +23 -0
- package/dist/agent/runner.d.ts.map +1 -0
- package/dist/agent/runner.js +154 -0
- package/dist/agent/runner.js.map +1 -0
- package/dist/agent/sanitizer.d.ts +10 -0
- package/dist/agent/sanitizer.d.ts.map +1 -0
- package/dist/agent/sanitizer.js +175 -0
- package/dist/agent/sanitizer.js.map +1 -0
- package/dist/agent/scan-activity.d.ts +18 -0
- package/dist/agent/scan-activity.d.ts.map +1 -0
- package/dist/agent/scan-activity.js +32 -0
- package/dist/agent/scan-activity.js.map +1 -0
- package/dist/agent/types.d.ts +177 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +5 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/agent/workspace-scanner.d.ts +35 -0
- package/dist/agent/workspace-scanner.d.ts.map +1 -0
- package/dist/agent/workspace-scanner.js +137 -0
- package/dist/agent/workspace-scanner.js.map +1 -0
- package/dist/dashboard-launcher.d.ts +52 -0
- package/dist/dashboard-launcher.d.ts.map +1 -0
- package/dist/dashboard-launcher.js +363 -0
- package/dist/dashboard-launcher.js.map +1 -0
- package/dist/gateway/activity.d.ts +52 -0
- package/dist/gateway/activity.d.ts.map +1 -0
- package/dist/gateway/activity.js +111 -0
- package/dist/gateway/activity.js.map +1 -0
- package/dist/gateway/config.d.ts +50 -0
- package/dist/gateway/config.d.ts.map +1 -0
- package/dist/gateway/config.js +200 -0
- package/dist/gateway/config.js.map +1 -0
- package/dist/gateway/gateway/activity.d.ts +52 -0
- package/dist/gateway/gateway/activity.d.ts.map +1 -0
- package/dist/gateway/gateway/activity.js +111 -0
- package/dist/gateway/gateway/activity.js.map +1 -0
- package/dist/gateway/gateway/config.d.ts +50 -0
- package/dist/gateway/gateway/config.d.ts.map +1 -0
- package/dist/gateway/gateway/config.js +200 -0
- package/dist/gateway/gateway/config.js.map +1 -0
- package/dist/gateway/gateway/handlers/anthropic.d.ts +12 -0
- package/dist/gateway/gateway/handlers/anthropic.d.ts.map +1 -0
- package/dist/gateway/gateway/handlers/anthropic.js +254 -0
- package/dist/gateway/gateway/handlers/anthropic.js.map +1 -0
- package/dist/gateway/gateway/handlers/gemini.d.ts +12 -0
- package/dist/gateway/gateway/handlers/gemini.d.ts.map +1 -0
- package/dist/gateway/gateway/handlers/gemini.js +101 -0
- package/dist/gateway/gateway/handlers/gemini.js.map +1 -0
- package/dist/gateway/gateway/handlers/models.d.ts +4 -0
- package/dist/gateway/gateway/handlers/models.d.ts.map +1 -0
- package/dist/gateway/gateway/handlers/models.js +36 -0
- package/dist/gateway/gateway/handlers/models.js.map +1 -0
- package/dist/gateway/gateway/handlers/openai.d.ts +16 -0
- package/dist/gateway/gateway/handlers/openai.d.ts.map +1 -0
- package/dist/gateway/gateway/handlers/openai.js +254 -0
- package/dist/gateway/gateway/handlers/openai.js.map +1 -0
- package/dist/gateway/gateway/index.d.ts +27 -0
- package/dist/gateway/gateway/index.d.ts.map +1 -0
- package/dist/gateway/gateway/index.js +293 -0
- package/dist/gateway/gateway/index.js.map +1 -0
- package/dist/gateway/gateway/mapping-store.d.ts +38 -0
- package/dist/gateway/gateway/mapping-store.d.ts.map +1 -0
- package/dist/gateway/gateway/mapping-store.js +74 -0
- package/dist/gateway/gateway/mapping-store.js.map +1 -0
- package/dist/gateway/gateway/restorer.d.ts +63 -0
- package/dist/gateway/gateway/restorer.d.ts.map +1 -0
- package/dist/gateway/gateway/restorer.js +284 -0
- package/dist/gateway/gateway/restorer.js.map +1 -0
- package/dist/gateway/gateway/sanitizer.d.ts +17 -0
- package/dist/gateway/gateway/sanitizer.d.ts.map +1 -0
- package/dist/gateway/gateway/sanitizer.js +228 -0
- package/dist/gateway/gateway/sanitizer.js.map +1 -0
- package/dist/gateway/gateway/types.d.ts +53 -0
- package/dist/gateway/gateway/types.d.ts.map +1 -0
- package/dist/gateway/gateway/types.js +5 -0
- package/dist/gateway/gateway/types.js.map +1 -0
- package/dist/gateway/handlers/anthropic.d.ts +12 -0
- package/dist/gateway/handlers/anthropic.d.ts.map +1 -0
- package/dist/gateway/handlers/anthropic.js +254 -0
- package/dist/gateway/handlers/anthropic.js.map +1 -0
- package/dist/gateway/handlers/gemini.d.ts +12 -0
- package/dist/gateway/handlers/gemini.d.ts.map +1 -0
- package/dist/gateway/handlers/gemini.js +101 -0
- package/dist/gateway/handlers/gemini.js.map +1 -0
- package/dist/gateway/handlers/models.d.ts +4 -0
- package/dist/gateway/handlers/models.d.ts.map +1 -0
- package/dist/gateway/handlers/models.js +36 -0
- package/dist/gateway/handlers/models.js.map +1 -0
- package/dist/gateway/handlers/openai.d.ts +16 -0
- package/dist/gateway/handlers/openai.d.ts.map +1 -0
- package/dist/gateway/handlers/openai.js +254 -0
- package/dist/gateway/handlers/openai.js.map +1 -0
- package/dist/gateway/index.d.ts +27 -0
- package/dist/gateway/index.d.ts.map +1 -0
- package/dist/gateway/index.js +293 -0
- package/dist/gateway/index.js.map +1 -0
- package/dist/gateway/mapping-store.d.ts +38 -0
- package/dist/gateway/mapping-store.d.ts.map +1 -0
- package/dist/gateway/mapping-store.js +74 -0
- package/dist/gateway/mapping-store.js.map +1 -0
- package/dist/gateway/restorer.d.ts +63 -0
- package/dist/gateway/restorer.d.ts.map +1 -0
- package/dist/gateway/restorer.js +284 -0
- package/dist/gateway/restorer.js.map +1 -0
- package/dist/gateway/sanitizer.d.ts +17 -0
- package/dist/gateway/sanitizer.d.ts.map +1 -0
- package/dist/gateway/sanitizer.js +228 -0
- package/dist/gateway/sanitizer.js.map +1 -0
- package/dist/gateway/types.d.ts +53 -0
- package/dist/gateway/types.d.ts.map +1 -0
- package/dist/gateway/types.js +5 -0
- package/dist/gateway/types.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2084 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/index.d.ts +5 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +5 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/store.d.ts +82 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +194 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/platform-client/index.d.ts +63 -0
- package/dist/platform-client/index.d.ts.map +1 -0
- package/dist/platform-client/index.js +294 -0
- package/dist/platform-client/index.js.map +1 -0
- package/dist/platform-client/types.d.ts +109 -0
- package/dist/platform-client/types.d.ts.map +1 -0
- package/dist/platform-client/types.js +3 -0
- package/dist/platform-client/types.js.map +1 -0
- package/gateway/activity.d.ts +52 -0
- package/gateway/activity.d.ts.map +1 -0
- package/gateway/activity.js +111 -0
- package/gateway/activity.js.map +1 -0
- package/gateway/config.d.ts +50 -0
- package/gateway/config.d.ts.map +1 -0
- package/gateway/config.js +200 -0
- package/gateway/config.js.map +1 -0
- package/gateway/handlers/anthropic.d.ts +12 -0
- package/gateway/handlers/anthropic.d.ts.map +1 -0
- package/gateway/handlers/anthropic.js +254 -0
- package/gateway/handlers/anthropic.js.map +1 -0
- package/gateway/handlers/gemini.d.ts +12 -0
- package/gateway/handlers/gemini.d.ts.map +1 -0
- package/gateway/handlers/gemini.js +101 -0
- package/gateway/handlers/gemini.js.map +1 -0
- package/gateway/handlers/models.d.ts +4 -0
- package/gateway/handlers/models.d.ts.map +1 -0
- package/gateway/handlers/models.js +36 -0
- package/gateway/handlers/models.js.map +1 -0
- package/gateway/handlers/openai.d.ts +16 -0
- package/gateway/handlers/openai.d.ts.map +1 -0
- package/gateway/handlers/openai.js +254 -0
- package/gateway/handlers/openai.js.map +1 -0
- package/gateway/index.d.ts +27 -0
- package/gateway/index.d.ts.map +1 -0
- package/gateway/index.js +293 -0
- package/gateway/index.js.map +1 -0
- package/gateway/mapping-store.d.ts +38 -0
- package/gateway/mapping-store.d.ts.map +1 -0
- package/gateway/mapping-store.js +74 -0
- package/gateway/mapping-store.js.map +1 -0
- package/gateway/restorer.d.ts +63 -0
- package/gateway/restorer.d.ts.map +1 -0
- package/gateway/restorer.js +284 -0
- package/gateway/restorer.js.map +1 -0
- package/gateway/sanitizer.d.ts +17 -0
- package/gateway/sanitizer.d.ts.map +1 -0
- package/gateway/sanitizer.js +228 -0
- package/gateway/sanitizer.js.map +1 -0
- package/gateway/types.d.ts +53 -0
- package/gateway/types.d.ts.map +1 -0
- package/gateway/types.js +5 -0
- package/gateway/types.js.map +1 -0
- package/openclaw.plugin.json +86 -0
- package/package.json +74 -0
- package/samples/Untitled +1 -0
- package/samples/clean-email.txt +20 -0
- package/samples/test-document.md +53 -0
- package/samples/test-email-popup.txt +44 -0
- package/samples/test-email.txt +32 -0
- package/samples/test-webpage.html +51 -0
- package/scripts/enterprise-enroll.sh +89 -0
- package/scripts/enterprise-unenroll.sh +75 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* OpenGuardrails AI Security Gateway
|
|
4
|
+
*
|
|
5
|
+
* Local HTTP proxy that intercepts LLM API calls, sanitizes sensitive data
|
|
6
|
+
* before sending to providers, and restores it in responses.
|
|
7
|
+
* Supports Anthropic, OpenAI, and Gemini protocols.
|
|
8
|
+
*/
|
|
9
|
+
import { createServer } from "node:http";
|
|
10
|
+
import { loadConfig, validateConfig, findBackendByApiKey, findDefaultBackend, findBackendByPathPrefix } from "./config.js";
|
|
11
|
+
import { handleAnthropicRequest } from "./handlers/anthropic.js";
|
|
12
|
+
import { handleOpenAIRequest } from "./handlers/openai.js";
|
|
13
|
+
import { handleGeminiRequest } from "./handlers/gemini.js";
|
|
14
|
+
import { handleModelsRequest } from "./handlers/models.js";
|
|
15
|
+
let config;
|
|
16
|
+
let currentServer = null;
|
|
17
|
+
/**
|
|
18
|
+
* Extract API key from request headers
|
|
19
|
+
*/
|
|
20
|
+
function extractApiKey(req) {
|
|
21
|
+
// Try x-api-key header (Anthropic style)
|
|
22
|
+
const xApiKey = req.headers["x-api-key"];
|
|
23
|
+
if (xApiKey && typeof xApiKey === "string") {
|
|
24
|
+
return xApiKey;
|
|
25
|
+
}
|
|
26
|
+
// Try Authorization: Bearer (OpenAI style)
|
|
27
|
+
const auth = req.headers["authorization"];
|
|
28
|
+
if (auth && typeof auth === "string" && auth.startsWith("Bearer ")) {
|
|
29
|
+
return auth.slice(7);
|
|
30
|
+
}
|
|
31
|
+
// Try x-goog-api-key (Gemini style)
|
|
32
|
+
const googKey = req.headers["x-goog-api-key"];
|
|
33
|
+
if (googKey && typeof googKey === "string") {
|
|
34
|
+
return googKey;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolve backend for a request based on path prefix, API key, or defaults
|
|
40
|
+
* Priority: pathPrefix > apiKey > defaultBackend
|
|
41
|
+
*/
|
|
42
|
+
function resolveBackend(req, apiType) {
|
|
43
|
+
const url = req.url || "";
|
|
44
|
+
// 1. Try to find backend by path prefix (most specific)
|
|
45
|
+
const byPath = findBackendByPathPrefix(url, config);
|
|
46
|
+
if (byPath) {
|
|
47
|
+
return byPath;
|
|
48
|
+
}
|
|
49
|
+
// 2. Try to find backend by API key
|
|
50
|
+
const apiKey = extractApiKey(req);
|
|
51
|
+
if (apiKey) {
|
|
52
|
+
const byKey = findBackendByApiKey(apiKey, config);
|
|
53
|
+
if (byKey) {
|
|
54
|
+
return byKey;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// 3. Fall back to default backend for the API type
|
|
58
|
+
return findDefaultBackend(apiType, config);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Main request handler
|
|
62
|
+
*/
|
|
63
|
+
async function handleRequest(req, res) {
|
|
64
|
+
const { method, url } = req;
|
|
65
|
+
// Log request (skip health checks to reduce noise)
|
|
66
|
+
if (url !== "/health") {
|
|
67
|
+
console.log(`[ai-security-gateway] ${method} ${url}`);
|
|
68
|
+
}
|
|
69
|
+
// CORS headers (for browser-based clients)
|
|
70
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
71
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
72
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, x-api-key, anthropic-version");
|
|
73
|
+
// Handle OPTIONS for CORS preflight
|
|
74
|
+
if (method === "OPTIONS") {
|
|
75
|
+
res.writeHead(204);
|
|
76
|
+
res.end();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// Health check (allow GET)
|
|
80
|
+
if (url === "/health") {
|
|
81
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
82
|
+
res.end(JSON.stringify({ status: "ok", version: "1.0.0" }));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Handle GET /v1/models — proxy to configured backend's models endpoint
|
|
86
|
+
if (method === "GET" && url === "/v1/models") {
|
|
87
|
+
await handleModelsRequest(res, config);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// Only allow POST for API endpoints
|
|
91
|
+
if (method !== "POST") {
|
|
92
|
+
res.writeHead(405, { "Content-Type": "application/json" });
|
|
93
|
+
res.end(JSON.stringify({ error: "Method not allowed" }));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// Route to appropriate handler based on path suffix
|
|
97
|
+
// This allows flexible path prefixes (e.g., /v1/coding/chat/completions)
|
|
98
|
+
try {
|
|
99
|
+
if (url?.endsWith("/messages")) {
|
|
100
|
+
// Anthropic Messages API (matches /v1/messages, /v1/xxx/messages, etc.)
|
|
101
|
+
const resolved = resolveBackend(req, "anthropic");
|
|
102
|
+
if (!resolved) {
|
|
103
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
104
|
+
res.end(JSON.stringify({ error: "No Anthropic-compatible backend configured" }));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
await handleAnthropicRequest(req, res, resolved.backend);
|
|
108
|
+
}
|
|
109
|
+
else if (url?.endsWith("/chat/completions")) {
|
|
110
|
+
// OpenAI/OpenRouter Chat Completions API
|
|
111
|
+
// Try to extract backend name from URL: /backend/{name}/chat/completions
|
|
112
|
+
const backendMatch = url.match(/^\/backend\/([^/]+)\//);
|
|
113
|
+
let resolved = null;
|
|
114
|
+
if (backendMatch) {
|
|
115
|
+
const backendName = backendMatch[1];
|
|
116
|
+
const backend = config.backends[backendName];
|
|
117
|
+
if (backend) {
|
|
118
|
+
resolved = { name: backendName, backend };
|
|
119
|
+
console.log(`[ai-security-gateway] Backend from URL: ${backendName}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Fallback to path prefix or default
|
|
123
|
+
if (!resolved) {
|
|
124
|
+
resolved = resolveBackend(req, "openai");
|
|
125
|
+
console.log(`[ai-security-gateway] Resolved backend: ${resolved?.name}`);
|
|
126
|
+
}
|
|
127
|
+
// Check explicit routing config
|
|
128
|
+
const explicitBackendName = config.routing?.["/v1/chat/completions"];
|
|
129
|
+
const backend = explicitBackendName
|
|
130
|
+
? config.backends[explicitBackendName]
|
|
131
|
+
: resolved?.backend;
|
|
132
|
+
if (!backend) {
|
|
133
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
134
|
+
res.end(JSON.stringify({ error: "No OpenAI-compatible backend configured" }));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const extraHeaders = {};
|
|
138
|
+
if (backend.referer) {
|
|
139
|
+
extraHeaders["HTTP-Referer"] = backend.referer;
|
|
140
|
+
}
|
|
141
|
+
if (backend.title) {
|
|
142
|
+
extraHeaders["X-Title"] = backend.title;
|
|
143
|
+
}
|
|
144
|
+
await handleOpenAIRequest(req, res, backend, extraHeaders);
|
|
145
|
+
}
|
|
146
|
+
else if (url?.match(/\/models\/(.+):generateContent$/)) {
|
|
147
|
+
// Gemini API (matches any path ending with /models/{model}:generateContent)
|
|
148
|
+
const match = url.match(/\/models\/(.+):generateContent$/);
|
|
149
|
+
const modelName = match?.[1];
|
|
150
|
+
if (modelName) {
|
|
151
|
+
const resolved = resolveBackend(req, "gemini");
|
|
152
|
+
if (!resolved) {
|
|
153
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
154
|
+
res.end(JSON.stringify({ error: "No Gemini backend configured" }));
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
await handleGeminiRequest(req, res, resolved.backend, modelName);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
161
|
+
res.end(JSON.stringify({ error: "Model name required" }));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
// Unknown endpoint
|
|
166
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
167
|
+
res.end(JSON.stringify({ error: "Not found", url }));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.error("[ai-security-gateway] Request handler error:", error);
|
|
172
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
173
|
+
res.end(JSON.stringify({
|
|
174
|
+
error: "Internal server error",
|
|
175
|
+
message: error instanceof Error ? error.message : String(error),
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Stop the gateway server
|
|
181
|
+
*/
|
|
182
|
+
export function stopGateway() {
|
|
183
|
+
return new Promise((resolve) => {
|
|
184
|
+
if (currentServer) {
|
|
185
|
+
currentServer.close(() => {
|
|
186
|
+
currentServer = null;
|
|
187
|
+
console.log("[ai-security-gateway] Server stopped");
|
|
188
|
+
resolve();
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
resolve();
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Check if gateway is running
|
|
198
|
+
*/
|
|
199
|
+
export function isGatewayServerRunning() {
|
|
200
|
+
return currentServer !== null;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Start gateway server
|
|
204
|
+
* @param configPath - Path to config file
|
|
205
|
+
* @param embedded - If true, don't call process.exit on errors (for in-process use)
|
|
206
|
+
*/
|
|
207
|
+
export function startGateway(configPath, embedded = false) {
|
|
208
|
+
// Stop existing server if running (same process)
|
|
209
|
+
if (currentServer) {
|
|
210
|
+
if (!embedded)
|
|
211
|
+
console.log("[ai-security-gateway] Stopping existing server for restart...");
|
|
212
|
+
currentServer.close();
|
|
213
|
+
currentServer = null;
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
// Load and validate configuration
|
|
217
|
+
config = loadConfig(configPath);
|
|
218
|
+
validateConfig(config);
|
|
219
|
+
if (!embedded) {
|
|
220
|
+
console.log("[ai-security-gateway] Configuration loaded:");
|
|
221
|
+
console.log(` Port: ${config.port}`);
|
|
222
|
+
console.log(` Backends: ${Object.keys(config.backends).join(", ") || "(none)"}`);
|
|
223
|
+
}
|
|
224
|
+
// Create HTTP server
|
|
225
|
+
const server = createServer(handleRequest);
|
|
226
|
+
currentServer = server;
|
|
227
|
+
// Handle server errors
|
|
228
|
+
server.on("error", (err) => {
|
|
229
|
+
console.error("[ai-security-gateway] Server error:", err);
|
|
230
|
+
currentServer = null;
|
|
231
|
+
if (!embedded) {
|
|
232
|
+
process.exit(1);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
// Embedded mode: never crash host process on gateway bind/runtime errors.
|
|
236
|
+
// The plugin can continue running and other features remain available.
|
|
237
|
+
console.warn("[ai-security-gateway] Embedded mode: gateway error ignored");
|
|
238
|
+
});
|
|
239
|
+
// Start listening
|
|
240
|
+
server.listen(config.port, "127.0.0.1", () => {
|
|
241
|
+
if (!embedded) {
|
|
242
|
+
console.log(`[ai-security-gateway] Server listening on http://127.0.0.1:${config.port}`);
|
|
243
|
+
console.log("[ai-security-gateway] Ready to proxy requests");
|
|
244
|
+
console.log("");
|
|
245
|
+
console.log("Endpoints:");
|
|
246
|
+
console.log(` POST http://127.0.0.1:${config.port}/v1/messages - Anthropic`);
|
|
247
|
+
console.log(` POST http://127.0.0.1:${config.port}/v1/chat/completions - OpenAI / OpenRouter`);
|
|
248
|
+
console.log(` POST http://127.0.0.1:${config.port}/v1/models/:model:generateContent - Gemini`);
|
|
249
|
+
console.log(` GET http://127.0.0.1:${config.port}/v1/models - List models (OpenAI / OpenRouter)`);
|
|
250
|
+
console.log(` GET http://127.0.0.1:${config.port}/health - Health check`);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
// In embedded mode, don't let the server prevent process exit
|
|
254
|
+
if (embedded) {
|
|
255
|
+
server.unref();
|
|
256
|
+
}
|
|
257
|
+
// Only register shutdown handlers if not embedded
|
|
258
|
+
if (!embedded) {
|
|
259
|
+
process.on("SIGINT", () => {
|
|
260
|
+
console.log("\n[ai-security-gateway] Shutting down...");
|
|
261
|
+
server.close(() => {
|
|
262
|
+
console.log("[ai-security-gateway] Server stopped");
|
|
263
|
+
process.exit(0);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
process.on("SIGTERM", () => {
|
|
267
|
+
console.log("\n[ai-security-gateway] Shutting down...");
|
|
268
|
+
server.close(() => {
|
|
269
|
+
console.log("[ai-security-gateway] Server stopped");
|
|
270
|
+
process.exit(0);
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
console.error("[ai-security-gateway] Failed to start:", error);
|
|
277
|
+
currentServer = null;
|
|
278
|
+
if (!embedded) {
|
|
279
|
+
process.exit(1);
|
|
280
|
+
}
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Re-export for programmatic use
|
|
285
|
+
export { sanitize, sanitizeMessages } from "./sanitizer.js";
|
|
286
|
+
export { restore, restoreJSON, restoreSSELine } from "./restorer.js";
|
|
287
|
+
export { addActivityListener, removeActivityListener, clearActivityListeners, } from "./activity.js";
|
|
288
|
+
// Start if run directly
|
|
289
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
290
|
+
const configPath = process.argv[2];
|
|
291
|
+
startGateway(configPath);
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent Mapping Store
|
|
3
|
+
*
|
|
4
|
+
* Maintains a global mapping between original values and placeholders.
|
|
5
|
+
* This ensures the same sensitive data always gets the same placeholder,
|
|
6
|
+
* allowing restoration to work across multiple requests in a conversation.
|
|
7
|
+
*/
|
|
8
|
+
import type { MappingTable } from "./types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Get or create a placeholder for an original value
|
|
11
|
+
* If the value was seen before, returns the same placeholder
|
|
12
|
+
*/
|
|
13
|
+
export declare function getOrCreatePlaceholder(originalValue: string, entityType: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Get the original value for a placeholder
|
|
16
|
+
*/
|
|
17
|
+
export declare function getOriginalValue(placeholder: string): string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Check if a string is a known placeholder
|
|
20
|
+
*/
|
|
21
|
+
export declare function isKnownPlaceholder(text: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Get all placeholders and their original values as a MappingTable
|
|
24
|
+
* This is used for restoration
|
|
25
|
+
*/
|
|
26
|
+
export declare function getGlobalMappingTable(): MappingTable;
|
|
27
|
+
/**
|
|
28
|
+
* Get current statistics
|
|
29
|
+
*/
|
|
30
|
+
export declare function getMappingStats(): {
|
|
31
|
+
totalMappings: number;
|
|
32
|
+
byType: Record<string, number>;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Clear all mappings (for testing or reset)
|
|
36
|
+
*/
|
|
37
|
+
export declare function clearMappings(): void;
|
|
38
|
+
//# sourceMappingURL=mapping-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping-store.d.ts","sourceRoot":"","sources":["../src/mapping-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAW/C;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAmBxF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAExE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,YAAY,CAEpD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAS3F;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAIpC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent Mapping Store
|
|
3
|
+
*
|
|
4
|
+
* Maintains a global mapping between original values and placeholders.
|
|
5
|
+
* This ensures the same sensitive data always gets the same placeholder,
|
|
6
|
+
* allowing restoration to work across multiple requests in a conversation.
|
|
7
|
+
*/
|
|
8
|
+
// Global mapping: original value -> placeholder
|
|
9
|
+
const originalToPlaceholder = new Map();
|
|
10
|
+
// Global mapping: placeholder -> original value (reverse lookup for restoration)
|
|
11
|
+
const placeholderToOriginal = new Map();
|
|
12
|
+
// Global counter per entity type
|
|
13
|
+
const typeCounters = new Map();
|
|
14
|
+
/**
|
|
15
|
+
* Get or create a placeholder for an original value
|
|
16
|
+
* If the value was seen before, returns the same placeholder
|
|
17
|
+
*/
|
|
18
|
+
export function getOrCreatePlaceholder(originalValue, entityType) {
|
|
19
|
+
// Check if we already have a placeholder for this value
|
|
20
|
+
const existing = originalToPlaceholder.get(originalValue);
|
|
21
|
+
if (existing) {
|
|
22
|
+
return existing;
|
|
23
|
+
}
|
|
24
|
+
// Create a new placeholder
|
|
25
|
+
const counter = (typeCounters.get(entityType) ?? 0) + 1;
|
|
26
|
+
typeCounters.set(entityType, counter);
|
|
27
|
+
const paddedId = counter.toString().padStart(8, "0");
|
|
28
|
+
const placeholder = `__PII_${entityType}_${paddedId}__`;
|
|
29
|
+
// Store both directions
|
|
30
|
+
originalToPlaceholder.set(originalValue, placeholder);
|
|
31
|
+
placeholderToOriginal.set(placeholder, originalValue);
|
|
32
|
+
return placeholder;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get the original value for a placeholder
|
|
36
|
+
*/
|
|
37
|
+
export function getOriginalValue(placeholder) {
|
|
38
|
+
return placeholderToOriginal.get(placeholder);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Check if a string is a known placeholder
|
|
42
|
+
*/
|
|
43
|
+
export function isKnownPlaceholder(text) {
|
|
44
|
+
return placeholderToOriginal.has(text);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get all placeholders and their original values as a MappingTable
|
|
48
|
+
* This is used for restoration
|
|
49
|
+
*/
|
|
50
|
+
export function getGlobalMappingTable() {
|
|
51
|
+
return new Map(placeholderToOriginal);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get current statistics
|
|
55
|
+
*/
|
|
56
|
+
export function getMappingStats() {
|
|
57
|
+
const byType = {};
|
|
58
|
+
for (const [type, count] of typeCounters.entries()) {
|
|
59
|
+
byType[type] = count;
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
totalMappings: placeholderToOriginal.size,
|
|
63
|
+
byType,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Clear all mappings (for testing or reset)
|
|
68
|
+
*/
|
|
69
|
+
export function clearMappings() {
|
|
70
|
+
originalToPlaceholder.clear();
|
|
71
|
+
placeholderToOriginal.clear();
|
|
72
|
+
typeCounters.clear();
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=mapping-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping-store.js","sourceRoot":"","sources":["../src/mapping-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,gDAAgD;AAChD,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAkB,CAAC;AAExD,iFAAiF;AACjF,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAkB,CAAC;AAExD,iCAAiC;AACjC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;AAE/C;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,aAAqB,EAAE,UAAkB;IAC9E,wDAAwD;IACxD,MAAM,QAAQ,GAAG,qBAAqB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC1D,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,2BAA2B;IAC3B,MAAM,OAAO,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IACxD,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAEtC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,SAAS,UAAU,IAAI,QAAQ,IAAI,CAAC;IAExD,wBAAwB;IACxB,qBAAqB,CAAC,GAAG,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;IACtD,qBAAqB,CAAC,GAAG,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAEtD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,OAAO,qBAAqB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AAChD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,qBAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO,IAAI,GAAG,CAAC,qBAAqB,CAAC,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC;QACnD,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;IACvB,CAAC;IACD,OAAO;QACL,aAAa,EAAE,qBAAqB,CAAC,IAAI;QACzC,MAAM;KACP,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,qBAAqB,CAAC,KAAK,EAAE,CAAC;IAC9B,qBAAqB,CAAC,KAAK,EAAE,CAAC;IAC9B,YAAY,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Security Gateway - Content Restorer
|
|
3
|
+
*
|
|
4
|
+
* Restores sanitized placeholders back to original values.
|
|
5
|
+
* Handles LLM corruption patterns (missing underscores, case variations).
|
|
6
|
+
*
|
|
7
|
+
* Placeholder format: __PII_<ENTITY_TYPE>_<SERIAL_ID>__
|
|
8
|
+
*/
|
|
9
|
+
import type { MappingTable } from "./types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Restore any content (object, array, string) using the mapping table
|
|
12
|
+
*/
|
|
13
|
+
export declare function restore(content: unknown, mappingTable: MappingTable): unknown;
|
|
14
|
+
/**
|
|
15
|
+
* Restore a JSON string
|
|
16
|
+
* Useful for SSE streaming where each chunk is a JSON string
|
|
17
|
+
*/
|
|
18
|
+
export declare function restoreJSON(jsonString: string, mappingTable: MappingTable): string;
|
|
19
|
+
/**
|
|
20
|
+
* Restore SSE data line (for streaming responses)
|
|
21
|
+
* Format: "data: {...}\n"
|
|
22
|
+
*/
|
|
23
|
+
export declare function restoreSSELine(line: string, mappingTable: MappingTable): string;
|
|
24
|
+
/**
|
|
25
|
+
* StreamRestorer - Stateful streaming restoration with smart buffering
|
|
26
|
+
*
|
|
27
|
+
* Only buffers when `__` is detected (potential placeholder start).
|
|
28
|
+
* Otherwise streams through immediately for best UX.
|
|
29
|
+
*/
|
|
30
|
+
export declare class StreamRestorer {
|
|
31
|
+
private buffer;
|
|
32
|
+
private mappingTable;
|
|
33
|
+
constructor(mappingTable: MappingTable);
|
|
34
|
+
/**
|
|
35
|
+
* Process incoming text chunk
|
|
36
|
+
* Returns text that can be safely output (already restored or confirmed non-placeholder)
|
|
37
|
+
*/
|
|
38
|
+
process(chunk: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Flush what we can safely output
|
|
41
|
+
* Keeps potential incomplete placeholders in buffer
|
|
42
|
+
*/
|
|
43
|
+
private flush;
|
|
44
|
+
/**
|
|
45
|
+
* Check if text could be the start of a placeholder
|
|
46
|
+
* Returns true if it matches the beginning of __PII_<TYPE>_<ID>__
|
|
47
|
+
*/
|
|
48
|
+
private couldBePlaceholder;
|
|
49
|
+
/**
|
|
50
|
+
* Finalize stream - flush any remaining buffer
|
|
51
|
+
* Call this at end of stream to ensure nothing is lost
|
|
52
|
+
*/
|
|
53
|
+
finalize(): string;
|
|
54
|
+
/**
|
|
55
|
+
* Check if there's pending data in buffer
|
|
56
|
+
*/
|
|
57
|
+
hasPendingData(): boolean;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Create a streaming restorer for a response
|
|
61
|
+
*/
|
|
62
|
+
export declare function createStreamRestorer(mappingTable: MappingTable): StreamRestorer;
|
|
63
|
+
//# sourceMappingURL=restorer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restorer.d.ts","sourceRoot":"","sources":["../src/restorer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAyG/C;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,YAAY,GAAG,OAAO,CAG7E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,GAAG,MAAM,CAWlF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,GAAG,MAAM,CAe/E;AAYD;;;;;GAKG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,YAAY,CAAe;gBAEvB,YAAY,EAAE,YAAY;IAItC;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAU9B;;;OAGG;IACH,OAAO,CAAC,KAAK;IAqEb;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAoB1B;;;OAGG;IACH,QAAQ,IAAI,MAAM;IASlB;;OAEG;IACH,cAAc,IAAI,OAAO;CAG1B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,YAAY,GAAG,cAAc,CAE/E"}
|