@aion0/bastion 0.1.7
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 +183 -0
- package/README.zh.md +468 -0
- package/config/default.yaml +73 -0
- package/dist/cli/commands/config.d.ts +3 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +31 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/env.d.ts +3 -0
- package/dist/cli/commands/env.d.ts.map +1 -0
- package/dist/cli/commands/env.js +83 -0
- package/dist/cli/commands/env.js.map +1 -0
- package/dist/cli/commands/health.d.ts +3 -0
- package/dist/cli/commands/health.d.ts.map +1 -0
- package/dist/cli/commands/health.js +45 -0
- package/dist/cli/commands/health.js.map +1 -0
- package/dist/cli/commands/openclaw.d.ts +3 -0
- package/dist/cli/commands/openclaw.d.ts.map +1 -0
- package/dist/cli/commands/openclaw.js +1062 -0
- package/dist/cli/commands/openclaw.js.map +1 -0
- package/dist/cli/commands/proxy.d.ts +8 -0
- package/dist/cli/commands/proxy.d.ts.map +1 -0
- package/dist/cli/commands/proxy.js +433 -0
- package/dist/cli/commands/proxy.js.map +1 -0
- package/dist/cli/commands/start.d.ts +3 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +62 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/stats.d.ts +3 -0
- package/dist/cli/commands/stats.d.ts.map +1 -0
- package/dist/cli/commands/stats.js +32 -0
- package/dist/cli/commands/stats.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +3 -0
- package/dist/cli/commands/stop.d.ts.map +1 -0
- package/dist/cli/commands/stop.js +28 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/commands/token.d.ts +3 -0
- package/dist/cli/commands/token.d.ts.map +1 -0
- package/dist/cli/commands/token.js +32 -0
- package/dist/cli/commands/token.js.map +1 -0
- package/dist/cli/commands/trust-ca.d.ts +3 -0
- package/dist/cli/commands/trust-ca.d.ts.map +1 -0
- package/dist/cli/commands/trust-ca.js +44 -0
- package/dist/cli/commands/trust-ca.js.map +1 -0
- package/dist/cli/commands/wrap.d.ts +3 -0
- package/dist/cli/commands/wrap.d.ts.map +1 -0
- package/dist/cli/commands/wrap.js +70 -0
- package/dist/cli/commands/wrap.js.map +1 -0
- package/dist/cli/daemon.d.ts +11 -0
- package/dist/cli/daemon.d.ts.map +1 -0
- package/dist/cli/daemon.js +82 -0
- package/dist/cli/daemon.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +35 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +60 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/manager.d.ts +12 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +73 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/paths.d.ts +10 -0
- package/dist/config/paths.d.ts.map +1 -0
- package/dist/config/paths.js +16 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/schema.d.ts +85 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +3 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/dashboard/api-routes.d.ts +6 -0
- package/dist/dashboard/api-routes.d.ts.map +1 -0
- package/dist/dashboard/api-routes.js +671 -0
- package/dist/dashboard/api-routes.js.map +1 -0
- package/dist/dashboard/api.d.ts +4 -0
- package/dist/dashboard/api.d.ts.map +1 -0
- package/dist/dashboard/api.js +25 -0
- package/dist/dashboard/api.js.map +1 -0
- package/dist/dashboard/page.d.ts +3 -0
- package/dist/dashboard/page.d.ts.map +1 -0
- package/dist/dashboard/page.js +1622 -0
- package/dist/dashboard/page.js.map +1 -0
- package/dist/dlp/actions.d.ts +13 -0
- package/dist/dlp/actions.d.ts.map +1 -0
- package/dist/dlp/actions.js +3 -0
- package/dist/dlp/actions.js.map +1 -0
- package/dist/dlp/ai-validator.d.ts +28 -0
- package/dist/dlp/ai-validator.d.ts.map +1 -0
- package/dist/dlp/ai-validator.js +214 -0
- package/dist/dlp/ai-validator.js.map +1 -0
- package/dist/dlp/engine.d.ts +34 -0
- package/dist/dlp/engine.d.ts.map +1 -0
- package/dist/dlp/engine.js +342 -0
- package/dist/dlp/engine.js.map +1 -0
- package/dist/dlp/entropy.d.ts +22 -0
- package/dist/dlp/entropy.d.ts.map +1 -0
- package/dist/dlp/entropy.js +43 -0
- package/dist/dlp/entropy.js.map +1 -0
- package/dist/dlp/message-cache.d.ts +45 -0
- package/dist/dlp/message-cache.d.ts.map +1 -0
- package/dist/dlp/message-cache.js +251 -0
- package/dist/dlp/message-cache.js.map +1 -0
- package/dist/dlp/patterns/context-aware.d.ts +4 -0
- package/dist/dlp/patterns/context-aware.d.ts.map +1 -0
- package/dist/dlp/patterns/context-aware.js +45 -0
- package/dist/dlp/patterns/context-aware.js.map +1 -0
- package/dist/dlp/patterns/high-confidence.d.ts +4 -0
- package/dist/dlp/patterns/high-confidence.d.ts.map +1 -0
- package/dist/dlp/patterns/high-confidence.js +140 -0
- package/dist/dlp/patterns/high-confidence.js.map +1 -0
- package/dist/dlp/patterns/prompt-injection.d.ts +4 -0
- package/dist/dlp/patterns/prompt-injection.d.ts.map +1 -0
- package/dist/dlp/patterns/prompt-injection.js +244 -0
- package/dist/dlp/patterns/prompt-injection.js.map +1 -0
- package/dist/dlp/patterns/validated.d.ts +4 -0
- package/dist/dlp/patterns/validated.d.ts.map +1 -0
- package/dist/dlp/patterns/validated.js +21 -0
- package/dist/dlp/patterns/validated.js.map +1 -0
- package/dist/dlp/remote-sync.d.ts +47 -0
- package/dist/dlp/remote-sync.d.ts.map +1 -0
- package/dist/dlp/remote-sync.js +252 -0
- package/dist/dlp/remote-sync.js.map +1 -0
- package/dist/dlp/semantics.d.ts +27 -0
- package/dist/dlp/semantics.d.ts.map +1 -0
- package/dist/dlp/semantics.js +93 -0
- package/dist/dlp/semantics.js.map +1 -0
- package/dist/dlp/structure.d.ts +25 -0
- package/dist/dlp/structure.d.ts.map +1 -0
- package/dist/dlp/structure.js +86 -0
- package/dist/dlp/structure.js.map +1 -0
- package/dist/dlp/validators.d.ts +6 -0
- package/dist/dlp/validators.d.ts.map +1 -0
- package/dist/dlp/validators.js +46 -0
- package/dist/dlp/validators.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +200 -0
- package/dist/index.js.map +1 -0
- package/dist/license/verify.d.ts +18 -0
- package/dist/license/verify.d.ts.map +1 -0
- package/dist/license/verify.js +71 -0
- package/dist/license/verify.js.map +1 -0
- package/dist/metrics/collector.d.ts +11 -0
- package/dist/metrics/collector.d.ts.map +1 -0
- package/dist/metrics/collector.js +17 -0
- package/dist/metrics/collector.js.map +1 -0
- package/dist/metrics/dashboard.d.ts +6 -0
- package/dist/metrics/dashboard.d.ts.map +1 -0
- package/dist/metrics/dashboard.js +66 -0
- package/dist/metrics/dashboard.js.map +1 -0
- package/dist/metrics/pricing.d.ts +10 -0
- package/dist/metrics/pricing.d.ts.map +1 -0
- package/dist/metrics/pricing.js +62 -0
- package/dist/metrics/pricing.js.map +1 -0
- package/dist/optimizer/cache.d.ts +14 -0
- package/dist/optimizer/cache.d.ts.map +1 -0
- package/dist/optimizer/cache.js +58 -0
- package/dist/optimizer/cache.js.map +1 -0
- package/dist/optimizer/estimator.d.ts +6 -0
- package/dist/optimizer/estimator.d.ts.map +1 -0
- package/dist/optimizer/estimator.js +12 -0
- package/dist/optimizer/estimator.js.map +1 -0
- package/dist/optimizer/reorder.d.ts +9 -0
- package/dist/optimizer/reorder.d.ts.map +1 -0
- package/dist/optimizer/reorder.js +27 -0
- package/dist/optimizer/reorder.js.map +1 -0
- package/dist/optimizer/trimmer.d.ts +9 -0
- package/dist/optimizer/trimmer.d.ts.map +1 -0
- package/dist/optimizer/trimmer.js +47 -0
- package/dist/optimizer/trimmer.js.map +1 -0
- package/dist/plugin-api/index.d.ts +3 -0
- package/dist/plugin-api/index.d.ts.map +1 -0
- package/dist/plugin-api/index.js +6 -0
- package/dist/plugin-api/index.js.map +1 -0
- package/dist/plugin-api/types.d.ts +77 -0
- package/dist/plugin-api/types.d.ts.map +1 -0
- package/dist/plugin-api/types.js +6 -0
- package/dist/plugin-api/types.js.map +1 -0
- package/dist/plugins/adapter.d.ts +12 -0
- package/dist/plugins/adapter.d.ts.map +1 -0
- package/dist/plugins/adapter.js +116 -0
- package/dist/plugins/adapter.js.map +1 -0
- package/dist/plugins/builtin/audit-logger.d.ts +9 -0
- package/dist/plugins/builtin/audit-logger.d.ts.map +1 -0
- package/dist/plugins/builtin/audit-logger.js +53 -0
- package/dist/plugins/builtin/audit-logger.js.map +1 -0
- package/dist/plugins/builtin/dlp-scanner.d.ts +19 -0
- package/dist/plugins/builtin/dlp-scanner.d.ts.map +1 -0
- package/dist/plugins/builtin/dlp-scanner.js +284 -0
- package/dist/plugins/builtin/dlp-scanner.js.map +1 -0
- package/dist/plugins/builtin/metrics-collector.d.ts +4 -0
- package/dist/plugins/builtin/metrics-collector.d.ts.map +1 -0
- package/dist/plugins/builtin/metrics-collector.js +111 -0
- package/dist/plugins/builtin/metrics-collector.js.map +1 -0
- package/dist/plugins/builtin/token-optimizer.d.ts +10 -0
- package/dist/plugins/builtin/token-optimizer.d.ts.map +1 -0
- package/dist/plugins/builtin/token-optimizer.js +120 -0
- package/dist/plugins/builtin/token-optimizer.js.map +1 -0
- package/dist/plugins/builtin/tool-guard.d.ts +20 -0
- package/dist/plugins/builtin/tool-guard.d.ts.map +1 -0
- package/dist/plugins/builtin/tool-guard.js +259 -0
- package/dist/plugins/builtin/tool-guard.js.map +1 -0
- package/dist/plugins/context.d.ts +8 -0
- package/dist/plugins/context.d.ts.map +1 -0
- package/dist/plugins/context.js +33 -0
- package/dist/plugins/context.js.map +1 -0
- package/dist/plugins/event-bus.d.ts +9 -0
- package/dist/plugins/event-bus.d.ts.map +1 -0
- package/dist/plugins/event-bus.js +25 -0
- package/dist/plugins/event-bus.js.map +1 -0
- package/dist/plugins/index.d.ts +18 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +148 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/loader.d.ts +14 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +98 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/types.d.ts +91 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +3 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/proxy/certs.d.ts +10 -0
- package/dist/proxy/certs.d.ts.map +1 -0
- package/dist/proxy/certs.js +110 -0
- package/dist/proxy/certs.js.map +1 -0
- package/dist/proxy/connect.d.ts +11 -0
- package/dist/proxy/connect.d.ts.map +1 -0
- package/dist/proxy/connect.js +298 -0
- package/dist/proxy/connect.js.map +1 -0
- package/dist/proxy/forwarder.d.ts +14 -0
- package/dist/proxy/forwarder.d.ts.map +1 -0
- package/dist/proxy/forwarder.js +342 -0
- package/dist/proxy/forwarder.js.map +1 -0
- package/dist/proxy/passthrough.d.ts +4 -0
- package/dist/proxy/passthrough.d.ts.map +1 -0
- package/dist/proxy/passthrough.js +68 -0
- package/dist/proxy/passthrough.js.map +1 -0
- package/dist/proxy/providers/anthropic.d.ts +4 -0
- package/dist/proxy/providers/anthropic.d.ts.map +1 -0
- package/dist/proxy/providers/anthropic.js +46 -0
- package/dist/proxy/providers/anthropic.js.map +1 -0
- package/dist/proxy/providers/classify.d.ts +14 -0
- package/dist/proxy/providers/classify.d.ts.map +1 -0
- package/dist/proxy/providers/classify.js +37 -0
- package/dist/proxy/providers/classify.js.map +1 -0
- package/dist/proxy/providers/claude-web.d.ts +8 -0
- package/dist/proxy/providers/claude-web.d.ts.map +1 -0
- package/dist/proxy/providers/claude-web.js +50 -0
- package/dist/proxy/providers/claude-web.js.map +1 -0
- package/dist/proxy/providers/gemini.d.ts +4 -0
- package/dist/proxy/providers/gemini.d.ts.map +1 -0
- package/dist/proxy/providers/gemini.js +38 -0
- package/dist/proxy/providers/gemini.js.map +1 -0
- package/dist/proxy/providers/index.d.ts +27 -0
- package/dist/proxy/providers/index.d.ts.map +1 -0
- package/dist/proxy/providers/index.js +32 -0
- package/dist/proxy/providers/index.js.map +1 -0
- package/dist/proxy/providers/messaging.d.ts +2 -0
- package/dist/proxy/providers/messaging.d.ts.map +1 -0
- package/dist/proxy/providers/messaging.js +53 -0
- package/dist/proxy/providers/messaging.js.map +1 -0
- package/dist/proxy/providers/openai.d.ts +4 -0
- package/dist/proxy/providers/openai.d.ts.map +1 -0
- package/dist/proxy/providers/openai.js +38 -0
- package/dist/proxy/providers/openai.js.map +1 -0
- package/dist/proxy/providers/telegram.d.ts +8 -0
- package/dist/proxy/providers/telegram.d.ts.map +1 -0
- package/dist/proxy/providers/telegram.js +35 -0
- package/dist/proxy/providers/telegram.js.map +1 -0
- package/dist/proxy/router.d.ts +12 -0
- package/dist/proxy/router.d.ts.map +1 -0
- package/dist/proxy/router.js +26 -0
- package/dist/proxy/router.js.map +1 -0
- package/dist/proxy/safety.d.ts +13 -0
- package/dist/proxy/safety.d.ts.map +1 -0
- package/dist/proxy/safety.js +58 -0
- package/dist/proxy/safety.js.map +1 -0
- package/dist/proxy/server.d.ts +8 -0
- package/dist/proxy/server.d.ts.map +1 -0
- package/dist/proxy/server.js +126 -0
- package/dist/proxy/server.js.map +1 -0
- package/dist/proxy/streaming.d.ts +21 -0
- package/dist/proxy/streaming.d.ts.map +1 -0
- package/dist/proxy/streaming.js +70 -0
- package/dist/proxy/streaming.js.map +1 -0
- package/dist/storage/database.d.ts +6 -0
- package/dist/storage/database.d.ts.map +1 -0
- package/dist/storage/database.js +44 -0
- package/dist/storage/database.js.map +1 -0
- package/dist/storage/encryption.d.ts +11 -0
- package/dist/storage/encryption.d.ts.map +1 -0
- package/dist/storage/encryption.js +47 -0
- package/dist/storage/encryption.js.map +1 -0
- package/dist/storage/migrations.d.ts +3 -0
- package/dist/storage/migrations.d.ts.map +1 -0
- package/dist/storage/migrations.js +265 -0
- package/dist/storage/migrations.js.map +1 -0
- package/dist/storage/repositories/audit-log.d.ts +115 -0
- package/dist/storage/repositories/audit-log.d.ts.map +1 -0
- package/dist/storage/repositories/audit-log.js +586 -0
- package/dist/storage/repositories/audit-log.js.map +1 -0
- package/dist/storage/repositories/cache.d.ts +26 -0
- package/dist/storage/repositories/cache.d.ts.map +1 -0
- package/dist/storage/repositories/cache.js +44 -0
- package/dist/storage/repositories/cache.js.map +1 -0
- package/dist/storage/repositories/dlp-config-history.d.ts +17 -0
- package/dist/storage/repositories/dlp-config-history.d.ts.map +1 -0
- package/dist/storage/repositories/dlp-config-history.js +30 -0
- package/dist/storage/repositories/dlp-config-history.js.map +1 -0
- package/dist/storage/repositories/dlp-events.d.ts +35 -0
- package/dist/storage/repositories/dlp-events.d.ts.map +1 -0
- package/dist/storage/repositories/dlp-events.js +57 -0
- package/dist/storage/repositories/dlp-events.js.map +1 -0
- package/dist/storage/repositories/dlp-patterns.d.ts +70 -0
- package/dist/storage/repositories/dlp-patterns.d.ts.map +1 -0
- package/dist/storage/repositories/dlp-patterns.js +187 -0
- package/dist/storage/repositories/dlp-patterns.js.map +1 -0
- package/dist/storage/repositories/optimizer-events.d.ts +28 -0
- package/dist/storage/repositories/optimizer-events.d.ts.map +1 -0
- package/dist/storage/repositories/optimizer-events.js +49 -0
- package/dist/storage/repositories/optimizer-events.js.map +1 -0
- package/dist/storage/repositories/plugin-events.d.ts +34 -0
- package/dist/storage/repositories/plugin-events.d.ts.map +1 -0
- package/dist/storage/repositories/plugin-events.js +64 -0
- package/dist/storage/repositories/plugin-events.js.map +1 -0
- package/dist/storage/repositories/requests.d.ts +68 -0
- package/dist/storage/repositories/requests.d.ts.map +1 -0
- package/dist/storage/repositories/requests.js +113 -0
- package/dist/storage/repositories/requests.js.map +1 -0
- package/dist/storage/repositories/sessions.d.ts +23 -0
- package/dist/storage/repositories/sessions.d.ts.map +1 -0
- package/dist/storage/repositories/sessions.js +42 -0
- package/dist/storage/repositories/sessions.js.map +1 -0
- package/dist/storage/repositories/tool-calls.d.ts +49 -0
- package/dist/storage/repositories/tool-calls.d.ts.map +1 -0
- package/dist/storage/repositories/tool-calls.js +61 -0
- package/dist/storage/repositories/tool-calls.js.map +1 -0
- package/dist/storage/repositories/tool-guard-rules.d.ts +50 -0
- package/dist/storage/repositories/tool-guard-rules.d.ts.map +1 -0
- package/dist/storage/repositories/tool-guard-rules.js +120 -0
- package/dist/storage/repositories/tool-guard-rules.js.map +1 -0
- package/dist/tool-guard/alert.d.ts +30 -0
- package/dist/tool-guard/alert.d.ts.map +1 -0
- package/dist/tool-guard/alert.js +113 -0
- package/dist/tool-guard/alert.js.map +1 -0
- package/dist/tool-guard/extractor.d.ts +10 -0
- package/dist/tool-guard/extractor.d.ts.map +1 -0
- package/dist/tool-guard/extractor.js +309 -0
- package/dist/tool-guard/extractor.js.map +1 -0
- package/dist/tool-guard/rules.d.ts +18 -0
- package/dist/tool-guard/rules.d.ts.map +1 -0
- package/dist/tool-guard/rules.js +255 -0
- package/dist/tool-guard/rules.js.map +1 -0
- package/dist/tool-guard/streaming-guard.d.ts +57 -0
- package/dist/tool-guard/streaming-guard.d.ts.map +1 -0
- package/dist/tool-guard/streaming-guard.js +389 -0
- package/dist/tool-guard/streaming-guard.js.map +1 -0
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +8 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +54 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/timeout.d.ts +5 -0
- package/dist/utils/timeout.d.ts.map +1 -0
- package/dist/utils/timeout.js +26 -0
- package/dist/utils/timeout.js.map +1 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +23 -0
- package/dist/version.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getSessionForSocket = getSessionForSocket;
|
|
37
|
+
exports.setupConnectHandler = setupConnectHandler;
|
|
38
|
+
const node_http_1 = require("node:http");
|
|
39
|
+
const node_https_1 = require("node:https");
|
|
40
|
+
const tls = __importStar(require("node:tls"));
|
|
41
|
+
const net = __importStar(require("node:net"));
|
|
42
|
+
const certs_js_1 = require("./certs.js");
|
|
43
|
+
const router_js_1 = require("./router.js");
|
|
44
|
+
const forwarder_js_1 = require("./forwarder.js");
|
|
45
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
46
|
+
const log = (0, logger_js_1.createLogger)('connect');
|
|
47
|
+
// API domains to MITM intercept — only these get decrypted
|
|
48
|
+
// LLM providers + messaging platforms (for OpenClaw integration)
|
|
49
|
+
const INTERCEPT_HOSTS = new Set([
|
|
50
|
+
// LLM providers
|
|
51
|
+
'api.anthropic.com',
|
|
52
|
+
'api.openai.com',
|
|
53
|
+
'generativelanguage.googleapis.com',
|
|
54
|
+
'claude.ai',
|
|
55
|
+
'chatgpt.com', // ChatGPT Web (OpenAI OAuth mode)
|
|
56
|
+
// Messaging platforms
|
|
57
|
+
'api.telegram.org',
|
|
58
|
+
'discord.com',
|
|
59
|
+
'gateway.discord.gg',
|
|
60
|
+
'api.slack.com',
|
|
61
|
+
'slack.com',
|
|
62
|
+
'graph.facebook.com', // WhatsApp Business API
|
|
63
|
+
'api.line.me',
|
|
64
|
+
]);
|
|
65
|
+
// Map socket → session ID for session tracking across CONNECT tunnels
|
|
66
|
+
const socketSessionMap = new WeakMap();
|
|
67
|
+
function getSessionForSocket(socket) {
|
|
68
|
+
return socketSessionMap.get(socket);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Parse session ID from Proxy-Authorization header.
|
|
72
|
+
* When wrap.ts sets HTTPS_PROXY=http://<uuid>@host:port, Node.js sends
|
|
73
|
+
* a Proxy-Authorization: Basic <base64(uuid:)> header in the CONNECT request.
|
|
74
|
+
*/
|
|
75
|
+
function parseSessionFromProxy(req) {
|
|
76
|
+
const authHeader = req.headers['proxy-authorization'];
|
|
77
|
+
if (!authHeader)
|
|
78
|
+
return undefined;
|
|
79
|
+
const match = authHeader.match(/^Basic\s+(.+)$/i);
|
|
80
|
+
if (!match)
|
|
81
|
+
return undefined;
|
|
82
|
+
try {
|
|
83
|
+
const decoded = Buffer.from(match[1], 'base64').toString('utf-8');
|
|
84
|
+
// Format is "username:password" — session UUID is the username
|
|
85
|
+
const username = decoded.split(':')[0];
|
|
86
|
+
// Validate it looks like a UUID
|
|
87
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(username)) {
|
|
88
|
+
return username;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Invalid base64
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Attach CONNECT handler to an existing HTTP server.
|
|
98
|
+
* - API hosts: MITM decrypt → plugin pipeline → forward to real upstream
|
|
99
|
+
* - All other hosts: plain TCP tunnel (no inspection)
|
|
100
|
+
*/
|
|
101
|
+
function setupConnectHandler(server, config, pluginManager) {
|
|
102
|
+
const ca = (0, certs_js_1.ensureCA)();
|
|
103
|
+
server.on('connect', (req, clientSocket, head) => {
|
|
104
|
+
const [hostname, portStr] = (req.url ?? '').split(':');
|
|
105
|
+
const port = parseInt(portStr, 10) || 443;
|
|
106
|
+
// Extract session ID from proxy auth header, or auto-generate for intercepted hosts
|
|
107
|
+
let sessionId = parseSessionFromProxy(req);
|
|
108
|
+
const source = sessionId ? 'wrap' : 'auto';
|
|
109
|
+
if (!sessionId && INTERCEPT_HOSTS.has(hostname)) {
|
|
110
|
+
sessionId = crypto.randomUUID();
|
|
111
|
+
}
|
|
112
|
+
if (sessionId) {
|
|
113
|
+
socketSessionMap.set(clientSocket, sessionId);
|
|
114
|
+
log.debug('Session mapped', { sessionId, hostname, source });
|
|
115
|
+
}
|
|
116
|
+
if (INTERCEPT_HOSTS.has(hostname)) {
|
|
117
|
+
log.info('CONNECT', { hostname, port, sessionId, source });
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
log.debug('CONNECT tunnel', { hostname, port });
|
|
121
|
+
}
|
|
122
|
+
if (INTERCEPT_HOSTS.has(hostname)) {
|
|
123
|
+
handleMITM(hostname, port, clientSocket, head, ca, config, pluginManager, sessionId, source);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
handleTunnel(hostname, port, clientSocket, head);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
log.info('CONNECT handler registered', {
|
|
130
|
+
interceptHosts: [...INTERCEPT_HOSTS],
|
|
131
|
+
caCert: (0, certs_js_1.getCACertPath)(),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Plain TCP tunnel — no inspection, no modification.
|
|
136
|
+
* Used for OAuth flows, browser traffic, etc.
|
|
137
|
+
*/
|
|
138
|
+
function handleTunnel(hostname, port, clientSocket, head) {
|
|
139
|
+
const serverSocket = net.connect(port, hostname, () => {
|
|
140
|
+
clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
|
|
141
|
+
if (head.length > 0)
|
|
142
|
+
serverSocket.write(head);
|
|
143
|
+
serverSocket.pipe(clientSocket);
|
|
144
|
+
clientSocket.pipe(serverSocket);
|
|
145
|
+
});
|
|
146
|
+
serverSocket.on('error', (err) => {
|
|
147
|
+
log.error('Tunnel error', { hostname, error: err.message });
|
|
148
|
+
clientSocket.end();
|
|
149
|
+
});
|
|
150
|
+
clientSocket.on('error', () => {
|
|
151
|
+
serverSocket.end();
|
|
152
|
+
});
|
|
153
|
+
clientSocket.on('close', () => {
|
|
154
|
+
serverSocket.end();
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* MITM interception for API hosts.
|
|
159
|
+
* 1. Accept TLS from client using host cert signed by our CA
|
|
160
|
+
* 2. Decrypt the HTTP request
|
|
161
|
+
* 3. Run through plugin pipeline (DLP, metrics, optimizer)
|
|
162
|
+
* 4. Forward to real upstream
|
|
163
|
+
*/
|
|
164
|
+
function handleMITM(hostname, port, clientSocket, head, ca, config, pluginManager, sessionId, sessionSource) {
|
|
165
|
+
const hostCert = (0, certs_js_1.getHostCert)(hostname);
|
|
166
|
+
// Tell client the tunnel is established
|
|
167
|
+
clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
|
|
168
|
+
// Wrap client socket with TLS (we act as the target server)
|
|
169
|
+
const tlsSocket = new tls.TLSSocket(clientSocket, {
|
|
170
|
+
isServer: true,
|
|
171
|
+
key: hostCert.key,
|
|
172
|
+
cert: hostCert.cert,
|
|
173
|
+
});
|
|
174
|
+
// Create a per-connection HTTP server to parse the decrypted request
|
|
175
|
+
const handler = createMITMRequestHandler(hostname, config, pluginManager, sessionId, sessionSource);
|
|
176
|
+
const fakeServer = (0, node_http_1.createServer)(handler);
|
|
177
|
+
// Inject the TLS socket as a "connection" to the HTTP server
|
|
178
|
+
fakeServer.emit('connection', tlsSocket);
|
|
179
|
+
if (head.length > 0) {
|
|
180
|
+
tlsSocket.unshift(head);
|
|
181
|
+
}
|
|
182
|
+
tlsSocket.on('error', (err) => {
|
|
183
|
+
log.debug('MITM TLS error', { hostname, error: err.message });
|
|
184
|
+
});
|
|
185
|
+
clientSocket.on('error', () => {
|
|
186
|
+
tlsSocket.destroy();
|
|
187
|
+
});
|
|
188
|
+
clientSocket.on('close', () => {
|
|
189
|
+
fakeServer.close();
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Create a request handler for MITM-intercepted connections.
|
|
194
|
+
* Knows the target hostname, so non-provider paths are forwarded directly.
|
|
195
|
+
*/
|
|
196
|
+
/**
|
|
197
|
+
* Create a fallback ProviderConfig for intercepted hosts where no
|
|
198
|
+
* registered provider prefix matches. Ensures all POST traffic from
|
|
199
|
+
* MITM'd hosts still flows through the plugin pipeline (DLP, audit).
|
|
200
|
+
*/
|
|
201
|
+
function createFallbackProvider(hostname) {
|
|
202
|
+
return {
|
|
203
|
+
name: hostname.replace(/\./g, '-'),
|
|
204
|
+
baseUrl: `https://${hostname}`,
|
|
205
|
+
authHeader: '',
|
|
206
|
+
transformHeaders(headers) {
|
|
207
|
+
const result = {};
|
|
208
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
209
|
+
const lower = key.toLowerCase();
|
|
210
|
+
if (lower !== 'host' && lower !== 'connection' && lower !== 'transfer-encoding') {
|
|
211
|
+
result[key] = value;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return result;
|
|
215
|
+
},
|
|
216
|
+
extractModel() { return hostname; },
|
|
217
|
+
extractUsage() {
|
|
218
|
+
return { inputTokens: 0, outputTokens: 0 };
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function createMITMRequestHandler(hostname, config, pluginManager, sessionId, sessionSource) {
|
|
223
|
+
return async (req, res) => {
|
|
224
|
+
// Redact secrets from logged paths (e.g., Telegram bot tokens)
|
|
225
|
+
const safePath = (req.url ?? '/').replace(/\/bot[^/]+\//, '/bot****/');
|
|
226
|
+
log.debug('MITM request', { method: req.method, hostname, path: safePath, sessionId });
|
|
227
|
+
// Try to match a known provider route
|
|
228
|
+
const route = (0, router_js_1.resolveRoute)(req);
|
|
229
|
+
// If scanMethods is configured and non-empty, only scan listed methods
|
|
230
|
+
const scanMethods = config.server.scanMethods ?? [];
|
|
231
|
+
const shouldScan = scanMethods.length === 0 || scanMethods.includes(req.method ?? '');
|
|
232
|
+
if (shouldScan) {
|
|
233
|
+
const provider = route?.provider ?? createFallbackProvider(hostname);
|
|
234
|
+
const upstreamUrl = route
|
|
235
|
+
? route.upstreamUrl
|
|
236
|
+
: `https://${hostname}${req.url ?? '/'}`;
|
|
237
|
+
try {
|
|
238
|
+
await (0, forwarder_js_1.forwardRequest)(req, res, {
|
|
239
|
+
provider,
|
|
240
|
+
upstreamUrl,
|
|
241
|
+
upstreamTimeout: config.timeouts.upstream,
|
|
242
|
+
pluginManager,
|
|
243
|
+
sessionId,
|
|
244
|
+
sessionSource,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
log.error('MITM forward failed', { error: err.message });
|
|
249
|
+
if (!res.headersSent) {
|
|
250
|
+
(0, router_js_1.sendError)(res, 500, 'Internal gateway error');
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
// Method excluded from scanning — forward directly
|
|
256
|
+
directForward(req, res, hostname, config.timeouts.upstream);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Forward a request directly to a known hostname via HTTPS.
|
|
262
|
+
* Used for non-API-endpoint requests on API domains (e.g., model listing, health checks).
|
|
263
|
+
*/
|
|
264
|
+
function directForward(req, res, hostname, timeout) {
|
|
265
|
+
const path = req.url ?? '/';
|
|
266
|
+
// Copy headers, fix host
|
|
267
|
+
const headers = { ...req.headers };
|
|
268
|
+
headers['host'] = hostname;
|
|
269
|
+
delete headers['connection'];
|
|
270
|
+
const upstreamReq = (0, node_https_1.request)({
|
|
271
|
+
hostname,
|
|
272
|
+
port: 443,
|
|
273
|
+
path,
|
|
274
|
+
method: req.method,
|
|
275
|
+
headers,
|
|
276
|
+
timeout,
|
|
277
|
+
}, (upstreamRes) => {
|
|
278
|
+
log.info('MITM direct response', { hostname, path, status: upstreamRes.statusCode });
|
|
279
|
+
res.writeHead(upstreamRes.statusCode ?? 502, upstreamRes.headers);
|
|
280
|
+
upstreamRes.pipe(res);
|
|
281
|
+
});
|
|
282
|
+
upstreamReq.on('error', (err) => {
|
|
283
|
+
log.error('MITM direct forward failed', { hostname, error: err.message });
|
|
284
|
+
if (!res.headersSent) {
|
|
285
|
+
res.writeHead(502, { 'content-type': 'application/json' });
|
|
286
|
+
res.end(JSON.stringify({ error: { message: 'Upstream request failed', type: 'gateway_error' } }));
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
upstreamReq.on('timeout', () => {
|
|
290
|
+
upstreamReq.destroy();
|
|
291
|
+
if (!res.headersSent) {
|
|
292
|
+
res.writeHead(504, { 'content-type': 'application/json' });
|
|
293
|
+
res.end(JSON.stringify({ error: { message: 'Upstream request timed out', type: 'gateway_error' } }));
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
req.pipe(upstreamReq);
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=connect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/proxy/connect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { ProviderConfig } from './providers/index.js';
|
|
3
|
+
import type { PluginManager } from '../plugins/index.js';
|
|
4
|
+
import type { RequestContext } from '../plugins/types.js';
|
|
5
|
+
export interface ForwardOptions {
|
|
6
|
+
provider: ProviderConfig;
|
|
7
|
+
upstreamUrl: string;
|
|
8
|
+
upstreamTimeout: number;
|
|
9
|
+
pluginManager: PluginManager;
|
|
10
|
+
sessionId?: string;
|
|
11
|
+
sessionSource?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function forwardRequest(req: IncomingMessage, res: ServerResponse, options: ForwardOptions): Promise<RequestContext>;
|
|
14
|
+
//# sourceMappingURL=forwarder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forwarder.d.ts","sourceRoot":"","sources":["../../src/proxy/forwarder.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAqD,MAAM,qBAAqB,CAAC;AAO7G,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,cAAc,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,aAAa,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAmBD,wBAAsB,cAAc,CAClC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,cAAc,CAAC,CAsJzB"}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.forwardRequest = forwardRequest;
|
|
4
|
+
const node_https_1 = require("node:https");
|
|
5
|
+
const node_http_1 = require("node:http");
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
const streaming_js_1 = require("./streaming.js");
|
|
8
|
+
const streaming_guard_js_1 = require("../tool-guard/streaming-guard.js");
|
|
9
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
10
|
+
const log = (0, logger_js_1.createLogger)('forwarder');
|
|
11
|
+
function computeApiKeyHash(headers) {
|
|
12
|
+
// Check common auth headers
|
|
13
|
+
const authHeader = headers['authorization'] || headers['x-api-key'];
|
|
14
|
+
if (!authHeader)
|
|
15
|
+
return undefined;
|
|
16
|
+
const key = authHeader.replace(/^Bearer\s+/i, '');
|
|
17
|
+
return (0, node_crypto_1.createHash)('sha256').update(key).digest('hex').slice(0, 16);
|
|
18
|
+
}
|
|
19
|
+
function bufferBody(req) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const chunks = [];
|
|
22
|
+
req.on('data', (chunk) => chunks.push(chunk));
|
|
23
|
+
req.on('end', () => resolve(Buffer.concat(chunks)));
|
|
24
|
+
req.on('error', reject);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async function forwardRequest(req, res, options) {
|
|
28
|
+
const { provider, upstreamUrl, upstreamTimeout, pluginManager, sessionId, sessionSource } = options;
|
|
29
|
+
const startTime = Date.now();
|
|
30
|
+
const requestId = crypto.randomUUID();
|
|
31
|
+
// Buffer the request body
|
|
32
|
+
const rawBody = await bufferBody(req);
|
|
33
|
+
let bodyStr = rawBody.toString('utf-8');
|
|
34
|
+
let parsedBody = {};
|
|
35
|
+
try {
|
|
36
|
+
parsedBody = JSON.parse(bodyStr);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Not JSON, use as-is
|
|
40
|
+
}
|
|
41
|
+
const model = provider.extractModel(parsedBody);
|
|
42
|
+
const isStreaming = Boolean(parsedBody.stream);
|
|
43
|
+
const reqHeaders = req.headers;
|
|
44
|
+
const apiKeyHash = computeApiKeyHash(reqHeaders);
|
|
45
|
+
// Build request context
|
|
46
|
+
const requestContext = {
|
|
47
|
+
id: requestId,
|
|
48
|
+
provider: provider.name,
|
|
49
|
+
model,
|
|
50
|
+
method: req.method ?? 'POST',
|
|
51
|
+
path: req.url ?? '/',
|
|
52
|
+
headers: reqHeaders,
|
|
53
|
+
body: bodyStr,
|
|
54
|
+
parsedBody,
|
|
55
|
+
isStreaming,
|
|
56
|
+
startTime,
|
|
57
|
+
sessionId,
|
|
58
|
+
sessionSource,
|
|
59
|
+
apiKeyHash,
|
|
60
|
+
};
|
|
61
|
+
// Run onRequest plugins
|
|
62
|
+
const pluginResult = await pluginManager.runOnRequest(requestContext);
|
|
63
|
+
// If a plugin short-circuited with a response (e.g., cache hit)
|
|
64
|
+
if (pluginResult.shortCircuit) {
|
|
65
|
+
const cached = pluginResult.shortCircuit;
|
|
66
|
+
res.writeHead(cached.statusCode, cached.headers);
|
|
67
|
+
res.end(cached.body);
|
|
68
|
+
return requestContext;
|
|
69
|
+
}
|
|
70
|
+
// If a plugin blocked the request (e.g., DLP)
|
|
71
|
+
if (pluginResult.blocked) {
|
|
72
|
+
res.writeHead(403, { 'content-type': 'application/json' });
|
|
73
|
+
res.end(JSON.stringify({
|
|
74
|
+
error: {
|
|
75
|
+
message: pluginResult.blocked.reason,
|
|
76
|
+
type: 'gateway_blocked',
|
|
77
|
+
},
|
|
78
|
+
}));
|
|
79
|
+
return requestContext;
|
|
80
|
+
}
|
|
81
|
+
// If a plugin crashed in fail-closed mode
|
|
82
|
+
if (pluginResult.pluginError) {
|
|
83
|
+
res.writeHead(502, { 'content-type': 'application/json' });
|
|
84
|
+
res.end(JSON.stringify({
|
|
85
|
+
error: {
|
|
86
|
+
message: 'Security pipeline error',
|
|
87
|
+
detail: `Plugin ${pluginResult.pluginError.pluginName} failed`,
|
|
88
|
+
type: 'gateway_pipeline_error',
|
|
89
|
+
},
|
|
90
|
+
}));
|
|
91
|
+
return requestContext;
|
|
92
|
+
}
|
|
93
|
+
// Use modified body if plugins changed it
|
|
94
|
+
if (pluginResult.modifiedBody) {
|
|
95
|
+
bodyStr = pluginResult.modifiedBody;
|
|
96
|
+
}
|
|
97
|
+
// Forward to upstream
|
|
98
|
+
const url = new URL(upstreamUrl);
|
|
99
|
+
const isHttps = url.protocol === 'https:';
|
|
100
|
+
const outgoingHeaders = provider.transformHeaders(req.headers);
|
|
101
|
+
outgoingHeaders['host'] = url.host;
|
|
102
|
+
const hasBody = req.method !== 'GET' && req.method !== 'HEAD';
|
|
103
|
+
if (hasBody) {
|
|
104
|
+
outgoingHeaders['content-length'] = Buffer.byteLength(bodyStr).toString();
|
|
105
|
+
}
|
|
106
|
+
const makeRequest = isHttps ? node_https_1.request : node_http_1.request;
|
|
107
|
+
return new Promise((resolve) => {
|
|
108
|
+
const upstreamReq = makeRequest({
|
|
109
|
+
hostname: url.hostname,
|
|
110
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
111
|
+
path: url.pathname + url.search,
|
|
112
|
+
method: req.method,
|
|
113
|
+
headers: outgoingHeaders,
|
|
114
|
+
timeout: upstreamTimeout,
|
|
115
|
+
}, (upstreamRes) => {
|
|
116
|
+
const statusCode = upstreamRes.statusCode ?? 500;
|
|
117
|
+
// Copy upstream response headers
|
|
118
|
+
const responseHeaders = {};
|
|
119
|
+
for (const [key, value] of Object.entries(upstreamRes.headers)) {
|
|
120
|
+
if (value)
|
|
121
|
+
responseHeaders[key] = Array.isArray(value) ? value.join(', ') : value;
|
|
122
|
+
}
|
|
123
|
+
if (isStreaming) {
|
|
124
|
+
// Streaming: send headers immediately, forward chunks in real-time
|
|
125
|
+
res.writeHead(statusCode, responseHeaders);
|
|
126
|
+
handleStreamingResponse(upstreamRes, res, requestContext, pluginManager, provider, startTime)
|
|
127
|
+
.then(() => resolve(requestContext));
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Non-streaming: buffer response, run onResponse hook, then send
|
|
131
|
+
handleBufferedResponse(upstreamRes, res, statusCode, responseHeaders, requestContext, pluginManager, provider, startTime)
|
|
132
|
+
.then(() => resolve(requestContext));
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
upstreamReq.on('error', (err) => {
|
|
136
|
+
log.error('Upstream request failed', { error: err.message, provider: provider.name });
|
|
137
|
+
if (!res.headersSent) {
|
|
138
|
+
res.writeHead(502, { 'content-type': 'application/json' });
|
|
139
|
+
res.end(JSON.stringify({
|
|
140
|
+
error: { message: 'Upstream request failed', type: 'gateway_error' },
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
resolve(requestContext);
|
|
144
|
+
});
|
|
145
|
+
upstreamReq.on('timeout', () => {
|
|
146
|
+
upstreamReq.destroy();
|
|
147
|
+
if (!res.headersSent) {
|
|
148
|
+
res.writeHead(504, { 'content-type': 'application/json' });
|
|
149
|
+
res.end(JSON.stringify({
|
|
150
|
+
error: { message: 'Upstream request timed out', type: 'gateway_error' },
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
resolve(requestContext);
|
|
154
|
+
});
|
|
155
|
+
if (hasBody) {
|
|
156
|
+
upstreamReq.write(bodyStr);
|
|
157
|
+
}
|
|
158
|
+
upstreamReq.end();
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
// Cap accumulated body for post-send plugin processing.
|
|
162
|
+
// Client always receives the full stream; only onResponseComplete sees the truncated body.
|
|
163
|
+
const MAX_STREAMING_BODY = 2 * 1024 * 1024; // 2MB
|
|
164
|
+
async function handleStreamingResponse(upstreamRes, clientRes, context, pluginManager, provider, startTime) {
|
|
165
|
+
let fullResponseData = '';
|
|
166
|
+
let bodyCapReached = false;
|
|
167
|
+
const sseEvents = [];
|
|
168
|
+
// Streaming tool guard: intercept tool_use SSE blocks when action=block
|
|
169
|
+
const useGuard = Boolean(context._toolGuardStreamBlock);
|
|
170
|
+
const guard = useGuard
|
|
171
|
+
? new streaming_guard_js_1.StreamingToolGuard({ blockMinSeverity: context._toolGuardStreamBlock, rules: context._toolGuardRules }, (data) => clientRes.write(data))
|
|
172
|
+
: null;
|
|
173
|
+
// Accumulate raw events for the guard (keyed by parse position)
|
|
174
|
+
let rawEventBuffer = '';
|
|
175
|
+
const parser = new streaming_js_1.SSEParser((event) => {
|
|
176
|
+
const data = (0, streaming_js_1.parseSSEData)(event);
|
|
177
|
+
if (data) {
|
|
178
|
+
sseEvents.push(data);
|
|
179
|
+
}
|
|
180
|
+
if (guard) {
|
|
181
|
+
// Reconstruct the raw SSE text for this event
|
|
182
|
+
const rawEvent = (event.event ? `event: ${event.event}\n` : '') +
|
|
183
|
+
`data: ${event.data}\n\n`;
|
|
184
|
+
guard.processEvent(rawEvent, data);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
return new Promise((resolve) => {
|
|
188
|
+
upstreamRes.on('data', (chunk) => {
|
|
189
|
+
const chunkStr = chunk.toString('utf-8');
|
|
190
|
+
// Cap accumulated body for post-send analysis; client still gets all data
|
|
191
|
+
if (!bodyCapReached) {
|
|
192
|
+
fullResponseData += chunkStr;
|
|
193
|
+
if (fullResponseData.length > MAX_STREAMING_BODY) {
|
|
194
|
+
bodyCapReached = true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (guard) {
|
|
198
|
+
// Let the parser drive the guard (events are processed in the SSEParser callback)
|
|
199
|
+
parser.feed(chunkStr);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// No guard: forward raw bytes unmodified, parse for inspection only
|
|
203
|
+
clientRes.write(chunk);
|
|
204
|
+
parser.feed(chunkStr);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
upstreamRes.on('end', async () => {
|
|
208
|
+
parser.flush();
|
|
209
|
+
if (guard)
|
|
210
|
+
guard.flush();
|
|
211
|
+
clientRes.end();
|
|
212
|
+
// Propagate streaming guard block results to request context
|
|
213
|
+
if (guard && guard.results.length > 0) {
|
|
214
|
+
const blocked = guard.results.filter(r => r.blocked);
|
|
215
|
+
if (blocked.length > 0) {
|
|
216
|
+
context.toolGuardHit = true;
|
|
217
|
+
context.toolGuardFindings = blocked.length;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const latencyMs = Date.now() - startTime;
|
|
221
|
+
// Extract usage by merging across all SSE events (take max per field).
|
|
222
|
+
// Anthropic splits usage: input_tokens in message_start, output_tokens in message_delta.
|
|
223
|
+
const usage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
|
|
224
|
+
for (const event of sseEvents) {
|
|
225
|
+
const extracted = provider.extractUsage(event);
|
|
226
|
+
if (extracted.inputTokens > usage.inputTokens)
|
|
227
|
+
usage.inputTokens = extracted.inputTokens;
|
|
228
|
+
if (extracted.outputTokens > usage.outputTokens)
|
|
229
|
+
usage.outputTokens = extracted.outputTokens;
|
|
230
|
+
if ((extracted.cacheCreationTokens ?? 0) > usage.cacheCreationTokens)
|
|
231
|
+
usage.cacheCreationTokens = extracted.cacheCreationTokens ?? 0;
|
|
232
|
+
if ((extracted.cacheReadTokens ?? 0) > usage.cacheReadTokens)
|
|
233
|
+
usage.cacheReadTokens = extracted.cacheReadTokens ?? 0;
|
|
234
|
+
}
|
|
235
|
+
const completeContext = {
|
|
236
|
+
request: context,
|
|
237
|
+
statusCode: upstreamRes.statusCode ?? 200,
|
|
238
|
+
body: fullResponseData,
|
|
239
|
+
parsedBody: null,
|
|
240
|
+
usage,
|
|
241
|
+
latencyMs,
|
|
242
|
+
isStreaming: true,
|
|
243
|
+
sseEvents,
|
|
244
|
+
};
|
|
245
|
+
await pluginManager.runOnResponseComplete(completeContext);
|
|
246
|
+
resolve();
|
|
247
|
+
});
|
|
248
|
+
upstreamRes.on('error', () => {
|
|
249
|
+
if (guard)
|
|
250
|
+
guard.flush();
|
|
251
|
+
clientRes.end();
|
|
252
|
+
resolve();
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
async function handleBufferedResponse(upstreamRes, clientRes, statusCode, responseHeaders, context, pluginManager, provider, startTime) {
|
|
257
|
+
return new Promise((resolve) => {
|
|
258
|
+
const chunks = [];
|
|
259
|
+
upstreamRes.on('data', (chunk) => {
|
|
260
|
+
chunks.push(chunk);
|
|
261
|
+
});
|
|
262
|
+
upstreamRes.on('end', async () => {
|
|
263
|
+
const body = Buffer.concat(chunks);
|
|
264
|
+
let bodyStr = body.toString('utf-8');
|
|
265
|
+
const latencyMs = Date.now() - startTime;
|
|
266
|
+
let parsedBody = null;
|
|
267
|
+
try {
|
|
268
|
+
parsedBody = JSON.parse(bodyStr);
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
// Not JSON
|
|
272
|
+
}
|
|
273
|
+
// Run onResponse hook (pre-send interception)
|
|
274
|
+
const interceptContext = {
|
|
275
|
+
request: context,
|
|
276
|
+
statusCode,
|
|
277
|
+
headers: responseHeaders,
|
|
278
|
+
body: bodyStr,
|
|
279
|
+
parsedBody,
|
|
280
|
+
isStreaming: false,
|
|
281
|
+
};
|
|
282
|
+
const hookResult = await pluginManager.runOnResponse(interceptContext);
|
|
283
|
+
if (hookResult.blocked) {
|
|
284
|
+
// Block: send error to client instead of the LLM response
|
|
285
|
+
clientRes.writeHead(403, { 'content-type': 'application/json' });
|
|
286
|
+
clientRes.end(JSON.stringify({
|
|
287
|
+
error: {
|
|
288
|
+
message: hookResult.blocked.reason,
|
|
289
|
+
type: 'gateway_response_blocked',
|
|
290
|
+
},
|
|
291
|
+
}));
|
|
292
|
+
}
|
|
293
|
+
else if (hookResult.pluginError) {
|
|
294
|
+
// Plugin crashed in fail-closed mode
|
|
295
|
+
clientRes.writeHead(502, { 'content-type': 'application/json' });
|
|
296
|
+
clientRes.end(JSON.stringify({
|
|
297
|
+
error: {
|
|
298
|
+
message: 'Security pipeline error',
|
|
299
|
+
detail: `Plugin ${hookResult.pluginError.pluginName} failed`,
|
|
300
|
+
type: 'gateway_pipeline_error',
|
|
301
|
+
},
|
|
302
|
+
}));
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
// Send response (original or modified)
|
|
306
|
+
if (hookResult.modifiedBody) {
|
|
307
|
+
bodyStr = hookResult.modifiedBody;
|
|
308
|
+
}
|
|
309
|
+
// We buffered the response, so set exact content-length and remove chunked encoding
|
|
310
|
+
delete responseHeaders['transfer-encoding'];
|
|
311
|
+
responseHeaders['content-length'] = Buffer.byteLength(bodyStr).toString();
|
|
312
|
+
clientRes.writeHead(statusCode, responseHeaders);
|
|
313
|
+
clientRes.write(bodyStr);
|
|
314
|
+
clientRes.end();
|
|
315
|
+
}
|
|
316
|
+
const usage = parsedBody
|
|
317
|
+
? provider.extractUsage(parsedBody)
|
|
318
|
+
: { inputTokens: 0, outputTokens: 0 };
|
|
319
|
+
const completeContext = {
|
|
320
|
+
request: context,
|
|
321
|
+
statusCode,
|
|
322
|
+
body: bodyStr,
|
|
323
|
+
parsedBody,
|
|
324
|
+
usage: {
|
|
325
|
+
inputTokens: usage.inputTokens,
|
|
326
|
+
outputTokens: usage.outputTokens,
|
|
327
|
+
cacheCreationTokens: usage.cacheCreationTokens ?? 0,
|
|
328
|
+
cacheReadTokens: usage.cacheReadTokens ?? 0,
|
|
329
|
+
},
|
|
330
|
+
latencyMs,
|
|
331
|
+
isStreaming: false,
|
|
332
|
+
};
|
|
333
|
+
await pluginManager.runOnResponseComplete(completeContext);
|
|
334
|
+
resolve();
|
|
335
|
+
});
|
|
336
|
+
upstreamRes.on('error', () => {
|
|
337
|
+
clientRes.end();
|
|
338
|
+
resolve();
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
//# sourceMappingURL=forwarder.js.map
|