mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 22:15:33 +00:00
feat(logging): P3 hotpath console.error migration + redaction gate (928->267)
Epic P3. Migrates hotpath console.error/warn to the structured logger
(diagnostics) or process.stderr.write (user-facing), and adds a redaction
safety gate so the migration cannot leak credentials.
Redaction gate (MR1):
- log-redaction: scrub known credential token shapes (sk-ant, sk-, xoxb,
ghp, glpat, AIza, JWT bodies, api_key=, Bearer/Basic/Token scheme) in
string values, Error.message, AND the log message string (defense-in-depth).
- logger: message now passes through maskSecretTokens.
tool-sanitization-proxy: deleted the private file-logging subsystem
(initLogFile/writeLog/log/warn, logFilePath, debugMode); 13 call sites now
route through the existing createLogger('cliproxy:tool-sanitization-proxy').
Sweep (~120 diagnostic -> structured createLogger; ~540 user-facing -> stderr):
- diagnostics converted across proxy, web-server/routes, glmt pipeline, quota
fetchers, executors, delegation, session-bridge, https-tunnel-proxy.
- user-facing CLI output (flows, arg-parser usage, installers, prompts, adapter
launch errors, error display) moved to process.stderr.write (preserves stderr).
- src/utils/error-manager.ts reclassified CLI-UX-exempt (user-facing display).
Metric: hotpath console.error 928 -> 267 (71%); createLogger files 35 -> 64.
Residual 267 is user-facing CLI output (not diagnostics); documented in
docs/hardening-debt-burndown.md. Redaction gate makes further conversion safe.
Tests: hotpath-redaction-regression (12 token shapes); updated delegation-handler,
arg-parser, model-warnings spies (console.error -> process.stderr.write).
validate + validate:ci-parity green.
This commit is contained in:
1 parent
5e1b8640ab
commit
87aeb8f193
60 files changed
+1710
-896
No files matched your search
@@ -2,6 +2,9 @@ import { DeltaAccumulator } from '../../glmt/delta-accumulator';
|
||||
import { GlmtTransformer } from '../../glmt/glmt-transformer';
|
||||
import { SSEParser } from '../../glmt/sse-parser';
|
||||
import type { OpenAIResponse, SSEEvent } from '../../glmt/pipeline';
|
||||
import { createLogger } from '../../services/logging';
|
||||
|
||||
const logger = createLogger('proxy:sse-stream-transformer');
|
||||
|
||||
const JSON_TRANSLATION_ERROR_MESSAGE = 'Failed to translate OpenAI-compatible JSON response';
|
||||
const STREAM_TRANSLATION_ERROR_MESSAGE = 'Failed to translate OpenAI-compatible SSE response';
|
||||
@@ -26,20 +29,13 @@ function createAnthropicErrorPayload(type: string, message: string): AnthropicEr
|
||||
};
|
||||
}
|
||||
|
||||
function formatErrorForLog(error: unknown): string {
|
||||
if (error instanceof Error) {
|
||||
return error.message;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.stringify(error);
|
||||
} catch {
|
||||
return String(error);
|
||||
}
|
||||
}
|
||||
|
||||
function logTranslationError(context: string, error: unknown): void {
|
||||
console.error(`[proxy-sse-transformer] ${context}: ${formatErrorForLog(error)}`);
|
||||
function logTranslationError(event: string, message: string, error: unknown): void {
|
||||
logger.error(event, message, {
|
||||
err:
|
||||
error instanceof Error
|
||||
? { name: error.name, message: error.message }
|
||||
: { message: String(error) },
|
||||
});
|
||||
}
|
||||
|
||||
export function createAnthropicErrorResponse(
|
||||
@@ -130,7 +126,11 @@ async function createAnthropicErrorProxyResponse(response: Response): Promise<Re
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logTranslationError('Failed to parse upstream error response', error);
|
||||
logTranslationError(
|
||||
'sse_transformer.upstream_error_parse_failed',
|
||||
'Failed to parse upstream error response',
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
return createAnthropicErrorResponse(response.status, type, message, headers);
|
||||
@@ -146,6 +146,7 @@ async function createAnthropicJsonResponse(response: Response): Promise<Response
|
||||
const anthropicResponse = new GlmtTransformer().transformResponse(openAIResponse);
|
||||
if (isSyntheticTransformationFallback(anthropicResponse)) {
|
||||
logTranslationError(
|
||||
'sse_transformer.json_synthetic_fallback',
|
||||
'OpenAI-compatible JSON translation produced synthetic fallback response',
|
||||
anthropicResponse
|
||||
);
|
||||
@@ -157,7 +158,11 @@ async function createAnthropicJsonResponse(response: Response): Promise<Response
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
} catch (error) {
|
||||
logTranslationError('OpenAI-compatible JSON translation failed', error);
|
||||
logTranslationError(
|
||||
'sse_transformer.json_translation_failed',
|
||||
'OpenAI-compatible JSON translation failed',
|
||||
error
|
||||
);
|
||||
return createAnthropicErrorResponse(502, 'api_error', JSON_TRANSLATION_ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
@@ -210,7 +215,11 @@ function createAnthropicStreamingResponse(response: Response): Response {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logTranslationError('OpenAI-compatible SSE translation failed', error);
|
||||
logTranslationError(
|
||||
'sse_transformer.sse_translation_failed',
|
||||
'OpenAI-compatible SSE translation failed',
|
||||
error
|
||||
);
|
||||
controller.enqueue(
|
||||
encoder.encode(
|
||||
formatSseEvent(
|
||||
|
||||
Reference in new issue
Block a user