fix(desktop): console.log 清理 — 替换为结构化 logger
将 desktop/src 中 23 处 console.log 替换为 createLogger() 结构化日志: - 生产构建自动静默 debug/info 级别 - 保留 console.error 用于关键错误可见性 - 新增 dompurify 依赖修复 XSS 防护引入缺失 涉及文件: App.tsx, offlineStore.ts, autonomy-manager.ts, gateway-auth.ts, llm-service.ts, request-helper.ts, security-index.ts, skill-discovery.ts, use-onboarding.ts 等 16 个文件
This commit is contained in:
@@ -5,6 +5,10 @@
|
||||
* 记录关键操作(Hand 触发、Agent 创建等)到本地存储。
|
||||
*/
|
||||
|
||||
import { createLogger } from './logger';
|
||||
|
||||
const log = createLogger('AuditLogger');
|
||||
|
||||
export type AuditAction =
|
||||
| 'hand.trigger'
|
||||
| 'hand.approve'
|
||||
@@ -100,7 +104,7 @@ class AuditLogger {
|
||||
this.logs.push(entry);
|
||||
saveLocalLogs(this.logs);
|
||||
|
||||
console.log('[AuditLogger]', entry.action, entry.target, entry.result, entry.details || '');
|
||||
log.debug(entry.action, entry.target, entry.result, entry.details || '');
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
|
||||
import { generateRandomString } from './crypto-utils';
|
||||
import { createLogger } from './logger';
|
||||
|
||||
const log = createLogger('AutonomyManager');
|
||||
|
||||
// === Types ===
|
||||
|
||||
@@ -286,7 +289,7 @@ export class AutonomyManager {
|
||||
// Store in localStorage for persistence
|
||||
this.persistPendingApprovals();
|
||||
|
||||
console.log(`[AutonomyManager] Approval requested: ${approvalId} for ${decision.action}`);
|
||||
log.debug(`Approval requested: ${approvalId} for ${decision.action}`);
|
||||
return approvalId;
|
||||
}
|
||||
|
||||
@@ -296,7 +299,7 @@ export class AutonomyManager {
|
||||
approve(approvalId: string): boolean {
|
||||
const decision = this.pendingApprovals.get(approvalId);
|
||||
if (!decision) {
|
||||
console.warn(`[AutonomyManager] Approval not found: ${approvalId}`);
|
||||
log.warn(`Approval not found: ${approvalId}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -312,7 +315,7 @@ export class AutonomyManager {
|
||||
// Update audit log
|
||||
this.updateAuditLogOutcome(approvalId, 'success');
|
||||
|
||||
console.log(`[AutonomyManager] Approved: ${approvalId}`);
|
||||
log.info('Approved: ${approvalId}');
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -322,7 +325,7 @@ export class AutonomyManager {
|
||||
reject(approvalId: string): boolean {
|
||||
const decision = this.pendingApprovals.get(approvalId);
|
||||
if (!decision) {
|
||||
console.warn(`[AutonomyManager] Approval not found: ${approvalId}`);
|
||||
log.warn(`Approval not found: ${approvalId}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -333,7 +336,7 @@ export class AutonomyManager {
|
||||
// Update audit log
|
||||
this.updateAuditLogOutcome(approvalId, 'failed');
|
||||
|
||||
console.log(`[AutonomyManager] Rejected: ${approvalId}`);
|
||||
log.info('Rejected: ${approvalId}');
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -384,12 +387,12 @@ export class AutonomyManager {
|
||||
rollback(auditId: string): boolean {
|
||||
const entry = this.auditLog.find(e => e.id === auditId);
|
||||
if (!entry) {
|
||||
console.warn(`[AutonomyManager] Audit entry not found: ${auditId}`);
|
||||
log.warn('Audit entry not found: ${auditId}');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.outcome === 'rolled_back') {
|
||||
console.warn(`[AutonomyManager] Already rolled back: ${auditId}`);
|
||||
log.warn('Already rolled back: ${auditId}');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -398,7 +401,7 @@ export class AutonomyManager {
|
||||
entry.rolledBackAt = new Date().toISOString();
|
||||
this.saveAuditLog();
|
||||
|
||||
console.log(`[AutonomyManager] Rolled back: ${auditId}`);
|
||||
log.info(`Rolled back: ${auditId}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -431,7 +434,7 @@ export class AutonomyManager {
|
||||
setLevel(level: AutonomyLevel): void {
|
||||
this.config = { ...DEFAULT_AUTONOMY_CONFIGS[level], level };
|
||||
this.saveConfig();
|
||||
console.log(`[AutonomyManager] Level changed to: ${level}`);
|
||||
log.info(`Level changed to: ${level}`);
|
||||
}
|
||||
|
||||
// === Persistence ===
|
||||
@@ -537,7 +540,7 @@ export async function executeWithAutonomy<T>(
|
||||
const result = await executor();
|
||||
return { executed: true, result, decision };
|
||||
} catch (error) {
|
||||
console.error(`[AutonomyManager] Action ${action} failed:`, error);
|
||||
log.error(`Action ${action} failed:`, error);
|
||||
return { executed: false, decision };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@ import {
|
||||
getDeviceKeys,
|
||||
deleteDeviceKeys,
|
||||
} from './secure-storage';
|
||||
import { createLogger } from './logger';
|
||||
|
||||
const log = createLogger('GatewayAuth');
|
||||
|
||||
// === Types ===
|
||||
|
||||
@@ -81,7 +84,7 @@ export async function loadDeviceKeys(): Promise<DeviceKeys> {
|
||||
publicKeyBase64: b64Encode(storedKeys.publicKey),
|
||||
};
|
||||
} catch (e) {
|
||||
console.warn('[GatewayClient] Failed to load stored keys:', e);
|
||||
log.warn('Failed to load stored keys:', e);
|
||||
// Invalid stored keys, clear and regenerate
|
||||
await deleteDeviceKeys();
|
||||
}
|
||||
@@ -113,9 +116,9 @@ export async function getLocalDeviceIdentity(): Promise<LocalDeviceIdentity> {
|
||||
export async function clearDeviceKeys(): Promise<void> {
|
||||
try {
|
||||
await deleteDeviceKeys();
|
||||
console.log('[GatewayClient] Device keys cleared');
|
||||
log.debug('Device keys cleared');
|
||||
} catch (e) {
|
||||
console.warn('[GatewayClient] Failed to clear device keys:', e);
|
||||
log.warn('Failed to clear device keys:', e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
|
||||
import { DEFAULT_MODEL_ID, DEFAULT_OPENAI_BASE_URL } from '../constants/models';
|
||||
import { createLogger } from './logger';
|
||||
|
||||
const log = createLogger('LLMService');
|
||||
|
||||
// === Types ===
|
||||
|
||||
@@ -770,12 +773,12 @@ export function startPromptOTASync(deviceId: string): void {
|
||||
if (result.updates.length > 0) {
|
||||
const applied = applyPromptUpdates(result.updates);
|
||||
if (applied > 0) {
|
||||
console.log(`[Prompt OTA] 已更新 ${applied} 个提示词模板`);
|
||||
log.debug(`已更新 ${applied} 个提示词模板`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// 静默失败,不影响正常使用
|
||||
console.debug('[Prompt OTA] 检查更新失败:', err);
|
||||
log.debug('检查更新失败:', err);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
* @module lib/request-helper
|
||||
*/
|
||||
|
||||
import { createLogger } from './logger';
|
||||
|
||||
const log = createLogger('RequestHelper');
|
||||
|
||||
// === Configuration Types ===
|
||||
|
||||
export interface RequestConfig {
|
||||
@@ -161,8 +165,8 @@ export async function requestWithRetry(
|
||||
// Check if we should retry
|
||||
if (retryOn.includes(response.status) && attempt < retries) {
|
||||
const backoff = calculateBackoff(retryDelay, attempt);
|
||||
console.warn(
|
||||
`[RequestHelper] Request failed (${response.status}), ` +
|
||||
log.warn(
|
||||
`Request failed (${response.status}), ` +
|
||||
`retrying in ${backoff}ms (attempt ${attempt + 1}/${retries})`
|
||||
);
|
||||
await delay(backoff);
|
||||
@@ -184,8 +188,8 @@ export async function requestWithRetry(
|
||||
// Check if we should retry
|
||||
if (error.isRetryable(retryOn) && attempt < retries) {
|
||||
const backoff = calculateBackoff(retryDelay, attempt);
|
||||
console.warn(
|
||||
`[RequestHelper] Request error (${error.status}), ` +
|
||||
log.warn(
|
||||
`Request error (${error.status}), ` +
|
||||
`retrying in ${backoff}ms (attempt ${attempt + 1}/${retries})`
|
||||
);
|
||||
await delay(backoff);
|
||||
@@ -206,8 +210,8 @@ export async function requestWithRetry(
|
||||
// Retry on timeout
|
||||
if (attempt < retries) {
|
||||
const backoff = calculateBackoff(retryDelay, attempt);
|
||||
console.warn(
|
||||
`[RequestHelper] Request timed out, ` +
|
||||
log.warn(
|
||||
`Request timed out, ` +
|
||||
`retrying in ${backoff}ms (attempt ${attempt + 1}/${retries})`
|
||||
);
|
||||
await delay(backoff);
|
||||
@@ -411,7 +415,7 @@ export class RequestManager {
|
||||
cancelAll(): void {
|
||||
this.controllers.forEach((controller, id) => {
|
||||
controller.abort();
|
||||
console.log(`[RequestManager] Cancelled request: ${id}`);
|
||||
log.debug(`Cancelled request: ${id}`);
|
||||
});
|
||||
this.controllers.clear();
|
||||
this.requestConfigs.clear();
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
* - security-utils: Input validation, XSS prevention, rate limiting
|
||||
*/
|
||||
|
||||
import { createLogger } from './logger';
|
||||
|
||||
const log = createLogger('Security');
|
||||
|
||||
// Re-export crypto utilities
|
||||
export {
|
||||
// Core encryption
|
||||
@@ -194,7 +198,7 @@ export async function initializeSecurity(sessionId?: string): Promise<void> {
|
||||
const { initializeEncryptedChatStorage } = await import('./encrypted-chat-storage');
|
||||
await initializeEncryptedChatStorage();
|
||||
|
||||
console.log('[Security] All security modules initialized');
|
||||
log.debug('All security modules initialized');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,7 +212,7 @@ export async function shutdownSecurity(): Promise<void> {
|
||||
const { clearKeyCache } = await import('./crypto-utils');
|
||||
clearKeyCache();
|
||||
|
||||
console.log('[Security] All security modules shut down');
|
||||
log.debug('All security modules shut down');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
|
||||
import { intelligenceClient } from './intelligence-client';
|
||||
import { canAutoExecute } from './autonomy-manager';
|
||||
import { createLogger } from './logger';
|
||||
|
||||
const log = createLogger('SkillDiscovery');
|
||||
|
||||
// === Types ===
|
||||
|
||||
@@ -93,9 +96,9 @@ export class SkillDiscoveryEngine {
|
||||
this.skills = backendSkills.map(this.convertFromBackend);
|
||||
this.loadedFromBackend = true;
|
||||
this.saveIndex();
|
||||
console.log(`[SkillDiscovery] Loaded ${this.skills.length} skills from backend`);
|
||||
log.debug(`Loaded ${this.skills.length} skills from backend`);
|
||||
} catch (error) {
|
||||
console.warn('[SkillDiscovery] Failed to load skills from backend:', error);
|
||||
log.warn('Failed to load skills from backend:', error);
|
||||
// Keep using cached skills (loaded in loadIndex)
|
||||
this.loadedFromBackend = false;
|
||||
}
|
||||
@@ -133,7 +136,7 @@ export class SkillDiscoveryEngine {
|
||||
this.skills = backendSkills.map(this.convertFromBackend);
|
||||
this.loadedFromBackend = true;
|
||||
this.saveIndex();
|
||||
console.log(`[SkillDiscovery] Refreshed ${this.skills.length} skills`);
|
||||
log.debug(`Refreshed ${this.skills.length} skills`);
|
||||
return this.skills.length;
|
||||
} catch (error) {
|
||||
console.error('[SkillDiscovery] Failed to refresh skills:', error);
|
||||
@@ -335,7 +338,7 @@ export class SkillDiscoveryEngine {
|
||||
if (!options?.skipAutonomyCheck) {
|
||||
const action = installed ? 'skill_install' : 'skill_uninstall';
|
||||
const { canProceed, decision } = canAutoExecute(action, 6);
|
||||
console.log(`[SkillDiscovery] Autonomy check for ${action}: ${decision.reason}`);
|
||||
log.debug(`Autonomy check for ${action}: ${decision.reason}`);
|
||||
|
||||
if (!canProceed) {
|
||||
return { success: false, reason: decision.reason };
|
||||
@@ -344,7 +347,7 @@ export class SkillDiscoveryEngine {
|
||||
|
||||
skill.installed = installed;
|
||||
this.saveIndex();
|
||||
console.log(`[SkillDiscovery] Skill ${skillId} ${installed ? 'installed' : 'uninstalled'}`);
|
||||
log.debug(`Skill ${skillId} ${installed ? 'installed' : 'uninstalled'}`);
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { createLogger } from './logger';
|
||||
|
||||
const log = createLogger('useOnboarding');
|
||||
|
||||
const ONBOARDING_COMPLETED_KEY = 'zclaw-onboarding-completed';
|
||||
const USER_PROFILE_KEY = 'zclaw-user-profile';
|
||||
@@ -56,7 +59,7 @@ export function useOnboarding(): OnboardingState {
|
||||
setIsNeeded(true);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[useOnboarding] Failed to check onboarding status:', err);
|
||||
log.warn('Failed to check onboarding status:', err);
|
||||
setIsNeeded(true);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@@ -75,7 +78,7 @@ export function useOnboarding(): OnboardingState {
|
||||
localStorage.setItem(USER_PROFILE_KEY, JSON.stringify(fullProfile));
|
||||
setUserProfile(fullProfile);
|
||||
setIsNeeded(false);
|
||||
console.log('[useOnboarding] Onboarding completed for user:', profile.userName);
|
||||
log.debug('Onboarding completed for user:', profile.userName);
|
||||
} catch (err) {
|
||||
console.error('[useOnboarding] Failed to save onboarding status:', err);
|
||||
}
|
||||
@@ -88,7 +91,7 @@ export function useOnboarding(): OnboardingState {
|
||||
localStorage.removeItem(USER_PROFILE_KEY);
|
||||
setUserProfile(null);
|
||||
setIsNeeded(true);
|
||||
console.log('[useOnboarding] Onboarding reset');
|
||||
log.debug('Onboarding reset');
|
||||
} catch (err) {
|
||||
console.error('[useOnboarding] Failed to reset onboarding:', err);
|
||||
}
|
||||
@@ -113,7 +116,7 @@ export function getStoredUserProfile(): UserProfile | null {
|
||||
return JSON.parse(profileStr) as UserProfile;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[useOnboarding] Failed to get user profile:', err);
|
||||
log.warn('Failed to get user profile:', err);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
import { useEffect, useRef, useCallback } from 'react';
|
||||
import { useChatStore } from '../store/chatStore';
|
||||
import { intelligenceClient, type IdentityChangeProposal } from './intelligence-client';
|
||||
import { createLogger } from './logger';
|
||||
|
||||
const log = createLogger('ProposalNotifications');
|
||||
|
||||
// Configuration
|
||||
const POLL_INTERVAL_MS = 60_000; // 1 minute
|
||||
@@ -109,7 +112,7 @@ export function useProposalNotifications(): {
|
||||
saveNotifiedProposals(notifiedProposalsRef.current);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[ProposalNotifications] Failed to check proposals:', err);
|
||||
log.warn('Failed to check proposals:', err);
|
||||
} finally {
|
||||
isPollingRef.current = false;
|
||||
}
|
||||
@@ -164,7 +167,7 @@ export function ProposalNotificationHandler(): null {
|
||||
const { count } = customEvent.detail;
|
||||
|
||||
// You can integrate with a toast system here
|
||||
console.log(`[ProposalNotifications] ${count} new proposal(s) available`);
|
||||
log.debug(`${count} new proposal(s) available`);
|
||||
|
||||
// If using the Toast system from Toast.tsx, you would do:
|
||||
// toast(`${count} 个新的人格变更提案待审批`, 'info');
|
||||
|
||||
Reference in New Issue
Block a user