@mongodb-js/agent-engine-runner-shared 0.11.3
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 +55 -0
- package/LICENSE.md +201 -0
- package/README.md +29 -0
- package/dist/agent_config.d.ts +167 -0
- package/dist/agent_config.d.ts.map +1 -0
- package/dist/agent_config.js +544 -0
- package/dist/call_interrupted.d.ts +12 -0
- package/dist/call_interrupted.d.ts.map +1 -0
- package/dist/call_interrupted.js +11 -0
- package/dist/checkpoint_workspace.d.ts +25 -0
- package/dist/checkpoint_workspace.d.ts.map +1 -0
- package/dist/checkpoint_workspace.js +44 -0
- package/dist/context.d.ts +235 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +322 -0
- package/dist/db_config.d.ts +28 -0
- package/dist/db_config.d.ts.map +1 -0
- package/dist/db_config.js +66 -0
- package/dist/db_naming.d.ts +54 -0
- package/dist/db_naming.d.ts.map +1 -0
- package/dist/db_naming.js +94 -0
- package/dist/error_reporting.d.ts +67 -0
- package/dist/error_reporting.d.ts.map +1 -0
- package/dist/error_reporting.js +311 -0
- package/dist/generated/workflow/v1/activity_pb.d.ts +342 -0
- package/dist/generated/workflow/v1/activity_pb.d.ts.map +1 -0
- package/dist/generated/workflow/v1/activity_pb.js +115 -0
- package/dist/generated/workflow/v1/common_pb.d.ts +184 -0
- package/dist/generated/workflow/v1/common_pb.d.ts.map +1 -0
- package/dist/generated/workflow/v1/common_pb.js +86 -0
- package/dist/generated/workflow/v1/runtime_pb.d.ts +200 -0
- package/dist/generated/workflow/v1/runtime_pb.d.ts.map +1 -0
- package/dist/generated/workflow/v1/runtime_pb.js +40 -0
- package/dist/generated/workflow/v1/state_pb.d.ts +254 -0
- package/dist/generated/workflow/v1/state_pb.d.ts.map +1 -0
- package/dist/generated/workflow/v1/state_pb.js +68 -0
- package/dist/guardrails_evaluator/core.d.ts +23 -0
- package/dist/guardrails_evaluator/core.d.ts.map +1 -0
- package/dist/guardrails_evaluator/core.js +122 -0
- package/dist/guardrails_evaluator/index.d.ts +10 -0
- package/dist/guardrails_evaluator/index.d.ts.map +1 -0
- package/dist/guardrails_evaluator/index.js +11 -0
- package/dist/guardrails_evaluator/regex.d.ts +20 -0
- package/dist/guardrails_evaluator/regex.d.ts.map +1 -0
- package/dist/guardrails_evaluator/regex.js +233 -0
- package/dist/hooks.d.ts +109 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +216 -0
- package/dist/http_path.d.ts +18 -0
- package/dist/http_path.d.ts.map +1 -0
- package/dist/http_path.js +53 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/launcher.d.ts +130 -0
- package/dist/launcher.d.ts.map +1 -0
- package/dist/launcher.js +325 -0
- package/dist/logger.d.ts +96 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +204 -0
- package/dist/mcp_oauth.d.ts +51 -0
- package/dist/mcp_oauth.d.ts.map +1 -0
- package/dist/mcp_oauth.js +389 -0
- package/dist/mcp_oauth_secret.d.ts +21 -0
- package/dist/mcp_oauth_secret.d.ts.map +1 -0
- package/dist/mcp_oauth_secret.js +122 -0
- package/dist/mcp_tools.d.ts +71 -0
- package/dist/mcp_tools.d.ts.map +1 -0
- package/dist/mcp_tools.js +301 -0
- package/dist/memory_appbound.d.ts +42 -0
- package/dist/memory_appbound.d.ts.map +1 -0
- package/dist/memory_appbound.js +159 -0
- package/dist/memory_writer.d.ts +49 -0
- package/dist/memory_writer.d.ts.map +1 -0
- package/dist/memory_writer.js +171 -0
- package/dist/metrics.d.ts +84 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +205 -0
- package/dist/models.d.ts +1458 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +1726 -0
- package/dist/node_logger.d.ts +43 -0
- package/dist/node_logger.d.ts.map +1 -0
- package/dist/node_logger.js +158 -0
- package/dist/owner_callback.d.ts +16 -0
- package/dist/owner_callback.d.ts.map +1 -0
- package/dist/owner_callback.js +40 -0
- package/dist/progress.d.ts +57 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +140 -0
- package/dist/runtime.d.ts +131 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +351 -0
- package/dist/secure_llm_proxy.d.ts +115 -0
- package/dist/secure_llm_proxy.d.ts.map +1 -0
- package/dist/secure_llm_proxy.js +922 -0
- package/dist/secure_wrapper.d.ts +332 -0
- package/dist/secure_wrapper.d.ts.map +1 -0
- package/dist/secure_wrapper.js +1249 -0
- package/dist/server/aer.d.ts +61 -0
- package/dist/server/aer.d.ts.map +1 -0
- package/dist/server/aer.js +1124 -0
- package/dist/server/auth.d.ts +56 -0
- package/dist/server/auth.d.ts.map +1 -0
- package/dist/server/auth.js +132 -0
- package/dist/server/base.d.ts +104 -0
- package/dist/server/base.d.ts.map +1 -0
- package/dist/server/base.js +150 -0
- package/dist/server/callInterrupt.d.ts +49 -0
- package/dist/server/callInterrupt.d.ts.map +1 -0
- package/dist/server/callInterrupt.js +68 -0
- package/dist/server/callback_delivery.d.ts +14 -0
- package/dist/server/callback_delivery.d.ts.map +1 -0
- package/dist/server/callback_delivery.js +141 -0
- package/dist/server/chunk_types.d.ts +50 -0
- package/dist/server/chunk_types.d.ts.map +1 -0
- package/dist/server/chunk_types.js +62 -0
- package/dist/server/cors.d.ts +52 -0
- package/dist/server/cors.d.ts.map +1 -0
- package/dist/server/cors.js +107 -0
- package/dist/server/drain.d.ts +169 -0
- package/dist/server/drain.d.ts.map +1 -0
- package/dist/server/drain.js +455 -0
- package/dist/server/function.d.ts +77 -0
- package/dist/server/function.d.ts.map +1 -0
- package/dist/server/function.js +337 -0
- package/dist/server/http_retry.d.ts +37 -0
- package/dist/server/http_retry.d.ts.map +1 -0
- package/dist/server/http_retry.js +157 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +5 -0
- package/dist/server/metadata.d.ts +50 -0
- package/dist/server/metadata.d.ts.map +1 -0
- package/dist/server/metadata.js +193 -0
- package/dist/server/oe_url.d.ts +36 -0
- package/dist/server/oe_url.d.ts.map +1 -0
- package/dist/server/oe_url.js +50 -0
- package/dist/server/owner_url.d.ts +35 -0
- package/dist/server/owner_url.d.ts.map +1 -0
- package/dist/server/owner_url.js +146 -0
- package/dist/server/query.d.ts +42 -0
- package/dist/server/query.d.ts.map +1 -0
- package/dist/server/query.js +28 -0
- package/dist/server/tool.d.ts +138 -0
- package/dist/server/tool.d.ts.map +1 -0
- package/dist/server/tool.js +1017 -0
- package/dist/span_names.d.ts +21 -0
- package/dist/span_names.d.ts.map +1 -0
- package/dist/span_names.js +31 -0
- package/dist/structured_logging/constants.d.ts +17 -0
- package/dist/structured_logging/constants.d.ts.map +1 -0
- package/dist/structured_logging/constants.js +71 -0
- package/dist/structured_logging/env.d.ts +18 -0
- package/dist/structured_logging/env.d.ts.map +1 -0
- package/dist/structured_logging/env.js +39 -0
- package/dist/structured_logging/install.d.ts +56 -0
- package/dist/structured_logging/install.d.ts.map +1 -0
- package/dist/structured_logging/install.js +107 -0
- package/dist/structured_logging/layout.d.ts +9 -0
- package/dist/structured_logging/layout.d.ts.map +1 -0
- package/dist/structured_logging/layout.js +144 -0
- package/dist/structured_logging/serialize.d.ts +27 -0
- package/dist/structured_logging/serialize.d.ts.map +1 -0
- package/dist/structured_logging/serialize.js +61 -0
- package/dist/structured_logging/stdio_capture.d.ts +59 -0
- package/dist/structured_logging/stdio_capture.d.ts.map +1 -0
- package/dist/structured_logging/stdio_capture.js +164 -0
- package/dist/structured_logging/uncaught.d.ts +14 -0
- package/dist/structured_logging/uncaught.d.ts.map +1 -0
- package/dist/structured_logging/uncaught.js +58 -0
- package/dist/structured_logging.d.ts +48 -0
- package/dist/structured_logging.d.ts.map +1 -0
- package/dist/structured_logging.js +47 -0
- package/dist/tls_client.d.ts +61 -0
- package/dist/tls_client.d.ts.map +1 -0
- package/dist/tls_client.js +298 -0
- package/dist/tool_api_error.d.ts +62 -0
- package/dist/tool_api_error.d.ts.map +1 -0
- package/dist/tool_api_error.js +399 -0
- package/dist/tool_memory_ownership.d.ts +10 -0
- package/dist/tool_memory_ownership.d.ts.map +1 -0
- package/dist/tool_memory_ownership.js +36 -0
- package/dist/toolpod_handlers.d.ts +126 -0
- package/dist/toolpod_handlers.d.ts.map +1 -0
- package/dist/toolpod_handlers.js +1016 -0
- package/dist/tracing/exporters.d.ts +51 -0
- package/dist/tracing/exporters.d.ts.map +1 -0
- package/dist/tracing/exporters.js +327 -0
- package/dist/tracing/index.d.ts +3 -0
- package/dist/tracing/index.d.ts.map +1 -0
- package/dist/tracing/index.js +2 -0
- package/dist/tracing/setup.d.ts +76 -0
- package/dist/tracing/setup.d.ts.map +1 -0
- package/dist/tracing/setup.js +436 -0
- package/dist/utils.d.ts +204 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +867 -0
- package/dist/workflow/activity.d.ts +71 -0
- package/dist/workflow/activity.d.ts.map +1 -0
- package/dist/workflow/activity.js +357 -0
- package/dist/workflow/attempt.d.ts +12 -0
- package/dist/workflow/attempt.d.ts.map +1 -0
- package/dist/workflow/attempt.js +96 -0
- package/dist/workflow/client.d.ts +46 -0
- package/dist/workflow/client.d.ts.map +1 -0
- package/dist/workflow/client.js +299 -0
- package/dist/workflow/context.d.ts +37 -0
- package/dist/workflow/context.d.ts.map +1 -0
- package/dist/workflow/context.js +350 -0
- package/dist/workflow/heartbeat.d.ts +15 -0
- package/dist/workflow/heartbeat.d.ts.map +1 -0
- package/dist/workflow/heartbeat.js +78 -0
- package/dist/workflow/index.d.ts +14 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +10 -0
- package/dist/workflow/memory.d.ts +17 -0
- package/dist/workflow/memory.d.ts.map +1 -0
- package/dist/workflow/memory.js +184 -0
- package/package.json +73 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TLS client configuration for HTTPS connections with mTLS support.
|
|
3
|
+
*
|
|
4
|
+
* This module provides utilities for configuring fetch requests with mTLS
|
|
5
|
+
* (mutual TLS) when connecting to platform services over HTTPS. When TLS
|
|
6
|
+
* certificate paths are available via environment variables, the client will
|
|
7
|
+
* present its certificate to the server for authentication.
|
|
8
|
+
*
|
|
9
|
+
* Uses Undici's Agent (Node.js built-in fetch is backed by Undici) for proper
|
|
10
|
+
* dispatcher configuration with client certificates.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync, statSync } from "node:fs";
|
|
13
|
+
import { context } from "@opentelemetry/api";
|
|
14
|
+
import { suppressTracing, W3CTraceContextPropagator, } from "@opentelemetry/core";
|
|
15
|
+
import { Agent as UndiciAgent } from "undici";
|
|
16
|
+
import { getLogger } from "./logger.js";
|
|
17
|
+
const logger = getLogger("agent_engine_runner_shared.tls_client");
|
|
18
|
+
const traceContextPropagator = new W3CTraceContextPropagator();
|
|
19
|
+
/** Carry the active trace across a platform RPC without recording transport. */
|
|
20
|
+
export function fetchPlatform(url, init, fetchImpl = fetch) {
|
|
21
|
+
const activeContext = context.active();
|
|
22
|
+
const headers = {
|
|
23
|
+
...(init.headers ?? {}),
|
|
24
|
+
};
|
|
25
|
+
traceContextPropagator.inject(activeContext, headers, {
|
|
26
|
+
set(carrier, key, value) {
|
|
27
|
+
carrier[key] = value;
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
return context.with(suppressTracing(activeContext), () => fetchImpl(url, { ...init, headers }));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Cached Undici agents per base URL for connection pooling.
|
|
34
|
+
* Key is the normalized base URL (scheme + host).
|
|
35
|
+
*/
|
|
36
|
+
const agentCache = new Map();
|
|
37
|
+
// Owner URLs are replica-specific and change as OE pods roll. Bound the
|
|
38
|
+
// origin cache so a long-lived runner cannot retain every historical replica.
|
|
39
|
+
export const TLS_AGENT_CACHE_MAX_ENTRIES = 32;
|
|
40
|
+
/**
|
|
41
|
+
* Explicit connect budget for long-running calls.
|
|
42
|
+
*
|
|
43
|
+
* Undici's own default (10s) already bounds the handshake, but a call that may
|
|
44
|
+
* legitimately run for ten minutes should not depend on an undefended library
|
|
45
|
+
* default to fail fast when the OE is simply unreachable.
|
|
46
|
+
*/
|
|
47
|
+
const CONNECT_TIMEOUT_MS = 10_000;
|
|
48
|
+
/**
|
|
49
|
+
* Agents for calls with an explicit deadline, keyed by `baseUrl|deadlineMs`.
|
|
50
|
+
* Separate from `agentCache` so a long-deadline agent is never handed to a
|
|
51
|
+
* caller that wanted the defaults.
|
|
52
|
+
*/
|
|
53
|
+
const longCallAgentCache = new Map();
|
|
54
|
+
/**
|
|
55
|
+
* Cached certificate file mtimes per base URL for rotation detection.
|
|
56
|
+
* Key is the normalized base URL (scheme + host).
|
|
57
|
+
* Value is [cert_mtime, key_mtime, ca_mtime] in milliseconds, or null if using PEM env vars.
|
|
58
|
+
*/
|
|
59
|
+
const agentMtimeCache = new Map();
|
|
60
|
+
/**
|
|
61
|
+
* Get certificate file modification times for rotation detection.
|
|
62
|
+
*
|
|
63
|
+
* Only container mode (TLS_*_PATH) supports rotation detection via mtime.
|
|
64
|
+
* VM mode (TLS_*_PEM) cannot detect rotation - the env var is static for
|
|
65
|
+
* the pod's lifetime, so rotation requires pod restart.
|
|
66
|
+
*
|
|
67
|
+
* @returns [cert_mtime, key_mtime, ca_mtime] in milliseconds, or null if not using file-based certs
|
|
68
|
+
*/
|
|
69
|
+
function getCertMtimes() {
|
|
70
|
+
try {
|
|
71
|
+
const certPath = process.env.TLS_CERT_PATH;
|
|
72
|
+
const keyPath = process.env.TLS_KEY_PATH;
|
|
73
|
+
const caPath = process.env.TLS_CA_CERT_PATH;
|
|
74
|
+
// Only track mtimes if using file-based certs (container mode)
|
|
75
|
+
if (!certPath || !keyPath || !caPath) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return [
|
|
79
|
+
statSync(certPath).mtimeMs,
|
|
80
|
+
statSync(keyPath).mtimeMs,
|
|
81
|
+
statSync(caPath).mtimeMs,
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Cert files don't exist or aren't readable - not using TLS
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get certificate content from environment.
|
|
91
|
+
*
|
|
92
|
+
* For container mode, reads from TLS_*_PATH file.
|
|
93
|
+
* For VM mode, reads TLS_*_PEM (raw PEM from SecretKeyRef) directly.
|
|
94
|
+
*
|
|
95
|
+
* @param certType - One of "CERT", "KEY", or "CA_CERT"
|
|
96
|
+
* @returns Certificate content as string, or empty string if not available
|
|
97
|
+
*/
|
|
98
|
+
function getCertContentFromEnv(certType) {
|
|
99
|
+
// Check for direct path first (container mode)
|
|
100
|
+
const pathEnv = `TLS_${certType}_PATH`;
|
|
101
|
+
const certPath = process.env[pathEnv];
|
|
102
|
+
if (certPath) {
|
|
103
|
+
try {
|
|
104
|
+
return readFileSync(certPath, "utf-8");
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
throw new Error(`Failed to read ${pathEnv} from ${certPath}: ${err}`, {
|
|
108
|
+
cause: err,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Check for raw PEM content (VM mode - SecretKeyRef injects decoded value)
|
|
113
|
+
// Return directly without writing to disk - Undici Agent accepts cert strings
|
|
114
|
+
const pemEnv = `TLS_${certType}_PEM`;
|
|
115
|
+
const pemContent = process.env[pemEnv];
|
|
116
|
+
if (!pemContent) {
|
|
117
|
+
return "";
|
|
118
|
+
}
|
|
119
|
+
return pemContent;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* mTLS connect options for an https:// URL, or null for http://.
|
|
123
|
+
*
|
|
124
|
+
* Fail-closed: an https URL without all three certificate sources throws rather
|
|
125
|
+
* than silently falling back to an unauthenticated connection. Shared by both
|
|
126
|
+
* agent builders so the policy and its message live in one place.
|
|
127
|
+
*/
|
|
128
|
+
function getConnectOptionsForURL(url) {
|
|
129
|
+
if (!url.startsWith("https://")) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const cert = getCertContentFromEnv("CERT");
|
|
133
|
+
const key = getCertContentFromEnv("KEY");
|
|
134
|
+
const ca = getCertContentFromEnv("CA_CERT");
|
|
135
|
+
if (!cert || !key || !ca) {
|
|
136
|
+
throw new Error(`HTTPS URL requires mTLS configuration (fail-closed policy): ` +
|
|
137
|
+
`all three env vars must be set when url uses https:// ` +
|
|
138
|
+
`(TLS_CERT_PATH or TLS_CERT_PEM, TLS_KEY_PATH or TLS_KEY_PEM, ` +
|
|
139
|
+
`TLS_CA_CERT_PATH or TLS_CA_CERT_PEM). ` +
|
|
140
|
+
`For AER→OE communication, the OE HTTPS server (port 8443) requires ` +
|
|
141
|
+
`client certificates. If TLS certificates are not available, use ` +
|
|
142
|
+
`http:// in the URL.`);
|
|
143
|
+
}
|
|
144
|
+
// Hostname verification is enabled by default (secure).
|
|
145
|
+
return { cert, key, ca, rejectUnauthorized: true };
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get fetch options configured for the given URL with mTLS support.
|
|
149
|
+
*
|
|
150
|
+
* If url uses HTTPS and TLS certificate environment variables are set
|
|
151
|
+
* (TLS_CERT_PATH, TLS_KEY_PATH, TLS_CA_CERT_PATH), returns options with
|
|
152
|
+
* an HTTPS agent configured for mTLS. For HTTP URLs or when certificates
|
|
153
|
+
* are not available, returns undefined (use default fetch behavior).
|
|
154
|
+
*
|
|
155
|
+
* The HTTPS agent is cached per base URL for connection pooling.
|
|
156
|
+
*
|
|
157
|
+
* Performance: checks the agent cache BEFORE reading certificate files to avoid
|
|
158
|
+
* blocking the event loop on filesystem I/O for every request. Certificate files
|
|
159
|
+
* are only read once per base URL when creating the agent, not on every fetch.
|
|
160
|
+
*
|
|
161
|
+
* Certificate rotation: In container mode (TLS_*_PATH), automatically detects
|
|
162
|
+
* cert-manager rotation by comparing file mtimes and invalidates the cached agent
|
|
163
|
+
* so the next request picks up renewed certificates. In VM mode (TLS_*_PEM), the
|
|
164
|
+
* env vars are static for the pod's lifetime, so rotation requires a pod restart.
|
|
165
|
+
*
|
|
166
|
+
* @param url - The target service URL (http:// or https://)
|
|
167
|
+
* @returns Fetch options with agent, or undefined for HTTP/no-TLS
|
|
168
|
+
* @throws Error if HTTPS is used but TLS configuration is incomplete
|
|
169
|
+
*/
|
|
170
|
+
export function getFetchOptionsWithTLS(url) {
|
|
171
|
+
// HTTP URLs don't need TLS configuration
|
|
172
|
+
if (!url.startsWith("https://")) {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
// Extract base URL for cache key (scheme + host)
|
|
176
|
+
const urlObj = new URL(url);
|
|
177
|
+
const baseUrl = `${urlObj.protocol}//${urlObj.host}`;
|
|
178
|
+
// Check if certs have been rotated (container mode only).
|
|
179
|
+
// Cert-manager rotates certificates by updating the Secret volume mount.
|
|
180
|
+
// Kubelet propagates the change to the pod's filesystem (up to ~60s delay).
|
|
181
|
+
// We detect rotation by comparing file mtimes and invalidate the cached agent.
|
|
182
|
+
const currentMtimes = getCertMtimes();
|
|
183
|
+
const cachedMtimes = agentMtimeCache.get(baseUrl);
|
|
184
|
+
if (currentMtimes !== null &&
|
|
185
|
+
cachedMtimes !== undefined &&
|
|
186
|
+
cachedMtimes !== null) {
|
|
187
|
+
// Both are file-based - compare mtimes for rotation
|
|
188
|
+
if (currentMtimes[0] !== cachedMtimes[0] ||
|
|
189
|
+
currentMtimes[1] !== cachedMtimes[1] ||
|
|
190
|
+
currentMtimes[2] !== cachedMtimes[2]) {
|
|
191
|
+
logger.info({
|
|
192
|
+
base_url: baseUrl,
|
|
193
|
+
old_mtimes: cachedMtimes,
|
|
194
|
+
new_mtimes: currentMtimes,
|
|
195
|
+
}, "TLS certificates rotated, invalidating cached HTTPS agent");
|
|
196
|
+
const oldAgent = agentCache.get(baseUrl);
|
|
197
|
+
if (oldAgent) {
|
|
198
|
+
oldAgent.close();
|
|
199
|
+
}
|
|
200
|
+
agentCache.delete(baseUrl);
|
|
201
|
+
agentMtimeCache.delete(baseUrl);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Check cache after rotation check
|
|
205
|
+
let agent = agentCache.get(baseUrl);
|
|
206
|
+
if (agent) {
|
|
207
|
+
// Refresh insertion order so eviction behaves as an LRU cache.
|
|
208
|
+
agentCache.delete(baseUrl);
|
|
209
|
+
agentCache.set(baseUrl, agent);
|
|
210
|
+
return { dispatcher: agent };
|
|
211
|
+
}
|
|
212
|
+
// Cache miss: read certificates (fail-closed) and create a new agent.
|
|
213
|
+
// Supports both direct paths (container mode) and PEM env content (VM mode).
|
|
214
|
+
const connect = getConnectOptionsForURL(url);
|
|
215
|
+
// Configure mTLS
|
|
216
|
+
try {
|
|
217
|
+
// Create Undici agent with client certificate.
|
|
218
|
+
// Node.js's built-in fetch is backed by Undici, so we must use an Undici
|
|
219
|
+
// Agent (not https.Agent) configured via the connect option.
|
|
220
|
+
agent = new UndiciAgent({
|
|
221
|
+
...(connect ? { connect } : undefined),
|
|
222
|
+
// Keep connections alive for connection pooling
|
|
223
|
+
keepAliveTimeout: 60_000, // 60 seconds
|
|
224
|
+
keepAliveMaxTimeout: 600_000, // 10 minutes
|
|
225
|
+
});
|
|
226
|
+
if (agentCache.size >= TLS_AGENT_CACHE_MAX_ENTRIES) {
|
|
227
|
+
const oldestBaseUrl = agentCache.keys().next().value;
|
|
228
|
+
if (oldestBaseUrl !== undefined) {
|
|
229
|
+
agentCache.get(oldestBaseUrl)?.close();
|
|
230
|
+
agentCache.delete(oldestBaseUrl);
|
|
231
|
+
agentMtimeCache.delete(oldestBaseUrl);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// Cache the agent and current mtimes for reuse.
|
|
235
|
+
agentCache.set(baseUrl, agent);
|
|
236
|
+
agentMtimeCache.set(baseUrl, currentMtimes);
|
|
237
|
+
logger.info({
|
|
238
|
+
base_url: baseUrl,
|
|
239
|
+
has_cert: !!connect?.cert,
|
|
240
|
+
has_key: !!connect?.key,
|
|
241
|
+
has_ca: !!connect?.ca,
|
|
242
|
+
cert_mtimes: currentMtimes,
|
|
243
|
+
}, "Configured HTTPS client with mTLS");
|
|
244
|
+
return { dispatcher: agent };
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
throw new Error(`Failed to configure mTLS for HTTPS connection: ${err}. ` +
|
|
248
|
+
`Check that TLS_*_PATH or TLS_*_PEM env vars are set correctly.`, { cause: err });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Cleanup function to close all cached Undici agents.
|
|
253
|
+
* Should be called during graceful shutdown.
|
|
254
|
+
*/
|
|
255
|
+
export function closeAllTLSAgents() {
|
|
256
|
+
for (const [baseUrl, agent] of agentCache.entries()) {
|
|
257
|
+
agent.close();
|
|
258
|
+
logger.debug({ base_url: baseUrl }, "Closed TLS agent");
|
|
259
|
+
}
|
|
260
|
+
agentCache.clear();
|
|
261
|
+
agentMtimeCache.clear();
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Fetch options for a call that may legitimately run for minutes.
|
|
265
|
+
*
|
|
266
|
+
* Node's fetch is backed by Undici, whose default `headersTimeout` of 300s
|
|
267
|
+
* applies *independently* of any AbortSignal the caller passes. Since the OE
|
|
268
|
+
* holds `/tool/execute` open until the tool returns, response headers do not
|
|
269
|
+
* arrive until then — so without raising it here, a longer per-call deadline is
|
|
270
|
+
* silently capped at 300s and surfaces as `UND_ERR_HEADERS_TIMEOUT` rather than
|
|
271
|
+
* as the caller's own timeout. The connect budget stays short and is set
|
|
272
|
+
* explicitly rather than inherited.
|
|
273
|
+
*
|
|
274
|
+
* Applies to http:// as well as https://, since the cap is Undici's, not TLS's.
|
|
275
|
+
*
|
|
276
|
+
* @param url - The target service URL
|
|
277
|
+
* @param deadlineMs - The call's own deadline; headers/body budgets match it
|
|
278
|
+
*/
|
|
279
|
+
export function getFetchOptionsForLongCall(url, deadlineMs) {
|
|
280
|
+
const urlObj = new URL(url);
|
|
281
|
+
const baseUrl = `${urlObj.protocol}//${urlObj.host}`;
|
|
282
|
+
const cacheKey = `${baseUrl}|${deadlineMs}`;
|
|
283
|
+
const cached = longCallAgentCache.get(cacheKey);
|
|
284
|
+
if (cached) {
|
|
285
|
+
return { dispatcher: cached };
|
|
286
|
+
}
|
|
287
|
+
const connect = getConnectOptionsForURL(url);
|
|
288
|
+
const agent = new UndiciAgent({
|
|
289
|
+
...(connect ? { connect } : undefined),
|
|
290
|
+
connectTimeout: CONNECT_TIMEOUT_MS,
|
|
291
|
+
headersTimeout: deadlineMs,
|
|
292
|
+
bodyTimeout: deadlineMs,
|
|
293
|
+
keepAliveTimeout: 60_000,
|
|
294
|
+
keepAliveMaxTimeout: 600_000,
|
|
295
|
+
});
|
|
296
|
+
longCallAgentCache.set(cacheKey, agent);
|
|
297
|
+
return { dispatcher: agent };
|
|
298
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classify external API call failures into structured, safe metadata.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors Python's `agent_engine_runner_shared/tool_api_error.py`.
|
|
5
|
+
*
|
|
6
|
+
* Native `fetch` (Node ≥ 24, backed by Undici) does NOT throw on non-2xx
|
|
7
|
+
* responses — it only rejects for transport/abort failures. So HTTP-status
|
|
8
|
+
* classification requires a thrown error that retains a numeric `status`,
|
|
9
|
+
* `statusCode`, or `response.status` property (e.g. an Axios-shaped error
|
|
10
|
+
* or a custom wrapper around a fetch Response). Transport failures
|
|
11
|
+
* (timeout, connection refused) are classified from error names and
|
|
12
|
+
* `cause.code` values that Undici/Node produces.
|
|
13
|
+
*/
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import type { ToolPodExecuteResponse } from "./models.js";
|
|
16
|
+
/**
|
|
17
|
+
* Request-local credential values that must never be surfaced. Combines the
|
|
18
|
+
* tenant environment secrets with the delegated authorization token installed
|
|
19
|
+
* for this execution; both are values a provider can echo.
|
|
20
|
+
*
|
|
21
|
+
* Reads pod-level tenant env; the ToolServer runs with secret restriction
|
|
22
|
+
* disabled (merge, no restore), so these values persist for the request. If
|
|
23
|
+
* per-request apply/restore is ever re-enabled, capture the values inside the
|
|
24
|
+
* credential window instead of reading them at classification time.
|
|
25
|
+
*/
|
|
26
|
+
export declare function requestCredentialValues(): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Credential values a name marks as secrets, at any length.
|
|
29
|
+
*
|
|
30
|
+
* A narrower view of `requestCredentialValues`: ordinary tenant env (`PATH`,
|
|
31
|
+
* `HOSTNAME`, `SHLVL`) is excluded, while a value under a credential-named
|
|
32
|
+
* variable is treated as a credential however short it is. Use this where the
|
|
33
|
+
* value is inserted into text a human reads, so that redaction cannot corrupt
|
|
34
|
+
* the message; use `requestCredentialValues` where the sink tolerates a blunt
|
|
35
|
+
* replacement. The delegated authorization token counts as credential-named.
|
|
36
|
+
*/
|
|
37
|
+
export declare function requestNamedCredentialValues(): string[];
|
|
38
|
+
export declare const ToolAPIErrorSchema: z.ZodObject<{
|
|
39
|
+
provider_type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
40
|
+
classification: z.ZodString;
|
|
41
|
+
http_status: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
42
|
+
retryable: z.ZodBoolean;
|
|
43
|
+
error_code: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
44
|
+
reason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
45
|
+
}, z.core.$strip>;
|
|
46
|
+
export type ToolAPIError = z.infer<typeof ToolAPIErrorSchema>;
|
|
47
|
+
export interface ClassifiedToolAPIError {
|
|
48
|
+
toolApiError: ToolAPIError;
|
|
49
|
+
message: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Return structured metadata and a safe message, or `undefined` when the
|
|
53
|
+
* error is not a recognized HTTP or transport failure.
|
|
54
|
+
*
|
|
55
|
+
* `credentials` are the request-local secret values applied for this call;
|
|
56
|
+
* they are matched exactly against recognized provider text so a provider
|
|
57
|
+
* echoing an unlabeled credential cannot surface it.
|
|
58
|
+
*/
|
|
59
|
+
export declare function classifyToolAPIError(error: unknown, providerType?: string, credentials?: readonly string[]): Promise<ClassifiedToolAPIError | undefined>;
|
|
60
|
+
/** Error `ToolPodExecuteResponse` for a thrown tool, with classification when recognized. */
|
|
61
|
+
export declare function executeErrorResponse(error: unknown, toolDef: unknown, podName: string, metadata: Record<string, unknown>, credentials?: readonly string[]): Promise<ToolPodExecuteResponse>;
|
|
62
|
+
//# sourceMappingURL=tool_api_error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool_api_error.d.ts","sourceRoot":"","sources":["../src/tool_api_error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAqC1D;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,EAAE,CAKlD;AAmBD;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,EAAE,CAavD;AAED,eAAO,MAAM,kBAAkB;;;;;;;iBAyB7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,YAAY,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AA+OD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,OAAO,EACd,YAAY,CAAC,EAAE,MAAM,EACrB,WAAW,GAAE,SAAS,MAAM,EAAO,GAClC,OAAO,CAAC,sBAAsB,GAAG,SAAS,CAAC,CAuC7C;AAUD,6FAA6F;AAC7F,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,WAAW,GAAE,SAAS,MAAM,EAAO,GAClC,OAAO,CAAC,sBAAsB,CAAC,CAkBjC"}
|