|
10 | 10 | import fs from "node:fs"; |
11 | 11 | import path from "node:path"; |
12 | 12 |
|
13 | | -import { normalizeWebSearchConfig, type WebSearchConfig } from "./web-search"; |
| 13 | +import { |
| 14 | + getWebSearchCredentialEnvNames, |
| 15 | + normalizePersistedWebSearchConfig, |
| 16 | + type PersistedWebSearchConfig, |
| 17 | +} from "./web-search"; |
14 | 18 |
|
15 | 19 | export const SESSION_VERSION = 1; |
16 | 20 | export const SESSION_DIR = path.join(process.env.HOME || "/tmp", ".nemoclaw"); |
@@ -55,7 +59,7 @@ export interface Session { |
55 | 59 | credentialEnv: string | null; |
56 | 60 | preferredInferenceApi: string | null; |
57 | 61 | nimContainer: string | null; |
58 | | - webSearchConfig: WebSearchConfig | null; |
| 62 | + webSearchConfig: PersistedWebSearchConfig | null; |
59 | 63 | policyPresets: string[] | null; |
60 | 64 | metadata: SessionMetadata; |
61 | 65 | steps: Record<string, StepState>; |
@@ -84,7 +88,7 @@ export interface SessionUpdates { |
84 | 88 | credentialEnv?: string; |
85 | 89 | preferredInferenceApi?: string; |
86 | 90 | nimContainer?: string; |
87 | | - webSearchConfig?: WebSearchConfig | null; |
| 91 | + webSearchConfig?: PersistedWebSearchConfig | null; |
88 | 92 | policyPresets?: string[]; |
89 | 93 | metadata?: { gatewayName?: string }; |
90 | 94 | } |
@@ -119,13 +123,30 @@ export function isObject(value: unknown): value is Record<string, unknown> { |
119 | 123 | return typeof value === "object" && value !== null && !Array.isArray(value); |
120 | 124 | } |
121 | 125 |
|
| 126 | +function escapeRegExp(value: string): string { |
| 127 | + return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); |
| 128 | +} |
| 129 | + |
| 130 | +const REDACTED_CREDENTIAL_ENV_NAMES = Array.from( |
| 131 | + new Set([ |
| 132 | + "NVIDIA_API_KEY", |
| 133 | + "OPENAI_API_KEY", |
| 134 | + "ANTHROPIC_API_KEY", |
| 135 | + "COMPATIBLE_API_KEY", |
| 136 | + "COMPATIBLE_ANTHROPIC_API_KEY", |
| 137 | + ...getWebSearchCredentialEnvNames(), |
| 138 | + ]), |
| 139 | +); |
| 140 | + |
| 141 | +const CREDENTIAL_ASSIGNMENT_RE = new RegExp( |
| 142 | + `(${REDACTED_CREDENTIAL_ENV_NAMES.map(escapeRegExp).join("|")})=\\S+`, |
| 143 | + "gi", |
| 144 | +); |
| 145 | + |
122 | 146 | export function redactSensitiveText(value: unknown): string | null { |
123 | 147 | if (typeof value !== "string") return null; |
124 | 148 | return value |
125 | | - .replace( |
126 | | - /(NVIDIA_API_KEY|OPENAI_API_KEY|ANTHROPIC_API_KEY|GEMINI_API_KEY|COMPATIBLE_API_KEY|COMPATIBLE_ANTHROPIC_API_KEY|BRAVE_API_KEY|TAVILY_API_KEY)=\S+/gi, |
127 | | - "$1=<REDACTED>", |
128 | | - ) |
| 149 | + .replace(CREDENTIAL_ASSIGNMENT_RE, "$1=<REDACTED>") |
129 | 150 | .replace(/Bearer\s+\S+/gi, "Bearer <REDACTED>") |
130 | 151 | .replace(/nvapi-[A-Za-z0-9_-]{10,}/g, "<REDACTED>") |
131 | 152 | .replace(/ghp_[A-Za-z0-9]{20,}/g, "<REDACTED>") |
@@ -192,7 +213,7 @@ export function createSession(overrides: Partial<Session> = {}): Session { |
192 | 213 | credentialEnv: overrides.credentialEnv || null, |
193 | 214 | preferredInferenceApi: overrides.preferredInferenceApi || null, |
194 | 215 | nimContainer: overrides.nimContainer || null, |
195 | | - webSearchConfig: normalizeWebSearchConfig(overrides.webSearchConfig), |
| 216 | + webSearchConfig: normalizePersistedWebSearchConfig(overrides.webSearchConfig), |
196 | 217 | policyPresets: Array.isArray(overrides.policyPresets) |
197 | 218 | ? overrides.policyPresets.filter((value) => typeof value === "string") |
198 | 219 | : null, |
@@ -223,7 +244,7 @@ export function normalizeSession(data: unknown): Session | null { |
223 | 244 | preferredInferenceApi: |
224 | 245 | typeof d.preferredInferenceApi === "string" ? d.preferredInferenceApi : null, |
225 | 246 | nimContainer: typeof d.nimContainer === "string" ? d.nimContainer : null, |
226 | | - webSearchConfig: normalizeWebSearchConfig(d.webSearchConfig), |
| 247 | + webSearchConfig: normalizePersistedWebSearchConfig(d.webSearchConfig), |
227 | 248 | policyPresets: Array.isArray(d.policyPresets) |
228 | 249 | ? (d.policyPresets as unknown[]).filter((value) => typeof value === "string") as string[] |
229 | 250 | : null, |
@@ -410,14 +431,9 @@ export function filterSafeUpdates(updates: SessionUpdates): Partial<Session> { |
410 | 431 | if (updates.webSearchConfig === null) { |
411 | 432 | safe.webSearchConfig = null; |
412 | 433 | } else { |
413 | | - const normalizedWebSearchConfig = normalizeWebSearchConfig(updates.webSearchConfig); |
| 434 | + const normalizedWebSearchConfig = normalizePersistedWebSearchConfig(updates.webSearchConfig); |
414 | 435 | if (normalizedWebSearchConfig) { |
415 | 436 | safe.webSearchConfig = normalizedWebSearchConfig; |
416 | | - } else if ( |
417 | | - isObject(updates.webSearchConfig) && |
418 | | - updates.webSearchConfig.fetchEnabled === false |
419 | | - ) { |
420 | | - safe.webSearchConfig = null; |
421 | 437 | } |
422 | 438 | } |
423 | 439 | } |
|
0 commit comments